Skip to content

guardrails: block-hook-bypass _py_write path( indicator false-positives on read-only os.path.*path( helpers #1178

Description

@kyle-sexton

Summary

guardrails hooks/block-hook-bypass.sh python-write rule false-positives on read-only path
arithmetic. A pure path-compute + print command is blocked as a filesystem-write bypass:

python3 -c "import os,sys; print(os.path.normpath(os.path.join(sys.argv[1],sys.argv[2])))" "$a" "$b"

BLOCKED: python3 -c file write bypasses Write/Edit hooks. The command writes nothing.

Root cause

The python3 -c invocation detection is correct. The defect is the _py_write write indicator
(hook line 309):

_py_write='open[[:space:]]*\(|\.write[[:space:]]*\(|pathlib|path[[:space:]]*\('

path[[:space:]]*\( is an unanchored substring meant to catch pathlib.Path(-style writes, but it
also matches path( as the suffix of a longer identifier: os.path.normpath( ends in ...path(.
The same over-match hits every read-only os.path.*path( helper — normpath(, abspath(, realpath(,
relpath(, commonpath( — none of which writes anything.

Reproduced end-to-end through the hook

command before after
os.path.normpath(...) exit 2 (blocked ❌) exit 0 (allowed ✓)
os.path.abspath(...) exit 2 (blocked ❌) exit 0 (allowed ✓)
os.path.realpath/relpath(...) exit 2 (blocked ❌) exit 0 (allowed ✓)
os.path.join(...) producer exit 2 (blocked ❌) exit 0 (allowed ✓)
open('x','w').write('a') exit 2 (blocked ✓) exit 2 (blocked ✓)
pathlib.Path('x').write_text('a') exit 2 (blocked ✓) exit 2 (blocked ✓)

Fix

Identifier-boundary-anchor the pathlib / path( indicators so they match pathlib.Path( but not a
*path( suffix:

_py_write='open[[:space:]]*\(|\.write[[:space:]]*\(|(^|[^[:alnum:]_])pathlib|(^|[^[:alnum:]_])path[[:space:]]*\('

Real writes stay blocked: .write_text(/.write_bytes( match \.write[[:space:]]*\( independently, and
pathlib with a leading boundary still fires. Regression fixtures for each os.path.*path( helper
(MUST-stay-quiet) plus a pathlib.Path().write_text (MUST-block) added to block-hook-bypass.test.sh.

Acceptance

  • python3 -c "…os.path.normpath(os.path.join(a,b))…" (and the other *path( helpers) is NOT blocked.
  • python3 -c "…pathlib.Path(x).write_text(y)…" and python3 -c "open('f','w')…" ARE still blocked.

Relation

New instance of the guardrails false-positive-over-fire class tracked by #547 (whose member list —
diff-scope, quoted-arg, boolean-intent, stdin, path-branch — does not include this _py_write
substring over-match). Per #547's convention the false positive lands as a regression fixture.

Residual (pre-existing, out of scope): the boundary pathlib token still matches a read-only
import pathlib; print(...), and open( matches read-opens — broader indicator-precision items, not
this path( substring bug.

Producer: SW2030 (Windows/PowerShell). Verified against guardrails@0.13.0 on Claude Code 2.1.218.

Metadata

Metadata

Assignees

Labels

priority: mediumReal value, no hard deadline; normal backlog flow.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions