Skip to content

Harden auto-approve edit gate: treat shell-rc and config-execution files as sensitive - #63438

Open
spartan8806 wants to merge 2 commits into
NousResearch:mainfrom
spartan8806:harden-auto-approve-sensitive-paths
Open

spartan8806 wants to merge 2 commits into
NousResearch:mainfrom
spartan8806:harden-auto-approve-sensitive-paths

Conversation

@spartan8806

Copy link
Copy Markdown

What

Expands the auto-approve edit gate's sensitive-path check (acp_adapter/edit_approval.py) so it also treats shell startup files, tool config files, and user persistence locations as sensitive, so they keep prompting even under autonomous policies.

Why

should_auto_approve_edit intentionally keeps sensitive paths prompting even under autonomous policies (its docstring: "sensitive paths still ask even under autonomous policies"). But _is_sensitive_auto_approve_path only covered .env*, id_rsa/id_ed25519, and .git/.ssh path components. It missed the files that most directly lead to code execution or persistence:

  • shell startup / login files: .bashrc, .bash_profile, .profile, .zshrc, .zshenv, ... (run at the next shell/login)
  • tool config that executes or redirects trust: .gitconfig (alias/pager gadgets), .npmrc, .pypirc, .netrc, .envrc
  • persistence: .config/autostart/*.desktop, .config/systemd/user/*.service

Under the session auto-approve policy these were written with no prompt, which is an easy path to running attacker-influenced content at the next shell/git/login.

Change

  • Adds the shell/tool rc/config names to the sensitive-name set.
  • Treats any dotfile located directly in the user's home directory as sensitive (catches rc files not individually enumerated).
  • Adds autostart/systemd to the sensitive path components.

Ordinary in-project edits still auto-approve; only home-directory config and persistence files now prompt. Offered as a hardening improvement per SECURITY.md §3.2 ("the approval gate can always catch more patterns").

Verified the change blocks the files above under every non-ask policy while leaving normal in-project file edits auto-approving.

…sitive

The auto-approve edit gate keeps "sensitive" paths prompting even under
autonomous policies, but the sensitive-path check only covered .env files,
SSH private keys, and .git/.ssh path components. It missed shell startup
files (.bashrc, .zshrc, .profile, ...), .gitconfig, .npmrc, and user
persistence locations (.config/autostart, .config/systemd). Under the
session auto-approve policy those were written without a prompt, which is
an easy way to get code to run at the next shell, git invocation, or login.

This expands the sensitive-name set to cover shell and tool rc/config files,
treats any dotfile directly in the user's home directory as sensitive, and
adds autostart/systemd path components. Ordinary in-project edits still
auto-approve; only home-directory config and persistence files now prompt.

Hardening improvement per SECURITY.md 3.2 (the approval gate can catch more
patterns).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@alt-glitch alt-glitch added type/security Security vulnerability or hardening comp/acp Agent Communication Protocol adapter P3 Low — cosmetic, nice to have labels Jul 12, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for hardening the ACP edit gate. The reported single-file gap is present on current main: acp_adapter/edit_approval.py:192-209 does not classify shell RC/config paths as sensitive, and the requester bypasses the prompt when that predicate permits the session policy (acp_adapter/edit_approval.py:299-304).

Problems

  • acp_adapter/edit_approval.py:164 represents multi-file V4A targets as ", ".join(paths). The proposed check evaluates that combined display string as one Path, so a mixed patch containing ~/.bashrc can still bypass the prompt under the session policy. Preserve and evaluate each V4A target independently.
  • The diff changes only acp_adapter/edit_approval.py; it adds no regression tests. Current coverage at tests/acp/test_edit_approval.py:247-269 checks only .env, not the proposed shell/config/persistence paths or multi-file V4A behavior.

Suggested changes

  • Carry the individual V4A target paths through the proposal/security decision, while retaining the joined path only for display.
  • Add focused tests for the new protected paths and a mixed sensitive/non-sensitive V4A patch.

Automated hermes-sweeper review.

Comment thread acp_adapter/edit_approval.py Resolved
…add regression tests

Carry the individual V4A target paths through EditProposal.target_paths so the
sensitivity and workspace-containment checks evaluate each real path instead of
the ", ".join(...) display string. Fixes the multi-file bypass where a sensitive
or out-of-workspace file that is not the trailing path could still auto-approve.
Adds regression tests for the new protected paths and multi-file V4A behavior.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01D9UWNzd6Zh4MNsGdqMDm5t
@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 16, 2026
@spartan8806

Copy link
Copy Markdown
Author

Following up on this — the two items from the review are addressed:

  • Multi-file V4A bypass: 843495c carries each V4A target path through the security decision independently, so a mixed patch containing e.g. ~/.bashrc no longer evaluates as one joined display string. The joined form is retained for display only, as suggested.
  • Test coverage: tests/acp/test_edit_approval.py adds 108 lines covering the new protected paths (shell RC / config-execution persistence) and the mixed sensitive + non-sensitive V4A case.

Happy to rebase or adjust anything if the approach isn't what you had in mind.

@egilewski

Copy link
Copy Markdown

suggesting changes

The patch improves ACP edit approval for V4A targets and several sensitive locations, but executable shell startup/configuration files outside the current sensitive-name and directory rules can still be auto-approved. A model-controlled edit to one of those files can persist code without an ACP permission request.

  • [P2] Executable shell startup/configuration files outside the sensitive policy remain auto-approved
    The approval predicate recognizes a fixed set of names and directory components, but not all supported shell startup/configuration locations. In session mode this leaves unlisted executable startup files outside the approval prompt, so a later shell invocation can execute attacker-controlled content.
    Remediation: Use one canonical execution/configuration-file policy shared with the file tools and cover Fish, Nushell, PowerShell, and other supported shell startup locations (or default to approval for executable home configuration). Add positive tests for every supported shell family and negative tests proving each is denied under both non-ask modes.

Security evidence:

  • trust boundary: ACP model-controlled write and patch operations pass through the edit-approval decision before the file-write sink.
  • source/sink/invariant: Sensitive targets force a prompt; the invariant remains incomplete for executable startup/configuration files outside the current allowlists.
  • current-main reproduction: The current implementation accepts an unlisted nested startup/configuration target under non-ask session policy while denying listed sensitive startup files.
  • PR-head or patch-replay validation: The patch preserves individual targets and blocks listed sensitive and outside-workspace targets, but does not add coverage for the unlisted startup/configuration family.
  • positive/negative cases: Coverage includes shell/login files, credential/VCS/persistence locations, mixed targets, safe workspace files, and outside-workspace targets; unlisted startup/configuration files remain accepted.
  • residual bypass search: Approval builders, patch target extraction, and write dispatch are covered; the remaining gap is the unlisted executable shell/configuration startup family.
  • reviewer validation: Source review and regression coverage confirm the residual family is still auto-approved.

Review setup: I reviewed a run-owned local rebase or patch replay against current GitHub main because the submitted branch is stale or conflicted; this does not mean the submitted branch itself merges cleanly.

Not checked:

  • Full repository test suite
  • Ruff validation
  • Windows-specific runtime validation

Signed: GPT-5.6-luna-max in Codex

@spartan8806

Copy link
Copy Markdown
Author

Thanks for the review, this is a fair catch and I agree with it. A name-based allowlist was the
wrong shape for this and an unlisted startup file is exactly the case it misses.

Before I push a fix I want to check the intended scope with you, because I think the gap is wider
than this PR.

The canonical policy you pointed me at, _SHELL_RC_FILES in tools/approval.py, currently matches
bashrc, zshrc, profile, bash_profile and zprofile. So if I make the ACP path share it as
written, the behaviour you flagged does not change, it just moves to one place. Fish, Nushell and
PowerShell startup files are not covered there either. Searching the repo, config.fish appears only
in scripts/install.sh, nushell only in tools/environments/local.py, and I get no hits at all
for Microsoft.PowerShell_profile or PROFILE.ps1.

If I am reading that correctly it would also affect the shell-command path and not just ACP edits,
though you would know far better than I would whether something else covers that case.

I did not want to touch a shared predicate the command path depends on, so I have not changed it. My
default, unless you would rather otherwise, is to keep this PR narrow: fix the ACP path against a
property-based check rather than a name list, and leave anything to do with the shared policy alone
for you to scope. If it is useful I can write up the command-path observation as a separate issue so
it is on record, or leave it entirely, whichever you prefer.

Happy to go a different direction if you have one in mind. It is your call and I do not want to
widen the diff on you.

Two smaller things:

  • On staleness, you are right that the branch is behind, currently by about 7700 commits, and I will
    rebase. For what it is worth acp_adapter/edit_approval.py itself has not changed on main since 12
    July, so the rebase should be mechanical rather than a rewrite.
  • You noted Windows runtime validation was not checked. I develop on Windows, so I can cover the
    PowerShell profile locations properly, including the per-host profile paths and the
    $PROFILE.AllUsersAllHosts case. I will include that in whichever option you pick.

Let me know which scope you want and I will get it up.

@Enough1122

Copy link
Copy Markdown

AI code review — automated review for reference, author can ignore or act on any point.

Harden auto-approve edit gate: treat shell-rc and config-execution files as sensitive

  1. SENSITIVE_AUTO_APPROVE_DIR_PARTS covers .git, .ssh, autostart, systemd — but not the credential dirs the sibling dashboard hardening treats as sensitive (.aws, .kube, .gnupg, .docker, .azure in hermes_cli/web_server.py's _SENSITIVE_MANAGED_DIR_NAMES). Under the session policy, an edit to ~/.aws/credentials or ~/.kube/config would still auto-approve. Consider aligning the two sets.

  2. The home-dotfile catch-all in _is_sensitive_auto_approve_path resolves symlinks before comparing parents (expanded.resolve(strict=False).parent == Path.home().resolve(strict=False)), so a home dotfile that is a symlink into another directory (e.g. ~/.mylogin → /opt/tool/rc) escapes the catch-all — its resolved parent is not home. The enumerated name set covers the common ones, but the catch-all's resolution order leaves an evasion path; checking the unresolved parent too would close it.

  3. V4A parsing: _proposal_for_patch_v4a captures target_paths from the parsed paths, but the new tests only exercise *** Update File: directives. V4A patches also support Delete/Rename directives — if those paths aren't captured, a patch that renames or deletes a sensitive path could under-report target_paths and auto-approve. Worth verifying the parser covers every directive type.

@alt-glitch alt-glitch removed the sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data label Aug 15, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/acp Agent Communication Protocol adapter P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants