fix(approval): gate unattended VCS mutations - #60077
Conversation
Related: fixes #60056 (autonomous session merged a prod PR with no approver). Part of the unattended-approval-context cluster: #58673 (gateway-hosted cron/kanban skip |
|
I audited my later draft #60799 against this PR and am closing mine as a duplicate because this one was submitted first and has the broader unattended-session coverage. Two concrete security cases from that audit may be worth adding here before merge:
My draft also covered mutating |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for addressing a real approval-boundary gap. Current main still only detects force-push variants in tools/approval.py:762-763, and its test suite explicitly treats normal git push as safe at tests/tools/test_approval.py:1724-1728.
Problems
gateway/run.py:10612passesevent=into_set_session_env, but existing one-argument test doubles were not updated. The PR CI fails gateway suites withTypeError: ... unexpected keyword argument 'event'.tools/approval.py:770only catchesgh apiwith explicit-X/--method; it misses the discussed GraphQL mutation form without either flag.- The generic
execute_codesession approval remains reusable attools/approval.py:2817-2821, so it cannot serve as explicit consent for a later raw-Python remote mutation.
Suggested changes
- Update affected gateway mocks and rerun the failing gateway coverage.
- Add GraphQL-mutation and generic-execute-code-session regression cases, and ensure remote mutations require their own consent key.
Automated hermes-sweeper review.
Remote repository mutations need a consent boundary even when they are not shell-destructive. This change teaches approval detection about VCS/gh mutation commands and marks kanban notification wakeups as unattended so no-human turns fail closed instead of queuing prompts no one can answer. Constraint: Preserve existing local headless auto-approve behavior unless the turn is explicitly unattended. Rejected: Block every headless local command | too broad for existing local automation contracts. Rejected: Add a new user-facing approval config key | the existing approvals.cron_mode trust switch already models no-approver jobs. Confidence: high Scope-risk: moderate Directive: Keep remote mutation approval semantics path-wide; execute_code and terminal dispatch must not diverge for unattended sessions. Tested: ./.hermes/with-env.sh python -m pytest tests/tools/test_approval.py tests/tools/test_gnu_long_option_abbreviation_bypass.py tests/tools/test_execute_code_approval_cluster.py tests/gateway/test_session_context_inheritance.py tests/tools/test_local_env_session_leak.py -q Tested: ./.hermes/with-env.sh python -m pytest tests/tools/test_approval_deny_rules.py tests/tools/test_cron_approval_mode.py -q Tested: ./.hermes/with-env.sh ruff check tools/approval.py gateway/session_context.py gateway/run.py gateway/kanban_watchers.py tests/tools/test_approval.py tests/tools/test_execute_code_approval_cluster.py tests/tools/test_gnu_long_option_abbreviation_bypass.py tests/gateway/test_session_context_inheritance.py Tested: ./.hermes/with-env.sh python -m py_compile tools/approval.py gateway/session_context.py gateway/run.py gateway/kanban_watchers.py tests/tools/test_approval.py tests/tools/test_execute_code_approval_cluster.py tests/tools/test_gnu_long_option_abbreviation_bypass.py tests/gateway/test_session_context_inheritance.py Not-tested: Full repository pytest suite.
97436e4 to
c60b2ba
Compare
|
Addressed in c60b2ba. I updated the gateway _set_session_env test doubles for the new event= keyword and added synthetic-event coverage, added detection for gh api graphql mutations without an explicit HTTP method, and gave raw-Python remote repository mutations their own execute_code:remote_repository_mutation consent key so generic session approval cannot authorize them. |
What does this PR do?
Fixes an approval-boundary gap where unattended Hermes sessions could perform irreversible remote repository mutations without human consent.
This PR adds approval detection for remote VCS/GitHub mutations, propagates an explicit no-human session marker for kanban notification wakeups, and removes repo-shipped guidance that suggested using
execute_codeas a workaround when the security scanner blocks piped terminal commands.Related Issue
Fixes #60056
Type of Change
Changes Made
tools/approval.pygh pr merge, mutatinggh api -X/--method POST|PUT|PATCH|DELETE,gh release create/delete/edit/upload, normalgit push, and remote ref deletion viagit push.gh apiandgit push --dry-run.approvals.cron_modetrust switch.gateway/session_context.py,gateway/run.py,gateway/kanban_watchers.pyHERMES_UNATTENDED_SESSIONmarker.skills/research/research-paper-writing/references/experiment-patterns.mdexecute_codeworkaround advice with guidance to rewrite commands, split fetch/parsing steps, or ask for explicit approval.How to Test
upstream/main:detect_dangerous_command("gh pr merge 60 --squash --delete-branch")returned false, as did mutatinggh api,gh release create, normalgit push, andgit push origin --delete feature.gh apiandgit push --dry-runstay allowed.HERMES_UNATTENDED_SESSION=1+check_all_command_guards("gh pr merge ...", "local")returnsapproved=False.HERMES_UNATTENDED_SESSION=1+check_execute_code_guard(...)returnsapproved=False.Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
execute_codeas a scanner workaroundcli-config.yaml.exampleif I added/changed config keys — N/A, no new config keyCONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — N/ATests