Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 37 additions & 6 deletions scripts/forbidden-content.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,39 @@
// Single source of truth for the miner package's secret-shape detector.
//
// scripts/check-miner-package.mjs uses this to reject any packed miner file that embeds a secret-like value, and
// the AMS MCP contract test (test/unit/miner-mcp-contract.test.ts) reuses the SAME pattern to assert no MCP tool
// response ever leaks one — importing it here rather than hand-duplicating the regex keeps the two byte-for-byte in
// sync instead of relying on manual vigilance.
export const FORBIDDEN_CONTENT =
/(BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY|github_pat_[A-Za-z0-9_]+|gh[pousr]_[A-Za-z0-9_]+|gts_[0-9a-f]{64}|[A-Z0-9_]*(TOKEN|SECRET|PRIVATE_KEY)=)/;
// scripts/check-miner-package.mjs + scripts/check-mcp-package.mjs use this to reject any packed miner/mcp file
// that embeds a secret-like value, and the AMS MCP contract test (test/unit/miner-mcp-contract.test.ts) reuses
// the SAME pattern to assert no MCP tool response ever leaks one — importing it here rather than hand-duplicating
// the regex keeps every consumer byte-for-byte in sync instead of relying on manual vigilance.
//
// The concrete provider-key shapes below are hand-copied (#7433) from the exact regex bodies of the entries in
// src/review/secret-patterns.ts's SECRET_PATTERNS that are in its HARD_SECRET_KINDS set (the "near-zero
// false-positive" subset). They are NOT imported directly: this file is a plain `.mjs` run via `node`
// (test:miner-pack / test:mcp-pack), and secret-patterns.ts is TypeScript with no runtime `.js` sibling on this
// path — a runtime `import` of it from node would fail, so the exact bodies are copied per the issue's stated
// fallback. `jwt`, `seed_or_mnemonic`, and `bittensor_key` are deliberately NOT included: jwt is out of scope
// for #7433, and seed_or_mnemonic/bittensor_key are documented in secret-patterns.ts as weak, false-positive-
// prone heuristics intentionally excluded from HARD_SECRET_KINDS (an ordinary `coldkey:` / `hotkey =` line or
// the word "mnemonic" in Bittensor docs is not a leaked credential).
export const FORBIDDEN_CONTENT = new RegExp(
[
// Already covered before #7433 (unchanged shapes):
"BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY",
"github_pat_[A-Za-z0-9_]+",
"gh[pousr]_[A-Za-z0-9_]+",
"gts_[0-9a-f]{64}",
"[A-Z0-9_]*(TOKEN|SECRET|PRIVATE_KEY)=",
// Added #7433 — exact bodies from secret-patterns.ts HARD_SECRET_KINDS entries:
"\\bAKIA[0-9A-Z]{16}\\b", // aws_access_key
"\\bxox[baprs]-[A-Za-z0-9-]{10,}\\b", // slack_token
"\\bAIza[0-9A-Za-z_-]{35}\\b", // google_api_key
"\\bglpat-[0-9A-Za-z_-]{20}(?![0-9A-Za-z_-])", // gitlab_token
"\\bnpm_[A-Za-z0-9]{36}\\b", // npm_token
"\\b(?:sk|rk)_live_[0-9A-Za-z]{24,}\\b", // stripe_secret_key
"\\bSG\\.[A-Za-z0-9_-]{22}\\.[A-Za-z0-9_-]{43}(?![A-Za-z0-9_-])", // sendgrid_key
"\\bhf_[A-Za-z0-9]{34}\\b", // huggingface_token
"\\b(?:pa|al)-[A-Za-z0-9]{20,}(?![A-Za-z0-9_-])", // voyage_api_key
"\\bfc-[A-Za-z0-9]{16,}(?![A-Za-z0-9_-])", // firecrawl_api_key
"\\bsk-(?:proj-|svcacct-|admin-)?[A-Za-z0-9_-]{20,}T3BlbkFJ[A-Za-z0-9_-]{20,}\\b", // openai_api_key
"\\bsk-ant-api03-[A-Za-z0-9_-]{93}AA\\b", // anthropic_api_key
].join("|"),
);
43 changes: 43 additions & 0 deletions test/unit/forbidden-content.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,46 @@ describe("FORBIDDEN_CONTENT is the single source of truth (#6290)", () => {
expect(FORBIDDEN_CONTENT.test(SECRET_SHAPED_PROBE)).toBe(true);
});
});

describe("FORBIDDEN_CONTENT covers the concrete provider-key formats (#7433)", () => {
// One representative fixture per newly-added HARD_SECRET_KINDS format, each assembled from fragments so this
// file never contains a contiguous credential-shaped literal (same convention as secret-patterns.test.ts).
const A = (n: number) => "A".repeat(n);
const a = (n: number) => "a".repeat(n);
const NEW_FORMAT_PROBES: Array<[string, string]> = [
["aws_access_key", "AKIA" + "IOSFODNN7EXAMPLE"],
["slack_token", "xox" + "b-" + a(12)],
["google_api_key", "AIza" + a(35)],
["gitlab_token", "glpat-" + a(20)],
["npm_token", "npm_" + a(36)],
["stripe_secret_key", "sk" + "_live_" + a(24)],
["sendgrid_key", "SG." + a(22) + "." + a(43)],
["huggingface_token", "hf_" + a(34)],
["voyage_api_key", "pa" + "-" + a(20)],
["firecrawl_api_key", "fc" + "-" + a(16)],
["openai_api_key", "sk-" + a(20) + "T3Blbk" + "FJ" + a(20)],
["anthropic_api_key", "sk-ant-" + "api03-" + a(93) + "AA"],
];

it.each(NEW_FORMAT_PROBES)("matches a %s-shaped value", (_name, probe) => {
expect(FORBIDDEN_CONTENT.test(probe)).toBe(true);
});

it("still matches the pre-existing shapes (private-key block, github_pat, gh*, gts, generic assignment)", () => {
expect(FORBIDDEN_CONTENT.test("BEGIN RSA PRIVATE KEY")).toBe(true);
expect(FORBIDDEN_CONTENT.test("github_pat_" + a(20))).toBe(true);
expect(FORBIDDEN_CONTENT.test("ghp_" + a(30))).toBe(true);
expect(FORBIDDEN_CONTENT.test("gts_" + "0".repeat(64))).toBe(true);
expect(FORBIDDEN_CONTENT.test("MY" + "_TOKEN=" + "x")).toBe(true);
});

it("does NOT hard-block the deliberately-excluded weak heuristics (jwt / seed / bittensor key shapes)", () => {
// These are intentionally kept out of the packaged-secret hard block (#7433) — an ordinary Bittensor
// coldkey/hotkey mention or a mnemonic word is not a leaked credential; a bare JWT is out of scope.
expect(FORBIDDEN_CONTENT.test("coldkey: my-wallet-name")).toBe(false);
expect(FORBIDDEN_CONTENT.test("the recovery mnemonic is stored offline")).toBe(false);
// A bare header-dot-payload JWT shape is not matched by the hard-block detector.
expect(FORBIDDEN_CONTENT.test("eyJ" + A(20) + "." + a(20) + "." + a(20))).toBe(false);
expect(FORBIDDEN_CONTENT.global).toBe(false);
});
});