fix: terminal tool crashes with 'embedded null byte' when command references a binary via absolute path - #79151
Conversation
…erences a binary via absolute path The gateway lifecycle guard (cron/lifecycle_guard.py) scans command tokens for referenced shell scripts. _read_referenced_script already skips binaries (NUL byte in first chunk -> 'nothing to scan', NousResearch#76762), but tools/terminal_tool.py passes a remote-read fallback (_read_script_in_env) as read_remote_script=. When the local read reports 'nothing to scan', the guard calls the fallback, which decoded the ELF bytes with errors='replace'. NUL (U+0000) is valid UTF-8, so it survives into the returned text. The guard then recursed into that NUL-laden text as if it were a shell script; tokenization produced paths with embedded NUL bytes and os.open raised ValueError: embedded null byte, failing every terminal call that references a binary by absolute path (e.g. venv python). Fix the whole bug class: - tools/terminal_tool.py: _read_script_in_env now mirrors _read_referenced_script and returns None when the file chunk contains a NUL byte (binary == nothing to scan); also guards the remote cat output for NUL. - cron/lifecycle_guard.py: _read_referenced_script tolerates ValueError from os.open just as it already tolerates it from Path.resolve, so a NUL-bearing path token can never crash the guard. Adds two regression tests: - test_binary_read_via_remote_callback_does_not_crash_guard (reproduces the gateway path: read_remote_script decodes a binary with errors='replace') - test_nul_bearing_script_path_does_not_crash_guard (defense-in-depth) Full suite: 84 passed. Runtime verified after the gateway was restarted: venv/bin/python -c 'print(1)' previously crashed, now runs clean.
|
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
ValueError: embedded null bytecrash in theterminaltool when a command references a binary by absolute path (e.g. the Hermes venv python). Fixes the whole bug class, including the sibling call path missed by #76762.Root cause
cron/lifecycle_guard.pyalready skips binaries in_read_referenced_script(NUL byte in first chunk -> "nothing to scan", #76762). Buttools/terminal_tool.pypasses_read_script_in_envasread_remote_script=. When the local read reports "nothing to scan", the guard calls the fallback, which decoded ELF bytes witherrors="replace". NUL (U+0000) is valid UTF-8, so it survives into the returned text. The guard then recurses into that NUL-laden text as if it were a shell script; tokenization produces NUL-bearing paths andos.openraisesValueError: embedded null byte, failing every terminal call that references a binary by absolute path.Changes
tools/terminal_tool.py:_read_script_in_envnow mirrors_read_referenced_script— returnsNonewhen the file chunk contains a NUL byte (binary == nothing to scan); also guards the remotecatoutput for NUL.cron/lifecycle_guard.py:_read_referenced_scripttoleratesValueErrorfromos.openjust as it already tolerates it fromPath.resolve, so a NUL-bearing path token can never crash the guard.Tests
Adds two regression tests:
test_binary_read_via_remote_callback_does_not_crash_guard— reproduces the gateway path (aread_remote_scriptcallback that decodes a binary witherrors="replace")test_nul_bearing_script_path_does_not_crash_guard— defense-in-depth for NUL-bearing path tokensFull suite: 84 passed. Runtime verified after gateway restart:
venv/bin/python -c 'print(1)'previously crashed, now runs clean.Closes #79148