Skip to content

fix(config): the built-in precommit profile no longer overrides what a config set - #796

Merged
rustic merged 3 commits into
mainfrom
fix/768-precommit-profile-override
Oct 22, 2026
Merged

rustic merged 3 commits into
mainfrom
fix/768-precommit-profile-override

Conversation

@sectoramen

Copy link
Copy Markdown
Contributor

Closes #768. Independent of #771 and #766 (no file in common except CHANGELOG.md, where each stream appends one bullet to ### 🐛 Bug Fixes, and docs/configuration.md, where this PR stays out of the --validator-budget paragraph #771 edits). Builds on #791: the strategy source strings and applyRedactionStrategy are reused unchanged.

Summary

Under --pre-commit-mode, PRE_COMMIT / _PRE_COMMIT_RUNNING / PRE_COMMIT_HOOK / GIT_HOOK_TYPE, or the published hook's --profile precommit, the built-in precommit profile's checks, confidence_levels, redaction.strategy and redaction.output_dir replaced what the config wrote. On main@46e2918: a config naming PASSPORT got the profile's six types, so a HIGH passport passed the hook at rc 0 with 0 bytes of output, and --stdin with strategy: simple returned SSN: ***-**-0001 / Passport number: X00000001. All 6 routes, both input paths and all 5 config sources reproduced (60 of 60 cells); only a config's own profiles.precommit escaped, and profiles: null and {} disagreed. Separately, every profile that omitted fail_on_incomplete, the built-in one included, turned a config's fail_on_incomplete: true off (rc 0 instead of 3 with an unreadable file), and --profile other switched off suppressions.show_suppressed / suppressions.generate_on_scan. After this PR the built-in profile yields to the config, every profile bool inherits it, and the one override left (defaults.checks: all still runs the six) is disclosed on one stderr line.

What changed (the issue's Resolution 1–4)

1. The built-in profile yields (internal/config/config.go)

  • injectBuiltinPrecommitProfile replaces the pre-decode injection. It runs after yaml.Unmarshal, only when the file has no profiles.precommit (missing, profiles: {} and a null profiles: now agree; null used to wipe it), and before backfillProfileBools, so the profile's bools come from the file exactly as for any profile that omits them.
  • Reconciled with containsField: redaction.strategy and redaction.output_dir are no longer set (they equalled the global defaults); confidence_levels: high,medium only when the file lacks defaults.confidence_levels; checks = the six union defaults.checks (upper-cased, deduplicated, unknown names kept so they are refused downstream as without a profile; all, any case, or empty adds nothing); format stays text (exempt). With no config file the profile is the one it always was.

2. Disclose the remaining override (cmd/active_profile.go, call sites in cmd/main.go, cmd/stdin.go)

  • reportBuiltinProfileChecks: when the built-in profile is active and the loaded file wrote defaults.checks as every check, one stderr line: Note: the built-in precommit profile runs 6 checks (CREDIT_CARD, SECRETS, SSN, EMAIL, PHONE, IP_ADDRESS), not the "all" that defaults.checks sets in <config>; set profiles.precommit.checks in that file to choose what pre-commit runs. Ungated like reportDisabledDetectionTypes/reportConfigProvenance (printed beside the provenance note on both paths); withheld only where --stdin stderr is the findings document; silent without a config file, without defaults.checks, under a profile the file defines, and when --checks chose the checks.
  • --debug record on both input paths (profileDebugRecord): the active profile, built-in or defined in <config>, why it was selected (--profile, --pre-commit-mode, or a detected hook environment), and what it supplied (format=text; confidence_levels=high,medium; checks=... for the built-in profile; the keys its YAML block sets for a file-defined one, via Config.ProfileKeys).

3. Every profile bool is registered (profileBoolFields)

  • fail_on_incomplete added. show_suppressed / generate_suppressions backfill from defaults.X || suppressions.Y, the same OR resolveConfiguration applies with no profile. Each entry gained a getProfile accessor (used by the debug record; pinned against the reflected field).

4. One definition, one listing

  • Config.GetPrecommitProfile (uncalled duplicate) is deleted; config.BuiltinPrecommitChecks is the one exported 6-type constant. Config.BuiltinProfile(name) reports whether a profile is built-in, what it supplied and whether the file's defaults.checks was all.
  • --list-profiles (writeProfileList) reads the loaded config, auto-discovered ones included (it tested the --config flag and printed No configuration file found for a discovered file), and marks precommit (built-in).

Docs (Docs & CHANGELOG list): docs/PRE_COMMIT_INTEGRATION.md gains "The precommit profile" (contents, how it yields, no respect_gitignore, --list-profiles/--debug) and the respect_gitignore claim at the old :785 is corrected; docs/configuration.md gains the settings precedence and "The built-in precommit profile", says profiles inherit fail_on_incomplete, and marks the precommit entry of "Pre-Configured Profiles" as examples/ferret.yaml's own profile; docs/INSTALLATION.md:325 no longer presents a user profile as the built-in one (and drops batch_size: 50, a key profiles do not have; main warns unknown config key "batch_size"); config.yaml:5-7 no longer says the redaction flags are CLI-only; examples/ferret.yaml notes that its precommit replaces the built-in profile. cmd/discovery_scope_test.go: comment only (format is the one value the built-in profile keeps). CHANGELOG: one bullet at the end of ### 🐛 Bug Fixes with the behaviour-change note.

Deviations from the issue, and why

  • Binary matrix lives in cmd/, not tests/integration, beside the other CLI contract tests and on their helpers (buildScanner, isolatedEnv, runCLI, precommitTriggers); the file-ownership split for the parallel streams put new contract tests there.
  • Disclosure fires for an empty defaults.checks too, since resolveConfiguration and the --checks parser both read "" as every check. all matching is case-insensitive, as the parser's is.
  • --debug also names a file-defined profile (and an undefined --profile on --stdin, which that path silently ignores today) rather than only the built-in one; the issue's "own-precommit runs byte-identical outside --debug" implies the record covers them.
  • --list-profiles without any config file lists the built-in profile (No configuration file found. Built-in profiles:) instead of No profiles available: --profile precommit works there, so the old line was wrong. Header text with a file: Available profiles in <config>:.
  • config.yaml:5-7: also corrects --suppression-file and --disable-ip-types, listed as CLI-only in the same three lines although suppressions.file and validators.intellectual_property.disabled_types are honoured.

Measured: main@46e2918 vs this branch

Every run: env -i, empty HOME / FERRET_CONFIG_DIR / XDG_CONFIG_HOME, empty --suppression-file, timeout 60, the same script against both binaries (the issue's matrix plus its "other fields" and second-member blocks, and a byte-identity loop).

Matrix A (pp.txt = one passport, pp.yaml = defaults.checks: PASSPORT,SSN, --format json):

row main branch
normal rc 0 [PASSPORT:HIGH] rc 0 [PASSPORT:HIGH]
--pre-commit-mode rc 0 [] rc 1 [PASSPORT:HIGH]
PRE_COMMIT=1, _PRE_COMMIT_RUNNING=1, PRE_COMMIT_HOOK=1, GIT_HOOK_TYPE=1 rc 0 [] (each) rc 1 [PASSPORT:HIGH] (each)
--profile precommit (no pre-commit mode) rc 0 [] rc 0 [PASSPORT:HIGH]
published hook args (--pre-commit-mode --profile precommit --respect-gitignore) rc 0 [] rc 1 [PASSPORT:HIGH]
same, text output (the issue's third run) rc 0, 0 B / 0 B rc 1, pp.txt: 1 high confidence issues found / Passport number detected
PRE_COMMIT=1 FERRET_PRECOMMIT=0 rc 0 [PASSPORT:HIGH] same
--pre-commit-mode --checks PASSPORT rc 1 [PASSPORT:HIGH] same
own.yaml (own precommit) rc 1 [PASSPORT:HIGH] same
other.yaml (profiles: {ci: …}), empty.yaml ({}) rc 0 [] rc 1 [PASSPORT:HIGH]
null.yaml (profiles:) rc 1 [PASSPORT:HIGH] same (null and {} now agree)
no config, --pre-commit-mode rc 0 [], 0 B stderr same

Matrix B/C (in.txt = SSN + passport, red.yaml = enabled: true, strategy: simple, checks: PASSPORT,SSN), 5 sources (--config, ./config.yaml, ./ferret.yaml, ./.ferret-scan.yaml, $FERRET_CONFIG_DIR/config.yaml outside the working dir) × 7 routes (none + the 6) × --stdin / --file:

main branch
cells with SSN: [SSN-REDACTED] and Passport number: [PASSPORT-REDACTED] 10 / 70 (the no-route controls only) 70 / 70
cells with X00000001 in cleartext 60 / 70 (every route, input and source: SSN: ***-**-0001|Passport number: X00000001) 0
exit codes 50 × rc 1 (pre-commit routes), 20 × rc 0 identical distribution
stderr --stdin pre-commit: <stdin>: 1 high confidence issues found; three working-dir sources: the provenance note 2 high confidence issues found; the same provenance note, nothing else

Other fields:

case main branch
strategy: synthetic, --stdin --pre-commit-mode rc 1 SSN: ***-**-0001 rc 1 synthetic SSN
… --redaction-strategy simple [SSN-REDACTED] same
output_dir: <dir>/cfgout, --file --pre-commit-mode copy in ./redacted, 0 in cfgout 1 in cfgout, 0 in ./redacted
same with own precommit cfgout same
confidence_levels: all, one LOW email, --quiet=false --pre-commit-mode rc 0 No matches found at the specified confidence levels. rc 0, the LOW line
same, own profile / FERRET_PRECOMMIT_EXIT_ON=low LOW line rc 0 / rc 1 same
confidence_levels: low, HIGH SSN, pre-commit rc 1 rc 1 (gate does not read the filter)
profiles: none / {ci} / {} on --stdin --pre-commit-mode, strategy: simple ***-**-0001 [SSN-REDACTED]
profiles: null / own / profiles.precommit.redaction.strategy: simple [SSN-REDACTED] same
realistic hook (committed .ferret-scan.yaml PASSPORT,SSN,SECRETS, staged traveller.txt, PRE_COMMIT=1 … --profile precommit --respect-gitignore) rc 0, 0 B stdout, provenance note only rc 1, 473 B naming the passport
shipped config.yaml in the user dir, enabled: true, strategy: "synthetic", --stdin rc 0 synthetic / PRE_COMMIT=1 rc 1 ***-**-0001 rc 0 synthetic / rc 1 synthetic
… PRE_COMMIT=1 --file stderr 0 B the one disclosure line (checks: all in that file)
--verbose --pre-commit-mode, pp.yaml vs no config byte-identical, 0 B 466 B (the passport), no config 0 B
--debug, --file / --stdin Pre-commit environment detected…, no profile named / nothing active profile "precommit" (built-in), selected because --pre-commit-mode was passed + supplied format=text; confidence_levels=high,medium; checks=CREDIT_CARD,SECRETS,SSN,EMAIL,PHONE,IP_ADDRESS,PASSPORT on both
--list-profiles with --config / discovered / none precommit: … / No configuration file found. No profiles available. / same precommit (built-in): … under Available profiles in pp.yaml: / … in .ferret-scan.yaml (auto-discovered): / No configuration file found. Built-in profiles:
#293: planted defaults.checks: EMAIL beside an SSN normal rc 0 [], pre-commit rc 1 [SSN:HIGH] same
planted profiles.precommit.checks: EMAIL / .redaction.output_dir pre-commit rc 0 / copy in elsewhere same (unchanged lever, as the issue measured)

Second member (ok.txt + mode-000 locked.txt):

case main branch
foi.yaml (fail_on_incomplete: true), no profile 3 3
foi.yaml --pre-commit-mode / --profile precommit 0 / 0 3 / 3
foiown.yaml --pre-commit-mode (own precommit) / --profile other 0 / 0 3 / 3
foi.yaml --pre-commit-mode --fail-on-incomplete 3 3
suppressions.generate_on_scan: true: no profile / pre-commit / --profile other 1 / 0 / 0 rules 1 / 1 / 1
defaults.generate_suppressions: true, pre-commit 1 1
suppressions.show_suppressed: true, one enabled rule: no profile / --profile other 1 / 0 entries 1 / 1

Acceptance criteria

criterion branch
second run prints [SSN-REDACTED] and [PASSPORT-REDACTED], as do --file, --profile precommit, the 4 env signals and 4 discovered sources yes: 70/70 cells above
third run exits 1 with one PASSPORT HIGH yes
no config: --pre-commit-mode runs exactly the six; pp.txt rc 0, no new stderr Validators to run: CREDIT_CARD,SECRETS,SSN,EMAIL,PHONE,IP_ADDRESS; rc 0, 0 B / 0 B
defaults.checks: all runs the six with exactly one stderr line naming profiles.precommit.checks; none under --stdin --enable-redaction without --output --file: 1 stderr line, rc 0; --stdin: 1 line; --stdin --enable-redaction: 0 lines (with --output: 1); --checks SSN: 0; file without defaults.checks: 0 B
planted .ferret-scan.yaml defaults.checks: EMAIL leaves the SSN at rc 1 yes
config output_dir and confidence_levels: all apply under pre-commit yes
fail_on_incomplete rc 3 under the built-in profile, a user precommit and --profile other; suppression bools survive --profile other yes
null and {} agree; both guards fail on an unclassified field (mutation); GetPrecommitProfile gone; docs and CHANGELOG yes (mutation below)

Byte identity (durations normalised): no config, own precommit (two configs) and examples/ferret.yaml × 6 routes (X=1, PRE_COMMIT=1, GIT_HOOK_TYPE=1, --pre-commit-mode, --profile precommit, published hook args) × 7 modes (--file text/json/verbose, --stdin text/json/redaction, --file --enable-redaction): 168 runs, 0 differ in stdout, stderr, rc or redacted copy.

Golden corpus and make score

  • UPDATE_GOLDEN=1 go test ./internal/goldencorpus/: 0 files changed (explicit Checks, below this layer), as the issue predicts.
  • make score: PASS on both, identical report: recall_all=1.0000 recall_hm=0.9906 prec_hm=0.9589 whole_leak=0; redaction sink simple 0/0, format_preserving 0/873; suppression layer 65/65/0/0.

Suppression hashes

Unchanged: no finding's text, line or type changes for any check that ran before. Hooks gain findings only for types the config names; those are new findings, not re-hashed ones.

Non-vacuity: each new test on main

cmd/precommit_profile_precedence_test.go and internal/config/profile_precedence_test.go use only symbols that exist on main; both were copied into a main@46e2918 worktree and run there.

test on main
config TestEveryProfileBoolInheritsFromTheConfig Profile bool "fail_on_incomplete" is not in profileBoolFields, so every profile that omits it -- the built-in precommit one included -- sets it to false over whatever the config wrote; config suppressions.show_suppressed: true, profile "other" omits show_suppressed and has false; same for suppressions.generate_on_scan and defaults.fail_on_incomplete (built-in and other)
config TestBuiltinPrecommitProfileYieldsToTheConfig built-in precommit profile sets redaction.strategy = format_preserving; resolveConfiguration lets a non-empty profile value outrank the config; same for redaction.output_dir = redacted; config sets defaults.confidence_levels, yet the built-in profile carries "high,medium"; built-in precommit checks = [CREDIT_CARD EMAIL IP_ADDRESS PHONE SECRETS SSN], want [… PASSPORT …]; fail_on_incomplete=false
config TestBuiltinPrecommitChecksNeverNarrow the PASSPORT and PERSON_NAME unions missing
config TestNullAndEmptyProfilesAgree config "profiles:\n": no precommit profile, so pre-commit mode runs without one
config TestGetPrecommitProfileIsGone Config.GetPrecommitProfile still exists
cmd TestBuiltinPrecommitProfileYieldsOnEveryRoute 60 failures, one per route × input × source, e.g. --config / --pre-commit-mode / --stdin: redacted output "SSN: ***-**-0001\nPassport number: X00000001\n"; the no-route controls pass
cmd TestPrecommitHookReportsThePassportTheConfigNamed pp.yaml / --pre-commit-mode: findings [], want exactly [PASSPORT:HIGH] (18: 6 routes × pp/other/empty; null passes on main) and rc = 0, want 1 (15)
cmd TestBuiltinPrecommitChecksAllIsDisclosedOnce checks: all, --file: rc 0 stderr "", want rc 0 and exactly one line naming profiles.precommit.checks
cmd TestConfigOutputDirAndConfidenceApplyUnderPrecommit the redacted copy did not land in the config's output_dir; stdout "No matches found at the specified confidence levels.\n", want the LOW email finding listed
cmd TestProfileBoolsInheritTheConfigAtTheBinary [--config foi.yaml --pre-commit-mode] with an unreadable file: rc 0, want 3 (and the other three); suppressions.generate_on_scan: true under --profile other wrote no rule; show_suppressed … 0 suppressed entries, want 1
cmd TestActiveProfileIsNamed --list-profiles with an auto-discovered config: "No configuration file found. No profiles available.\n"; --debug: stderr does not contain "active profile \"precommit\" (built-in), …" on --file and --stdin
decoys: cmd TestBuiltinPrecommitProfileDecoys, config TestAConfigsOwnPrecommitProfileIsKeptAsWritten, TestBuiltinPrecommitProfileWithoutConfigIsUnchanged pass on main by design (no config runs the six, own precommit, FERRET_PRECOMMIT=0, planted EMAIL)
new-API tests: TestProfileBoolRegistryAccessorsAgree, TestBuiltinPrecommitChecksConstant, TestBuiltinProfileReport, TestProfileKeys, cmd TestDocsStateTheBuiltinPrecommitChecks do not compile on main. Mutation instead: a swapped getter gives quiet: getProfile = false after setting true; main's two docs contain the six-check string 0 times

Mutation (guards on an unclassified field): adding MutantBool bool and MutantText string to Profile fails both guards: Profile bool "mutant_bool" is unclassified: register it in profileBoolFields … and Profile field "mutant_bool" / "mutant_text" is unclassified for the built-in precommit profile …. Deleting the fail_on_incomplete registry entry fails TestEveryProfileBoolInheritsFromTheConfig with the main-side messages above. Both reverted.

scripts/pr-checklist.sh origin/main

Run with its /tmp/_* log paths redirected to the session scratch dir (same commands otherwise).

=== dimensions ===
-- changelog                       RAN — entry present under [Unreleased] (29 line(s)); required because it changes what a release 
1 gofmt                            RAN — clean
1 go vet                           RAN — clean
1 go build                         RAN — ok
2 full suite (-count=1)            RAN — 89 packages ok
3 golden regen                     RAN — 0 files changed
4 redaction                        NEEDS-MANUAL — scan a fixture with --enable-redaction for EVERY strategy; for
                                   xlsx/docx read the part INSIDE the zip (grepping the
                                   compressed bytes returns 0 and looks like a pass)
5 suppression                      NEEDS-MANUAL — generate with the PARENT binary, apply with YOURS — same-binary
                                   round-tripping passes even when the hash inputs changed
6 timing / O(n^2)                  NEEDS-MANUAL — scaling curve at N/2N/4N with a NON-VACUITY floor (assert
                                   findings > 0 at every size) and distinct values (identical
                                   repeats let a quadratic measure as linear)
7 all 7 formats                    NEEDS-MANUAL — text json yaml csv junit gitlab-sast sarif
8 web / CSP                        N/A — no web/template/asset file touched
10 non-vacuity                     NEEDS-MANUAL — new tests MUST fail on the parent commit; prove it by reverting
                                   the production change (or the guard) and re-running
11 cross-platform vet              RAN — linux darwin windows
12 flake (3 repeats)               RAN — ./cmd ./internal/config 
13 race (whole module)             RAN — 89 packages ok
-- -short mode                     FAIL — fails under -short
-- tree clean                      RAN — this run dirtied nothing

=== summary: 10 ran, 1 failed, 5 need manual work ===
  • -short mode FAIL: the script discards that run's output. Re-run three times with output kept on the same commit: 3/3 ok (89 packages). The one test this host fails intermittently is TestGrowthRatioMissesASparseQuadratic (internal/goldencorpus, a CPU-clock growth ratio against an 8.0 threshold, not skipped under -short); see the flake counts below, which are the same on main. The checklist's -short pass ran straight after the whole-module race run.
  • 4 redaction: simple is the strategy the matrix asserts on 70 cells (--file copies and --stdin streams); format_preserving is the no-config path, byte-identical to main in 168 runs including --enable-redaction on both inputs; synthetic is measured above (--stdin, and the shipped config in the user dir). No redactor or extractor code changed: this PR changes which configured strategy reaches it, so office containers are not a separate path.
  • 5 suppression: rules generated by the main binary (--generate-suppressions, 3 rules: SSN, PASSPORT, EMAIL), enabled, then applied by the branch binary: pp.yaml normal and --pre-commit-mode give 0 results / 3 suppressed (the pre-commit run now includes PASSPORT); own-precommit config 0 / 2. Hash inputs unchanged.
  • 6 timing: no scanning path changed. A hook now runs the validators its config names, which is the stated cost; defaults.checks: all and no-config hooks run the same six as before.
  • 7 formats: all 7 formats × {no config, own precommit} × {normal, --pre-commit-mode} on main vs branch: 28 runs, 0 differ (durations normalised). Golden regeneration across every output: 0 files.
  • 10 non-vacuity: the table above.

Other gates from the worktree, before the checklist: gofmt -l . empty, go vet ./... clean, go build ./... ok; go test ./... 89 packages ok and one FAIL, TestGrowthRatioMissesASparseQuadratic (9.29x against 8.0), the known host timing flake, untouched (the checklist's own full-suite run above was clean). Same minute, interleaved: isolated -run 5/5 pass on the branch and 5/5 on main; whole-package runs failed it 1/3 on the branch (8.84x) and 2/3 on main (8.08x, 8.32x). go test -race on ./internal/config/ ./cmd/: ok.

Open questions for the reviewer

  • The shipped config.yaml sets checks: all, so a user who copies it into the user config dir (as docs/INSTALLATION.md suggests) now sees the disclosure line on every hook run, and LOW findings because it also sets confidence_levels: all. That is the issue's stated trade-off; flagging it because it is the most common way users will meet the line.
  • With defaults.checks: all and --stdin --enable-redaction with no --output, the hook still runs only the six and the line is withheld (stderr is the findings document), so a passport stays in cleartext in that stream with no disclosure. The issue's acceptance criterion asks for exactly this; a follow-up could carry the note inside the findings document instead.
  • --stdin silently ignores an undefined --profile (the --file path exits 1). Out of scope here; the debug record now says profile "x" is not defined in …; no profile active on that path.

union added 2 commits October 20, 2026 20:07
…a config set

Under --pre-commit-mode, the four hook environment variables, or the
published hook's --profile precommit, the built-in precommit profile's
checks, confidence_levels, redaction.strategy and redaction.output_dir
replaced what the config wrote, on both input paths and for every config
source. A config naming PASSPORT got six other types, so a HIGH passport
passed the hook at exit 0, and strategy: simple came back as
format_preserving with the passport in cleartext. Only a config's own
profiles.precommit escaped, and a null profiles: wiped the built-in
profile while profiles: {} kept it. Separately, fail_on_incomplete was
missing from the profile bool registry, so every profile turned a
config's fail_on_incomplete: true off, and the suppression bools were
backfilled from defaults.* only.

internal/config: the built-in profile is added after the file is decoded,
only when the file defines no profiles.precommit (missing, null and {}
agree), and yields to the file: no redaction strategy or output_dir of
its own; high,medium only when the file lacks defaults.confidence_levels;
checks are the six plus every type defaults.checks names, so a config can
widen a hook and cannot narrow it; format stays text. fail_on_incomplete
is registered, and show_suppressed / generate_suppressions backfill from
defaults.X or the suppressions: key. The uncalled GetPrecommitProfile is
removed; BuiltinPrecommitChecks holds the six.

cmd: one stderr line names the remaining override when a config's
defaults.checks is all (withheld where --stdin stderr is the findings
document); --debug names the active profile, why it was selected and what
it supplied, on both input paths; --list-profiles reads the loaded
config, auto-discovered ones included, and marks the built-in profile.

Tests: reflection guards that fail on an unclassified Profile field or an
unregistered Profile bool, and a binary matrix of 6 routes x 2 input
paths x 5 config sources.

Closes #768
…768)

No doc listed the built-in precommit profile's contents. docs/configuration.md
presented examples/ferret.yaml's own profile (PASSPORT, PERSON_NAME) as the
one a hook runs, docs/INSTALLATION.md showed a user profile as the built-in
one, docs/PRE_COMMIT_INTEGRATION.md said the profile sets respect_gitignore
(it does not), and the shipped config.yaml said the redaction flags have no
config equivalent although its own redaction: block is honoured.

The pre-commit and configuration guides now state the six checks, how the
built-in profile yields to a config, the settings precedence, and that a
profile inherits every key it leaves out, fail_on_incomplete included.
TestDocsStateTheBuiltinPrecommitChecks holds both guides to
config.BuiltinPrecommitChecks.

Refs #768
@sectoramen sectoramen added this to the v2.6.0 milestone Oct 20, 2026
@sectoramen sectoramen added bug Something isn't working documentation Improvements or additions to documentation go Pull requests that update go code P0 Audit tier P0: fail-open or cleartext leak; fix first labels Oct 20, 2026
@sectoramen
sectoramen requested a review from rustic October 20, 2026 20:26
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Ferret Scan Results

Gate mode: report — findings are reported here and do not fail this check; a scan that fails or is incomplete still does.

⚠️ Sensitive data detected in 6 scanned file(s):

  • High confidence: 11 findings
  • Medium confidence: 4 findings

Findings Summary:

File Type Confidence Line
main.go SSN HIGH 1705
main.go SSN HIGH 3440
stdin.go PERSON_NAME HIGH 112
stdin.go INSURANCE_MEMBER_ID MEDIUM 117
config.yaml TWITTER HIGH 321
config.yaml TWITTER HIGH 324
config.yaml TWITTER HIGH 334
config.yaml TWITTER HIGH 436
config.yaml INTELLECTUAL_PROPERTY HIGH 104
config.yaml INTELLECTUAL_PROPERTY HIGH 306

... and 5 more findings

Next Steps:

  1. Review the findings above
  2. Remove or redact any actual sensitive data
  3. Add suppressions for false positives
  4. Re-run the scan after making changes

🤖 This comment was automatically generated by Ferret Scan

@rustic
rustic merged commit 6caca5c into main Oct 22, 2026
12 of 13 checks passed
@rustic
rustic deleted the fix/768-precommit-profile-override branch October 22, 2026 20:41
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Ferret Scan Results

Gate mode: report — findings are reported here and do not fail this check; a scan that fails or is incomplete still does.

⚠️ Sensitive data detected in 6 scanned file(s):

  • High confidence: 11 findings
  • Medium confidence: 4 findings

Findings Summary:

File Type Confidence Line
main.go SSN HIGH 1705
main.go SSN HIGH 3440
stdin.go PERSON_NAME HIGH 112
stdin.go INSURANCE_MEMBER_ID MEDIUM 117
config.yaml TWITTER HIGH 321
config.yaml TWITTER HIGH 324
config.yaml TWITTER HIGH 334
config.yaml TWITTER HIGH 436
config.yaml INTELLECTUAL_PROPERTY HIGH 104
config.yaml INTELLECTUAL_PROPERTY HIGH 306

... and 5 more findings

Next Steps:

  1. Review the findings above
  2. Remove or redact any actual sensitive data
  3. Add suppressions for false positives
  4. Re-run the scan after making changes

🤖 This comment was automatically generated by Ferret Scan

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation go Pull requests that update go code P0 Audit tier P0: fail-open or cleartext leak; fix first

Projects

None yet

Development

Successfully merging this pull request may close these issues.

config: the built-in precommit profile silently overrides config checks (fail-open), redaction strategy and output_dir

2 participants