Skip to content

fix(gate): two hard rules stop firing on non-credentials; status names a stale copy (0.2.18) - #147

Merged
chrispezza merged 2 commits into
mainfrom
claude/noisy-hard-rules
Oct 12, 2026
Merged

chrispezza merged 2 commits into
mainfrom
claude/noisy-hard-rules

Conversation

@chrispezza

Copy link
Copy Markdown
Contributor

Three of the four noisy hard rules. In seatbelt each false positive here was a denied call. #122's remaining part (git branch -D after a squash merge) is not in this PR: it needs the hook to ask git a question, which is a design change to rules that are string operations today.

What changed

#87: credential-on-the-command-line on source code. The entry read the assigned-secret redaction kind, which is as wide as redaction should be: case-insensitive, spaces allowed round the =. redact() now also reports shell-assigned-secret when the same match has no space round the = and its name is all capitals, or follows --, ?, & or an opening quote. The bundled entry names the new kind. Redacted text is byte-for-byte what it was.

#98: templates labelled environment_file. .env.example, .env.sample, .env.template and .env.tpl carry no label. The suffix is read last, so .env.example.local is still an environment file.

#126: a copied policy keeps a superseded rule. bouncer status compares hard rules with the bundled ones by name and prints each predicate that differs and each bundled entry the policy lacks. It runs only when asked, and says "differs", since it cannot tell an old copy from a deliberate edit.

The built hook, seatbelt, mock backend

command 0.2.17 this branch
heredoc holding const tokens = list("tokens"); deny none
node -e 'const password = readLine();' deny none
python3 -c 'c = Client(api_key=settings_value)' deny none
API_KEY=… ./run.sh, docker run -e DB_PASSWORD=…, --password=…, ?api_key=…, -d 'token=…' deny deny
cat deploy/.env.example deny none
heredoc writing deploy/.env.tpl, then grep deny none
cat .env, cat deploy/.env.local deny deny

What it gives up

A lowercase assignment in a bare argument (password=hunter2 ./deploy.sh, curl -d token=value) no longer matches the hard rule and is judged by the secrets question. That is the price of not reading login(password=value) as a credential. The changelog says so.

For an existing install with a copied policy

The #87 fix does not reach a copy. bouncer status on this machine's ~/.bouncer/bouncer.yaml prints:

Hard rules that are not the bundled ones:
  credential-on-the-command-line differs
    redacts_as: yours has "assigned-secret"; the bundled entry has "shell-assigned-secret"

Checks

  • 1650 tests pass. Six new holdout cases, FROZEN updated in the same commit. Four of them are judged cases the mock asks on by keyword; the test's friction list names them and says why.
  • The pinned list of twelve fixtures that hard rules decide is unchanged.
  • bench --against the base bundle with --against-policy the base policy: paired median +0.1 ms judged, +0.1 ms hard-rule, 30 pairs. CACHE_VERSION is 8 on both sides.
  • No new attack surface. The change narrows two deny rules; the list above is what that lets through to the classifier.

Closes #87, closes #98, closes #126.

🤖 Generated with Claude Code

chrispezza and others added 2 commits October 12, 2026 15:15
…s a stale copy (0.2.18)

credential-on-the-command-line read the wide assigned-secret redaction kind,
which is case-insensitive and allows spaces round the equals sign, so source
code in an inline script was a credential and a deny in seatbelt (#87).
redact() now reports shell-assigned-secret beside it when the assignment is
written as a shell, a long flag, a query string or a quoted form value writes
one, and the bundled entry names that kind. Redacted text is unchanged.

describeSensitivity no longer labels .env.example, .env.sample, .env.template
or .env.tpl as environment_file (#98).

bouncer status compares a policy's hard rules with the bundled ones by name
and prints what differs and what is missing (#126). A copied policy keeps the
rule it was copied with, and nothing said so.

Six holdout cases, refrozen. Bench against the base bundle with the base
policy: paired median +0.1 ms judged, +0.1 ms hard-rule, 30 pairs.

Closes #87, closes #98, closes #126.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ough 0.2.18

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant