Summary
plugins/guardrails/lib/path-detection/hardcoded-path-patterns.sh defangs Shared tokens inside a per-candidate-line while read loop, spawning a sed and a grep per candidate. On the input shape the exclusion exists to serve — many legitimate Users/Shared references — this degrades badly enough to blow a hook timeout, and a guard killed at its timeout fails open.
Mechanism
The loop's only escape is the trailing head -3. That short-circuit fires when candidates survive the defang. When every candidate is a Shared path, none survives, nothing is ever written, head -3 never closes the pipe, and the loop runs to completion — one sed + one grep per line, on every line.
So the guard is slowest precisely on innocent content, and fastest on violations. That is the wrong way round for something with a timeout.
Measurement
Measured on Git Bash (Windows), where process spawn dominates, against 400 lines of /Users/Shared/lib<N>/a.txt:
| Implementation |
Wall time |
| Before the Shared carve-out existed |
0.92s |
| Per-line defang loop (current shape) |
108s |
| Block-hoisted defang |
0.92s |
Roughly 120x, and the absolute number is well past any reasonable PreToolUse budget. This plugin has already been bitten by guards dying at their timeout (#1345).
Suggested fix
Hoist the defang out of the loop; the algorithm and the matcher stay identical.
- Run one
sed over the whole candidate block. sed is line-oriented in this pipeline — no N/H multiline commands — so hoisting cannot change any individual line's result.
- Run one
grep -nE over the defanged block to get the block-relative indices of the survivors, then select those lines from the original block (awk selecting by NR, doing no regex work) so the reported line is still the original, never the defanged copy.
- Skip the pipeline entirely when the candidate block contains no Shared token at all — a bash-builtin substring test. The defang is then a provable no-op, so every candidate survives, and the common case costs nothing beyond the current baseline.
grep -E remains the sole matcher, so no second regex dialect enters and the shared HPP_* bodies stay the single source of truth.
This shape is implemented and verified in melodic-software/medley PR #1685 (same driver family, same machine-path-patterns.sh bodies), pinned there by a bulk all-Shared regression case. Porting it back here would reconverge the two implementations rather than leave them forked.
Related
Summary
plugins/guardrails/lib/path-detection/hardcoded-path-patterns.shdefangsSharedtokens inside a per-candidate-linewhile readloop, spawning asedand agrepper candidate. On the input shape the exclusion exists to serve — many legitimateUsers/Sharedreferences — this degrades badly enough to blow a hook timeout, and a guard killed at its timeout fails open.Mechanism
The loop's only escape is the trailing
head -3. That short-circuit fires when candidates survive the defang. When every candidate is a Shared path, none survives, nothing is ever written,head -3never closes the pipe, and the loop runs to completion — onesed+ onegrepper line, on every line.So the guard is slowest precisely on innocent content, and fastest on violations. That is the wrong way round for something with a timeout.
Measurement
Measured on Git Bash (Windows), where process spawn dominates, against 400 lines of
/Users/Shared/lib<N>/a.txt:Roughly 120x, and the absolute number is well past any reasonable PreToolUse budget. This plugin has already been bitten by guards dying at their timeout (#1345).
Suggested fix
Hoist the defang out of the loop; the algorithm and the matcher stay identical.
sedover the whole candidate block.sedis line-oriented in this pipeline — noN/Hmultiline commands — so hoisting cannot change any individual line's result.grep -nEover the defanged block to get the block-relative indices of the survivors, then select those lines from the original block (awkselecting byNR, doing no regex work) so the reported line is still the original, never the defanged copy.grep -Eremains the sole matcher, so no second regex dialect enters and the sharedHPP_*bodies stay the single source of truth.This shape is implemented and verified in
melodic-software/medleyPR #1685 (same driver family, samemachine-path-patterns.shbodies), pinned there by a bulk all-Shared regression case. Porting it back here would reconverge the two implementations rather than leave them forked.Related