Skip to content

[SECURITY] PreToolUse hooks not enforced on subagent tool calls, allowing security bypass #21460

@allannapier

Description

@allannapier

Summary

PreToolUse hooks configured in ~/.claude/settings.json are not enforced when subagents spawned via the Task tool make their own tool calls. This creates a security vulnerability where restrictions that apply to the main agent can be completely bypassed by spawning a subagent.

Steps to Reproduce

  1. Configure a PreToolUse hook to restrict file writes outside the project directory:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit|Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/restrict-operations.py",
            "timeout": 5
          }
        ]
      }
    ]
  }
}
  1. Hook script blocks writes outside project directory (exits with code 1 for unauthorized paths)

  2. Try to write a file outside project with main agent:

    • Correctly blocked by PreToolUse hook
  3. Spawn a subagent with the same request:

Task tool with python-coder subagent
Prompt: "Create a file at /Users/username/test.py"
  • Bypasses the hook - file is created successfully

Expected Behavior

PreToolUse hooks should apply recursively to all agents in the execution tree:

  • Main agent's tool calls → hook enforced
  • Subagent's tool calls → hook enforced
  • Sub-subagent's tool calls → hook enforced

Actual Behavior

PreToolUse hooks only apply to the agent where settings.json is loaded:

  • Main agent's tool calls → hook enforced ✅
  • Subagent's tool calls → hook NOT enforced ❌

Security Impact

This allows complete bypass of security restrictions:

  1. File access controls - Can write/read files anywhere on the system
  2. Command restrictions - Can execute unrestricted bash commands
  3. Self-modification - Can modify or delete the hooks themselves
  4. Privilege escalation - Any security boundary can be bypassed

Attempted Workarounds

1. SubagentStart Hook

Added SubagentStart hook to intercept subagent spawning and analyze prompts for suspicious patterns. Limitation: Can only do heuristic analysis of the prompt text, cannot actually enforce tool-level restrictions within the subagent.

2. Intercept Task Tool

Added Task tool to PreToolUse matcher to block suspicious subagent prompts. Limitation: Prompt analysis is unreliable - many legitimate prompts contain file paths, and adversarial prompts can obfuscate intent.

Proposed Solutions

  1. Global Hook Enforcement (Preferred)

    • Hooks configured in ~/.claude/settings.json should apply system-wide to all agents
    • Each subagent inherits parent's hook configuration
    • Hooks execute in subagent's context when subagent makes tool calls
  2. Explicit Hook Inheritance Flag

    • Add inherit: true option to hook configuration
    • Allows opt-in recursive enforcement for security-critical hooks
  3. Subagent Security Context

    • Add security context parameter to Task tool
    • Explicitly declare which hooks must be enforced on subagent

Environment

  • Platform: macOS (Darwin 25.2.0)
  • Claude Code Version: Latest (using claude-sonnet-4-5-20250929)
  • Hook Types Affected: PreToolUse
  • Tools Affected: Write, Edit, Bash, NotebookEdit (any tool called by subagents)

Related Issues

Example Use Cases Requiring This Fix

  1. Corporate environments - Enforce write restrictions to prevent data exfiltration
  2. Sandboxed execution - Prevent agents from escaping sandbox via subagents
  3. Code review automation - Ensure read-only analysis can't be bypassed
  4. Credential protection - Block access to sensitive files/directories
  5. Audit logging - Ensure all tool calls are logged, not just main agent

Additional Context

This is a fundamental security architecture issue. While hooks provide excellent visibility and control for the main agent, the lack of recursive enforcement makes them insufficient for security-critical use cases. Any security boundary enforced via hooks can be trivially bypassed by using the Task tool.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions