Context
Produced by plugin-quality:audit after two live block events during an otherwise-successful
autonomous session. Audited version: 0.17.3 (active install). This is the one finding in this
audit batch with a direct, reproduced empirical confirmation, not a static-review inference.
Finding (IMPORTANT, empirically proven)
hooks/block-hook-bypass.sh:375 — the fd-duplication redirect sentinel restore silently no-ops
under Bash's patsub_replacement shopt (default-on since Bash 5.2), causing a false-positive block
on a bare echo/printf ... >&N (no leading digit) or &>target redirect with no accompanying
real file write.
The line:
normalized="${normalized//"$soh"/&}"
is meant to restore a sentinel character back to & after the &-as-separator split, so a
fd-duplication redirect like >&2 survives the segment split as one token and is correctly
recognized as NOT a real file write. Under Bash's patsub_replacement shopt (on by default since
Bash 5.2, which is what this marketplace's own CI runs — ubuntu-24.04, Bash 5.2.21), this specific
substitution form silently no-ops, so the sentinel is never restored. The result: a completely
benign echo "some diagnostic" >&2 gets misclassified as a real file-write bypass and the whole
Bash tool call is blocked.
Reproduced two ways: (1) a same-process controlled experiment toggling patsub_replacement on
vs. off against otherwise-identical code, and (2) direct invocation of the hook with the exact raw
command that triggered the block live (a multi-line telemetry-upsert script containing
echo "..." >&2 and an unrelated gh api ... >/dev/null). Both confirm the false positive
definitively.
Test-suite gap, not an environment mismatch. block-hook-bypass.test.sh passes 203/203 on the
exact same Bash build that has the bug, and the plugin's own CI runs on that same affected version
range. The suite simply never isolates a standalone >&N/&> redirect with no accompanying real
file write in the same segment — every existing test case pairs a real write with something else,
so this exact shape was never exercised.
Blast radius contained: this ${var//pat/&} anti-pattern appears exactly once in this plugin
(line 375); the shared marketplace-wide hook-utils.sh single-source-of-truth helper does not use
this pattern, so other hooks built on it are not affected.
Confirmed clean — not findings, recorded so they aren't re-litigated
- Permission-invisibility claim confirmed correct (fetched 2026-07-26,
https://code.claude.com/docs/en/hooks): PreToolUse exit 2 blocks prior to, and independent
of, Claude Code's permission system — no permissions.allow/autoMode.allow entry can prevent
this class of block. This means the earlier ask ("what permission prompts should go on the
allow-list") does not apply to this friction point at all — it needs a code fix, not a config
change.
- Kill switch is properly documented:
block_hook_bypass_enabled appears in both plugin.json's
userConfig schema and the README's kill-switch table.
- Bash/PowerShell routing correctly ruled out as a factor, and the POSIX-style
/tmp/... path
in the triggering command is inert to the trace (Git Bash on Windows resolves it fine; not
Windows-path-specific).
Suggested remediation order (cheapest → most ambitious)
- Replace the buggy substitution with a form immune to
patsub_replacement
(e.g. an escaped \& literal, or a loop-based restore) — needs empirical verification on Bash
versions older than 5.2 before shipping, since the replacement form's behavior may differ there
too.
- Add a regression test case: a standalone
echo ... >&2 / printf ... >&1 / &>target with no
real file write anywhere else in the command, run explicitly under both patsub_replacement on
and off, so this exact gap can't reopen silently on a future Bash version bump.
- Broader (optional, out of scope for this specific bug): a grep sweep of the marketplace's other
guardrails hooks for the same ${var//pat/&} anti-pattern, since this instance was found by
targeted search rather than an exhaustive one.
Context
Produced by
plugin-quality:auditafter two live block events during an otherwise-successfulautonomous session. Audited version: 0.17.3 (active install). This is the one finding in this
audit batch with a direct, reproduced empirical confirmation, not a static-review inference.
Finding (IMPORTANT, empirically proven)
hooks/block-hook-bypass.sh:375— the fd-duplication redirect sentinel restore silently no-opsunder Bash's
patsub_replacementshopt (default-on since Bash 5.2), causing a false-positive blockon a bare
echo/printf ... >&N(no leading digit) or&>targetredirect with no accompanyingreal file write.
The line:
normalized="${normalized//"$soh"/&}"is meant to restore a sentinel character back to
&after the&-as-separator split, so afd-duplication redirect like
>&2survives the segment split as one token and is correctlyrecognized as NOT a real file write. Under Bash's
patsub_replacementshopt (on by default sinceBash 5.2, which is what this marketplace's own CI runs —
ubuntu-24.04, Bash 5.2.21), this specificsubstitution form silently no-ops, so the sentinel is never restored. The result: a completely
benign
echo "some diagnostic" >&2gets misclassified as a real file-write bypass and the wholeBash tool call is blocked.
Reproduced two ways: (1) a same-process controlled experiment toggling
patsub_replacementonvs. off against otherwise-identical code, and (2) direct invocation of the hook with the exact raw
command that triggered the block live (a multi-line telemetry-upsert script containing
echo "..." >&2and an unrelatedgh api ... >/dev/null). Both confirm the false positivedefinitively.
Test-suite gap, not an environment mismatch.
block-hook-bypass.test.shpasses 203/203 on theexact same Bash build that has the bug, and the plugin's own CI runs on that same affected version
range. The suite simply never isolates a standalone
>&N/&>redirect with no accompanying realfile write in the same segment — every existing test case pairs a real write with something else,
so this exact shape was never exercised.
Blast radius contained: this
${var//pat/&}anti-pattern appears exactly once in this plugin(line 375); the shared marketplace-wide
hook-utils.shsingle-source-of-truth helper does not usethis pattern, so other hooks built on it are not affected.
Confirmed clean — not findings, recorded so they aren't re-litigated
https://code.claude.com/docs/en/hooks):
PreToolUseexit 2blocks prior to, and independentof, Claude Code's permission system — no
permissions.allow/autoMode.allowentry can preventthis class of block. This means the earlier ask ("what permission prompts should go on the
allow-list") does not apply to this friction point at all — it needs a code fix, not a config
change.
block_hook_bypass_enabledappears in bothplugin.json'suserConfig schema and the README's kill-switch table.
/tmp/...pathin the triggering command is inert to the trace (Git Bash on Windows resolves it fine; not
Windows-path-specific).
Suggested remediation order (cheapest → most ambitious)
patsub_replacement(e.g. an escaped
\&literal, or a loop-based restore) — needs empirical verification on Bashversions older than 5.2 before shipping, since the replacement form's behavior may differ there
too.
echo ... >&2/printf ... >&1/&>targetwith noreal file write anywhere else in the command, run explicitly under both
patsub_replacementonand off, so this exact gap can't reopen silently on a future Bash version bump.
guardrails hooks for the same
${var//pat/&}anti-pattern, since this instance was found bytargeted search rather than an exhaustive one.