trufflehog catalog hook scans gitignored target/ in Rust consumers, blocks every commit #288
Labels
No labels
burndown-2026-06
coherence-core
consult
headless
interactive
P0
P1
P2
P3
P4
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
coilyco-flight-deck/agentic-os#288
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
The catalog
trufflehog (secret scan, offline)hook shipped from this repo(
.pre-commit-hooks.yaml) false-positives on Rust build artifacts. On any hostthat has run a release build,
target/exists (gitignored, untracked) and thehook reads its binary blobs (
libzerocopy-*.rlib,.rmeta,*.pdb), emittingthousands of bogus "unverified" hits that block every
git commit.Reported in coilyco-bridge/agentic-os-kai#530. It blocked landing the
.forgejo/migration in
coilysiren/repo-recallon a tower untilrm -rf target/(4.6GB,fully reproducible). It affects every Rust-shaped consumer of this hook
(repo-recall, cli-guard, ward, coily).
Current hook entry
.pre-commit-hooks.yaml, idtrufflehog:Root cause
trufflehog's
git file://.scan reads the working copy, and for untracked filesit does not consult
.gitignore(upstream feature request trufflesecurity/trufflehog#3356confirms trufflehog does not honor
.gitignore). So gitignoredtarget/getswalked. Removing
target/is what clears the hits, which proves the working-treeread is the source.
Fix shape (verify empirically with a live trufflehog before landing)
This repo is the right home because the entry is centrally managed: consumers
carry it via the
apply-agentic-os-hooks.pymanaged block, and that block isregenerated with no per-hook args, so a consumer-side override does not survive.
Candidates, in rough order of preference:
--exclude-globsappended inline (travels with the shared entry, no per-repofile needed): e.g.
--exclude-globs 'target/**,dist/**,build/**,node_modules/**'.Caveat to verify:
--exclude-globsis documented as filtering at thegit loglevel, so confirm it also suppresses the untracked working-tree read, not just
committed objects.
--exclude-paths <file>of regexes (the pattern already used inagentic-os-kai's
.github/workflows/trufflehog.ymlCI scan:(^|/)target/,\.venv/,node_modules/, the cache dirs). Downside: needs a committedregex file each consumer carries, awkward for a shared entry.
The carrying agent should reproduce with
cargo build --release+ an emptycommit, pick whichever flag actually suppresses the working-tree read, and keep
real staged-secret detection intact (do not blanket
--only-verified, the hookis offline
--no-verification).Rollout (part of done)
Landing the entry change is not enough on its own. Consumers pin a
rev:, soafter this lands and a release tag cuts, bump the pin fleet-wide via
make apply-agentic-os-hooks/apply-agentic-os-hooks.py --rev vXso Rustrepos actually receive the fix. Closing agentic-os-kai#530 depends on that
rollout reaching the affected repos.
Repro
Filed from the agentic-os-kai#530 carry container, which is scoped to
agentic-os-kai and cannot push this repo. Cross-ref: coilyco-bridge/agentic-os-kai#530.
Carry-readiness notes (from the agentic-os-kai#530 re-dispatch)
Re-confirmed this from a kai-scoped carry container and pinned down the open empirical question as far as docs allow, so whoever carries this lands a near-mechanical diff.
Why the working-tree read happens (mechanism). trufflehog's
git file://.source scans the working-tree state (staged + unstaged + untracked) as a synthetic diff against HEAD - that is precisely why it works as a pre-commit hook, catching secrets before they are committed. It does not honor.gitignorefor those untracked files (upstream trufflesecurity/trufflehog#3356). So gitignored-but-untrackedtarget/blobs get walked.--since-commit HEADdoes not change this - the working-tree synthetic diff is independent of the commit range.Generator confirmed to wipe consumer-side args.
apply-agentic-os-hooks.py:180stamps bare- id: {h}lines with no args, so anyargs:an affected repo adds to its managed block is erased on the next rollout. Confirms this repo is the only durable home.Flag recommendation, in order:
--exclude-paths=<file>is the documented path-regex filter for the git source (one regex per line, matched against object paths). It is the same approach already proven in agentic-os-kai's own.github/workflows/trufflehog.yml. Proven regex set to reuse:(^|/)target/,(^|/)\.venv/,(^|/)venv/,(^|/)node_modules/,(^|/)__pycache__/,(^|/)\.mypy_cache/,(^|/)\.pytest_cache/,(^|/)\.ruff_cache/- add(^|/)(dist|build)/for Rust/JS artifacts. Downside unchanged: needs a committed regex file in every consumer, awkward for a single shared entry.--exclude-globs 'target/**,dist/**,build/**,node_modules/**'is inline (no per-consumer file) and is the cleaner shape if it covers the untracked working-tree read. That coverage is undocumented for the git source - docs describe--exclude-globsat thegit loglevel. This is the one thing that still needs a live trufflehog against a realtarget/to confirm before landing.Carry environment requirement. The verify step needs the trufflehog binary plus a populated
target/(cargo build --releasein a Rust consumer, then an empty commit). The kai-scoped container has neither (Python repo, no binary), which is why this cannot be verified from there. Carry this in a container that grants this repo and has trufflehog on PATH.Rollout is part of done: land entry change -> release tag cuts -> bump
rev:fleet-wide viaapply-agentic-os-hooks.py --rev vX. agentic-os-kai#530 stays open until the rollout reaches the Rust consumers (repo-recall, cli-guard, ward, coily).Cross-ref: coilyco-bridge/agentic-os-kai#530.
🔒 Reserved by
ward agent --driver claude— containerengineer-claude-agentic-os-288on hostkais-macbook-pro-2.localis carrying this issue (reserved 2026-07-01T04:36:43Z). Concurrentward agentruns are blocked until it finishes or the reservation goes stale (2h0m0s TTL);--forceoverrides.— Claude (she/her), via
ward agentWARD-OUTCOME: done - trufflehog catalog hook now excludes gitignored build/cache dirs via scripts/trufflehog-scan.sh (--exclude-paths), landed on main
d4ef2bb.The interesting part was that the bug did not reproduce against the shipped trufflehog (3.95.7): its
git file://.source only scans committed history plus the staged index, and never touches the untracked gitignoredtarget/. So the reported flood must come from an older trufflehog whose synthetic working-tree diff included untracked files. That reframed the job from "reproduce and fix" to "make the shared entry robust across the version that does the untracked read."What fought back was the flag choice. The issue's preferred candidate, inline
--exclude-globs, turned out to be the wrong tool: I staged a secret insidetarget/and--exclude-globs 'target/**'did NOT drop it - it only filters committed git-log objects, not the synthetic working/staged diff.--exclude-paths(a regex file) DID filter that diff by path while keeping a real leak outside the excluded dirs failing. That empirical split is the whole reason for the shape that landed.To dodge the "every consumer carries an exclude file" downside I moved the entry to
language: scriptpointing at a wrapper committed here, so the regex list ships centrally through the pre-commit cache. Verified end to end withpre-commit try-repo: gitignoredtarget/full of secrets passes, a real staged secret fails. Confidence is high on correctness.One rough edge worth flagging: the fix is version-robust but I could not exercise the actual untracked-read path, since no old-enough trufflehog was on hand - I proved
--exclude-pathsfilters the same synthetic-diff mechanism the old read rode, which is strong but indirect. Follow-up, and the real closer for agentic-os-kai#530: after this release tag cuts, the fleetrev:pin still needs bumping on the Rust consumers (repo-recall, cli-guard, ward, coily). That rollout is a host/ansible step outside this container's grant, so #530 should stay open until it lands there.🔒 Reserved by
ward agent --driver claude— containerengineer-claude-agentic-os-288on hostkais-macbook-pro-2.localis carrying this issue (reserved 2026-07-01T07:04:27Z). Concurrentward agentruns are blocked until it finishes or the reservation goes stale (2h0m0s TTL);--forceoverrides.— Claude (she/her), via
ward agentWARD-OUTCOME: done - trufflehog wrapper (scripts/trufflehog-scan.sh, --exclude-paths) already landed on main
d4ef2bbclosing #288; I independently re-verified it end to end.Candid note: I came up to a fully-carried issue. A prior container (linux-0c646...cc47) had already implemented the wrapper, pushed
d4ef2bbstraight to canonical main, closed the issue, and left its own done retrospective. My HEAD was already sitting exactly on that commit, so there was no feature left to build - the honest job was to re-verify rather than re-do.What I checked, and where it fought back a little: my first "real staged secret still fails" test came back green (exit 0) and briefly worried me, until I realized I'd used
AKIA...EXAMPLE, which is trufflehog's own blessed placeholder that detectors deliberately ignore. Swapping in a genuinely detectable token flipped it to a proper failure (exit 183), while the same token confined to a gitignoredtarget/passed clean. So the two load-bearing behaviors both hold: real staged leaks still block, gitignored build dirs don't. shellcheck passes on the wrapper and the full suite is green at HEAD.Confidence: high on correctness of what landed. The one honest caveat carries over from the original carry - trufflehog 3.95.7 doesn't walk untracked
target/anyway, so the exclude is proven against the synthetic-diff mechanism but not against the actual old-version untracked read that triggered the report. Follow-up unchanged and real: this only reaches the Rust consumers after a release tag cuts and the fleetrev:pin is bumped (apply-agentic-os-hooks.py --rev vX), which is a host/ansible step outside this container. agentic-os-kai#530 should stay open until that rollout lands.