mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-10 06:58:59 +02:00
feat(redact): six new credential patterns — GitLab, HuggingFace, npm, DigitalOcean, Bearer, GCP SA (#1946)
Coverage gaps from the #1946 security review, including token types for tooling gstack itself drives (glab): HIGH (block): gitlab.token (glpat-/glptt-/gldt-), huggingface.token (hf_), npm.token (npm_), digitalocean.token (dop_v1_), gcp.service_account (the JSON-escaped "private_key" form that dodges pem.private_key's literal-block match when minified, confirmed by "private_key_id" proximity). MEDIUM (warn): auth.bearer — the most FP-prone shape in the set (docs are full of "Authorization: Bearer <token>"), so it requires header-context proximity and the same entropy>=3.0 + placeholder validator recipe as env.kv. "Bearer YOUR_TOKEN_HERE" never fires; calibration over coverage, per the cries-wolf principle. All shapes are linear-time; test/redact-pattern-lint.test.ts covers them automatically. Engine tests add positive + placeholder-negative cases per pattern. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
6bc00abb0f
commit
028cb52ca8
@@ -42,6 +42,17 @@ describe("HIGH credential patterns", () => {
|
||||
["slack.webhook", "https://hooks.slack.com/services/T00000000/B11111111/" + "a".repeat(24)],
|
||||
["discord.webhook", "https://discord.com/api/webhooks/123456789012345678/" + "a".repeat(60)],
|
||||
["pem.private_key", "-----BEGIN RSA PRIVATE KEY-----"],
|
||||
// #1946 coverage-gap additions
|
||||
["gitlab.token", "remote: glpat-" + "Ab12Cd34Ef56Gh78Ij90"],
|
||||
["gitlab.token", "trigger glptt-" + "a1b2c3d4e5f6a7b8c9d0e1f2"],
|
||||
["gitlab.token", "deploy gldt-" + "Zy98Xw76Vu54Ts32Rq10"],
|
||||
["huggingface.token", "hf_" + "AbCdEfGhIjKlMnOpQrStUvWxYz012345"],
|
||||
["npm.token", "npm_" + "a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8"],
|
||||
["digitalocean.token", "dop_v1_" + "0123456789abcdef".repeat(4)],
|
||||
[
|
||||
"gcp.service_account",
|
||||
'{"private_key_id": "abc123", "private_key": "-----BEGIN PRIVATE KEY-----\\nMIIE..."}',
|
||||
],
|
||||
];
|
||||
for (const [id, text] of cases) {
|
||||
test(`flags ${id}`, () => {
|
||||
@@ -121,6 +132,38 @@ describe("MEDIUM demoted credential-shaped patterns (TENSION-1)", () => {
|
||||
expect(ids("API_KEY=changeme")).not.toContain("env.kv");
|
||||
expect(ids("API_KEY=${MY_VAR}")).not.toContain("env.kv");
|
||||
});
|
||||
|
||||
// #1946 — Bearer is the most FP-prone shape in the wave: docs and examples
|
||||
// are full of "Authorization: Bearer <token>". MEDIUM + header proximity +
|
||||
// the env.kv entropy recipe keep it calibrated.
|
||||
test("auth.bearer fires on a high-entropy token in header context", () => {
|
||||
const text = "curl -H 'Authorization: Bearer 8Fk2pQ9vXz4wL7mN3rT6yB1cD5eG0hJq'";
|
||||
const f = scan(text, { repoVisibility: "private" }).findings.find(
|
||||
(x) => x.id === "auth.bearer",
|
||||
);
|
||||
expect(f).toBeDefined();
|
||||
expect(f?.tier).toBe("MEDIUM");
|
||||
});
|
||||
test("auth.bearer skips placeholders and env interpolations", () => {
|
||||
expect(ids("Authorization: Bearer YOUR_TOKEN_HERE_PLACEHOLDER")).not.toContain("auth.bearer");
|
||||
expect(ids("Authorization: Bearer ${ACCESS_TOKEN_FROM_ENV}")).not.toContain("auth.bearer");
|
||||
});
|
||||
test("auth.bearer requires header context (bare 'Bearer x' prose doesn't fire)", () => {
|
||||
expect(ids("the Bearer 8Fk2pQ9vXz4wL7mN3rT6yB1cD5eG0hJq walked in")).not.toContain(
|
||||
"auth.bearer",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#1946 pattern negatives (placeholders never fire)", () => {
|
||||
test("short or placeholder shapes don't trip the new HIGH patterns", () => {
|
||||
expect(ids("glpat-xxxx")).not.toContain("gitlab.token");
|
||||
expect(ids("hf_token")).not.toContain("huggingface.token");
|
||||
expect(ids("npm_install")).not.toContain("npm.token");
|
||||
expect(ids("dop_v1_short")).not.toContain("digitalocean.token");
|
||||
// pem header WITHOUT the GCP JSON shape stays pem.private_key only.
|
||||
expect(ids("-----BEGIN PRIVATE KEY-----")).not.toContain("gcp.service_account");
|
||||
});
|
||||
});
|
||||
|
||||
describe("PII patterns", () => {
|
||||
|
||||
Reference in New Issue
Block a user