Skip to content

Fix: claude bash-chain-guard quoting fix and split error messages - #193

Merged
ejfine merged 2 commits into
mainfrom
more-chain-guard
Aug 6, 2026
Merged

Fix: claude bash-chain-guard quoting fix and split error messages#193
ejfine merged 2 commits into
mainfrom
more-chain-guard

Conversation

@ejfine

@ejfine ejfine commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Why is this change necessary?

The chain-detection regex was /\s(&&|\|\||;)\s|\s&(\s|$)/, which requires whitespace on both sides of the operator. That makes it both too narrow and too broad, and every repo on this template has the same holes today.

Missed (chaining that should be blocked, but ran through):

  • cd /some/path; git diff — no space before the semicolon
  • cd a&&ls — no spaces at all

False-positived (legitimate single commands that got blocked):

  • find . -name x -exec rm {} \; — the trailing \; of find -exec
  • cat x.json | jq -r ".a; .b" — a semicolon inside a string literal

The regex never accounted for shell quoting, so it could not tell a chain operator from the same character inside a quoted or escaped span.

How does this change address the issue?

Strip quoted spans and backslash-escapes first, then test what remains:

const CHAIN_OPERATORS = /&&|\|\||;/; // a single `|` is a pipe, which is allowed
const BACKGROUND_AMPERSAND = /(?<![<>])&(?!>)/; // a lone `&` backgrounds the command, but `2>&1` and `&>file` are redirections

function stripQuotedAndEscaped(command) {
  return command.replace(/\\.|'[^']*'|"(?:\\.|[^"\\])*"/g, "");
}

Because the operators are matched against the stripped string, they no longer need whitespace anchors — which is what fixes the missed cases.

Chaining and backgrounding are also split into two separate checks so each error message names its own fix. The backgrounding message now points at the Bash tool's run_in_background option, which the single combined message could not do. BACKGROUND_AMPERSAND uses lookaround so 2>&1 and &>file are treated as redirections rather than backgrounding.

What side effects does this change have?

  • Commands that previously slipped through are now blocked. This is the intended fix, but agents mid-task in existing repos will start seeing exit-2 on cd x; y forms that used to run.
  • find -exec ... \; and quoted semicolons stop being blocked, so anything that had worked around the false positive no longer needs to.
  • Pipes (|) remain allowed, unchanged.
  • Failure mode is unchanged: the hook still silently no-ops on any internal error rather than blocking.

How is this change tested?

Smoke-tested by piping hook-shaped JSON into the real hook and checking the exit code:

command expected result
cd /some/path; git diff blocked blocked (regression case)
cd a&&ls blocked blocked (regression case)
pnpm dev & blocked, names run_in_background blocked
find . -name x -exec rm {} \; passes passes
cat x.json | jq -r ".a; .b" passes passes
uv run pytest tests/unit 2>&1 | tail -20 passes passes

Invocation used:

echo '{"tool_input":{"command":"cd /some/path; git diff"}}' | node .claude/hooks/bash-chain-guard.js

Also, I played around with it for a while in a downstream repo

Summary by CodeRabbit

  • Bug Fixes
    • Improved command safety checks by detecting command chaining and background execution.
    • Preserved support for valid output redirection patterns.
    • Added clearer guidance when a command is blocked.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ejfine, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7d4766e5-f9e0-4e5b-ac24-07a872cd5450

📥 Commits

Reviewing files that changed from the base of the PR and between 62afb4d and 42beb49.

📒 Files selected for processing (1)
  • .claude/hooks/bash-chain-guard.js
📝 Walkthrough

Walkthrough

The Bash hook now removes quoted strings and escaped characters before scanning commands. It blocks chaining operators and standalone backgrounding while allowing supported redirection forms. It reports each violation type with a specific remediation message.

Changes

Bash command validation

Layer / File(s) Summary
Sanitize and classify shell operators
.claude/hooks/bash-chain-guard.js
The hook sanitizes command text before scanning. It rejects &&, `

Estimated code review effort: 3 (Moderate) | ~15–30 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the quoting fix and the split error messages in the Bash chain guard.
Description check ✅ Passed The description explains the problem, solution, side effects, and testing, but omits the issue link and Other sections.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/hooks/bash-chain-guard.js:
- Around line 12-13: Update stripQuotedAndEscaped so each removed quoted or
escaped span is replaced with whitespace or another non-operator sentinel rather
than deleted, preserving token separators and preventing adjacent shell
operators from becoming standalone. Add regression tests covering redirection
followed by an escaped character and by a quoted character before &, ensuring
BACKGROUND_AMPERSAND does not allow either form.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4290d225-0de2-43d6-8ec8-386f5079ed6b

📥 Commits

Reviewing files that changed from the base of the PR and between 4885a0a and 62afb4d.

📒 Files selected for processing (1)
  • .claude/hooks/bash-chain-guard.js

Comment thread .claude/hooks/bash-chain-guard.js Outdated
stripQuotedAndEscaped deleted quoted/escaped spans, which could join
previously-separate shell tokens. `printf hi >\x&` collapsed to
`printf hi >&`, so the BACKGROUND_AMPERSAND lookbehind read the `&` as
part of a redirection and let a real backgrounding operator bypass the
guard.

Replace each stripped span with a single space instead of deleting it,
keeping tokens separated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ejfine
ejfine marked this pull request as ready for review August 6, 2026 11:04
@ejfine
ejfine requested a review from zendern August 6, 2026 11:04
@ejfine
ejfine merged commit b7bedc2 into main Aug 6, 2026
7 checks passed
@ejfine
ejfine deleted the more-chain-guard branch August 6, 2026 14:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants