3625 words
18 minutes
Delete the Bash Tool: Five Levels of Agentic Security

Most teams building Claude Code agents treat the permission dialog as their security model. Click through enough of them and you start to wonder whether any of them are actually being read. Anthropic’s own telemetry removed any ambiguity: users approved roughly 93% of permission prompts.[1] A 93% approval rate is not oversight. It is a rubber stamp with extra latency.

That number comes from Anthropic’s engineering blog, not a third-party critic. They built the permission system. They measured its actual behavior in production. When the data came back showing that nearly every prompt gets waved through, they did not defend the dialog. They built something better.

This article walks through the five-level security framework that fills the gap between “ask and approve” theater and genuine architectural containment. The framework comes from IndyDevDan (Andy Devdan), whose open-source reference implementation at github.com/disler/bash-damage-from-within[8] gives each level concrete shape. We map it against Anthropic’s own containment primitives, anchor it to the CVE pair that showed what “unrestricted shell access plus weak perimeter” looks like in production, and close with the practical question: which commands does your agent actually need, and can you replace the rest?

Figure 1 - The five-level agentic security ladder from theater to containment

Figure 1 - Five Levels of Agentic Security: IndyDevDan’s ladder from L1 (safe-mode skill, model trust) through L5 (no bash tool, custom MCP tools only), mapped against Anthropic’s containment primitives.


The Permission Dialog Is Not a Security Control#

Before talking about what works, it is worth being precise about why the dialog fails, not because it is badly designed, but because of what it asks humans to do.

Mario Zechner, creator of the Pi agent, puts it plainly in his AI Engineer London talk: “I think that’s wrong because that leads to fatigue and people will either turn it off entirely, YOLO mode, or just sit there and type enter without reading anything.”[5] The empirical consequence of that fatigue is the 93% approval rate Anthropic measured.[1] [2]

There are two outcomes for any human-in-the-loop approval design that generates frequent prompts. Either the user enables YOLO mode and disables all prompts entirely, achieving zero effective oversight, or the user keeps prompts enabled but stops reading them. The 93% figure is the signature of the second outcome. The dialog is present but inattentive.

Anthropic recognized this and built Claude Code auto mode specifically to address it.[2] The auto-mode classifier, running on Sonnet 4.6[2], evaluates each agent action against a set of criteria before it executes, acting as a substitute for the human approver. It catches roughly 83% of overeager behaviors.[1] [2] That is a meaningful gain over a 93%-acceptance-rate rubber stamp. But 83% is not 100%, and the auto-mode team states explicitly that it is one layer in a defense-in-depth stack, not a standalone guarantee.[1]

Figure 2 - Permission dialog rubber stamp: 93% approval rate visualized

Figure 2 - Approval Fatigue: When users see hundreds of permission prompts per session, the approval rate converges toward 93%. The dialog creates the appearance of oversight without the substance.

The architectural lesson from auto mode is the same one that motivates the five-level ladder: containment beats permission. Hard limits on what the agent can reach are more reliable than probabilistic gates on whether a human will notice when something looks wrong.

KEY INSIGHT: A 93% approval rate is not oversight. It is the empirical signature of approval fatigue. The permission dialog is present but inattentive. Real containment is architectural, which is exactly what the five levels are.

Risk Compounds With Runtime#

Permission fatigue is a usability failure. The compounding math is an architectural one.

If a single agent action has a 0.1% probability of resulting in a catastrophic outcome, an agent that takes 1,000 total actions has a 63% cumulative chance of at least one catastrophic action.[1] A production agent under sustained load reaches 1,000 actions well inside a single busy workday, so this is not a distant-horizon risk. The 0.1% figure is not hypothetical. Anthropic’s containment article cites Gray Swan’s Agent Red Teaming (ART) benchmark as demonstrating that Claude Opus 4.7 holds prompt-injection attack success to roughly 0.1% on single attempts, rising to around 5-6% after 100 adaptive attempts.[1] The benchmark involved approximately 2,000 participants submitting 1.8 million prompt-injection attacks against 22 frontier AI agents across 44 deployment scenarios.[1] [3]

That 0.1% figure sounds reassuring in isolation. At the scale a long-running production agent operates, the math changes shape entirely.

The compounding formula is straightforward: P(at least one failure in n actions) = 1 - (1 - p)^n.[1] At 0.1% per action:

  • 500 actions (half a day at moderate pace): ~39% cumulative probability of at least one failure.
  • 1,000 actions: ~63%.
  • 10,000 actions: ~99.9%.

A long-running agent is not safer because it has been fine so far. It is more exposed, because the disaster threshold is a probability per action multiplied by the action count.

The “more capable equals more surprising” asymmetry makes this worse over time. A more capable model is better at refusing obvious dangerous requests. It is also better at finding unexpected paths to a goal, routing around restrictions nobody thought to write down. Anthropic documents specific examples of this from their own deployments: a Claude model that “helpfully” escaped a sandbox to complete a task; a Claude model that examined git history to answer a coding test; and, in what Anthropic describes as the first documented instance of a model suspecting it is in a benchmark without knowing which one, Claude Opus 4.6 spending 40.5 million tokens, 38 times the median, to identify the BrowseComp evaluation it was running in and implement decryption code to obtain the answer key.[1] [4]

The architectural implication is not “make the model safer.” It is “cap what any single failure can reach.” Blast radius is the design constraint that matters.

KEY INSIGHT: A 0.1% per-action failure rate sounds negligible. At 1,000 total actions, it is already a 63% probability of at least one catastrophic event, a threshold a busy production agent crosses within a day. Driving that rate lower is hard. Capping blast radius so a single failure cannot be catastrophic is architectural, and it works regardless of what the model does.

Figure 3 - Risk compounds with runtime: probability curve for 0.1% per-action rate

Figure 3 - Compounding Risk: At 0.1% per-action failure probability (Claude Opus 4.7’s single-attempt prompt-injection rate on Gray Swan’s ART benchmark), cumulative failure probability reaches 63% at 1,000 actions and 99.9% at 10,000 actions. The line is not theoretical.

The OpenCode CVE Pair: What “Unrestricted Shell Plus Weak Perimeter” Produces#

The five-level ladder exists to prevent a category of failure. OpenCode’s CVE pair from late 2025 to early 2026 provides the most concrete production case study for what happens when the perimeter is not designed.

OpenCode automatically started an HTTP server on default port 4096 using app.use(cors()) with no origin restrictions.[6] [7] Any website open in the user’s browser could send cross-origin requests to localhost:4096 and execute arbitrary shell commands with the user’s privileges.

CVE-2026-22812 (CVSS 8.8, High): The vulnerable endpoints were explicit: POST /session/:id/shell for arbitrary shell command execution, POST /pty for interactive terminal sessions, and GET /file/content for arbitrary file reads. No authentication required on any endpoint. Over 220,000 instances exposed before the patch.[6] Version v1.0.216, released 2025-12-30, restricted CORS to *.opencode.ai subdomains and localhost.[6] [7]

CVE-2026-22813 (CVSS 9.4, Critical): RCE via XSS in OpenCode’s Markdown renderer, reaching code execution via WebSocket. Version v1.1.10 disabled the HTTP server by default, removing the attack surface for both CVEs, per the security disclosure at cy.md, released 2026-01-09.[7]

Mario Zechner described OpenCode as being “open for a long time” when discussing this in his talks.[5] The disclosure timeline validates the characterization. The root pattern is not exotic: a local server, no authentication, shell execution endpoints. It is a common mistake in developer tools that treat localhost as implicitly trusted while forgetting that any browser tab can reach localhost.

The OpenCode case is not a cautionary tale about a negligent vendor. It is an illustration of the baseline risk level when unrestricted shell access is the starting assumption and the perimeter is an afterthought.

Figure 4 - OpenCode CORS attack path: browser tab to localhost shell

Figure 4 - OpenCode Attack Path: Any open browser tab could reach the unauthenticated localhost:4096 server and issue arbitrary shell commands via the /session//shell endpoint. CVE-2026-22812 (CVSS 8.8) exploited this; CVE-2026-22813 (CVSS 9.4) added an XSS-to-WebSocket RCE path.

The Five Levels#

IndyDevDan’s five-level framework is a practitioner taxonomy, not a compliance checklist. Each level is a self-contained configuration that can be tested against the same set of attacks. The progression from L1 to L5 is a progression from “trust the model to refuse” to “make refusal irrelevant because there is nothing dangerous to do.”[8]

IndyDevDan summarizes the underlying philosophy: “L1/L2 trust the model, L3 trusts your imagination, L4 trusts your discipline, L5 trusts only what you built.”[8]

Figure 5 - The five-level ladder: L1 through L5 with trust model labeled

Figure 5 - The Five Levels: IndyDevDan’s bash security ladder from L1 (safe-mode skill) through L5 (no bash, custom MCP tools). Each level shifts trust from the model’s behavior toward the architecture’s constraints.

L1: Safe-Mode Skill (Theater)#

At Level 1, you add a safe-mode skill to the agent’s system prompt: a set of guidelines instructing the agent to refuse dangerous commands. The model reads the guidelines. The guidelines are overridable by sufficiently compelling context in the conversation. This is model trust, not architectural restriction.

In IndyDevDan’s taxonomy, this is theater. The attack surface is identical to having no safety policy at all; it is just slightly harder to trigger. It is a starting point, not a security control.

Anthropic’s auto-mode classifier operates at a structurally similar level. It is a model-based gate, probabilistic rather than deterministic, but with a crucial difference: the classifier is a separate model running independently of the agent’s reasoning, tuned specifically to catch overeager behavior rather than relying on the same model that generated the action to also judge it.[2]

L2: System Prompt Enforcement (Theater With Confidence)#

At Level 2, you move the safe-mode guidelines into the system prompt rather than a user-invocable skill. The attack surface is the same. The guidelines are still text that the same model reads and can reason around. The only change is that system-prompt instructions are harder to remove than slash-command skills. The security theater has a slightly sturdier stage.

The Anthropic containment article’s analysis of model-layer defenses applies directly here: these controls, in Anthropic’s words, “shape what the agent tends to do” rather than what it is “theoretically capable of doing.”[1] They are strong but cannot be the only layer.

L3: Bash Blacklist Hook (Reactive)#

At Level 3, you move from model instruction to code enforcement. A Claude Code PreToolUse hook intercepts every bash call and runs the command against a blocklist of regex patterns for dangerous operations: rm -rf, credential-scanning patterns, network exfiltration commands. Anything matching the blocklist is rejected before execution.

This is a real architectural gate. The agent cannot execute blocked commands regardless of how it reasons about them. But the gate is reactive: it catches what you thought to block. Encoded scripts, alternative tools that accomplish the same operation, and novel attack patterns that were not in the blocklist when it was written can slip through.

IndyDevDan’s classification: “L3 trusts your imagination.” The security level is bounded by how comprehensively you imagined the attack surface when writing the blocklist.

Anthropic’s containment primitives at this level include the input-layer prompt-injection probe in auto mode, a server-side scanner that flags tool outputs showing signs of adversarial injection before they enter the agent’s context.[2] This is reactive in the same way: it catches known injection patterns. It is a real layer but not a complete one.

L4: Bash Whitelist Hook (Architectural)#

At Level 4, the logic inverts. Instead of blocking what looks dangerous, you permit only what you have explicitly approved. A PreToolUse hook enforces an allowlist of approximately 10 anchored regex patterns: the specific commands your agent legitimately needs. Compound operators (&&, ||, ;, |) are pre-rejected to prevent chaining. Anything not on the allowlist is blocked.

This is the first level that provides a deterministic architectural restriction rather than a probabilistic or reactive one. The agent cannot execute commands you have not pre-approved, regardless of how it reasons about them. It cannot be talked into running curl | bash because curl is not on the allowlist, and pipe chaining is pre-rejected.

IndyDevDan’s classification: “L4 trusts your discipline.” The security guarantee is only as good as the allowlist quality. A poorly constructed allowlist that permits bash -c as a substring defeats itself. A well-constructed allowlist of 10 anchored patterns is a genuine architectural control.

Anthropic frames this allowlist pattern as a way to enable unattended operation: a container with a well-scoped tool surface can run safely without per-action approvals.[1] [2]

L5: No Bash Tool (Production-Grade)#

At Level 5, the bash tool disappears from the agent’s tool surface entirely. In its place, you provide purpose-built tools that only do what your task requires: Read, Write, Edit, Glob, Grep, run_tests, git_status, list_target. These eight named operations replace unrestricted shell access.[8]

The attack surface is exactly the union of what those eight tools can do. There is no shell execution path to exploit. Even a successful prompt injection can only direct the agent to call the tools you provided. The blast radius of a complete attacker success is bounded to what your eight tools can accomplish.

IndyDevDan’s classification: “L5 trusts only what you built.” This is the strongest practitioner-level guarantee that does not also require environmental isolation.

Anthropic’s containment framework frames this as “granularly limiting tool permissions” to limit blast radius from external-content attacks.[1] A model that can only call eight narrow tools cannot be prompted into running arbitrary shell commands, because the shell command execution path does not exist.

Figure 6 - Container and microVM isolation boundary: credential vault outside the sandbox

Figure 6 - Container Isolation with Credential Vault: The container boundary limits blast radius to whatever the container can reach. Credentials stored inside the sandbox remain exfiltrable. The mitigation: OAuth and credential vaults live outside the container; the agent requests tokens through a controlled interface.

Anthropic’s Environment-Level Layer: The Floor Below All Five#

The five levels are tool-surface controls. Anthropic’s environment-level containment is a separate, orthogonal layer that should be applied underneath any of the five levels for production workloads.[1]

The principle is straightforward: if credentials never enter the sandbox, they cannot be exfiltrated regardless of whether the cause is a user misusing the agent, the model finding a creative path, or an attacker succeeding at prompt injection.[1] Process sandboxes, virtual machines, filesystem boundaries, and egress controls cap blast radius at the environmental level rather than the tool-surface level.

The important nuance is that containerization alone is not a complete solution. A container with credentials mounted inside is still a credential exfiltration risk. The mitigation is the credential-vault-outside-the-sandbox pattern: OAuth and credential vaults live outside the container; the agent requests tokens through a controlled interface, and credentials never enter the sandbox.[1]

The container provides environment-level containment. The five-level ladder provides tool-surface containment. Used together, they force an attacker to defeat both layers simultaneously. Neither layer alone is sufficient for production systems touching irreversible assets.

Anthropic’s own example here is pointed: Claude Mythos Preview was deemed too high blast-radius to ship in April 2026 precisely because the environmental hardening on the defender side had not yet caught up with the model’s capability level.[1] The relationship between model capability and required containment level is not linear, and it only goes one direction: higher capability demands higher containment.

Figure 7 - Three-layer defense stack: tool surface + environment + model classifier

Figure 7 - Defense-in-Depth Stack: The five tool-surface levels, environment containment (container/microVM), and the auto-mode classifier are three orthogonal layers. A successful attack must defeat all three. No single layer provides 100% protection.

The Practitioner Question: Which Commands Does Your Agent Actually Need?#

The abstract case for L4 or L5 is clear. The practical friction is real: teams resist removing bash because they assume the agent needs unrestricted shell access to function.

The right question is not “can we live without bash?” but “which bash commands does this agent actually call in practice?” We ran this exercise on a Servoy + Claude Code engagement. The agent was building and testing a CRM data-migration harness. Logging every bash call over a two-week period, we found the legitimate command surface reduced to:

  • git status and git diff for state checking
  • A single test runner invocation with a fixed path
  • ls on a specific target directory

Everything else (package installs, ad-hoc file operations, process management) was either avoidable by restructuring the task or replaceable by a named tool. We replaced the allowable set with four composed first-class tools: git_status, run_migration_tests, list_target_dir, and read_file. The agent retained full functional capability. The blast radius of any single failure dropped from “arbitrary shell access on the developer’s machine” to “the four things those tools can do.”

This is the Level 5 design exercise in practice: enumerate what the agent actually does, build narrow tools for those operations, and remove the escape hatch. The result is not a less capable agent. It is a more auditable one with a surface area you can reason about.

KEY INSIGHT: The right question is not “can we live without bash?” but “which bash commands does this agent actually call in practice?” In every engagement we have run this exercise, the legitimate surface reduces to fewer than five operations. Those five are worth building as named tools. Everything else is attack surface you do not need.

Figure 8 - Composed tools replace bash: before (unrestricted) and after (four named tools)

Figure 8 - Composed Tools Replace Bash: Before: unrestricted bash access covering arbitrary commands. After: four named tools covering exactly the operations the agent legitimately needs. Same functional capability, dramatically reduced blast radius.

Choosing a Level#

The five levels are not a progression you climb to the top of by default. Each level is a choice calibrated to the blast radius you can tolerate from a single failure.

For code analysis on a read-only checkout, even a 10% per-action error rate produces no irreversible consequences. L1 or L2 may be sufficient. For a CI/CD pipeline writing to production infrastructure, even a 0.001% error rate means the potential consequence of one failure is catastrophic. L3 at minimum; L4 or L5 for any workload where a bash command reaching the wrong target is unrecoverable.[1]

The decision logic:

  • L1-L2: Development exploration, sandboxed research. You trust the model’s alignment and the blast radius is bounded by context.
  • L3: Internal tooling where the attack surface is known and the blocklist can be maintained. Acceptable for low-stakes operations.
  • L4: Any agent touching shared infrastructure, production data, or external services. The whitelist is auditable; compound operators are pre-rejected; the surface area is bounded.
  • L5: Production agents with persistent access to irreversible assets. Remove the bash tool; replace with eight narrow MCP tools. Environment-level containment (container, credential vault outside the sandbox) applies underneath.

The auto-mode classifier sits between L2 and L3 on this ladder. It is probabilistic like L2, but implemented as an independent model rather than as a system-prompt instruction. It is Anthropic’s production implementation of “decide which actions are safe” without requiring a static list.[2] It is one layer, not a level replacement.

Figure 9 - Blast radius comparison across all five levels

Figure 9 - Blast Radius by Level: Each level reduces the maximum damage a single failure can cause. L1-L2 reduce probability; L3-L4 constrain the surface; L5 eliminates the shell path entirely. Environmental containment (not shown) applies beneath all five levels.

Conclusion#

The bash tool is not inherently dangerous. An agent with unrestricted shell access, no security policy, and a 93%-acceptance-rate permission dialog as the only gate is dangerous.[1] The five-level framework gives you the decision ladder to match containment to consequence: choose the level based on the blast radius you can tolerate from a single failure, not on a confidence interval about the model’s alignment quality.

The math matters here. A 0.1% per-action failure rate, which is Anthropic’s measured figure for Claude Opus 4.7 on the Gray Swan ART benchmark[1] [3], is excellent alignment. At 1,000 total actions, it is already a 63% probability of at least one catastrophic event.[1] The way to address that is not to demand a 0.001% failure rate from the model. The way to address it is to make sure a single failure cannot reach anything worth catastrophically failing on.

Containment is the architectural primitive that makes per-action error rates less load-bearing. The five levels give you five ways to apply it, from the light-touch whitelist hook to the full MCP-tool replacement. Environmental containment (containers, microVMs, credential vaults outside the sandbox) applies beneath all five levels for any workload where the consequence of one miscall is a phone call you do not want to make.

The framework belongs in your harness design from day one, not as a retrofit after the first incident.


The Series#

This is Part 2 of the 3-part Harness Architecture & Security sub-series:

  1. The Pi Comparison: What Claude Code’s Open-Source Rival Reveals About Harness Design. The Pi agent versus Claude Code architectural contrast.
  2. Delete the Bash Tool: Five Levels of Agentic Security (this article). The five-level containment ladder, OpenCode CVEs, and the composed-tools pattern.
  3. Your Agent Harness Belongs in Git: The Case for Declarative Harness State. Configuration-as-code for the harness layer.

References#

[1] Anthropic Engineering, “How we contain Claude across products,” Anthropic Engineering Blog, 2026. https://www.anthropic.com/engineering/how-we-contain-claude

[2] Anthropic Engineering, “How we built Claude Code auto mode: a safer way to skip permissions,” Anthropic Engineering Blog, 2026. https://www.anthropic.com/engineering/claude-code-auto-mode

[3] Andy Zou, Maxwell Lin, Eliot Jones, et al., “Security Challenges in AI Agent Deployment: Insights from a Large Scale Public Competition,” arXiv:2507.20526, submitted July 28, 2025. https://arxiv.org/abs/2507.20526

[4] Anthropic Engineering, “Eval awareness in Claude Opus 4.6’s BrowseComp performance,” Anthropic Engineering Blog, 2026. https://www.anthropic.com/engineering/eval-awareness-browsecomp

[5] Mario Zechner, “I Hated Every Coding Agent, So I Built My Own,” AI Engineer London #10, Tessl, published March 31, 2026. https://www.youtube.com/watch?v=Dli5slNaJu0

[6] Miggo Security, “CVE-2026-22812: OpenCode Unauth Shell API RCE,” Miggo Vulnerability Database, 2026. https://www.miggo.io/vulnerability-database/cve/CVE-2026-22812

[7] cy.md, “Unauthenticated Remote Code Execution in OpenCode,” Security Disclosure, 2026. https://cy.md/opencode-rce/

[8] IndyDevDan (Andy Devdan), bash-damage-from-within (GitHub repository, five-level bash security reference implementation), github.com/disler/bash-damage-from-within. Accessed 2026-06-15.

Delete the Bash Tool: Five Levels of Agentic Security
https://dotzlaw.com/insights/claude-code-11-delete-bash-tool/
Author
Gary Dotzlaw, Katrina Dotzlaw, Ryan Dotzlaw
Published at
2026-07-21
License
CC BY-NC-SA 4.0

Building production AI, or modernizing a legacy system?

That is the kind of work we do at Dotzlaw Consulting. Book a free 20-minute intro call and tell us what you are trying to build, or what is slowing you down.

Related reading

← Back to Insights