Skip to content

Approval gate: fail-closed option for non-interactive contexts + permanent allowlist bypasses cron_mode deny #60505

Description

@UELABS2022

Two related gaps in tools/approval.py (verified on current main and on a v0.18.0 install):

1. Non-interactive, non-gateway, non-cron contexts auto-approve dangerous commands with no fail-closed option

In check_dangerous_command, when the context is neither interactive CLI (HERMES_INTERACTIVE / ACP contextvar), nor gateway (HERMES_GATEWAY_SESSION / session platform), nor cron (HERMES_CRON_SESSION), flagged dangerous commands are approved with only a log warning:

AUTO-APPROVED dangerous command in non-interactive non-gateway context (pattern: %s): %s

There is no config option to make this branch deny instead. approvals.cron_mode covers only the cron case. Any scripted/headless invocation of the agent (test harnesses, CI, embedding hermes-agent as a library) silently loses the dangerous-command gate.

Proposal: an approvals.noninteractive_mode: deny | approve knob mirroring cron_mode. Ideally defaulting to deny (fail closed), but even defaulting to the current behavior would let operators opt in to safety.

2. Permanent command_allowlist pattern keys bypass approvals.cron_mode: deny

In check_dangerous_command, the permanent-allowlist checks (_command_matches_permanent_allowlist, then is_approved(session_key, pattern_key)) run before the HERMES_CRON_SESSION cron-deny branch.

Pattern-key entries in command_allowlist are written when a user picks "Always" on an interactive/gateway approval prompt (e.g. a Telegram button). That single tap stores a pattern class key such as recursive delete — which then approves every command matching that class in cron jobs too, silently overriding approvals.cron_mode: deny.

Observed in practice: cron jobs on our install were correctly BLOCKED on shell command via -c/-lc flag until a Telegram "Always" tap added that key to command_allowlist; from then on cron ran such commands ungated despite cron_mode: deny.

Note that check_execute_code_guard already orders these checks correctly — cron deny is evaluated before the session/permanent approval check. The terminal-command guard should match.

Proposal: in check_dangerous_command, evaluate the HERMES_CRON_SESSION + cron_mode branch before consulting is_approved/permanent allowlist (or add a separate approvals.cron_respects_allowlist toggle). An "Always" answered from an interactive surface shouldn't become a standing grant to unattended cron sessions.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existsarea/authAuthentication, OAuth, credential poolscomp/agentCore agent runtime: loop, agent_init, prompt builder, context-compression, responses endpointcomp/cronCron scheduler and job managementsweeper:risk-security-boundarySweeper risk: may affect sandboxing, auth, credentials, or sensitive datatype/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions