Skip to content

fix(EXPOSED_SECRETS): redact matched secrets in finding evidence#28

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

fix(EXPOSED_SECRETS): redact matched secrets in finding evidence#28
dmchaledev wants to merge 1 commit into
mainfrom
claude/amazing-franklin-o9ldg8

Conversation

@dmchaledev

Copy link
Copy Markdown
Contributor

Summary

The flagship EXPOSED_SECRETS rule re-exposes the very secret it detects by echoing the full matched value into the finding's evidence field. That field is serialized into the JSON report (--format=json), which is exactly the output users pipe into CI logs, store as build artifacts, and feed to other tooling. For a scanner whose remediation is "rotate this credential," reproducing the raw secret in its own report — a surface that is typically more visible and longer-lived than the source config — is a self-inflicted leak.

Repro (built CLI at current main)

// secret.json
{ "serverUrl": "https://x.example.com",
  "openaiKey": "sk-abcdefghijklmnopqrstuvwxyz1234567890ABCD",
  "rateLimit": { "enabled": true } }
$ node dist/cli.js secret.json --format=json | grep evidence
"evidence": "Matched value(s): sk-abcdefghijklmnopqrstuvwxyz1234567890ABCD"
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ full key, verbatim

The current code (src/rules/runtime-rules.ts) only truncates at 60 chars (s.slice(0, 57) + '...'), which still reproduces the entire key for every realistic secret shape, and short passwords in full.

Fix

Evidence now surfaces the detected secret type, a short structural prefix (sk-, ghp_, AKIA, or password — a known indicator, not entropy), and the value's length — enough to locate the offending value without reproducing it:

$ node dist/cli.js secret.json --format=json | grep evidence
"evidence": "Detected 1 likely secret(s): OpenAI API key (sk-a…[redacted, 43 chars])"
  • SECRET_PATTERNS gains a human-readable label per pattern.
  • New maskSecret() helper reveals only the leading ≤4 chars + length.
  • Regression tests assert the raw secret never appears in evidence, that a non-sensitive prefix + length + type are still surfaced, and that clean configs don't fire.

The change is localized to the EXPOSED_SECRETS rule; detection logic (which strings match) is unchanged.

Verification

  • npm run typecheck ✓ · npm run lint (0 warnings) ✓ · npm test102 passed, 4 suites ✓ · npm run build
  • grep for the raw key in --format=json output: 0 matches (was the full key).

Why this isn't a duplicate

None of the open issues (#27 URL scanning, #26 weak token, #19 YAML, #11 --rule) or open PRs touch secret redaction — they all concern detection/coverage. This is about not leaking what's already detected.

Acceptance criteria

  • The raw matched secret never appears in evidence (or any report field).
  • Evidence still identifies the secret type and is enough to locate the value.
  • No change to which values are flagged (detection unchanged).
  • Tests covering redaction and the clean-config no-op.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FkLc6snp9a3yaJEYF5D6ni


Generated by Claude Code

The EXPOSED_SECRETS rule echoed the full matched secret verbatim into a
finding's `evidence` field (e.g. "Matched value(s): sk-...full key..."),
which is serialized into the JSON report. Scan reports are routinely
written to CI logs, SARIF artifacts, and GitHub Code Scanning — surfaces
that are typically more visible and longer-lived than the config file
itself — so the scanner was re-exposing the very credential it asks the
user to rotate.

Evidence now surfaces the detected secret type, a short structural prefix
(e.g. `sk-`, `ghp_`, `AKIA`, `password`), and the value's length — enough
to locate the offending value without reproducing it. Adds regression
tests asserting the raw secret never appears in evidence.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FkLc6snp9a3yaJEYF5D6ni
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