fix(guard): skip NUL-binary text in recursion and bound remote script reads - #80241
fix(guard): skip NUL-binary text in recursion and bound remote script reads#80241tilllt wants to merge 1 commit into
Conversation
… reads After NousResearch#79454 two fringe cases remain in the lifecycle-guard scanner: - _contains_unsafe_gateway_action still feeds NUL-bearing binary text returned by read_remote_script callbacks into the recursion, which re-tokenizes machine code into NUL-bearing paths and crashes os.open with ValueError: embedded null byte (NousResearch#77703, NousResearch#77780). - The remote `cat` fallback in _read_script_in_env has no size bound, so a large remote binary (>1 MiB) is fully scanned, mirroring the oversized-content path that triggers false-positive blocks. Treat NUL-bearing script text as "nothing to scan" at the recursion site (mirroring _read_referenced_script) and apply the same 1-MiB bound as the local branch to the remote cat fallback. Verified with guard-fringe-tests.py against this branch: T3 (recursion guard) and T4 (remote size bound) now pass; T1/T2 (expanduser NUL in path resolution) are a separate fix and intentionally out of scope.
monerostar
left a comment
There was a problem hiding this comment.
Ubuntu 26.04 on linux-5800x.
Spot-check: this helps the remote script_text NUL path, but the fuller suite from #80258 still fails 6 cases here on this branch (tilde+NUL walk, oversized remote fail-closed, adversarial total-function). Prefer landing #80258 as the whole-class fix; this can fold into it or close as superseded once that merges.
|
Closing as superseded by #80258 — but this one deserves specific credit: your regression suite on #79454 (T1–T4, the expanduser-before-os.open trace, the recursion-boundary analysis, and the remote size-bound gap) is what mapped the residual class and drove the architectural fix. #80258 adopts T1–T4 verbatim as regression tests, sanitizes at the three boundaries you identified (ingestion path candidates, recursion-boundary callback text, bounded remote read at the source), and adds a never-raises adversarial sweep + a walk-crash fallback so future sibling frames degrade to a logged warning instead of a production outage. 0/4 of your tests passed on merged main; 4/4 pass on #80258. Thank you for the rigor — this is exactly the kind of analysis that turns hot-fixes into a closed bug class. |
|
Superseded by #80258 — the ingestion-level sanitizer ( |
What does this PR do?
Closes the two remaining fringe cases in the lifecycle-guard scanner that
survived #79454 (merged as
49d8a155c4):NUL-binary text in the recursion (
cron/lifecycle_guard.py):_contains_unsafe_gateway_actionfeedsscript_textreturned by aread_remote_scriptcallback into the recursive scan without checkingfor NUL bytes. Remote backends (terminal_tool's env-based
catfallback, SSH/Modal/Daytona) can return binary content; re-tokenizing
machine code produces NUL-bearing paths that crash
os.openwithValueError: embedded null byte(terminal guard crashes with embedded null byte when remote-read fallback feeds binary content into the scanner (full-path ELF executables) #77703, lifecycle_guard crashes onValueError: embedded null bytefrom os.open — breaks all terminal commands #77780). This mirrors theexisting NUL skip in
_read_referenced_scriptand treats such text as"nothing to scan".
Unbounded remote
cat(tools/terminal_tool.py):the env-based
catfallback in_read_script_in_envhad no sizebound. A large remote binary (>1 MiB) was fully scanned, hitting the
same oversized-content path that triggers false-positive blocks
("cannot restart or stop the gateway"). Applies the same 1-MiB bound
as the local read branch.
Verification
guard-fringe-tests.pyagainst this branch (importslifecycle_guard.pyvia importlib and statically inspects
terminal_tool.py):For comparison, main + #79454 (
49d8a155c4) fails 0/4 of these fringetests. T1/T2 (expanduser NUL in
_resolve_terminal_script_path/_resolve_script_path) are a separate, intentionally out-of-scope fix.Related
ValueError: embedded null bytefrom os.open — breaks all terminal commands #77780 (macOS + Linux evidence)