The Fix Landed, the Reply Never Did: DeepSeek Harness Rebuilt Its Workflow Sandbox and Told No One

NVD published CVE-2026-108789 at 03:17Z this morning against deepseek-harness — DeepSeek’s open-source agent harness, 248,111 stars and 29,828 forks as we write — naming the agent function of the dsh-workflow-worker-thread component, affected “up to 0.1.1-rc.2”. VulDB, the assigning CNA, scored it 5.5 medium on CVSS 3.1 (CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:L) and 2.0 on CVSS 4.0, classified it CWE-840 (business logic errors), and closed the description with the sentence this site has now quoted a dozen times this month: “The vendor was contacted early about this disclosure but did not respond in any way.” No fixed version is listed. An hour earlier the same CNA published CVE-2026-108788 against the same project — a sandbox issue in the spawn function reached through a Profile argument, CWE-264, 5.3 local.

Taken at face value, that is an unfixed arbitrary-severity bug in one of the most-starred repositories on GitHub, ignored by its vendor. Taken from the repository, it is something more interesting and considerably more useful: the vendor fixed it thoroughly, structurally, and silently — on 13 September, a month after the report — and then did none of the things that would let anyone find that out.

What was actually reported

The CVE’s own reference points to GitHub Discussion #243, opened by Mengxun326 on 13 August 2026 — the day the repository was created — and titled “Security: workflow tool vm escape via injected host-realm functions (user-privilege file/network access, sandbox bypass)”. It sits at zero comments. The reporter states plainly why it is a public thread: the repository has “no SECURITY.md or private reporting channel”. We confirmed that SECURITY.md still returns 404 at master today.

The finding is a textbook realm confusion, and the report is unusually good. The workflow tool ran model-written orchestration scripts inside a Node VM context created from an empty object, vm.createContext({}), and then injected host-realm closures — agent, parallel, pipeline, phase, log — into that context as globals. Because those closures are objects belonging to the worker realm, agent.constructor is the worker realm’s Function. One expression crosses the boundary:

agent.constructor('return process')().getBuiltinModule('fs')

Freezing the injected functions, which the harness did, changes nothing: Object.freeze protects the binding, not the prototype chain that leads back to the host realm’s Function. From there the reporter observes that the worker thread is an ordinary Node worker — not covered by the file sandbox, with an unrestricted global fetch, and with process.getBuiltinModule() handing out fs, child_process and net. The environment scrub the harness performs (keeping only TMP/TEMP) was verified by the reporter to work and to be beside the point. Their stated impact: arbitrary file read and write as the harness user — naming the credentials store, SSH keys and browser profiles — unrestricted outbound network, and command execution, all bypassing a file sandbox that confined only the shell executors and the filesystem tools. They rate it High, note there is no remote pre-auth vector because the web API is loopback-only, and supply a deliberately read-only proof of concept that reads C:\Windows\win.ini and exfiltrates nothing. They also offered to write the patch.

That is the gap worth staring at. The reporter calls it local user-privilege RCE that defeats the product’s advertised sandbox. The CNA calls it CWE-840, business logic errors, 5.5 and 2.0. We have made this point about VulDB’s agno batch and we will keep making it: when a scoring pipeline renders “escape the sandbox and read the credential store” as C:L/I:L/A:L, any downstream policy that filters on a 7.0 threshold has been handed a reason to ignore the finding. The classification is the more consequential error. CWE-840 describes a flawed business rule; this is a realm boundary that was never a boundary, and CWE-265 or a privilege-context weakness would have put it in front of the people who look for exactly this pattern in JavaScript sandboxes.

The repository answers a question nobody asked it

DeepSeek Harness keeps its architecture decisions in-tree, under .agents/notes/, in English and Chinese. One of them is .agents/notes/implemented/architecture/2026-09-13-workflow-ptc-sandbox-reuse.md. Its status line reads implemented. Its Problem section, quoted verbatim, reads:

“Dynamic workflows evaluate model-written JavaScript and start subagents. A worker thread keeps script execution off the host event loop, but code escaping its VM can use Node with the host process’s file authority.”

That is the reported vulnerability, restated by the maintainers in their own vocabulary, in a note whose “Alternatives considered” section explicitly rejects “Retain worker-thread execution” on the grounds that “worker termination cannot apply the Session’s OS file policy”. The decision was to run workflows through the shared Node PtcRuntime — the same OS-confined process runtime the project had introduced two days earlier for direct code execution — so that file enforcement, heap limits, output limits and process cleanup are owned by the sandbox rather than by the VM.

The note arrived in commit 35af8698c24c, authored 13 September 2026 at 05:05:56Z, titled fix(workflow): execute orchestration in the sandboxed PTC runtime — a fix, not a refactor — touching 158 files for +2,987/−3,192 lines, with a follow-up commit the same morning closing lifecycle and CI gaps. Thirty-one days after the report. At master today there is no path anywhere in the 17,128-entry tree containing worker-thread: both dsh-workflow-worker-thread and the related dsh-code-runtime-worker-thread — which the reporter flagged as the same exposure class, running injected code via new AsyncFunction directly in the worker realm — are gone from the source tree. The replacement package, packages/workflow/workflow-ptc, carries the new trust model in its module header: “Getters and proxy traps may execute inside the confined Node process; process isolation and cancellation belong to PTC, not the VM.”

This is the right fix, and it is worth saying so without hedging. The project did not go hunting for the escape primitive — the VM is still there, the helper injection is still there, and the note states outright that the VM “is not a security boundary”. It moved the boundary to the operating system, where a boundary can actually hold, and it rejected the cheaper option in writing. Compare the pattern we keep documenting in this space: metacharacter screening in front of a shell, or a code node with no confinement at all. DeepSeek deleted the launcher instead of filtering its inputs.

Everything after the commit went wrong

Here is where a good engineering outcome becomes a bad security outcome, and all four failures are independently verifiable.

Nobody told the reporter. Discussion #243 still has zero comments, fifty-nine days after the fix shipped. The person who found the bug, wrote a scoped PoC, offered a patch and had no private channel to use received no acknowledgement — which is presumably why they went to a CNA, and why the public record now says the vendor never responded. From VulDB’s position that statement is accurate.

There is no advisory, so there is no fixed version. The repository publishes no GitHub Security Advisory and the NVD record carries no fix. We established the boundary ourselves by ancestry: the fix commit is not contained in dsh-v0.1.5-rc.2, and is contained in dsh-v0.1.6-alpha.1, released 15 September 2026 at 04:57Z. That is the answer the advisory would have given. Every operator now has to derive it the way we just did.

The vulnerable artefacts are still on npm, and still the default. @deepseek-ai/dsh-workflow-worker-thread remains published, undeprecated, with 20 versions, last modified 22 September. Its dist-tags.latest points at 0.0.1-rc.3, and its next tag at 0.1.5-rc.3 — both pre-fix. A plain npm install @deepseek-ai/dsh-workflow-worker-thread today installs vulnerable code that no longer exists upstream. The same is true of @deepseek-ai/dsh-code-runtime-worker-thread. The successor @deepseek-ai/dsh-workflow-ptc first appeared on the registry at 03:16Z on 15 September, an hour and forty minutes before the GitHub release — so the fix was distributed, cleanly, while the thing it replaced was left sitting next to it with a friendlier tag. This is the registry-versus-repository signalling failure inverted: there, the fix was on npm and the repo looked frozen; here the fix is in both and the vulnerable package is what a default install resolves to.

The affected range in the CVE is seven weeks stale. “Up to 0.1.1-rc.2” names a tag cut on 21 August. The project has shipped fourteen releases since, through dsh-v0.2.1-alpha.2 on 9 October. A reader checking whether their 0.2.x install is affected finds a range that stops below it and an absent fix field, and can reasonably reach either of two wrong conclusions. The range is not false — it is simply frozen at the moment the submission was filed, which is what happens when the vendor does not engage with the record.

Why a silent fix is a supply-chain event

The temptation is to file this as a courtesy problem. It is not. A silent fix produces three measurable defects that a two-line advisory would have eliminated: no scanner can map an installed version to a verdict; no downstream consumer learns that a package in their lockfile was retired for a security reason rather than a design preference; and the public record ends up asserting the opposite of the truth, which is corrosive in both directions — it defames a vendor that did the work, and it tells operators there is nothing to upgrade to.

The exposure also deserves its agentic framing, because this is not a bug a human operator triggers by accident. The workflow tool exists to execute model-written scripts. The adversary in its threat model is any content the model was induced to treat as instructions — a retrieved page, a repository file, a task description. One line at the top of a generated workflow body converted “orchestrate subagents” into “read the harness user’s credential store and make outbound requests”, with the product’s own sandbox reporting that it was doing its job. That is the containment-not-capability shape again: the control existed, was configured, logged nothing unusual, and governed the wrong process.

Remediation

  • Run dsh-v0.1.6-alpha.1 or later. That is the first release containing the sandboxed-PTC workflow substrate, established here by commit ancestry rather than by any vendor statement. Note that every release this project has ever cut is a prerelease, so version-gating tools that skip prereleases will never offer you the fix either.
  • Audit lockfiles for @deepseek-ai/dsh-workflow-worker-thread and @deepseek-ai/dsh-code-runtime-worker-thread. Both are still installable, undeprecated, and resolve by default to pre-fix versions. Neither package exists in the current source tree. Their presence is a reliable marker of a pre-15-September install.
  • Do not treat the VM as the boundary — the vendor does not. The post-fix model puts confinement in the OS sandbox around the process. If you deploy the harness with that sandbox disabled, unavailable on your platform, or running as a user with broad file authority, you have re-created the original exposure with newer code.
  • If you depended on the pre-fix workflow tool in an agent exposed to untrusted content, treat the harness user’s secrets as reachable. The reporter’s stated impact includes the credentials store and SSH keys. We have no evidence of exploitation and imply none, but “no evidence” and “no telemetry” are different claims.

Verification note. Both CVE identifiers, publication timestamps (108789 at 03:17:17Z and 108788 at 02:16:57Z on 12 October 2026), vulnStatus: Received, CWE assignments, all CVSS 2.0/3.1/4.0 scores and vectors, the affected-version strings, the assigning source cna@vuldb.com and the “did not respond” sentence were read first-hand from the NVD 2.0 API. The vulnerability description, PoC design, impact statement, severity rating, disclosure rationale and the “no SECURITY.md” claim are the reporter’s published statements in Discussion #243 and are attributed to them as such; we did not run the PoC, install the harness, or execute any code against any system. The report’s 13 August date, its zero-comment state, the repository’s star, fork, creation and push metadata, the 404 on SECURITY.md, the full master file tree, the release and tag lists with their timestamps, the commit metadata and file statistics for 35af8698c24c, and the ancestry comparisons establishing that the fix is absent from dsh-v0.1.5-rc.2 and present in dsh-v0.1.6-alpha.1 were all read first-hand from the GitHub API. The architecture note and the workflow-ptc module header were read first-hand at master and quoted verbatim. All npm facts — version lists, publication and modification times, and dist-tags — were read first-hand from the public registry metadata. CVE-2026-108788’s underlying report is hosted on a Feishu document that did not render for us; we make no claim about its contents beyond the NVD record. The reading that the CWE and CVSS assignments understate the finding, and the argument that a silent fix is itself a supply-chain defect, are our editorial analysis.

Sources: