Sessionboxer

Claude Code 2.1.321 loads the mods Claude writes without asking in auto and bypass mode. What such a mod can reach, and how to keep it off

Claude Code 2.1.321 loads mods Claude writes without asking in auto and bypass modes, and their tool calls skip ask rules. What changed, and how to stop it.

A Sessionboxer session: the chat lists Read and Execute tool calls marked completed and a desktop screenshot the agent took, next to the live Desktop showing the same page in Firefox

You run Claude Code in auto mode, or with --dangerously-skip-permissions, so it can work while you do something else. Maybe your company also set allowUnsandboxedCommands: false, so every shell command Claude runs stays inside the Bash sandbox. That setup still works after this week's update. One thing around it changed: when Claude writes a mod during the session, the mod now loads at the end of the turn without a question, and a mod is code that runs outside that sandbox.

Claude Code 2.1.321 came out on November 9. Here is what changed, what such a mod can reach, and how to keep it from loading.

What 2.1.321 changed

The v2.1.321 release notes list most fixes, and the changelog page has the full "Changed" section. Two of its lines are about mods:

Changed mods Claude writes to turn on without asking in auto and bypass modes, even with managed allowUnsandboxedCommands: false or allowManagedDomainsOnly, and not to load in dontAsk mode

Changed mods' own tool calls ($.tool.call, $.mcp.call) to raise no permission prompt, even under your own ask rules or in dontAsk and -p runs; deny rules and organization policy still apply

A mod is a plugin made of JavaScript or TypeScript event handlers that run inside Claude Code. We wrote about the trust side of that in Claude Code mods are not sandboxed when mods shipped in 2.1.287.

"Mods Claude writes" are the ones you get by asking for them. The Create a mod page describes the flow. You say "make a mod that shows the current git branch above the prompt", and Claude writes it under ~/.claude/dev-mods/<session id>/. In the default and acceptEdits modes that folder is a protected path, so you approve each file, and then Claude Code asks whether to turn on hot reloading for the session. In auto and bypass modes, the page says, "the mod can run when the turn ends."

The new part is the "even with". By the changelog's wording, the two managed sandbox settings it names used to keep that question in place in auto and bypass modes. From 2.1.321 they do not.

What a mod Claude wrote can reach

The Mods overview is direct about this. Once a mod loads, it can read and write files anywhere your user account can, start programs, make network requests, read environment variables and settings files, see every prompt and tool call, and rewrite them. Then this:

Mods aren't sandboxed. If you turn on sandboxing, the sandbox isolates the Bash commands Claude runs, and a process that a mod starts runs outside it.

Permission rules do not cover a mod's own file and process calls either. The admin page gives the example: with Read(.env) denied, a mod can still read that file with $.fs.read.

The second changelog line adds the tool calls a mod makes on Claude's behalf. $.tool.call and $.mcp.call no longer stop at a prompt, even where your own ask rules would have asked. Deny rules and your organization's policy still apply to them.

Put those together for a session in bypass mode. Claude writes a mod. The writes to the mods folder are approved, because bypass mode approves writes to protected paths. The mod loads when the turn ends. From then on it runs as you, outside the Bash sandbox, for the rest of that session. In auto mode the classifier still reviews the writes to the mods folder, since the permission modes page routes protected paths to it even when an allow rule matches. After the mod loads, the classifier is out of the picture.

Anthropic documents every piece of this. It matters most when someone other than you gets to suggest what Claude writes. A README or an AGENTS.md in a repository you cloned can do that, as the auto mode bypass through AGENTS.md showed last week. A prompt injection that ends in "write a mod" now ends in code that loads by itself.

There are limits. A mod Claude writes loads only in the session that made it, and never in a workspace you have not trusted.

The built-in guard may not be loaded

Claude Code ships a policy mod, sec-default@builtin, that runs ahead of the mods users install. With it loaded, a user's mod cannot approve a call that a deny rule refuses or an ask rule would prompt for. The admin page says when it loads: on a machine with managed settings or a managed-mcp.json, or in a session that runs on a Team or Enterprise /login.

That leaves out a lot of setups. With an API key, on Bedrock, on Google Cloud or on Foundry, you get the guard only if the machine has managed settings. A session that runs on a long-lived CLAUDE_CODE_OAUTH_TOKEN does not count as a Team or Enterprise login, "even when a Team or Enterprise login is stored on the machine." That is the kind of token claude setup-token prints.

To see whether the guard is there, open /plugin and look for cc-plugin-sec-default on the Installed tab.

How to keep mods Claude writes from loading

Pick the one that fits how you run Claude Code:

  • For jobs that should do a few known things, use dontAsk mode with an allowlist. Mods Claude writes do not load there. Keep in mind that a mod you installed yourself still can, and its tool calls now skip your ask rules.
  • For a team, set disableSideloadFlags in managed settings. The admin page says it "keeps mods Claude writes during a session from loading". It also rejects --plugin-dir, --plugin-url, --agents and --mcp-config, so read its entry before you roll it out.
  • If you already restrict plugins to an allowlist of marketplaces, mods Claude writes load only when that list includes skills-dir. Leave it out.
  • On your own machine, "disableAllHooks": true in ~/.claude/settings.json, or starting with --safe-mode or --bare, keeps them from loading. These also stop your settings hooks, so they are the blunt option.

To check a session, run /plugin. The dim line under the tabs gives the count and names of the mods that loaded, such as 1 mod active · first-mod. On disk, look in ~/.claude/dev-mods/.

The other fixes in the same release

2.1.321 also fixes a few things that matter if you rely on Claude Code's own sandbox and permission checks. MCP OAuth requests could send secrets such as refresh tokens to another host when a server redirected them. A read-only Bash command could run outside the sandbox without a prompt. Bash permission checks could judge a file write from the wrong folder and not prompt. Deny rules and protected-file prompts did not apply to the file that a cp or mv into a directory writes.

Run claude --version to see what you have. 2.1.322 followed on November 10 and only adds fast mode on Vertex.

Where Sessionboxer fits

We build Sessionboxer, so weigh this part with that in mind.

Sessionboxer runs each session's agent in its own Docker container, and Claude Code there runs with bypassPermissions, which is how the agent works without permission prompts. The sandbox image pins its Claude Code version. When we checked the repository on November 10, the pin was 2.1.272, which is older than mods themselves. So today a box has no mods at all, and this change does not reach it until a newer image ships.

When it does, the reasoning is the one from the earlier post. A mod Claude writes in a box runs with the box's permissions: the clone of the repositories for that task and the session's tokens, which live on tmpfs and stay out of snapshots. Sessionboxer runs Claude Code with the token from claude setup-token, so by Anthropic's description the built-in guard would not load there. The container is the boundary in its place.

Two features help you look after the fact. Inspect LLM records every request Claude Code sends to the model, so a prompt a mod rewrote shows up rewritten. It does not see a mod's own network requests to other hosts. And with automatic snapshots on, each turn ends with one, so you can fork from the turn before the mod was written. The Inspect LLM chapter and the Snapshots and forks chapter explain both.

What Sessionboxer does not do: it does not set disableSideloadFlags in the box, and it does not tell you when a mod loaded. If you want mods off in your sessions once the image moves past 2.1.287, add that setting yourself.

What to do today

Update to 2.1.321 or later. If you run auto or bypass mode on a laptop, decide whether Claude should load its own mods there, and if not, set one of the settings above. Check /plugin for the guard. Keep unattended runs in dontAsk mode, or in a container or VM that holds only the task, as in How to run Claude Code with --dangerously-skip-permissions, safely.

Features mentioned

Keep reading