fix(cron): guard against NUL-byte crash in lifecycle_guard scanners - #78572
Shunkleburger wants to merge 1 commit into
Conversation
Sibling of NousResearch#76762: os.open() raises ValueError ('embedded null byte') on NUL-containing paths, but _read_referenced_script() only caught OSError, letting the crash escape and block every terminal command when a scanned script's decoded binary contents tokenize into a NUL-containing path. - _iter_command_segments: strip \x00 at the tokenizer (covers all three scanners: path refs, bash -c payloads, launchctl detection) - _read_referenced_script: catch ValueError alongside OSError - _resolve_script_path: NUL-strip before Path() construction Verified: tests/hermes_cli/test_gateway_restart_loop.py 82 passed; NUL-path repro returns safely; gateway lifecycle detection unchanged.
|
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
The terminal
lifecycle_guardscanner crashes withValueError: embedded null bytewhen a scanned command references a script whose decoded binary contents tokenize into a NUL-containing path.os.open()raisesValueError, but the crash sites only catchOSError— so a single bad token blocks every terminal command until the gateway recovers. This is the sibling of #76762, which fixed theresolve()path but missed the script-content path.Root cause
_read_referenced_script()doesos.open(path, flags)insideexcept OSError—os.openraisesValueError: embedded null bytefor NUL-containing paths, which escapes uncaught.Upstream, the crash propagates through both scanners:
scan_for_referenced_scripts()(cronscript:values) via_resolve_script_path()scan_command_for_gateway_lifecycle_commands()(terminal commands) via the shell-tokenized command segments — NUL bytes leak into tokens when a scanned script's binary contents are decodedFix (3 defensive layers, same class as #76762)
_iter_command_segments()\x00at the tokenizer — covers all three scanners (path refs,bash -cpayloads,launchctldetection)_read_referenced_script()except (OSError, ValueError)at theos.opencrash site_resolve_script_path()Path()construction (cronscript:values)NUL bytes are never valid shell characters or path components, so stripping them cannot change legitimate behavior.
Verification
tests/hermes_cli/test_gateway_restart_loop.py— 82 passedhermes gateway restartstill blocked, normal cron script values resolve identicallyTest plan for CI
tests/hermes_cli/test_gateway_restart_loop.pybash /tmp/evil\x00path.sh) throughcontains_gateway_lifecycle_command_or_referenced_script()and asserting it returnsFalsewithout raising.