fix(terminal): guard against NUL bytes in referenced script scanning - #79390
fix(terminal): guard against NUL bytes in referenced script scanning#79390argokaz wants to merge 1 commit into
Conversation
The lifecycle guard's fallback script reader (_read_script_in_env in tools/terminal_tool.py) read files as text WITHOUT filtering binaries, while the primary reader (_read_referenced_script in cron/lifecycle_guard.py) correctly returns None for files containing NUL bytes (NousResearch#76762). When a terminal command references a binary (e.g. venv/bin/python), the fallback decoded the ELF as text, tokenized machine code into junk paths, and the recursion hit os.open(path) with an embedded NUL, crashing with "embedded null byte" on every terminal-tool call. Two fix sites needed: 1. LOCAL read path: return None when the bytes contain a NUL byte (mirrors _read_referenced_script). 2. cat FALLBACK path: the local read is skipped for files >1MB (st_size <= 1024*1024 guard), so a large binary falls through to env.execute("cat ...") whose output carries live NUL bytes — return None when the output contains a NUL byte. Fixes the crash class reported in NousResearch#77988.
Duplicate of #79022: both patches add the same local and remote NUL-binary guards in |
|
Closing as superseded by #80258, which fixes this whole bug class architecturally rather than per-callsite: path candidates are sanitized once at the ingestion boundary (NUL/empty/unexpandable tokens rejected before any OS call), text from any Your report and fix targeted a real member of this class — thank you. The per-callsite patches kept leaving sibling frames exposed (#76762 → #77703 → #77780 → #78256 each crashed one frame away from the previous fix), which is why we went with the boundary fix instead of merging the fragments individually. #80258 carries regression tests for the NUL-path, binary-callback, oversized-read, unset-HOME, and walk-crash cases plus an adversarial never-raises sweep. |
Summary
Fixes the "embedded null byte" crash in the terminal tool's lifecycle guard. When a terminal command references a binary (e.g.
venv/bin/python), the guard's fallback script reader (_read_script_in_envintools/terminal_tool.py) read the file as text without filtering binaries. The ELF got decoded as text, machine code was tokenized into junk paths, and the recursion hitos.open(path)with an embedded NUL →ValueError: embedded null byteon every terminal-tool call.The primary reader (
_read_referenced_scriptincron/lifecycle_guard.py) already returnsNonefor files containing NUL bytes (#76762) — this PR mirrors that behavior in the fallback path, which was missed.Changes
Two fix sites in
_read_script_in_env:Nonewhen the bytes contain a NUL byte (if b"\x00" in data: return None), mirroring_read_referenced_script.catfallback path — the local read is skipped for files >1MB (st_size <= 1024*1024guard), so a large binary falls through toenv.execute("cat ...")whose output carries live NUL bytes (0x00 is valid UTF-8;errors="replace"does NOT strip it). Addedif "\x00" in output: return Nonebefore returning.Repro
Any terminal command referencing a binary path crashes the guard:
Workarounds existed (run via a separate
.pyfile, avoid$VARpaths in scripts, split compound commands) but the root cause is the unfiltered binary read.Test plan
mainwithvenv/bin/python-referencing commandsCloses #77988