You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[hooks] exclude_commands is bypassed for head/tail (line-range fast path skips the exclusion check) #2823
Same through the hook path: echo '{"tool_input":{"command":"tail -5 x.log"}}' | rtk hook claude returns an updatedInput rewrite even with tail excluded.
Cause (master at the time of writing): the head/tail line-range fast path in rewrite_segment_inner returns before the exclusion check —
is_excluded is only consulted in the Classification::Supported arm (~L886) and the TOML/Unsupported arm (~L897), both downstream of this early return. Commands routed through classification (e.g. rg) respect exclusions; head/tail never reach it.
Since exclusions exist precisely to encode "the user said hands off this command", the fast path should check is_excluded(cmd_part, excluded) before rewriting. PR with the one-line guard + tests incoming.
The head/tail line-range fast path in rewrite_segment_inner (src/discover/registry.rs ~L867) returns before checking is_excluded, silently bypassing configured exclude_commands for head/tail; add the exclusion check before the early return.
This looks like a simple fix that could be auto-resolved. Repo maintainers can use /wshm fix to attempt it.
📁 Relevant files
src/discover/registry.rs
Triaged automatically by wshm · This is an automated analysis, not a human review.
Still present in v0.45.0 (Homebrew, macOS arm64). Confirming the root cause above, and adding a consequence that I think raises the severity beyond "config ignored".
Under an agent harness, this fast path can defeat the harness permission system.
Claude Code matches its permissions.deny rules against the command afterPreToolUse hooks apply updatedInput. So a rewrite that renames a file read also renames it out of the deny rules. Three-cell probe, same file, same tool, same session, with exclude_commands = ["^cat", "^head", "^tail"] configured:
$ git status # compact output -> hook is live
$ head -n 50 /tmp/denyverify/id_rsa # not rewritten (exclusion honored) -> DENIED by rule Read(**/id_rsa*)
$ head -50 /tmp/denyverify/id_rsa # rewritten -> `rtk read ... --max-lines 50` -> returned the file contents
The only difference is the -N shorthand that takes the line-range fast path. rtk read is not a command the harness recognises as a file read, so the deny rule stops applying. On a setup where defaultMode is bypassPermissions, that deny list is the only enforcement there is.
Two notes for whoever picks up the fix:
No exclude_commands value can work around it. I tested "head", "^head", "^(head|tail)", and even the literal "head -" that provably matches the string — all still rewrite head -50 f, while head -n 50 f and head f are excluded correctly. That matches the early-return analysis in the issue body exactly.
The hook itself behaves well otherwise — v0.45.0 emits updatedInput with no permissionDecision, so it is not force-allowing anything, and rm -rf, git push --force, git push -f and sed are all correctly left alone. This is specifically the head/tail fast path.
Activity
pszymkowiak commentedon Jul 4, 2026
🔍 Automated Triage
bughighSummary
The head/tail line-range fast path in rewrite_segment_inner (src/discover/registry.rs ~L867) returns before checking is_excluded, silently bypassing configured exclude_commands for head/tail; add the exclusion check before the early return.
📁 Relevant files
src/discover/registry.rsTriaged automatically by wshm · This is an automated analysis, not a human review.
bluedepin commentedon Aug 22, 2026
Still present in v0.45.0 (Homebrew, macOS arm64). Confirming the root cause above, and adding a consequence that I think raises the severity beyond "config ignored".
Under an agent harness, this fast path can defeat the harness permission system.
Claude Code matches its
permissions.denyrules against the command afterPreToolUsehooks applyupdatedInput. So a rewrite that renames a file read also renames it out of the deny rules. Three-cell probe, same file, same tool, same session, withexclude_commands = ["^cat", "^head", "^tail"]configured:The only difference is the
-Nshorthand that takes the line-range fast path.rtk readis not a command the harness recognises as a file read, so the deny rule stops applying. On a setup wheredefaultModeisbypassPermissions, that deny list is the only enforcement there is.Two notes for whoever picks up the fix:
exclude_commandsvalue can work around it. I tested"head","^head","^(head|tail)", and even the literal"head -"that provably matches the string — all still rewritehead -50 f, whilehead -n 50 fandhead fare excluded correctly. That matches the early-return analysis in the issue body exactly.updatedInputwith nopermissionDecision, so it is not force-allowing anything, andrm -rf,git push --force,git push -fandsedare all correctly left alone. This is specifically thehead/tailfast path.Happy to re-test against the PR when it lands.
docs(config): scope the exclude_commands coverage claim to what is pe…