The attribution guard was scoped to commits, and it leaked - #5
The attribution guard was scoped to commits, and it leaked#5NicolasMarino wants to merge 2 commits into
Conversation
bash-guard denied AI attribution in `git commit` only. The README said so on purpose: PR and issue bodies were allowed to carry the trailer. That decision shipped a Claude session URL onto three pull requests of a public repository. The rule was never about the word "commits" — it is about anything that leaves this machine carrying the user's name. Such a link is not a credential, but it is a private identifier, and publishing it is not the agent's call to make. The check now covers every publishing verb: git commit, git tag, gh pr, gh release, gh issue. It also opens body files. `gh` reads bodies from disk as often as from the command line, and a footer sitting in that file is invisible to any check on the command text — which is exactly how one got published. Non-publishing commands are untouched, so searching the repository for these strings still works. A guard that blocks the hunt for a leak is worse than no guard. settings.fragment.json also gains attribution.commit/pr = "" and sessionUrl = false, which turns the footers off at the source. The hook is the net under that, not a replacement for it. The suite had a case asserting the old behaviour, expected to pass. It encoded the bug, so it is now a denial. 51 golden inputs, plus a temp-file block for the on-disk body-file branch that a payload-only test can never reach.
The first cut matched `gh pr` whole, so `gh pr view` and `gh pr list` counted as publishing. Reading a pull request to check it for a leak was denied, which is precisely backwards — the guard exists to let that check happen. Found by using it: auditing the very PR that carries this change was blocked. Now only the writing subcommands match: create, edit, comment, merge, ready, reopen, close, upload. Three golden inputs cover the reads, bringing the suite to 54.
|
Follow-up: The first cut matched Found by using it: auditing this very PR for the strings was blocked by the guard this PR adds. Now only writing subcommands match — One ergonomic cost, worth knowingThe guard scans the whole command string. So a compound command that both edits a file quoting the patterns and commits in the same shell invocation will trip, even though the commit message itself is clean. Editing docs about the guard is the only place this bites, and splitting the edit from the commit is better practice anyway. |
bash-guarddenied AI attribution ingit commitonly, and the README said so deliberately — PR and issue bodies were allowed to carry the trailer.That decision put a Claude session URL on three pull requests of a public repository. The rule was never about the word "commits": it is about anything that leaves this machine carrying my name. Such a link is not a credential — it needs my login — but it is a private identifier, and publishing it was never the agent's call.
What changed
Every publishing verb, not just commits:
git commit,git tag,gh pr,gh release,gh issue.Body files are opened and scanned.
ghreads bodies from disk as often as from the command line, so a footer sitting in a--body-fileis invisible to any check on the command text. That is precisely how one got published.Non-publishing commands are untouched. Searching the repository for these strings still works — a guard that blocks the hunt for a leak is worse than no guard.
settings.fragment.jsongainsattribution.commit/pr = ""andsessionUrl = false, which turns the footers off at the source. The hook is the net under that, not a replacement for it.The test that asserted the bug
The suite already had a case for a
gh pr createbody carrying the footer, expected to pass. It encoded the decision rather than catching it, so it is now a denial.51 golden inputs. The on-disk body-file branch gets its own block with a real temp file, because a payload-only test can never reach it — the file has to exist for the code path to run.
Verified
shellcheckclean, suite green, and the live hook denied a realgh pr commentcarrying a session URL before any of this was committed.It also denied the first draft of the commit message, and the first draft of this description — both quoted the patterns verbatim while explaining them. True positives, and fair warning that the guard is strict about the literal strings. Worth knowing before you write a commit about the guard.
Why a separate branch
This is unrelated to #4, which is still open on
feat/sdd-local-overrides. The README table conflicted on the rows that PR adds; resolved here in favour ofmain, so #4 keeps its own rows when it lands.