Skip to content

fix(hooks): anchor the secret-scanner capability guard to end of line, not end of input (#737) - #774

Merged
tractorjuice merged 1 commit into
mainfrom
fix/737-secret-scanner-multiline-level-guard
Aug 13, 2026
Merged

tractorjuice merged 1 commit into
mainfrom
fix/737-secret-scanner-multiline-level-guard

Conversation

@tractorjuice

Copy link
Copy Markdown
Owner

Fixes #737.

The bug

v6.8.0 exempted a value that is exactly read, write, none, true, false or null from the five reference-guarded key-value rules, so that id-token: write would stop being read as credential material:

const LEVEL = String.raw`(?!(?:read|write|none|true|false|null)\b\s*$)`;

All nine rules that interpolate LEVEL are built with the flags 'gi'. Without m, JavaScript's $ anchors to end of input, not end of line, so the exemption fired only when the permission line was the last content in the string:

allowed  id-token at END of input
BLOCKED  id-token MID-message   -> ["token: write"]

Why it matters more than the issue says

The same guard is duplicated in secret-file-scanner.mjs, the PreToolUse Edit|Write hook, where input is multi-line by definition. The OIDC case the v6.8.0 changelog entry was written for never worked there at all. On main today, a realistic release workflow is still blocked:

{"decision":"block","reason":"Warning: Potential secrets detected in file content: token (1x)
File: .github/workflows/release.yml ..."}

For secret-detection.mjs (UserPromptSubmit) the effect is the one reported: any prompt or subagent report quoting the line mid-paragraph is blocked, which halted a long /arckit:repo-audit run part-way through.

Trailing whitespace after the value was a second latent false positive from the same anchor.

The fix

End of line is spelled out rather than relying on $, which is one line per file instead of adding m to nine regex constructors:

const LEVEL = String.raw`(?!(?:read|write|none|true|false|null)\b[^\S\r\n]*(?:\r?\n|$))`;
case want before after
OIDC mid-file allow BLOCK allow
OIDC at EOF allow allow allow
trailing spaces after value allow BLOCK allow
literal credential mid-file BLOCK BLOCK BLOCK
token: writeKeyABC123 BLOCK BLOCK BLOCK

Why CI did not catch it

All four capability vectors in tests/plugin/scanner-reference-guard.test.mjs were single-line strings with nothing after them, so $ coincided with end of line by accident.

This PR adds six regression tests covering the multi-line and trailing-whitespace cases against both hooks, plus two vectors asserting literal credentials are still caught when they are not the last line. Verified they fail on the old guard and pass on the new one:

6 fail / 49 pass   (guard reverted)
0 fail / 55 pass   (guard fixed)

The parity test now also compares the REF and LEVEL constants, not just the SECRET_PATTERNS block. The block interpolates both, so the copies could drift on the guards undetected, which is exactly where this bug lived.

Note for the reporter

@johnfelipe your trace was right on the mechanism. One correction: CLAUDE_CODE_OAUTH_TOKEN is not the trigger. The underscore is a word character, so \b(token|auth_?token|...) never matches inside ..._OAUTH_TOKEN. The trigger is id-token: write, where the hyphen supplies the boundary, which you identified in your second note.

Your workaround reasoning was also correct: the CLI loads hooks from ~/.claude/plugins/cache/, so patching a local checkout has no runtime effect and a cache patch is overwritten on the next plugin update. This release removes the need for either.

Verification

  • node --test tests/plugin/*.test.mjs tests/plugin/test_*.mjs — 296 pass, 0 fail
  • python -m pytest tests/codex -q — 35 pass
  • python scripts/converter.py — exit 0, fix propagated to arckit-kimi (the only extension shipping these hooks verbatim); no hand-maintained Codex/Gemini hook carries a duplicate guard
  • python scripts/sync-shared-assets.py --check — all 9 shared assets present in 14 community plugins
  • npx markdownlint-cli2 CHANGELOG.md — 0 issues

🤖 Generated with Claude Code

https://claude.ai/code/session_01VKbHCujejpxkgvh47BndDE

…, not end of input (#737)

v6.8.0 exempted a value that is exactly read/write/none/true/false/null
from the five reference-guarded key-value rules, so that `id-token: write`
would stop being read as credential material. The exemption was anchored
with `$`, but all nine rules are built with the `gi` flags and no `m`, so
`$` matched end of input rather than end of line. The guard fired only
when the permission line was the final content in the string.

secret-file-scanner.mjs runs on whole file contents, where a permissions
block is followed by the jobs it authorises, so the OIDC case the fix was
written for stayed blocked in that hook throughout. secret-detection.mjs
blocked any prompt or subagent report quoting the line mid-paragraph,
which is what halted a long /arckit:repo-audit run part-way through.
Trailing spaces after the value defeated the guard too.

Both copies now spell end of line out as `[^\S\r\n]*(?:\r?\n|$)`, fixing
it in one place per file rather than adding `m` to nine constructors.
Literal credentials, provider token formats and values that merely begin
with a level word are still blocked, mid-file as well as at the end.

Six regression tests cover the multi-line and trailing-whitespace cases
against both hooks; every capability vector in that suite had been a
single line with nothing after it, which is why `$` looked correct. The
parity test now compares the REF and LEVEL guard constants as well as the
pattern block, because the block interpolates them and this bug lived in
a line the old comparison never read.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VKbHCujejpxkgvh47BndDE
@tractorjuice
tractorjuice merged commit 51744bc into main Aug 13, 2026
3 checks passed
@tractorjuice
tractorjuice deleted the fix/737-secret-scanner-multiline-level-guard branch August 13, 2026 19:55
@tractorjuice tractorjuice added bug Something isn't working security Security hardening and vulnerability fixes labels Aug 31, 2026
@tractorjuice tractorjuice self-assigned this Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working security Security hardening and vulnerability fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

error about secret detection with audit repo

1 participant