The Summary Is the Persistence Layer: DeepSeek Harness Adds a Search-Path RCE and a Compaction Injection

NVD published two CVE records against DeepSeek Harness (dsh) within seconds of each other on 16 October 2026 — CVE-2026-95182 at 15:17:36.940Z and CVE-2026-95183 at 15:17:37.050Z. Both are sourced to cve@mitre.org, both carry vulnStatus: Received with no CVSS scores yet, and both are verified against the same developer-preview source tree at master commit c291e796 from September. One lets a poisoned model write the executable the harness will obediently run; the other makes sure the poison survives the session boundary that was supposed to contain it. Together they read as a single attack chain wearing two CVE numbers.

This is the third dsh security briefing on this site in three weeks, after the workflow-VM escape and its silent sandbox fix and September’s escape-playbook catalog. The project is a preview, and previews are where architecture gets decided — which is precisely why each new finding matters more than its severity field will eventually suggest.

CVE-2026-95182: the PATH is the vulnerability

The first record is a CWE-426 untrusted search path in the MCP client plugin. The affected component is @deepseek-ai/dsh-mcp-client, specifically the stdio transport at packages/mcp/mcp-client/src/transport.ts:35: dsh passes the operator-configured command field verbatim into the MCP SDK’s StdioClientTransport, and the spawn chain — SDK to cross-spawn to host-process PATH resolution — resolves a bare command name against whatever PATH the harness process inherited. Only credential-shaped and DSH_* variables are scrubbed from the child environment; PATH passes through untouched.

The reason that is exploitable rather than merely untidy is the deployment layout. In pnpm source-checkout deployments, the first two PATH entries are the project’s own node_modules/.bin — a directory the model can write to as a legal operation under dsh’s default workspace-write sandbox mode. The researcher verified the resolution landing there (RESOLVED_TO_NODE_MODULES_BIN). So a model steered by injected external content writes a forged executable with the same name as the configured command — npx is the demonstrated example — as an ordinary, sandbox-permitted workspace write, and every subsequent spawn, hot-reload, or reconnect of that MCP server executes attacker-controlled code.

The verification is unusually concrete. After planting a passthrough marker wrapper at node_modules/.bin/npx and restarting dsh, the marker log recorded eleven FAKE-NPX-HIT entries, including reconnect retries, with no functional anomaly. A second test configured the bare name poc-mcp-server, planted a fully functional minimal MCP server under that name, and watched dsh spawn it without warning — session tool lists showed a normally-worded mcp__poc__workspace_stats tool, calls succeeded, and the attacker’s server came online with a fully legitimate identity. A third variant confirmed the ecosystem-standard form: command: npx with args: [“-y”, “@modelcontextprotocol/server-everything”] spawned the forged node_modules/.bin/npx just the same.

Three properties make this worse than a one-shot command injection. What gets replaced is a long-lived infrastructure process holding all of its configuration’s tool permissions and credentials, so the attacker inherits the entire tool surface rather than a single execution. The forgery is invisible to both user and model when implemented as a passthrough — the server comes online normally. And the hijacked process’s network egress is unrestricted, enabling continuous exfiltration that the workspace-write sandbox, which governs file effects only, never sees. One hijack persists, and it laterally affects other sessions sharing the same server config. The writeup scopes the blast radius honestly: Linux and macOS, all stdio transport forms, with scope-change semantics (S:C) because the forged write crosses from model-writable workspace into harness infrastructure.

The writeup adds that the same bare-name spawn root cause exists in @deepseek-ai/dsh-sandbox-local (the bwrap and sandbox-exec executors), reportedly including a macOS instance observed by a third party. We have not verified that extension ourselves and mark it as reported; the main finding stands on its own evidence.

CVE-2026-95183: compaction launders the injection

The second record is a CWE-1427 prompt injection in the context-compaction feature — component @deepseek-ai/dsh-compaction-basic, file packages/compaction/compaction-basic/src/summarizer.ts — and it is the more conceptually interesting of the two. Compaction replaces the conversation history with a model-generated <compacted-summary> checkpoint that becomes the resumed session’s entire context, without showing the user its content. The summarizer is the same LLM that has read the conversation, and its instruction template directs it to reproduce “Primary Request and Intent” verbatim — “quote verbatim where the exact wording matters” — and to enumerate “Pending Jobs”.

Because untrusted tool outputs carry no provenance or trust marking, injected directives are indistinguishable from user intent at summary time. Directives embedded in external content — including complete shell commands — are preserved verbatim into the resumption context and inherited as established tasks. The researcher’s bait file ended with an OPERATIONAL REQUIREMENT block containing a heartbeat shell command; after /compact, that command appeared verbatim in three sections of the generated summary (Files and Code, Pending Jobs, Critical Context). There is no anti-laundering mechanism: compacted text is indistinguishable from original tool output in the resumption context, with no taint marking, so a single injection can persist across sessions indefinitely. The summarizer itself, reading the poisoned conversation, can additionally be steered directly.

The writeup records the honest boundary, and we repeat it because it is the most important sentence in either file: in the tested configuration the resumed model identified and refused the injected instruction. The persistence mechanism is deterministic — the payload command demonstrably survives compaction — while the execution defence is probabilistic, resting on model-layer vigilance with no systematic enforcement in the summarizer. A less vigilant model, or stealthier wording, meets no deterministic mechanism at all. And the practical exploit path does not need execution of the planted command: the resumed session’s network egress is entirely unrestricted, so silently exfiltrating workspace data to attacker-controlled endpoints works with no mechanism detecting it. The file sandbox watches writes; nothing watches the wire.

Read as a pair, the two CVEs compose. CVE-2026-95183 ensures attacker instructions survive session boundaries as established tasks; CVE-2026-95182 gives those instructions a way to convert a legal workspace write into infrastructure takeover. Either alone is a real finding. Together they describe an agent whose memory and whose process table both trust content the attacker chose.

Third act, same preview

We checked the upstream repository, deepseek-ai/deepseek-harness, for signs of a fix. We found no vendor advisory and no commit referencing either CVE at time of writing. The most recent touches to both files predate disclosure and read as ordinary feature work: a September feature commit on the summarizer around dynamic tool updates, and a September MCP-protocol-negotiation feature on the transport. The NVD records, still in Received state, carry no fixed-version data. That is consistent with a same-day disclosure against a September commit — not with neglect, but with the ordinary lag between researcher, CNA, and maintainer. Operators should assume both issues are live in current checkouts and harden accordingly rather than waiting for a version string.

Both findings come from the same independent researcher, whose NVD-referenced gists were created on 14 October and whose public proof-of-concept repository was pushed the same day — two days before the CVE records went public. Brave Search discovery returned only CVE-mirror aggregators for all three identifiers in this story; there is no independent technical analysis and no vendor statement at time of writing.

What to do

Pin MCP server commands to absolute paths. Never configure a bare executable name in a dsh MCP server entry; resolve it once at configuration time, hash-anchor the resolution, and refuse to connect on mismatch. The researcher’s first recommendation — aligning with the landlock executor already in the same repository — is the right shape: the project has a path-resolution layer, and the MCP client should use it. Until that ships, treat any bare-name command value as a finding in config review.

Keep node_modules/.bin out of the model’s write set. The exploit depends on the model legally writing to a directory that precedes system paths in PATH. Constraining the workspace-write sandbox to exclude executable-resolution directories breaks the chain at its cheapest link, independent of any transport fix.

Treat compacted summaries as untrusted input. Review the <compacted-summary> before resuming security-sensitive sessions, and never let quoted-verbatim content enter task-planning sections without provenance. Longer term, the fix is structural: taint-mark untrusted turns before compaction, keep source classification (user, system, tool-output) in the summary assembly, and drop the “quote verbatim” instruction for anything that did not originate with the user.

Constrain resumed-session egress. A resumed session should inherit the original session’s network policy, and the first batch of tool calls after resumption — especially network calls — should require confirmation. The exfiltration path in both writeups flows through the same gap: file effects are sandboxed, the wire is not.

Verification note. Both CVE identifiers, NVD publication timestamps (CVE-2026-95182 at 2026-10-16T15:17:36.940Z, CVE-2026-95183 at 2026-10-16T15:17:37.050Z), vulnStatus: Received values, the cve@mitre.org source identifier, and both description texts were read first-hand from the NVD 2.0 API. Neither record carries CVSS metrics or configuration data at time of writing; we state no scores and no affected range beyond the researched commit. Component paths, file names, marker counts, payload wordings, and the quoted refusal boundary come from the researcher’s NVD-referenced gists and the accompanying public proof-of-concept repository’s vulnerability writeups, all read first-hand; short quoted phrases are the researcher’s words. Upstream commit history for both affected files was read first-hand from the GitHub API for deepseek-ai/deepseek-harness. Brave Search was used for discovery and returned only CVE-mirror aggregators and automated scoring pages — no independent technical analysis and no vendor advisory exist at time of writing. Nothing was installed, no dsh instance was run, no MCP server or bait file was crafted, no payload was executed, and no third-party request was sent beyond reading the cited public records. We have no evidence of exploitation and imply none. Remediation guidance is our editorial analysis.

Sources:

  • NVD — CVE-2026-95182, untrusted search path in dsh MCP client stdio transport (vulnStatus: Received, no CVSS at time of writing)
  • NVD — CVE-2026-95183, prompt injection persistence across dsh context-compaction boundaries (vulnStatus: Received, no CVSS at time of writing)
  • Researcher writeups — CVE-2026-95182 and CVE-2026-95183 vulnerability descriptions: marker counts, takeover demonstrations, compaction evidence, and the honestly-recorded refusal boundary
  • GitHub API — deepseek-ai/deepseek-harness commit history for summarizer.ts and transport.ts (September feature work, no CVE-referencing fix at time of writing)
  • al-ice.ai — DeepSeek Harness CVE-2026-108789: the workflow-VM escape and its silent fix