Skip to content

guardrails: per-line Shared defang blows the hook timeout on Shared-heavy content (fails open) #1792

Description

@kyle-sexton

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions