fix(gateway): bound lifecycle scans of binary paths - #79375
Conversation
Distinguish missing paths from local binaries before remote fallback, reject unscannable NUL path candidates without crashing, and cap remote script reads before recursive lifecycle scanning.\n\nAdds regression coverage for local and remote binaries, oversized remote content, missing remote scripts, and post-open read failures.\n\nFixes NousResearch#78800.
fd01eba to
3e297e4
Compare
|
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
ValueErroror calling the remote readerWhy this is still needed after #77703
#77703 prevents the eventual
ValueErrorand skips NUL-bearing output, but the remote fallback still uses an unboundedcat. For a local executable larger than 1 MiB,_read_script_in_env()skips its bounded local branch and then cats the same file through the environment. The NUL check only runs after the entire binary has been captured.This PR fixes that remaining memory/hang path by distinguishing
missingfrompresent but binary, and by bounding genuinely remote reads. In production, a 273 MB Rust debug ELF drove the gateway to multi-GB memory use and stalled it before the executable ran.Root cause
_read_referenced_script()returnedNoneboth when a local path was missing and when it had positively identified a NUL-bearing binary. The caller interpreted both states as remotely missing and invokedread_remote_script. Path-based executables could therefore be read again and fed into the lifecycle regex/path walk.The tri-state result now permits remote fallback only when
os.open()could not open the path. Once a local path has opened, binary detection or a later read failure cannot reclassify it as remotely missing.Tests
uv run --extra dev pytest -q tests/hermes_cli/test_gateway_restart_loop.py tests/tools/test_terminal_tool.py tests/cron— 493 passeduv run --extra dev ruff check cron/lifecycle_guard.py tools/terminal_tool.py tests/hermes_cli/test_gateway_restart_loop.py tests/tools/test_terminal_tool.py— passedpython -m py_compilefor all modified Python files — passedtarget/debug/solana-deploylifecycle-guard harness — allowed in 0.004s with no remote callbackOne pre-existing cron test warning remains:
_send_to_platformcoroutine was never awaited.Fixes #78800
Related: #76762, #77703, #78942