Skip to content

Commit 4f72948

Browse files
committed
fix(scripts): extend the packaged-secret detector with the repo's other precise secret formats
1 parent c18b123 commit 4f72948

2 files changed

Lines changed: 80 additions & 6 deletions

File tree

scripts/forbidden-content.mjs

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

test/unit/forbidden-content.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,46 @@ describe("FORBIDDEN_CONTENT is the single source of truth (#6290)", () => {
8181
expect(FORBIDDEN_CONTENT.test(SECRET_SHAPED_PROBE)).toBe(true);
8282
});
8383
});
84+
85+
describe("FORBIDDEN_CONTENT covers the concrete provider-key formats (#7433)", () => {
86+
// One representative fixture per newly-added HARD_SECRET_KINDS format, each assembled from fragments so this
87+
// file never contains a contiguous credential-shaped literal (same convention as secret-patterns.test.ts).
88+
const A = (n: number) => "A".repeat(n);
89+
const a = (n: number) => "a".repeat(n);
90+
const NEW_FORMAT_PROBES: Array<[string, string]> = [
91+
["aws_access_key", "AKIA" + "IOSFODNN7EXAMPLE"],
92+
["slack_token", "xox" + "b-" + a(12)],
93+
["google_api_key", "AIza" + a(35)],
94+
["gitlab_token", "glpat-" + a(20)],
95+
["npm_token", "npm_" + a(36)],
96+
["stripe_secret_key", "sk" + "_live_" + a(24)],
97+
["sendgrid_key", "SG." + a(22) + "." + a(43)],
98+
["huggingface_token", "hf_" + a(34)],
99+
["voyage_api_key", "pa" + "-" + a(20)],
100+
["firecrawl_api_key", "fc" + "-" + a(16)],
101+
["openai_api_key", "sk-" + a(20) + "T3Blbk" + "FJ" + a(20)],
102+
["anthropic_api_key", "sk-ant-" + "api03-" + a(93) + "AA"],
103+
];
104+
105+
it.each(NEW_FORMAT_PROBES)("matches a %s-shaped value", (_name, probe) => {
106+
expect(FORBIDDEN_CONTENT.test(probe)).toBe(true);
107+
});
108+
109+
it("still matches the pre-existing shapes (private-key block, github_pat, gh*, gts, generic assignment)", () => {
110+
expect(FORBIDDEN_CONTENT.test("BEGIN RSA PRIVATE KEY")).toBe(true);
111+
expect(FORBIDDEN_CONTENT.test("github_pat_" + a(20))).toBe(true);
112+
expect(FORBIDDEN_CONTENT.test("ghp_" + a(30))).toBe(true);
113+
expect(FORBIDDEN_CONTENT.test("gts_" + "0".repeat(64))).toBe(true);
114+
expect(FORBIDDEN_CONTENT.test("MY" + "_TOKEN=" + "x")).toBe(true);
115+
});
116+
117+
it("does NOT hard-block the deliberately-excluded weak heuristics (jwt / seed / bittensor key shapes)", () => {
118+
// These are intentionally kept out of the packaged-secret hard block (#7433) — an ordinary Bittensor
119+
// coldkey/hotkey mention or a mnemonic word is not a leaked credential; a bare JWT is out of scope.
120+
expect(FORBIDDEN_CONTENT.test("coldkey: my-wallet-name")).toBe(false);
121+
expect(FORBIDDEN_CONTENT.test("the recovery mnemonic is stored offline")).toBe(false);
122+
// A bare header-dot-payload JWT shape is not matched by the hard-block detector.
123+
expect(FORBIDDEN_CONTENT.test("eyJ" + A(20) + "." + a(20) + "." + a(20))).toBe(false);
124+
expect(FORBIDDEN_CONTENT.global).toBe(false);
125+
});
126+
});

0 commit comments

Comments
 (0)