fix(approval): guard literal current-PID self-kill - #6234
dorukardahan wants to merge 10 commits into
Conversation
6f31ad2 to
a4bbd2b
Compare
a4bbd2b to
7522c65
Compare
teknium1
left a comment
There was a problem hiding this comment.
Thanks for covering a real self-termination gap: current tools/approval.py:639-648 covers name- and pgrep-based forms but not a literal current PID.
Problems
agent/credential_pool.py:114changes the fallback for every 429 lackingreset_atto 60–90 seconds. Currentagent/agent_runtime_helpers.py:833-846treatsusage_limit_reachedas a credential-exhaustion path, whileagent/credential_pool.py:251-257deliberately uses the one-hour fallback when no reset is available. This would re-probe quota-exhausted credentials without a provider signal that the limit is transient.- The added
killoption branch also matcheskill -0 <own-pid>; signal 0 is a liveness check, not self-termination.
Suggested changes
- Preserve the credential-wide fallback unless a verified provider-specific transient signal justifies a shorter cooldown; retain the targeted model-scoped behavior introduced by
473d0dfe. - Re-scope the PID guard to terminating signals and add a
kill -0non-match test.
Automated hermes-sweeper review.
|
Follow-up hardening in ec269f5: replaced the single-layout self-PID regex with quote-aware command-position |
|
Rebased-by-merge onto current |
|
Refreshed this PR onto current Hardening added during review:
Validation on exact head
|
|
Follow-up on the final independent review:
Validation on exact head
|
|
CI follow-up on exact head The failed slice used existing one-argument detector mocks. Local execution now preserves the established
|
# Conflicts: # tests/tools/test_approval.py # tools/approval.py
Summary
Guard literal
kill <current Hermes PID>commands in the approval layer so an agent cannot silently terminate its own runtime.The guard parses real command-position
killinvocations and every PID operand instead of relying on one regex layout. It covers:-s,-n, and--signalforms--, executable paths, grouped commands, and attached redirections+PID spellings accepted bykill(1)Signal 0, signal-list/table modes, and help/version modes remain approval-free because they do not send a terminating signal.
Root cause
Existing approval rules covered name-based Hermes termination (
pkill hermes) and command-substitution forms (kill $(pgrep ...)), but static regex cannot know the current runtime PID. A dynamic parser is needed to distinguish executablekillsyntax from quoted prose while handling real argv layouts.Feedback and hardening addressed
-n, leading-zero, and+0forms.-lTERM,-L9, and--list=TERMare recognized as non-sending modes.+PID spellings normalize without integer conversion.--forms and supported execution options (command -p,exec,setsid,time, andenv) are handled.env -vu,exec -ca), and Bashtimeremains distinct from external GNUtime.killgrammars stay distinct: unsupported GNU long options are not false positives, while trailing options cannot hide an already-sent builtin TERM.env -S/--split-stringremains under the existing execution-bearing-option approval policy instead of partially reimplementing its argv grammar.;cannot hide an uninspected suffix.echo "kill <PID>"is not treated as execution.Scope / risk
killcommand.killmodes are not newly blocked.Verification
Current candidate:
dbb3d17bfb59d5bb2a14f091eed335e9b03d3731scripts/run_tests.sh -j 3 tests/tools/test_approval.py tests/tools/test_command_guards.py tests/tools/test_execution_flag_detection.py -q→ 611 passedtest_approval.py: 419 passedtest_execution_flag_detection.py: 159 passedtest_command_guards.py: 33 passedpython -m pytest tests/tools/test_denial_circuit_breaker.py tests/tools/test_execute_code_approval_cluster.py -q→ 37 passedpython -m ruff check tools/approval.py tests/tools/test_approval.pypython -m py_compile tools/approval.py tests/tools/test_approval.pygit diff --checkmain