Skip to content

fix(EXPOSED_SECRETS): redact detected secrets in finding evidence#44

Open
dmchaledev wants to merge 1 commit into
mainfrom
claude/amazing-franklin-97aue0
Open

fix(EXPOSED_SECRETS): redact detected secrets in finding evidence#44
dmchaledev wants to merge 1 commit into
mainfrom
claude/amazing-franklin-97aue0

Conversation

@dmchaledev

Copy link
Copy Markdown
Contributor

Summary

The EXPOSED_SECRETS rule — the check that exists to keep credentials out of plaintext config — echoes each detected secret verbatim in its own report. src/rules/runtime-rules.ts pushed the raw matched string into evidence, truncated only past 60 chars:

matched.push(s.length > 60 ? s.slice(0, 57) + '...' : s);
// ...
evidence: `Matched value(s): ${matched.slice(0, 3).join('; ')}`,

That evidence field flows into the scan report's default JSON/stdout output, and into any CI log or stored artifact derived from it. So a tool sold as a CI/CD security gate re-exposes the very secrets it flags — most real credentials (AWS AKIA… = 20 chars, GitHub ghp_… = 40, OpenAI sk-…) are ≤ 60 chars and printed in full.

Repro (built CLI, before this change)

$ node dist/cli.js leak-test.json --format=json | jq -r '.findings[]|select(.ruleId=="EXPOSED_SECRETS").evidence'
Matched value(s): sk-abcdefghijklmnop1234567890; AKIAIOSFODNN7EXAMPLE; ghp_abcdefghijklmnopqrstuvwxyz0123456789

Fix

Report each match by type and length only, never the value. Secret patterns are now named ({ name, pattern }) to drive a human-readable type label:

$ node dist/cli.js leak-test.json --format=json | jq -r '.findings[]|select(.ruleId=="EXPOSED_SECRETS").evidence'
Detected 3 secret(s) — values redacted: OpenAI API key (29 chars, redacted); AWS access key ID (20 chars, redacted); GitHub token (40 chars, redacted)

The finding still tells the user exactly what and how many secrets were found — enough to locate them in their own config — without reproducing the credential.

Tests

Adds an EXPOSED_SECRETS suite (the rule previously had zero coverage):

  • fires on a recognizable secret;
  • the serialized finding contains none of the raw credentials (the key regression guard), while evidence still names the type/count/length;
  • does not fire on benign strings or when rawStrings is absent.

All 122 tests pass; npm run build and npm run lint are clean.

Scope

Localized to the EXPOSED_SECRETS rule body + pattern list, plus tests and a CHANGELOG note. No rule IDs, severities, or public types change. Independent of the current open backlog (#11/#19/#26/#27/#39) — none touch secret-evidence redaction.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HVdyf7Ehsa8z1kBq7LX5NA


Generated by Claude Code

The secret-scanning rule printed each matched credential verbatim in the
finding's `evidence` field (truncated only past 60 chars). That evidence
flows into the scan report's stdout/JSON output and any artifact or CI log
derived from it, so the scanner re-exposed the very secrets it exists to
flag — an ironic and real leak for a tool sold as a CI/CD security gate.

Report matches by type and length only (e.g. "OpenAI API key (29 chars,
redacted)"), never the value. Secret patterns are now named to drive the
type label. Adds EXPOSED_SECRETS tests (previously uncovered) asserting the
rule fires and that no raw credential appears anywhere in the finding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HVdyf7Ehsa8z1kBq7LX5NA
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants