Explainer
What your security team will ask before a coding agent touches the repo
Somewhere between the pilot and the rollout there is a forty-minute conversation with someone whose job is to say no. It is the meeting that decides whether your team adopts a coding agent at all, and it is usually lost in one of two ways.
The first is the vendor answer — SOC 2, encryption in transit, a trust page. None of it is untrue and none of it addresses the question, which is not "is the company careful" but "what can this process do on a developer's laptop." The second is the engineer answer: it's fine, I watch it. That one loses faster, because everyone in the room knows what watching a terminal for six hours actually looks like.
The questions below are the ones that get asked. Each has an honest answer available from current documentation. Some of those answers are reassuring. Three of them are versions of nothing stops that — here is what you do instead, and saying so is what makes the rest credible.
Verified against: Claude Code 2.1.224 (npm latest;
the stable tag was 2.1.220), @openai/codex 0.147.0,
and MCP specification revision 2026-07-28, all checked on
2026-08-07 against code.claude.com/docs,
learn.chatgpt.com and modelcontextprotocol.io. Every mechanism named
below exists in those docs today; where a control people assume exists does not, the post says
so rather than filling the gap. Nothing here describes an unpublished vulnerability.
First, the thing the whole review turns on
Almost every specific answer below is an instance of one structural fact, so it is worth stating once. A coding agent has four places a control can sit, and they are not interchangeable — they differ in what can argue with them.
Anthropic states the boundary between the first two rows about as directly as documentation ever
puts anything: "Permission rules are enforced by Claude Code, not by the model. Instructions in
your prompt or CLAUDE.md shape what Claude tries to do, but they don't change what
Claude Code allows." Keep that sentence in view for the next seven sections.
1. Where does our source code go, and to whom?
It goes to the model provider. All of it that the agent reads, plus your prompts, plus tool output. There is no configuration that changes this, and it is worth refusing the softer phrasings up front — Anthropic's own sandbox comparison page says it in the middle of a warning about isolation: "Isolation also does not change what is sent to the model. Your prompts and the files Claude reads are transmitted to the Anthropic API or your configured provider with or without a sandbox."
So "our code doesn't leave the building" is false for every tool in this category, including the ones with the best security stories. The answerable questions are narrower and better:
- Is it trained on? Under commercial terms, no. The data usage page: for Team and Enterprise plans, the API, third-party platforms and Claude Gov, "Anthropic does not train generative models using code or prompts sent to Claude Code under commercial terms," absent an explicit opt-in such as the Development Partner Program. Consumer plans invert the default — "We will train new models using data from Free, Pro, and Max accounts when this setting is on (including when you use Claude Code from these accounts)."
- How long is it kept? 30 days for commercial accounts as standard. Zero data retention exists but is narrower than people assume: "ZDR is not included in the standard Claude for Enterprise plan and cannot be enabled from your admin settings" — it is turned on per organization by an account team after an eligibility check.
- What escapes the 30 days? Two paths, both user-initiated and both easy to miss
in a review. Transcripts sent with
/feedback,/bugor/shareare "retained for 5 years." And the periodic "How is Claude doing this session?" prompt has a second step that, if answered Yes, uploads the conversation, subagent transcripts and the raw session log — "Known API key and token patterns are redacted before upload. Source code, file contents, and other conversation content are uploaded as-is" — retained up to six months. Nothing goes without that explicit Yes, which is exactly why it belongs in the training you give the team, not in the architecture diagram.
Now the part that turns this from a policy question into a configuration question. All of the above
is a property of the account the engineer signed into, not of the repository they opened.
A personal Pro login and a corporate Enterprise seat produce identical-looking sessions in the same
checkout with materially different data postures. The ZDR page is explicit about the failure mode:
"If a developer signs in to Claude Code with a personal account or with an API key from a
different organization, those sessions are not covered." The fix is
forceLoginMethod and forceLoginOrgUUID delivered through managed settings,
and if your review produces one action item, that is a good candidate for it.
On routing through your own cloud: Bedrock, Google Cloud's Agent Platform and Microsoft Foundry
change where inference happens and turn telemetry, error reporting and /feedback off by
default. They do not make the client silent. The WebFetch domain safety check sends the requested
hostname to api.anthropic.com before every fetch, and the docs are specific that this
"runs regardless of which model provider you use and is not affected by
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC." One hostname, no path, no content, and
you can disable it with skipWebFetchPreflight at the cost of the blocklist. Small.
But if you tell your security team "on Bedrock nothing reaches Anthropic," you have told them
something the documentation does not say, and that is the kind of thing that gets found later.
2. What can the agent execute, and what stops rm -rf?
Out of the box, less than people fear. Claude Code starts read-only, ships a fixed list of
read-only commands like ls, cat and git status that run
without a prompt, and asks before anything that can modify the system. Writes are confined to the
directory it was started in and below.
The honest answer to rm -rf specifically has three parts.
The model is not what stops it. Two things are. A deny rule, evaluated
before the call, first-match-wins in the order deny → ask → allow, and unoverridable: "If a tool
is denied at any level, no other level can allow it." A bare tool name in deny goes
further and removes the tool from the model's context entirely, so it never appears as an option.
And an OS sandbox, where a forbidden write fails at the kernel rather than at a policy check.
The sandbox is off by default and it fails open. sandbox.enabled
defaults to false. When it is on, macOS uses Seatbelt and Linux and WSL2 use bubblewrap;
native Windows is not supported. Its default read boundary is wider than most reviewers expect —
"read access to the entire computer, except certain denied directories. Note that this default
still allows reading credential files such as ~/.aws/credentials and
~/.ssh/." And if it cannot start, Claude Code "shows a warning and runs
commands without sandboxing" unless you set sandbox.failIfUnavailable. For a
managed deployment, that setting is not optional; a security control that silently degrades to
nothing is worse than no control, because you stop watching.
In the permissive modes, the floor is very low. Running with
--dangerously-skip-permissions, the docs say you are "only prompted for explicit ask
rules, connector tools your organization set to ask, MCP tools marked
requiresUserInteraction, and removals targeting / or your home
directory." So there is a floor and it is your home directory. Inside the working tree, nothing
stops a destructive command in that mode except the fact that the working tree is a git repository —
which is the real answer, and the reason the working-directory boundary matters more than any
pattern-matching rule. Committed work survives. Six hours of uncommitted work does not.
The curl | bash in the README
Reading a README does nothing; it is text arriving in a context window. The risk is the agent proposing the command afterwards, and the defaults here are decent: network-fetching commands are not auto-approved, unmatched commands fail closed to a manual prompt, and suspicious command patterns require approval even when previously allowlisted.
Where this goes wrong is not the attacker's move, it is yours. Someone gets tired of approving
curl and adds an allow rule. Anthropic prints a warning about exactly this, and it is
worth reading slowly because it explains why argument-scoped Bash rules are a trap:
"Bash permission patterns that try to constrain command arguments are fragile. For example,
Bash(curl http://github.com/ *) intends to restrict curl to GitHub URLs, but won't
match variations like" — options before the URL, a different protocol, a redirect, or
URL=http://github.com && curl $URL. There is a companion caveat on the file
side: Read and Edit deny rules cover the built-in tools and recognised
shell commands, but "don't apply to arbitrary subprocesses that read or write files indirectly,
like a Python or Node script that opens files itself."
The generalisation is uncomfortable and correct: a permission rule that tries to be clever about arguments is a suggestion; a rule that denies a whole tool, and a boundary the kernel enforces, are controls. If your review wants argument-level guarantees, the answer is the sandbox's domain allowlist, not a regex. Our guide to pre-approval policies covers the hook-based version of the same decision.
Codex CLI answers this question in a different shape, and it is worth knowing which shape your
reviewer wants. Where Claude Code gates the decision and makes the OS boundary opt-in,
Codex constrains the capability first: a sandbox mode is always in force, with
read-only, workspace-write and danger-full-access as the
dial, and network off by default in the first two. See
Codex CLI sandbox modes. If your security team's
instinct is "I don't want to rely on a config file being right," that is the family of answers they
are reaching for.
3. How do credentials reach it — and what happens when it shells out?
The agent's own credential is the easy half. It is an OAuth token or an API key, stored in the macOS Keychain where available and behind file permissions elsewhere.
The hard half is every other secret on the machine. GH_TOKEN, NPM_TOKEN,
AWS_SECRET_ACCESS_KEY, a database URL in a .env your test runner sources.
Those reach the agent the same way they reach every other program you launch from that terminal:
inheritance. The sandboxing docs state it plainly — "sandboxed Bash commands inherit the parent
process environment by default, including any credentials set there" — and note that
settings.json's env block applies "to every session and to subprocesses
Claude Code spawns from it."
And it does not stop at the child. An environment block is copied at every fork. The agent runs
npm test; npm runs your test script; your test script spawns a worker;
somewhere in there a dependency's postinstall executes. Every one of those processes
holds the same token, and none of them was named in any permission rule, because the permission
system saw one command: npm test.
Claude Code has three controls here, and they are worth knowing individually because they are not equivalent.
| Control | What it actually does |
|---|---|
CLAUDE_CODE_SUBPROCESS_ENV_SCRUB |
One switch, no enumeration. Strips Anthropic and cloud provider credentials from subprocess
environments — Bash, hooks and MCP stdio servers. "The parent Claude process keeps these
credentials for API calls, but child processes cannot read them." On Linux it also runs
Bash subprocesses in an isolated PID namespace so they cannot read other processes'
environments via /proc. It does not touch GH_TOKEN.
|
sandbox.credentials — deny | Named variables are unset before each sandboxed command; named file paths become unreadable inside the sandbox. Precise, and it breaks the tools that needed the credential. "There is no built-in credential deny list, so only the files and variables you list are restricted." Sandboxed Bash only. |
sandbox.credentials — mask |
The interesting one, and under-known. The subprocess sees a per-session sentinel; the sandbox
proxy substitutes the real value on outbound requests to the hosts you list in
injectHosts. "The command and anything it logs never hold the real
credential, but its requests still authenticate." Requires
network.tlsTerminate, since the proxy has to see the request to rewrite it.
2.1.199+.
|
Only the third of those survives the failure mode that actually bites, which is a credential ending
up in a log, a transcript or an error message rather than being stolen outright. And note what none
of them do: they are all scoped to what Claude Code spawns. Nothing here reaches a process your
Makefile starts in the background.
Which is why the durable answer to this question is not a settings key. It is: do not export long-lived credentials into the shell you launch an agent from. Short-lived, narrowly scoped tokens minted per task, and a secret manager the agent has to ask for rather than a variable it inherits. That is ordinary hygiene which most teams have already half-implemented; the agent just makes the cost of not finishing it much more visible.
4. What about prompt injection through content the agent reads?
Your agent reads an issue body, a dependency's README, a CI log, a web page. All of it arrives as text in the same context window as your instructions, and from the model's position there is no typographic difference between a bug reporter describing a crash and a bug reporter writing a paragraph addressed to the agent. This is not a bug that gets patched. It is what "reading" means for a language model.
Vendors do publish defences, and Anthropic's list is worth quoting because of its ordering. Under "Protect against prompt injection", the core protections are: "Permission system: Sensitive operations require explicit approval", "Context-aware analysis: Detects potentially harmful instructions by analyzing the full request", "Input sanitization", and "Network command approval" — with a separate note that web fetch "uses a separate context window to avoid injecting potentially malicious prompts." The page ends with a warning Anthropic did not have to print: "While these protections significantly reduce risk, no system is completely immune to all attacks."
Read that list again and notice that the first item is the only one that is not a model-side heuristic. Context-aware analysis and input sanitization are probabilistic defences applied by, or on behalf of, the very component the attack targets. The permission system is different in kind: it does not read the text, it does not evaluate the argument, and there is nothing an attacker can write that addresses it. It is not a smarter reader. It is not a reader.
So the useful form of this question in a review is not how good is the injection classifier.
It is: on the day the classifier is wrong, what was on the allow list? If the answer
includes a broad Bash allow, or bypassPermissions outside a container, the
classifier was carrying the whole load and you now know how much load that was. If the answer is
"reads and edits inside the repo, and everything that publishes or spends comes to a human", the
blast radius of a successful injection is a bad commit on a branch.
One nuance specific to the egress side. If you have the sandbox on with a network allowlist, that is
a real containment boundary — but read its documented limits before you present it as one.
"Allowing broad domains such as github.com can create paths for data
exfiltration", and because the built-in proxy makes its decision from the client-supplied
hostname without terminating TLS, "code running inside the sandbox can potentially use domain
fronting or similar techniques to reach hosts outside the allowlist." An allowlist containing
your source host is an allowlist containing a write channel.
The version of this that composes with third-party instructions — a skill or an agent file that tells the agent to trust its inputs — we traced end to end in the security model of agent skills.
5. What is written down, and who can read it?
Two answers, because there are two logs with different audiences, and only one of them exists by default.
On the laptop, always. Claude Code writes a full transcript per session to
~/.claude/projects/<project>/<session>.jsonl — "every message, tool call,
and tool result" — plus subagent transcripts, spilled large tool outputs, pre-edit file
snapshots under file-history/, and history.jsonl, which is
"every prompt you've typed, with timestamp and project path." Default lifetime 30 days,
adjustable with cleanupPeriodDays.
The documentation does not soften what that means, and neither should your review:
Transcripts and history are not encrypted at rest. OS file
permissions are the only protection. If a tool reads a .env
file or a command prints a credential, that value is written
to projects/<project>/<session>.jsonl
So the honest answer to "who can read it" is: anyone who can read that user's home directory. Full
disk encryption is the control, and it belongs to your endpoint team, not to the agent. If your
threat model cannot accept a plaintext copy of everything the agent touched sitting on a laptop for
a month, lower cleanupPeriodDays, or set
CLAUDE_CODE_SKIP_PROMPT_HISTORY to stop transcript writes entirely — and accept that you
have also given up session resume, which developers will notice within a day.
Off the machine, only if you build it. Claude Code emits OpenTelemetry metrics and
events to a collector you configure, enabled with CLAUDE_CODE_ENABLE_TELEMETRY=1 — a
separate thing from the Anthropic-bound product telemetry that DISABLE_TELEMETRY turns
off. Fifteen event types, of which one matters more than the rest for a security team:
claude_code.tool_decision, carrying the tool name, a tool_use_id that
matches the hooks and trace spans, the decision, and a source that
distinguishes config, hook, user_permanent,
user_temporary, user_reject and user_abort. That field is the
difference between "a human approved this" and "a rule someone wrote in March approved this."
Content is off by default and gated per category: prompts are redacted unless
OTEL_LOG_USER_PROMPTS=1, tool parameters and commands need
OTEL_LOG_TOOL_DETAILS=1, and there is a trap in the middle —
"When OTEL_LOG_ASSISTANT_RESPONSES is unset, OTEL_LOG_USER_PROMPTS
controls it instead." Turn on prompt logging expecting prompts and you get responses too.
Administrators can pin the destination through managed settings, and when they do, Claude Code
"removes conflicting developer-set variables at startup to prevent signal routing around the
managed collector."
The summary a reviewer should walk away with: the default configuration produces a detailed private record on each developer's disk and no central record anywhere. That is exactly backwards from what most compliance regimes want, and it is a day of work to fix.
6. If something goes wrong, can we tell afterwards what it did?
Partly, and less than the transcript's richness suggests. Be precise about what each artifact is worth, because this is where a confident wrong answer does real damage six months later.
- The
.jsonltranscript is evidence, not an audit log. It is complete and it is readable, but it lives on the machine that had the incident, it is writable by the same user, and it is deleted on a 30-day timer by the process that wrote it. Copy it early or you are relying on a cleanup sweep not having run. - Git is the tamper-evident record you already have. Reflog, object store, commit timestamps and authorship survive the agent, and this is the strongest argument for keeping the working-directory boundary tight: everything the agent did to your code is diffable, and diffing is a thing your team already does well.
- Your endpoint tooling sees the commands. The agent's Bash calls are ordinary
child processes with ordinary
execveevents. Whatever EDR you run does not need to know what a coding agent is. This is the most underused answer in the entire review. - The OTel stream, if you turned it on before the incident. Which is the point.
- Egress logs, with a caveat. The built-in sandbox proxy logs by hostname and does not inspect TLS by default. If your answer needs to be "we can see what left", that requires a proxy that terminates TLS with its CA inside the boundary — which the docs point you to, and which is a genuine project rather than a setting.
What does not exist, and you should not promise: a vendor-side, per-tool-call audit trail for Claude Code. Claude Enterprise has a Compliance API and an audit log covering organization events with a 180-day window, and a separate analytics API for aggregated usage and cost. Neither is a record of which commands an agent ran in which repository. That record is the local transcript plus your own collector, and nothing else.
One trap worth naming out loud. A session run in bypassPermissions or a fully automatic
mode produces a transcript that looks the same whether the run was routine or hostile, because
nothing was ever refused and no decision was ever recorded. The forensic value of an approval log is
that it contains denials. A log of a session where nothing could be denied is a narrative, and
narratives are what you get instead of evidence.
So decide the incident question before the pilot. If the required answer is "we can produce
a per-tool-call record with the human decision attached", that is a collector plus managed settings
plus ConfigChange hooks to catch someone loosening the policy mid-session. All of it is
documented and none of it is default.
7. What about extensions, skills and MCP servers?
This is the question your security team is most right to press on, and the one where the honest answer is weakest.
Start with the mechanics. A stdio MCP server configured in a project file is a process launched
on the developer's machine, with the developer's environment, when the session starts. Adding
one to .mcp.json is not adding a config entry; it is agreeing to run a binary. And it
runs outside every boundary discussed so far — the sandbox comparison page states that with the
built-in Bash sandbox, "MCP servers and hooks are separate processes that run unconstrained on
the host." Hooks carry the same weight; Anthropic's own disclaimer reads: "Command hooks
execute shell commands with your full user permissions. They can modify, delete, or access any files
your user account can access."
Next, what the protocol guarantees — and here it pays to read the specification more carefully than the summaries do. Its security principles sound absolute: "Tools represent arbitrary code execution and must be treated with appropriate caution" and "Hosts must obtain explicit user consent before invoking any tool." But those sentences are in lower case, and the specification opens by stating that RFC 2119 keywords apply "when, and only when, they appear in all capitals." Immediately after the principles comes the line that settles it: "While MCP itself cannot enforce these security principles at the protocol level, implementors SHOULD…" build consent flows, implement access controls, follow best practices. If someone brings you "the MCP spec requires consent before every tool call", it does not. Consent is a promise each host keeps or does not.
There are genuinely normative requirements, and they are the ones worth citing. On the tools page: "clients MUST consider tool annotations to be untrusted unless they come from trusted servers", and "there SHOULD always be a human in the loop with the ability to deny tool invocations." On authorization, the sentence that answers the local case outright — "Implementations using an STDIO transport SHOULD NOT follow this specification, and instead retrieve credentials from the environment." A local MCP server is exempt from MCP's authorization model by design, and told to read its secrets from the same inherited environment we walked through in question three.
The specification's own security guidance is unusually candid about what that means. Under "Local MCP Server Compromise" it lists the risks as "Arbitrary code execution. Attackers can execute any command with MCP client privileges" and "No visibility. Users have no insight into what commands are being executed", and it imposes one real requirement: a client offering one-click server configuration "MUST implement proper consent mechanisms prior to executing commands", showing the exact command without truncation. Sandboxing those servers is a SHOULD. Nowhere is it required.
And the ecosystem is not curated. Anthropic says so directly: "Anthropic reviews connectors against its listing criteria before adding them to the Anthropic Directory, but does not security-audit or manage any MCP server." Note also what the spec does not name: "tool poisoning", "rug pulls" and "tool shadowing" are terms from third-party research, not from the specification. Useful vocabulary; do not attribute it to the standard in a review, because someone will check.
There are two specific gaps worth writing into your review, because both are the sort of thing that is fine right up until it is not:
- Trust verification does not run in automation. First-time codebases and new MCP
servers require a trust prompt — with the note that "Trust verification is disabled when
running non-interactively with the
-pflag." That is precisely the CI and scripted case, where nobody is present to be asked. - Configuration is a persistence mechanism. The sandbox runtime documentation warns
that "a sandboxed session that can write them can persist hooks, permission rules, or MCP
servers that run unsandboxed the next time you launch Claude Code." Claude Code protects its
own settings files and
.mcp.jsonfrom sandboxed writes for this reason. A one-session compromise that can edit config is not a one-session compromise.
The practical posture is not "ban extensions". It is to treat agent configuration as privileged
source: .mcp.json, .claude/settings.json, hook scripts and skill files go
behind CODEOWNERS, get reviewed by a human who knows what they do, and get an inventory somebody
owns. Our piece on setting up Claude Code for a team
covers which of those files should be committed and which must never be, and
the agent skills security model covers the case
where the privileged file is a Markdown document nobody thought to review.
The answer sheet
If you need one page to take into the meeting, this is it.
| They ask | Honest answer | The control that helps |
|---|---|---|
| Where does our code go? | To the model provider, always. Not trained on under commercial terms; 30-day standard retention. | forceLoginMethod / forceLoginOrgUUID; ZDR if you qualify |
| What can it execute? | Whatever your rules allow. Read-only by default; the OS sandbox is opt-in and fails open. | Bare-tool deny; sandbox.enabled + failIfUnavailable |
What stops rm -rf? | A deny rule or the kernel. In bypass mode, only / and your home directory still prompt. | Deny rules, a container for unattended runs, and git |
| How do secrets reach it? | By inheritance, all the way down to a dependency's postinstall script. Nothing stops that by default. | CLAUDE_CODE_SUBPROCESS_ENV_SCRUB, sandbox.credentials mask, short-lived tokens |
| Prompt injection? | Unsolved, and the model-side defences are probabilistic. Not a reason to refuse; a reason to bound. | The permission gate on anything that writes, publishes or spends |
| What is logged? | Everything, in plaintext, on the developer's disk, for 30 days. Nothing centrally. | cleanupPeriodDays, disk encryption, an OTel collector |
| Can we investigate? | From git, EDR and the local transcript. There is no vendor per-tool-call audit trail. | tool_decision events; ConfigChange hooks; copy transcripts early |
| The extension supply chain? | A real gap. MCP servers and hooks run unconstrained, and the protocol cannot enforce consent. | CODEOWNERS on agent config; an inventory; managed-only hooks |
Notice how few of those controls are exotic. Managed settings, a deny list, a collector, CODEOWNERS, short-lived tokens, full disk encryption. A security team that hears this list recognises its own vocabulary, which is the actual goal of the meeting — not to prove the agent is safe, which is not a property anything has, but to show that its risks land in categories you already manage.
Where Backgrind fits
One honest note, and it is deliberately narrow, because a security post that resolves into a product pitch deserves to be dismissed.
Every answer above ultimately rests on a human decision at a gate being a real decision. That is a
user-interface property, not a security property: an approval prompt sitting in a terminal behind a
browser, an IDE and four other tabs gets answered on autopilot, and an autopilot approval is
indistinguishable in the log from a considered one. Backgrind runs the CLI you
already have — your login, your .claude/, your permission rules — in an always-on-top
window, and surfaces a held PreToolUse ask as an ambient prompt with the command in
front of you, wherever you happen to be looking. Its own "always allow" writes a scoped
repo·tool·command rule locally with a decision log next to it, so a rule you granted at 2 a.m. is
something you can read back. Because it is bring-your-own-CLI, agent content never reaches our
server, which is the only claim in this paragraph that belongs on a data flow diagram.
It is not a security control and we would not put it in the table above. It is the difference between a gate you configured and a gate you actually operate. If you want the configured half done properly first, start with pre-approval policies, then what your team should commit — or see the overlay in the live demo.
Is it safe to let a coding agent run in a repo with production credentials in .env?
Treat it as no. Not because the agent is hostile, but because anything a tool reads is written to
the session transcript in plaintext, and Anthropic names that case specifically: if a tool reads
a .env file, that value lands in the .jsonl. Add the file to
permissions.deny for Read, add it to sandbox.credentials as a denied
path, and if the workflow genuinely needs the credential, use mask mode so the
subprocess only ever holds a sentinel.
Does routing through Amazon Bedrock mean nothing reaches Anthropic?
Not quite, and the documentation does not claim it. Using Bedrock, Google Cloud's Agent Platform
or Microsoft Foundry turns off telemetry, error reporting and /feedback by default,
and inference happens on that platform under its retention policy. But the WebFetch domain safety
check still sends the requested hostname — not the path or contents — to
api.anthropic.com, and it is explicitly not covered by
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC. Disable it with
skipWebFetchPreflight if your policy requires zero egress, and accept that WebFetch
then consults no blocklist.
Is running the agent in a container enough?
It is the strongest single answer for the execution question and it does nothing for the data question. A container bounds what a compromised session can reach on the host, which is exactly what you want before running anything unattended. It does not change which files are uploaded to the model, it does not stop credentials mounted into the container from being inherited by every process inside it, and it does not give you an audit trail. Anthropic's own framing: sandbox isolation "reduces the impact of a breach, but it does not eliminate risk."
Can we just deny curl and wget and call the network handled?
No. Denying those two names blocks those two names. Any language runtime the agent is already allowed to run has an HTTP client, and Anthropic's permissions page warns separately that argument-scoped Bash patterns do not survive options, protocol changes, redirects or a variable holding the URL. Network egress is answerable at exactly one layer: a sandbox or proxy with a domain allowlist, enforced below the process. Anything above that is a speed bump.
What is the smallest configuration that would satisfy a reasonable review?
Roughly five things, all in managed settings so they cannot be loosened locally: pin the login to
your organization; deny reads of credential paths and deny the tools you never want offered;
enable the sandbox with failIfUnavailable on; set
disableBypassPermissionsMode to "disable" outside containers; and point
telemetry at a collector you own. Then put agent configuration files behind CODEOWNERS. That is
an afternoon, and it converts most of the answers in this post from "it depends on the developer"
to "it is enforced".
Sources
Training policy, retention periods, /feedback and session-survey uploads, telemetry
variables, provider defaults and the WebFetch preflight:
Claude Code — Data usage
(checked 2026-08-07, v2.1.224).
ZDR eligibility and the personal-account gap:
Zero data retention.
Permission enforcement, deny/ask/allow precedence, bare-name removal, permission modes,
disableBypassPermissionsMode, the fragile-Bash-pattern warning and the Read/Edit
subprocess caveat:
Configure permissions.
Prompt-injection protections, MCP review posture, trust verification and the -p exception:
Security.
Sandbox defaults, OS primitives, credential deny and mask,
failIfUnavailable, and the "Security limitations" section including domain fronting:
Configure the sandboxed Bash tool.
Isolation comparison, "does not change what is sent to the model", unconstrained MCP servers and hooks,
and the config-persistence warning:
Choose a sandbox environment.
Hook disclaimer, PreToolUse decision contract and ConfigChange:
Hooks reference.
Transcript paths, plaintext storage and cleanupPeriodDays:
The .claude directory
and Settings.
OpenTelemetry events, tool_decision attributes and the content flags:
Monitoring usage.
Security principles, the RFC 2119 casing rule and the "cannot enforce at the protocol level" line,
specification revision 2026-07-28:
Model Context Protocol specification;
untrusted annotations and the human-in-the-loop requirement:
Tools;
the STDIO authorization exemption:
Authorization;
local server compromise and the one-click consent requirement:
Security best practices.
Codex sandbox modes, the network-off default and the sandbox/approval split:
Codex — agent approvals and security
and Sandboxing,
cross-checked against @openai/codex 0.147.0.
Versions read from the npm registry on 2026-08-07.