CSIPE

Published

- 19 min read

Claude Code Auto Mode Still Needs a Real Boundary


Books by the author

Compare all 5

As an Amazon Associate I earn from qualifying purchases. Buying through these links costs you nothing extra and helps pay for the blog.

A developer asks a coding agent to summarise a website. The agent fetches a page, downloads an archive, rejects the supplied executable, writes a small decoder of its own, and runs it. Every step looks like a plausible attempt to finish the job. The last step also causes attacker-controlled code to run on the developer’s machine.

That is the useful part of the Claude Code Auto Mode research published by security researcher Johann Rehberger on 26 August 2026. The result is not that every website can take over every Claude Code session, or that Auto Mode provides no protection. It is narrower and more important: a model-based approval system can miss danger that emerges from the interaction between several ordinary actions.

Auto Mode may still be a sensible improvement over clicking through every prompt or switching permission checks off. It is not a wall around the machine. If a coding agent can read untrusted material, run commands, reach the internet, and touch valuable credentials from one environment, the real security decision was made before its classifier saw the first command.

What the researcher actually demonstrated

The test began with a harmless-looking request to summarise an attacker-controlled website. According to Rehberger’s account of the lab setup, the site was made to resemble an archive of notebook records. It gave the agent a credible reason to retrieve and inspect a ZIP file rather than presenting an obvious command such as “download and run this program.”

The first move mattered. Claude Code initially tried its web-fetching tool, received an HTTP 415 response, and then chose to use curl through the shell. A redirect led it to the archive. The archive contained ordinary catalogue files, encoded records, a native decoder, and a Python file named struct.py.

Claude refused to run the supplied decoder. That was a sound local decision. It then wrote a short Python decoder itself and ran the code from inside the extracted directory. The replacement imported Python’s base64 module, which in turn imported struct. Because the working directory contained the attacker’s struct.py, Python loaded that file instead of the expected standard-library module.

This behaviour is called module shadowing. Python builds a search path when it starts, and the script directory or current directory can appear before standard-library locations in that path, as the Python documentation on sys.path initialisation explains. A file with a trusted module’s name can therefore be executed during an otherwise innocent import when code runs from an untrusted directory.

The poisoned module preserved enough normal behaviour for the decoder to work, while also starting a separate process. In Rehberger’s controlled test, that process made a command-and-control callback and opened Calculator to make execution visible. A second variant launched another headless Claude Code process, which performed basic system inspection and wrote outside the original workspace.

The percentages need restraint. Rehberger reported three successes in five attempts for two variants and four in five for another. He explicitly described those as small samples, not a universal success rate. Independent coverage published on 28 August reported the same 60% to 80% lab range and the same qualification.

Five trials can show that a path exists. They cannot tell us how frequently a random web page will trigger it, how the rate changes across operating systems and settings, or whether a later product update will stop this exact chain. Treating 80% as a population estimate would oversell the evidence. Treating the working executions as irrelevant because the sample was small would miss the engineering lesson.

The failure appeared across the chain, not in one cartoonishly dangerous command. Fetch a page. Follow a redirect. Unpack an archive. Decline an unknown binary. Write a decoder. Import a standard module. Each step can look defensible when reviewed alone. Their composition crossed the boundary.

What Auto Mode is built to do

Manual approval sounds safe until it becomes muscle memory. Anthropic says Claude Code users approve 93% of permission prompts, a number that captures the familiar problem of warning fatigue. If a developer approves almost everything, the dialog is often friction without much judgment.

Auto Mode tries to improve that trade. In Anthropic’s technical description published on 25 March 2026, one model does the coding while separate checks inspect what enters the session and what the agent tries to do. An input probe looks for prompt injection in files, web pages, shell output, and other tool results. An output classifier reviews actions against user intent and policy before execution.

That separation is thoughtful. The action classifier sees user messages and tool calls, but not the agent’s own persuasive explanation or the hostile content that may have influenced it. This makes it harder for an injected page to argue directly with the component that decides whether a command should run. The classifier can also block actions such as sending data to an unnamed external service, deleting shared resources, or searching for unrelated credentials.

Auto Mode does not send every operation through the same check. Built-in low-risk tools and narrow allow rules can pass first. Writes inside the project are generally allowed. Shell commands, network access, operations outside the project, and other actions with greater downside reach the classifier. The current Claude Code permission documentation describes Auto Mode as having a classifier review actions instead of asking the user each time.

The system is intentionally probabilistic. Anthropic’s own published evaluation gives the deployed two-stage classifier a 17% false-negative rate on 52 real “overeager” actions and a 5.7% false-negative rate on 1,000 synthetic data-exfiltration attempts. Those figures do not measure Rehberger’s prompt-injection chain, so they should not be mixed into one score. They do show that Anthropic describes residual misses as a known property rather than claiming a perfect gate in its engineering article.

The company frames the comparison carefully there. Auto Mode can be substantially safer than --dangerously-skip-permissions, which removes the approval layer. It may be worse than a human who reads every high-stakes request carefully and understands the surrounding system. The feature aims to remove routine interruption while catching enough dangerous behaviour to improve the common case.

That is a useful product goal. A classifier does not need to be perfect to prevent destructive branch deletion, an accidental production command, or a credential upload that would otherwise occur. Teams should keep those benefits. They should also stop assigning the classifier a second job it was not built to perform: containing whatever runs after an allowed command.

The distinction is easy to lose because both jobs appear as “security.” One asks whether an action looks authorised and safe. The other limits the damage when that judgment is wrong. Approval is a decision. Containment is an enforced property of the environment.

Why the dangerous command looked ordinary

Security tools work best when the risky property is visible at the point of enforcement. A command that uploads .env to an unknown server carries much of its danger in the command itself. A recursive delete against a shared path is similarly legible. A short decoder importing base64 does not advertise that Python will find a hostile struct.py beside it.

The working directory supplied the missing context. So did the archive’s contents, Python’s import rules, the process tree, and the later network connection. A reviewer looking only at the immediate command would need to reconstruct all of those relationships before allowing it with confidence.

Humans miss this too. A developer may inspect the line, recognise normal standard-library imports, and approve it. The problem is not simply that a model made a weak decision. The problem is that command approval is a poor control point for hidden behaviour assembled across files and runtimes.

Rehberger’s chain also used a clever reversal. The unknown native decoder looked dangerous, so Claude declined it. Writing a fresh decoder felt safer because the agent could see the code it had produced. Yet generated code inherits the environment where it runs. A clean-looking script in an attacker-controlled directory can load attacker-controlled neighbours without referring to them by name.

That pattern extends beyond Python. Build tools execute project hooks. Package managers read manifests and lifecycle scripts. Test runners discover plugins. Shells load configuration. Compilers invoke helpers. A coding agent can write every visible line itself and still hand control to untrusted material through the runtime around those lines.

This is why “the agent wrote it” is not a trust label. The relevant question is what the resulting process can load, call, read, modify, and contact. Provenance for one file does not prove safety for the execution context.

The chain also demonstrates the weakness of action-by-action review. An agent may be allowed to download a file because downloading is common. It may be allowed to unpack it because archives are common. It may be allowed to run a small script because scripts are the work. The policy approves ingredients while no control examines the finished dish.

A real boundary changes the question. Instead of trying to predict whether the decoder is malicious, the environment can ensure that the decoder has no home-directory access, no production token, no SSH agent, and no arbitrary route to the internet. The code may still run. Its useful options become smaller, and so does its blast radius.

A zero on a test set is not a law of nature

Rehberger contrasted his result with a reported 0.00% attack success rate from a fixed prompt-injection evaluation. The apparent contradiction is useful because both results can be true. A system can block every case in one benchmark and fail against a new chain built outside it.

A benchmark answers a bounded question. It tests particular attacks, tasks, environments, model versions, policies, and scoring rules. Zero successful attacks across those trials means zero were observed there. It does not establish zero probability for a motivated adversary choosing a different route.

Simon Willison’s 27 August assessment focused on that gap. He treated Rehberger as a credible prompt-injection researcher and agreed that coding agents facing adversarial input belong in an isolated environment. His summary also highlighted an awkward result from several runs: after Claude noticed the compromise and attempted to terminate the malicious process, Auto Mode denied the cleanup command.

That cleanup behaviour deserves care. It was observed in a few runs of one lab chain. It does not show that Auto Mode always blocks incident response or that the classifier prefers malware. It shows that a local policy can be asymmetric: creation slips through as an ordinary-looking operation, while termination looks destructive and gets stopped.

Defensive systems often have this problem. A policy designed to prevent process killing may obstruct cleanup after a missed execution. A rule that blocks file deletion may preserve malicious files. A network control that permits a new connection but makes emergency changes slow can turn a small miss into a longer incident.

The answer is not to make every cleanup command automatically safe. Malware can ask to kill monitoring processes too. Teams need a separate response path whose authority and purpose are explicit, rather than expecting the same autonomous session that caused a problem to repair itself under unchanged rules.

Good evaluations should therefore test sequences and recovery, not only first-order bad commands. Can hostile content steer an agent through several allowed steps? Does the classifier connect a generated file with its later execution? What happens when the agent changes tools after a denial? Can a human stop the process quickly? Does cleanup work after one layer misses?

One score cannot carry all of that. The honest use of a benchmark is to compare defined configurations and find regressions. The dishonest use is to turn a result from a closed set into a promise about open-ended attackers.

Teams buying or deploying coding-agent controls should ask for the test boundary. Which tasks were included? Which tools were available? Did the agent have a network route? Were attacks adaptive? Did the evaluation include archives, build hooks, module loading, child processes, and recovery after a miss? A zero without that context is decoration.

The classifier and the sandbox have different jobs

A classifier can recognise intent. A sandbox enforces limits. They overlap at the edges, but substituting one for the other creates a brittle system.

Consider a coding agent asked to investigate a failing test. The classifier can judge whether reading the repository fits the task, whether sending a file to an external paste service was authorised, and whether deleting a remote branch exceeds the request. Those are semantic decisions. The meaning of the user’s words matters.

The operating-system boundary handles different facts. This process can read only the checked-out repository and a scratch directory. It cannot connect to arbitrary destinations. It receives a short-lived test credential rather than the developer’s cloud session. It cannot reach the SSH agent. Its filesystem can be discarded after the run.

Those properties remain true even when the model is confused, the classifier misses, or a dependency behaves unexpectedly. They do not require the enforcement layer to understand Python import precedence or decode an obfuscated file. The process asks for a forbidden resource, and the environment says no.

A container can help, but the word alone proves little. A container with the user’s home directory mounted, the Docker socket exposed, host networking enabled, and cloud credentials in environment variables is a convenient process wrapper, not a meaningful security boundary. The same applies to a virtual machine that automatically imports host credentials and shared folders.

The boundary has to match the assets. If the threat is hostile repository content, keep personal files and long-lived credentials outside the runtime. If the task needs package downloads, route them through a controlled proxy or an allowlist rather than granting an open route. If the agent must push a branch, issue a narrowly scoped identity that cannot alter production settings or read every private repository.

Monitoring has a third job. It shows what the agent attempted, what the environment denied, what processes appeared, and what left the machine. Logs do not contain an attacker by themselves. They make a miss visible and support a trustworthy decision about whether to keep, inspect, or destroy the workspace.

The layers fit together cleanly. The model tries to solve the task. Auto Mode filters questionable choices and reduces approval fatigue. The runtime boundary caps the consequences of an allowed mistake. Monitoring tells the operator when the first two behaved unexpectedly.

This is the core argument of The Secure Harness: autonomy becomes dependable when the surrounding system constrains effects rather than hoping every decision is correct. Rehberger’s test is a compact example because the agent’s individual choices remained plausible while the environment granted the chain somewhere valuable to land.

What to change before the next agent run

A team does not need to abandon Auto Mode or wait for a flawless prompt-injection detector. It needs to decide what the agent is allowed to lose, leak, alter, and contact when every approval layer gets one decision wrong.

Start with the actual execution path, not a policy document. Follow one coding-agent session from the developer’s terminal into its shell, filesystem, network, credential stores, source-control identity, package registry, and deployment tooling. The dangerous gaps usually appear in inherited convenience.

  1. Separate adversarial work from the developer workstation. Run unattended tasks and any job that reads public issues, websites, archives, pull requests, or unfamiliar repositories in an isolated virtual machine, container with a hardened host boundary, or dedicated machine. Make the workspace disposable. Do not mount the whole home directory for convenience.

  2. Give the runtime a task-sized filesystem. Mount the target repository and a scratch directory, preferably with everything else absent. Keep SSH configuration, browser profiles, password-manager state, personal documents, and unrelated repositories outside. A malicious import cannot steal a file that the process cannot see.

  3. Remove ambient credentials. Do not pass the developer’s shell environment wholesale into the agent. Issue short-lived credentials for the narrow operation, such as reading one repository or pushing one branch. Deny access to production accounts by default. Expiry turns a forgotten workspace into a smaller future problem.

  4. Control where the computer can connect. Package installation and documentation lookup do not require unrestricted outbound access. Use a proxy, mirror, or destination allowlist where the workflow permits it. Record denied and allowed connections. The plain-English test is simple: if attacker-controlled code ran, could it call home or send a secret away?

  5. Treat downloaded directories as hostile execution contexts. Inspect archives before running code from inside them. For Python, avoid executing an ad hoc decoder with an untrusted directory at the front of the import path; isolated mode and a clean working directory can reduce this specific risk. The durable rule is broader: untrusted content should not get to choose the runtime’s plugins, modules, hooks, or helpers.

  6. Keep deny and ask rules for high-impact effects. Auto Mode is not a reason to approve production deployment, credential changes, release signing, secret retrieval, or destructive infrastructure actions automatically. Put those behind explicit tools with narrow arguments or a separate human-controlled release step.

  7. Test the stop path. Start a harmless long-running process in the isolated environment and confirm that an operator can terminate it, cut network access, preserve logs, and destroy the workspace without relying on the same agent session. A stop control that exists only in a runbook may fail when minutes matter.

  8. Review chains, not only commands. Alert when a session downloads an archive, writes executable code, and starts a child process, even if no single event is forbidden. Preserve process ancestry and network records long enough to reconstruct the sequence. Behaviour gains meaning from what happened immediately before it.

  9. Keep the final release boundary separate. Agent-produced changes should move through tests, review, signing, and deployment under a different identity from the one that explored untrusted material. A compromised workspace should not be able to declare its own output trustworthy.

These controls sound stricter than a permission prompt because they are. They can also make routine work calmer. A developer can grant the agent more freedom inside a small, disposable lab than on a laptop holding years of credentials and personal files.

There is no universal network policy or container profile for every coding task. A documentation edit needs little access. A dependency update may need a package mirror. An integration test may require a temporary service. The team should make those differences explicit rather than letting the host’s existing access become the default.

The most revealing review question is not “Do we trust Claude Code?” Ask: “What remains reachable after we assume Claude Code, its classifier, and one dependency all make a mistake in the same run?” The answer describes the real boundary.

How to respond if you already used untrusted content

Reading this research does not mean every past Auto Mode session was compromised. The demonstrated chain required attacker-controlled content, a particular sequence of choices, a hostile archive, and a useful execution environment. Panic is not evidence.

Teams should still examine high-exposure sessions with proportionate care. Look for jobs that summarised unknown sites, opened contributed archives, processed unfamiliar repositories, or ran generated scripts from downloaded directories. The closer a session resembles the demonstrated path, the stronger the case for reviewing it.

Check shell and agent logs for archive downloads, extraction, interpreter commands, unexpected child processes, and commands run from temporary or downloaded directories. Review outbound network records for destinations unrelated to the task. On a developer workstation, inspect changes to startup files, scheduled tasks, SSH configuration, and credential stores if the evidence warrants it.

A clean final git diff does not clear the environment. Code execution can leave no repository change while touching another directory, starting a detached process, or using a credential. Conversely, a strange temporary file does not prove a full compromise. Build the timeline before choosing the response.

If logs show suspicious execution, isolate the host or workspace before cleaning it. Preserve enough evidence to understand which identities and destinations were reachable. Rotate credentials based on exposure, not based only on confirmed use. A token visible to the process belongs in the review set even when no log records its theft.

For a disposable environment with narrow, expired credentials, destruction and recreation may be the cleanest response. For a developer laptop with broad access, involve the organisation’s incident-response process. Reinstalling one package or deleting the extracted archive does not answer what the process did after execution.

Also check product updates and vendor guidance before reproducing or diagnosing the issue. The research was current on 26 August 2026. Classifiers, models, default modes, and detection rules can change quickly. Do not spend a live workstation on an exploit test; use an authorised lab with fake data and controlled network endpoints.

This review has a practical purpose: restore confidence in the identities, artifacts, and systems the session could influence. That question is smaller and more useful than proving an abstract classifier failure.

Keep the useful automation, move the trust

Auto Mode addresses a real problem. A security dialog approved 93% of the time can become theatre, and a separate classifier can catch mistakes that a tired developer would wave through. Anthropic’s own numbers and design notes present it as a risk-reduction layer with known misses, not as mathematical certainty.

Rehberger’s August 2026 demonstration found one of those misses through composition. The agent did not begin with an obviously hostile command. It pursued a reasonable task, rejected an untrusted executable, wrote understandable code, and ran it in the wrong place. The machine around the decision supplied the danger.

That is why the strongest fix sits outside the conversation. Put public content and executable tools in a workspace that cannot see personal files or production credentials. Narrow what leaves that workspace. Preserve enough evidence to understand a failure. Keep release authority elsewhere.

Then let the classifier do its proper job. It can reduce bad actions, absorb routine prompts, and stop many avoidable mistakes. When it misses, the boundary should make the incident boring.

That is the standard worth aiming for. Not a coding agent that never makes a wrong call, and not a benchmark that never records a failure. A system where one wrong call cannot quietly inherit the whole developer workstation.

For more calm, practical security explanations, join the newsletter. It is one email per month.

Sources