When the gate finds a leak
Read a leak finding, decide whether it's real, and get the feature published without it.
The gate stopped with exit code 3 and LEAK_FOUND. Nothing went public, and nothing was even pushed to the gate branch. This page walks through what to do next. The gate explains what the scan reads.
1. Read the finding
Section titled “1. Read the finding”Here a feature added a file with a GitHub token in it. It was a fake one, made up for this example:
The summary and finding lines are on stdout, and the error and its Next lines on stderr; the gate’s three progress lines are left out. Each finding has an ID, the rule that matched, where it is, and the commits that added it. With --json the findings are in error.details:
{ "feature": "add-search", "findings": [ { "id": "f-34b03e644091", "rule": "github-pat", "where": "tree", "path": "drill/fake-token.txt", "line": 1, "secret": "[REDACTED]", "waivable": true, "commits": ["5f1dba8f020104dfa49ed45e51f77fc060924a30"] } ], "mode": "preserve", "not_waivable": [], "waivable": ["f-34b03e644091"]}where:treemeans a file’s content, andpatha file’s name. Other values name the text the finding is in, such as the branch name or the pull request’s title.secret: always[REDACTED]. Ghostbranch never prints the value, so a finding is safe to paste into a chat or an issue.commits: the feature commits that add or change it. A later commit that only carries it along isn’t listed.waivable: whether a person may waive it. Only a finding in a file, in its content or its name, can be waived.
2. Decide whether it’s real
Section titled “2. Decide whether it’s real”Look at the file and line yourself. Then:
- It’s a real secret. Remove it, and rotate it now. It’s already in your shadow repo’s history, so treat it as seen by anyone who can read that repo.
- It’s a fake value, such as a placeholder key in a test fixture. You can rewrite the fixture so the scan no longer sees a key, for example by building the value from parts when the test runs. Or a person can waive the finding.
- It’s a false positive of some other kind, such as a long random-looking ID that isn’t a secret. A person waives it.
3. Fix it
Section titled “3. Fix it”Remove or rewrite the content on your feature branch, push to the shadow repo, and gate again.
How you remove it matters. In the default history mode, preserve, every feature commit is published as it is. A new commit that deletes the file still leaves the older commit that added it, and the gate still blocks:
So you have three ways out:
- Rewrite the commit that added it, on your branch, then push with
git push --force-with-lease shadow add-search. The lease makes the push fail if the shadow branch moved since you fetched it. Then gate again. - Publish as one commit, with
--mode squash. Only the final state is published and scanned, so the older commit never ships. - Curate the commits, with
--mode curated: group the commit that added the value with the one that removed it. See History modes.
Squash was the choice here. The feature had one more commit with the real change, and promote gated it again in squash mode before it made the plan:
Some lines are left out: the gate’s push of its candidate, the wait for CI, a warning about check runs, and the Next lines. Its pull request had one commit and one file, and the token wasn’t in it. After the merge, sync closed the feature and deleted its shadow branches, including the one that held the commit with the token.
4. Or waive it (a person)
Section titled “4. Or waive it (a person)”If a person judges a finding safe to publish, they can waive it at their own terminal. --plan shows what it would waive:
Without --plan, it scans the feature again on your machine, shows each finding with the first and last few characters of the value, and asks for a one-time code. The reason goes into the ledger. Then gate again, and the waived finding no longer blocks.
- A waiver covers exactly that content in that file. If the content changes, it’s a new finding with a new ID.
- It lives on the machine that made it. Publish from that machine, or waive it again on the other one.
ghostbranch waivers listshows the waivers for the repo, andghostbranch waivers revoke <id>takes one back. Revoking needs no code.
A finding in a commit message, a branch name or the pull request’s title or body can never be waived. Change the text: reword the commit, or publish in squash or curated mode with a new message.
Why agents never waive
Section titled “Why agents never waive”A coding agent can remove a leak, rewrite a fixture, or choose squash, just as you would. It can’t waive a finding, and it isn’t meant to try. A waiver says “I’ve looked at this value and it’s safe to make public”, and only a person can make that call. The one-time code is there so the agent can’t make it for you.
When the gate finds something an agent can’t safely fix, the agent stops and gives you the waivers add command from next, with the finding’s ID. An agent that rewrote a fake value should tell you which finding it rewrote. Working with a coding agent covers what else an agent leaves to you.
Errors you may see
Section titled “Errors you may see”LEAK_FOUNDexit 3The leak scan found something in what would be published.
Remove the content in details.findings from the feature and gate again. A finding whose commits are listed may be only in older commits: publish with --mode squash or --mode curated so they never ship, or rewrite those commits on the feature branch, force-push it to the shadow repo, and gate again. If a person judges a finding in details.waivable a false positive, they run the waivers add command in next. Agents never waive findings or edit waivers.json.
WAIVER_UNKNOWNexit 2waivers add named an id that is not a waivable finding in any commit the feature would publish now (it scans every commit, as a preserve promote does; under a squash or curated default whose older commit has a broken marker, only the final tree), or waivers revoke named an id with no stored waiver for the repo.
- the content changed, so the finding has a new id
- the finding was only in a commit that is no longer on the feature branch (waivers add scans every commit on it, whatever the default mode, except that under a squash or curated default whose older commit has a broken marker it scans only the final tree)
- the finding is in the commit message, branch or PR text, which is never waivable
- a typo
- for waivers revoke: the waiver was already revoked, or is for another repo
For waivers add, gate again and use the ids in error.details.waivable. For waivers revoke, run ghostbranch waivers list --repo <public> and revoke the ids it shows; error.details.ids lists the ids with no waiver.
WAIVERS_UNREADABLEwarningwaivers.json could not be read, so no waiver applied and every finding blocked.
A person fixes the file or clears it with ghostbranch waivers revoke --all. If the message says a newer Ghostbranch wrote it, upgrade ghostbranch on this machine instead: revoke --all keeps a newer file.
This needs a person.