Skip to content
GhostbranchdocsGitHub

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.

Here a feature added a file with a GitHub token in it. It was a fake one, made up for this example:

Terminal
ghostbranch gate add-search
three progress lines, on stderr, left out
Gate for add-search: checks not_run, leak scan found.
1 commit(s), scanned as preserve
leak f-34b03e644091: github-pat in drill/fake-token.txt:1 (commit(s) 5f1dba8f0201)
error: 1 leak finding(s) in what would be published; nothing was pushed to the public repo [LEAK_FOUND]
Next:
ghostbranch gate add-search --repo you/project
amend the feature's last commit to remove it (a fix commit on top would leave it in a commit that preserve and curated promotes publish), push with `git push --force-with-lease shadow add-search`, then gate again
ghostbranch waivers add add-search f-34b03e644091 --repo you/project --reason <reason> (a person must run this)
only if a person judges these findings false positives: they review them at their console and give a reason

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:

gate.json (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: tree means a file’s content, and path a 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.

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.

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:

Terminal
ghostbranch gate add-search
three progress lines, on stderr, left out
Gate for add-search: checks not_run, leak scan found.
2 commit(s), scanned as preserve
leak f-34b03e644091: github-pat in drill/fake-token.txt:1 (commit(s) 5f1dba8f0201)
error: 1 leak finding(s) in what would be published; nothing was pushed to the public repo [LEAK_FOUND]
Next:
ghostbranch promote add-search --mode squash --plan --repo you/project --message <message>
if the problem is only in older commits (the final tree no longer has it), publish the feature as one commit, so they never ship
ghostbranch promote add-search --mode curated --plan --repo you/project --curate <curate> --title <title>
or curate the commits in a --curate file (see the gate result's commits): group each commit the finding names with a later one that removes the problem
ghostbranch gate add-search --repo you/project
or rewrite the commits in each finding's commits on the feature branch in the shadow repo (fix the files), push with `git push --force-with-lease shadow add-search`, then gate again
ghostbranch waivers add add-search f-34b03e644091 --repo you/project --reason <reason> (a person must run this)
only if a person judges these findings false positives: they review them at their console and give a reason

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:

Terminal
ghostbranch promote add-search --mode squash -m "Add search" --plan
progress, on stderr:
The gate for add-search is missing or stale, so it runs first
Scanning what add-search would publish for leaks (squash)
ok: CI passed
the result:
Plan 20261013T032248Z-ea7bcd saved to /home/you/.local/state/ghostbranch/workspaces/default/plans/20261013T032248Z-ea7bcd.json.
squash: 1 public commit(s)
Publish it with: ghostbranch apply 20261013T032248Z-ea7bcd

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.

If a person judges a finding safe to publish, they can waive it at their own terminal. --plan shows what it would waive:

Terminal
ghostbranch waivers add add-search f-34b03e644091 --reason "fake token in a test fixture" --plan
Dry run; nothing was changed. This would:
- waive leak finding f-34b03e644091 for you/project

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 list shows the waivers for the repo, and ghostbranch 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.

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.

LEAK_FOUNDexit 3The leak scan found something in what would be published.
What to do

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.
Causes
  • 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
What to do

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.
What to do

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.