Skip to content

The attribution guard was scoped to commits, and it leaked - #5

Open
NicolasMarino wants to merge 2 commits into
mainfrom
fix/attribution-guard-scope
Open

The attribution guard was scoped to commits, and it leaked#5
NicolasMarino wants to merge 2 commits into
mainfrom
fix/attribution-guard-scope

Conversation

@NicolasMarino

Copy link
Copy Markdown
Owner

bash-guard denied AI attribution in git commit only, 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. gh reads bodies from disk as often as from the command line, so a footer sitting in a --body-file is 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.json 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 test that asserted the bug

The suite already had a case for a gh pr create body 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

shellcheck clean, suite green, and the live hook denied a real gh pr comment carrying 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 of main, so #4 keeps its own rows when it lands.

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.
@NicolasMarino

Copy link
Copy Markdown
Owner Author

Follow-up: 631d533.

The first cut matched gh pr as a whole noun, so gh pr view and gh pr list counted as publishing. Reading a pull request to check it for a leak was denied — precisely backwards, since letting that check happen is the point.

Found by using it: auditing this very PR for the strings was blocked by the guard this PR adds.

Now only writing subcommands match — create, edit, comment, merge, ready, reopen, close, upload. Three more golden inputs cover the reads (view, list, diff), bringing the suite to 54.

One ergonomic cost, worth knowing

The 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.

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.

1 participant