Skip to content

feat(cli): add a local secrets scan-file command - #166

Draft
tim-thacker-nullify wants to merge 1 commit into
mainfrom
fix/fde-1721-local-secrets-scan-file
Draft

tim-thacker-nullify wants to merge 1 commit into
mainfrom
fix/fde-1721-local-secrets-scan-file

Conversation

@tim-thacker-nullify

@tim-thacker-nullify tim-thacker-nullify commented Sep 21, 2026 •

Copy link
Copy Markdown
Member

Claude

ide/nullify-vscode's on-save scanner shells out to nullify secrets scan-file on every save, and this public CLI had no such command. Fixes FDE-1721.

  • That subcommand only ever existed in the internal monorepo CLI; the public CLI install.sh installs had no local scanning at all, so the extension's only feature failed out of the box.
  • internal/scanfile/: new package, a from-scratch regex+entropy secret detector. Zero new third-party dependencies — stdlib regexp/crypto/sha256/math only.
  • internal/scanfile/nullify_rules.go: Nullify's own custom rules, ported verbatim (same IDs/regexes/keywords/entropy floors) from secrets/internal/scanners/gitleaks/nullify-config.go in the monorepo. That function is plain data with no engine dependency, so it crosses into this repo cleanly.
  • internal/scanfile/baseline_rules.go: 18 hand-written rules for common public formats (AWS keys, GitHub/GitLab PATs, Slack, Stripe, PEM blocks, a generic high-entropy heuristic) not already covered by the Nullify set.
  • internal/scanfile/rules.go: the same entropy-threshold and test/placeholder filtering the internal engine uses, ported verbatim.
  • internal/commands/scanfile.go + cmd/cli/cmd/secrets.go: hand-written nullify secrets scan-file [path] [--stdin] [--path] [--repository], matching the internal command's flags. Registered at the top level, not under api secrets (generated, API-backed, stays untouched), since scan-file never calls the platform API.

Engine decision: did not port secrets/internal/scanners/gitleaks.

  • It wraps github.com/zricethezav/gitleaks/v8 (MIT, clean license by itself).
  • Its detect/sources/report packages are not a small addition.
  • sources unconditionally pulls in mholt/archives (7z/rar/brotli/lzip/xz support, a dozen further modules).
  • report/detect pull in charmbracelet/lipgloss and Masterminds/sprig, used only for gitleaks' own CLI/report rendering.
  • tetratelabs/wazero/wasilibs/go-re2 (a WASM regex runtime) also enter the module graph, gated behind an opt-in build tag so not linked into the default binary.
  • Traced the real closure against this monorepo's already-resolved go.sum: on the order of 60 new transitive modules, none exercised by a single-file/stdin scan.
  • This task runs with local Go execution disabled, so there was no way to go build a 60-module go.mod/go.sum change to verify it. Hand-editing that blind is the "partial/broken port" this kind of task should not force — built the minimal detector above instead.

Licensing/packaging — flagging for a human to double-check even though I believe it's clean:

  • No new dependency added, so nothing new enters this OSS binary's third-party license set.
  • Nullify's own custom rules (nullify_rules.go) move from a private monorepo into this public repo's source. Not a licensing issue, but a visibility change worth a second look — rule names/regexes become public; no secret values or customer data are involved.

Output contract, checked against ide/nullify-vscode/src/extension.ts's scan():

  • NormalizedFinding matches secrets/internal/scanfile.NormalizedFinding field-for-field (detectorClass, severity, file, startLine/endLine/startColumn/endColumn, cwe, message, fingerprint), so parseFindings/applyFindings need no changes.
  • Args, stdin piping, JSON-array-on-stdout, errors-on-stderr, and the exit-code gate all match.

Known gap, disclosed rather than hidden: the fingerprint hash scheme matches the platform's dedup key, but the secret-type values come from this package's own rule IDs, not the platform's internal rules.csv classification — correlation with pre-existing platform findings for the same secret isn't guaranteed. This rule set also does not claim parity with gitleaks' several-hundred-rule default set.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

The IDE extension's on-save scanner defaults to `nullify secrets
scan-file`, which only ever existed in the internal monorepo CLI. The
public, OSS-distributed CLI that install.sh actually ships has no local
scanning at all, so the extension's only feature was broken out of the
box (FDE-1721).

Adds `nullify secrets scan-file` as a hand-written command alongside the
generated ones, backed by a new internal/scanfile package: a from-scratch,
stdlib-only regex+entropy detector (zero new dependencies), porting
Nullify's own custom rule set verbatim plus a curated baseline of common
public secret formats. See internal/scanfile's package doc for why this
doesn't vendor the internal engine's github.com/zricethezav/gitleaks/v8
dependency directly, and the PR description for the full trade-off.

Output matches secrets/internal/scanfile.NormalizedFinding field-for-field
so the extension's existing parsing needs no changes.

Test fixtures build AWS/Anthropic-key-shaped strings via concatenation
rather than as one literal, so this repo's own commit doesn't trip a
secret scanner on its own detector's test data.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@tim-thacker-nullify tim-thacker-nullify added the minor Minor version updates (features) label Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minor Minor version updates (features)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant