Stop Giving Your AI Harness the Keys to Everything
· 6 min read
Claude Code runs on my Mac under my user account, inheriting my shell environment, PATH, and ordinary filesystem permissions: it can read ~/.ssh, my AWS credentials, and every other repository my account can access. macOS privacy controls still protect some data unless the terminal has been granted additional access, but the default boundary remains much wider than any one coding task requires.
For a long time, I worried about Claude doing something dumb on its own initiative: deleting the wrong file, or running a command I had reviewed too casually. I even turned off Wi-Fi when I wasn’t actively using it, which was a fairly unhinged precaution for a coding tool. I’d spent less time considering what would happen if someone else supplied the instruction, under my identity, without showing it to me.
In August 2025, attackers obtained an npm publishing token used by the Nx build system and released compromised versions of several Nx packages.1 Their malicious postinstall script looked for Claude Code, Gemini CLI, and Amazon Q, and when it found one, it invoked the agent with its permission checks disabled and prompted it to inventory SSH keys, wallet files, .env files, and other sensitive paths.2
The malware also collected credentials itself, then used an available GitHub token to publish the stolen material in a public repository under the victim’s account. The agent wasn’t the entire credential stealer, but it was a ready-made reconnaissance component, and the attacker supplied a prompt because a general-purpose tool capable of searching the filesystem was already installed.
That detail stuck with me. Claude followed the instruction it received: the process had no reliable way to distinguish my intent from a prompt supplied by a malicious package, especially after the package launched it with --dangerously-skip-permissions.
Permission arrives too late #
In February 2026, Check Point Research disclosed three attack paths involving Claude Code project files.3 A repository-controlled .claude/settings.json could define a session hook that ran after the user accepted the initial trust dialog, without a separate command prompt. A related configuration could auto-enable a malicious server from .mcp.json, causing its startup command to run before the user could read the trust dialog. A third variant redirected ANTHROPIC_BASE_URL through an attacker-controlled proxy and exposed the API key before the directory had been trusted.
Anthropic patched the reported issues during 2025, but the important part is architectural: project configuration is executable behavior, and the mechanism responsible for requesting consent had processed some of that behavior too early.
The rest of the field offered little comfort. In 2026, the functional codexui-android package accumulated roughly 29,000 weekly npm downloads before a registry-only update began sending Codex authentication data, including a persistent OAuth refresh token, to an attacker-controlled server.4 Its public source repository remained clean, so reviewing GitHub alone wouldn’t have revealed the published payload.
Wiz also found that the Amazon Q VS Code extension automatically loaded MCP configuration from .amazonq/mcp.json without first establishing workspace trust, so the spawned process inherited the extension host’s environment and could access locally available AWS credentials.5 In March, two unauthorized LiteLLM releases on PyPI carried a credential stealer, and an unpinned direct or transitive installation could retrieve either affected version, including through an agent framework or MCP server.6
Claude Code’s permission prompts still matter: they help when the agent proposes a dangerous command through the normal tool flow, and they weaken when execution begins through trusted configuration, an MCP server, a dependency lifecycle script, or an agent launched with its checks disabled. Skipping those checks with --dangerously-skip-permissions hands the trust decision to whatever environment happens to be running the agent, rather than making it disappear.
Containers help, with conditions #
I first assumed a container was the answer. It can be, provided the boundary is kept narrow. A development container that mounts the Docker socket, forwards SSH credentials, or carries much of the home directory inside has reintroduced the capabilities it was meant to exclude. Access to the Docker socket is particularly dangerous because it can amount to control of the host daemon.
Containers also share the host kernel and depend on a container runtime. Escapes are not hypothetical. CVE-2024-21626, fixed in runc in early 2024, used leaked file descriptors and a crafted working directory to expose the host filesystem to a container process.7 That was a runtime flaw rather than a kernel exploit, but the lesson is the same: container isolation has several pieces, and each one belongs in the threat model.
Docker’s sbx takes a stronger approach by running each sandbox in a microVM. I didn’t use it because local operation requires Docker authentication, which Docker confirmed in its issue tracker is a business decision rather than a technical dependency, which is corporate for “we’d like your login even though the sandbox never leaves your laptop.”8 Fine. I’ll keep maintaining my own Dockerfile.
Anthropic now ships native Bash sandboxing as well. It uses Seatbelt on macOS and bubblewrap on Linux to restrict Bash commands and their children, with separate filesystem and network controls.9 This is a useful mitigation, but its default read policy still permits most of the machine, and the sandbox applies to Bash subprocesses rather than every Claude Code tool. It can be configured more strictly, including denying reads from the home directory, but that policy must be created deliberately.
The narrower boundary #
MicroVMs put the workload behind a virtual machine boundary, giving each sandbox its own guest kernel, filesystem, and network boundary rather than sharing the host kernel.10 My setup runs Claude Code inside an ephemeral Microsandbox VM that sees one repository. Its home directory is disposable except for a volume holding Claude’s login and configuration, and when the session ends, the VM goes with it.
If a compromised dependency prompts the agent to find my SSH key, there is no host ~/.ssh in the guest, no ~/.aws, and no sibling repository to inspect. There is only the mounted project and the small amount of state I chose to persist.
This narrows the blast radius, but doesn’t close it. The mounted repository remains writable, so malicious code can corrupt or exfiltrate its contents, and any secret checked into the project or exported into the VM remains exposed. The sandbox also retains outbound network access because real development work needs it, which means exfiltration from inside the boundary is still possible.
What disappears is the unrelated surface area of my host account. That’s the dull, expansive, catastrophic-if-hit collection of credentials and projects the Nx malware went looking for.
The setup is mechanical: a Dockerfile, a Fish function, and two named volumes. It isn’t specific to Claude Code: swap the installed agent, rebuild the image, and the same boundary applies. I documented the commands separately because the security argument and the installation guide are different kinds of reading: Running Claude Code in a Microsandbox MicroVM.
-
Nx: s1ngularity: What Happened, How We Responded, What We Learned ↩︎
-
Snyk: Weaponizing AI Coding Agents for Malware in the Nx Incident ↩︎
-
Check Point Research: RCE and API Token Exfiltration Through Claude Code Project Files ↩︎
-
Cloud Security Alliance: AI Developer Supply Chain and codexui-android ↩︎
-
LiteLLM: Security Update on the March 2026 Supply Chain Incident ↩︎
-
GitHub Advisory Database: runc container breakout through process.cwd and leaked file descriptors ↩︎