system: OPERATIONAL
← back to all hacks
AGENTS CRITICAL NEW

DeepSeek Harness: the sandboxed agent that could switch off its own sandbox

A confined coding agent could lift its own confinement with one shell command, because the harness decided who to trust from a header the caller supplies.

2026-09-16 // 7 min affects: deepseek-harness, coding-agents, llm-agents, deepseek

What is this?

On September 8, 2026, Nir Zadok and Moshe Siman Tov Bustan of OX Research published their disclosure of a flaw in DeepSeek Harness (dsh), DeepSeek’s open-source, local-first harness for running coding agents on a developer’s machine. A sandboxed agent could turn its own sandbox off with a single shell command, on shipped defaults, with no network exposure and no credentials. VulnCheck, acting as CNA, published the record the same day and rated it 9.4.

The harness shipped in August 2026 and passed 215,000 GitHub stars within weeks. It presents a browser UI backed by a local HTTP API, and it runs agent commands inside an OS sandbox — bubblewrap, Landlock or Seatbelt depending on the platform — precisely so that an agent handling untrusted material cannot reach past its workspace.

The interesting part is not that the sandbox had a hole. It is that the sandbox’s own control plane was inside the sandbox.

How it works

Three ordinary design choices, each defensible alone, composed into an escape.

The sandbox confined files, not networking. The bubblewrap invocation passed --unshare-pid but not --unshare-net, so the network namespace stayed shared with the host. The Seatbelt profile was (allow default) with (deny file-write*) layered on top. Loopback was therefore reachable from inside. The project’s own CLI reference for the affected release states plainly that under the default setting, reads and network access are not confined.

The local API decided trust from a client-supplied header. A single function, isTrustedApiRequest, read the Host request header and granted access if the value was a loopback authority or appeared in a configured trustedHosts list. It never compared that value against the connection’s actual peer address. Against the threat it was written for — DNS rebinding, where a browser cannot lie about Host — it worked. Against any client not bound by browser rules, it did not. A comment in the file says as much: the check, in the authors’ own words, “is not an auth layer.”

Routine shell calls needed no approval. Approval was applied only when a command requested broader access than the session already held. The escape did not request anything. It changed the session’s setting.

agent (inside sandbox)
   │  bash tool call — no approval required

curl → http://127.0.0.1:<port>/…   Host: <loopback authority>
   │  isTrustedApiRequest() reads the header, not the peer

session mode := danger-full-access,  approval := never


every subsequent command runs unconfined, unprompted

The harness also hands the agent’s shell the address of that interface and the current session identifier, so no discovery step is needed.

Two details from OX’s session log are worth dwelling on. No approval/request event appears anywhere in it. And the policy change is attributed as source: {kind: 'user'}the harness could not tell its own agent’s shell from the human at the keyboard. OX also ran a matched control: two sessions, same default preset, same write to a path outside the workspace. The one that had made the call succeeded; the other was blocked. The sandbox was working right up until it was asked to stop.

A second path existed wherever the port was reachable from outside — a tunnel, reverse proxy, SSH forward, editor port forward. Then an unauthenticated remote caller could drive the agent and download every stored conversation, with no API key and no model call.

Why it matters

The only precondition for the local escape was that the agent execute a command induced by attacker-supplied text. That is the exact input class the sandbox exists to contain. So the control was defeated by the thing it was built to stop — which makes this a containment failure rather than a filtering one, and containment is the layer you fall back to when filtering fails.

A coding-agent harness is worth attacking because it holds a shell running with the ambient authority of the developer who launched it: source trees, build and test commands, and whatever SSH keys, cloud credentials and internal systems that workstation can reach. We have covered adjacent versions of this before — localhost as an agent attack surface, DNS rebinding against local MCP servers, the harness/model privilege boundary, and repository configuration that runs attacker code outside the sandbox. The pattern repeats because the agent sits on the same host as the thing that governs it.

Two things about the handling deserve mention. Community members described this same escape on the project’s public discussion board on August 13 and August 14, 2026, eleven days before the vendor-track report. And the fixed build 0.1.2-alpha.1 (August 27) was never published to npm, which is where the project’s own instructions send users; the first fixed npm release was 0.1.2-alpha.2 on August 30. A patch that does not reach the install path is not a patch yet.

The project’s SAFETY.md states that the software has not had a security audit and that sandboxing and approval prompts do not guarantee isolation. That notice is honest, and it should be read as a specification rather than boilerplate.

Defenses

Put the agent’s control plane outside the agent’s reach. If the API that can change a session’s privilege level is reachable from inside the sandbox, the sandbox is advisory. Unshare the network namespace (--unshare-net on bubblewrap, an explicit deny network* on Seatbelt) or bind the control API to a socket the sandbox profile denies. This is the fix that generalizes beyond one product.

Never derive trust from a header the client controls. Host, Origin and X-Forwarded-For are caller-supplied. Local-only means checking the peer address, or better, requiring a credential — which is what the fix does: a one-time token printed at startup, exchanged for a signed cookie that every call must carry.

Gate the privilege change, not only the privileged act. Approval that fires when a command asks for more access, but not when a call grants it, leaves the whole escalation path unattended. Treat any mutation of sandbox mode, approval policy or allowlist as the highest-approval action in the system.

Make the actor distinguishable in your audit trail. A policy change recorded as coming from “user” when it came from an agent’s shell defeats detection and forensics both. Bind a distinct principal to agent-originated calls, and alert on any privilege change carrying that principal.

Audit what you already run. Upgrade past the fixed release; check which harness version any third-party desktop wrapper ships, since wrapper maintainers pin their own copies. Remove tunnels, proxies and editor port forwards that expose an agent’s local interface. And where you cannot verify containment, assume a compromised session has the developer’s full authority and scope your credentials accordingly.

Note what the fix does not change: the sandbox still does not confine reads or network access, and the agent’s shell still receives the interface address. Authentication closed the door that was open; it did not move the door out of the room.

Status

ItemDetail
ReferenceCVE-2026-82533 (VulnCheck as CNA), CWE-807 — reliance on untrusted inputs in a security decision
SeverityCVSS 9.4
AffectedDeepSeek Harness (dsh) 0.1.1-rc.2 and earlier
Community reportsPublic discussion-board reports, August 13 and August 14, 2026
DisclosureReported to VulnCheck August 24, 2026
Fix0.1.2-alpha.1, August 27, 2026 (GitHub only); first fixed npm release 0.1.2-alpha.2, August 30, 2026
VerificationOX Research re-tested and confirmed remediation, August 30, 2026
PublicationCVE record and OX write-up, September 8, 2026
Residual gapSandbox still does not confine reads or network; agent shell still receives the control-interface address

Sources