Repository navigation
Bypass-hunt release: four same-class escapes of the v0.5.0 fixes, all
reproduced under the headline network-deny.yaml profile (shell granted)
before being fixed. Every shape was ALLOWED at v0.5.0.
Fixed
- Pipe into an interpreter hid the script's target
(echo 'cat ~/.ssh/id_rsa' | bash). The v0.5.0 fix recursed into
bash -cpayloads, butX | bashreaches the interpreter with its script
on stdin — the path hides inside a quoted argument ofXthat token-level
parsing cannot see. The pipe-into-interpreter shape now scans the preceding
segments' text for the first path-like substring (mirroring how the raw URL
scan already catchesecho 'curl …' | sh), so the read is trapped with
path-denied. - Compound commands inspected only the first segment
(true && cat ~/.ssh/id_rsa,a; b,x || y). A separator-ended prefix
(true &&) completely hid the rest of the line from the verb classifier.
_parse_bashnow splits on|,;,&&,||(shlex keeps quoted
separators inside a token, soecho 'a|b'is not split) and parses EVERY
segment, carrying the strictest surface: any segment's host, then any
segment's read path, then any write path. - Shell output redirects bypassed the write scope (
echo x > ~/.bashrc).
A>/>>(and2>/&>forms) redirect target is a file WRITE, but
no write path was ever surfaced from shell commands, so writes stayed
outside the profile'spaths.writescope (./out/**). Redirect targets
now surface asaccess="write"paths — in-scope writes (> ./out/f.txt)
stay allowed. teewrote outside the scope unimpeded (tee ~/.bashrc). Same class:
a write verb whose target was never inspected;tee's positional target
now surfaces as a write path.
Testing
8 new regression tests (tests/test_cli.py), each red on the v0.5.0 tree:
the four bypass shapes assert their specific deny rule (path-denied /
network-not-in-profile / path-not-in-profile), and four benign shapes
(in-scope redirect, in-scope pipe-to-tee, quoted pipe, plain read) assert no
over-blocking. 87 → 95 tests.