Skip to content

dsh-emudhra-guard

Verified

dsh-emudhra-guard · v0.2.1 · AGPL-3.0-or-later

Distrust the compromised eMudhra/emSign intermediate CAs (CVE-2026-111943) in DeepSeek Harness — and in any Node.js or Electron runtime, including old ones — until the runtime ships an updated CA bundle.

Install

dsh plugin add dsh-emudhra-guard

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Published to npm without a public repository. Inspect the package contents before installing.

Tags

Creators

Readme

dsh-emudhra-guard

Distrust the compromised eMudhra / emSign intermediate CAs in DeepSeek Harness and in any Node.js or Electron runtime — including old Node versions — until the runtime ships an updated CA bundle.

  • Incident: eight eMudhra intermediate CA private keys were generated incorrectly and are recoverable from the public certificates (disclosed 2026-10-20, CVE-2026-111943)
  • Advisory: https://emudhra.we-have-a-problem.com/
  • Affected clients: anything still trusting those intermediates can be impersonated by anyone holding the key material

Contributors

Contributor Role
myst_ly1 maintainer, publisher (npm publish)
DeepSeek V4.1 Flash AI contributor — implementation, verification and documentation
DeepSeek Harness agent runtime this work was designed, built and tested in

Built with DeepSeek V4.1 Flash running inside DeepSeek Harness; the 12-host trust measurements, the old-runtime fallback tests and the packaging were executed on this machine.

Why this exists

DeepSeek Harness 0.2.1-rc.2 bundles Electron 44.7.0 / Node 24.21.0. Its built-in CA store still contains emSign ECC Root CA - G3 (86A1ECBA…BE6B), the root that the compromised TLS intermediates chain to. Measured from the shipped runtime before this guard:

Before guard After guard
Advisory checker hosts trusted (of 12) 6 0
Harmless control sites OK OK

The proper fix is a DSH build on Electron ≥ 44.9.1 (Node ≥ 24.22.1). Until that build exists, this guard is a stop-gap that works without upgrading anything.

Compatibility

Runtime Status Notes
Node.js 24.x (incl. 24.21.0 shipped by DSH) ✅ verified spki mode
Node.js 20 / 22 LTS ✅ expected spki mode
Node.js 15.6 – 18.x ✅ expected spki mode
Node.js 10 – 15.5 (no crypto.X509Certificate) ✅ fallback automatic der mode
Node.js 14+ ✅ supported (engines) guard.cjs avoids modern syntax
Electron main process ✅ also installs setCertificateVerifyProc for windows/net
Electron renderer / browser context ❌ verifier lives in the main process

Two matching strategies, chosen automatically:

  • spki — SHA-256 of the SubjectPublicKeyInfo. Catches every cross-sign and re-issue of the eight leaked CAs (needs crypto.X509Certificate, Node ≥ 15.6).
  • der — SHA-256 of the certificate DER against the 16 published certificates. Works on any runtime with crypto (Node ≥ 10), including very old ones.
  • Override with DSH_EMUDHRA_MODE=auto|spki|der.

Install

A. As a DSH plugin

dsh plugin --profile <profile> add dsh-emudhra-guard

Restart DeepSeek Harness completely afterwards. The patch applies at startup; sockets opened before that are not re-verified.

Offline / local install (no registry needed):

powershell -ExecutionPolicy Bypass -File .\install-profile.ps1 -Profile desktop

B. In any Node.js or Electron app (no DSH required)

# Node.js: preload for this process only
NODE_OPTIONS="--require /path/to/dsh-emudhra-guard/preload.cjs" node app.js

# or from code
node -e "require('dsh-emudhra-guard/guard').apply()"
// Electron main process — first line of your entry point
require('dsh-emudhra-guard/preload')

Verify

Print the guard status from any runtime:

node -e "const g=require('dsh-emudhra-guard/guard');g.apply();console.log(g.isActive(),g.mode(),g.blockedCount())"
# expected: true spki 8

End-to-end against the advisory's checker hosts — every host must fail, while normal sites work:

node -e "require('dsh-emudhra-guard/guard').apply();
const https=require('https');
const hs=['dv-g3b-emudhra.we-have-a-problem.com','ov-g3c-emudhra.we-have-a-problem.com','ev-g3b-emudhra.we-have-a-problem.com'];
let n=0;for(const h of hs){const r=https.get({host:h,timeout:8000},x=>{console.log('TRUSTED',h);x.resume();if(++n===hs.length)done();
});r.on('error',e=>{console.log('BLOCKED',h,e.code);if(++n===hs.length)done();});}
function done(){https.get('https://example.com',x=>{console.log('control',x.statusCode);x.resume();}).on('error',e=>console.log('control FAIL',e.code));}"

Expected: BLOCKED … ERR_EMUDHRA_DISTRUSTED ×3, then control 200.

On an already patched runtime (Node ≥ 24.22.1 / 22.23.4 / 26.12.1, Electron ≥ 44.9.1) the chain is rejected by the runtime itself before the guard's hook runs, so you may see the runtime's own certificate error (e.g. UNABLE_TO_GET_ISSUER_CERT_LOCALLY) instead of ERR_EMUDHRA_DISTRUSTED. The security criterion is 0 checker hosts trusted.

How it works

  1. Wraps tls.checkServerIdentity and tls.connect (wrapping caller-supplied checkServerIdentity too), then calls module.syncBuiltinESMExports() so ESM named imports (import { connect } from 'node:tls') see the patched functions. Covers https, http2, fetch/undici and raw tls.
  2. Walks the presented chain (issuerCertificate / issuerCert), bounded and cycle-safe, and rejects when any certificate matches the compromised set.
  3. In the Electron main process, installs session.setCertificateVerifyProc returning -2 (net::ERR_FAILED) for blocked chains, -3 (Chromium's own verdict) otherwise.
  4. On modern runtimes it also refuses chains where the leaf does not verify under the issuer Node reports, so a decoy certificate cannot hide the real chain.

Requests fail with code ERR_EMUDHRA_DISTRUSTED.

Limits (read before relying on it)

  • Only connections opened after the guard loads are checked; pooled keep-alive sockets that already exist are not re-verified. Restart the app after installing.
  • Callers using rejectUnauthorized: false already accept anything; they are unaffected.
  • new tls.TLSSocket() used directly, without tls.connect, is not hooked.
  • This is a mitigation, not a substitute for updating the runtime CA bundle.

Uninstall

  • DSH: dsh plugin --profile <profile> remove dsh-emudhra-guard, or delete ~/.dsh/profiles/<profile>/node_modules/dsh-emudhra-guard and remove the emudhra-guard insert block from that profile's cordis.patch.yml.
  • Any app: remove the --require preload / the require(...) line.

License

GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later) — a strong copyleft license: if you run a modified version as a network service, you must offer the corresponding source to its users. See LICENSE and NOTICE for third-party attribution (the distrust technique follows the MIT-licensed kill-emudhra package; the MIT notice is retained in NOTICE).

Source availability: this package ships its complete source (index.js, guard.cjs, preload.cjs) inside the published npm tarball, so the AGPL's corresponding-source obligation is satisfied by the package itself. There is deliberately no external repository link — the distributed tarball is the canonical source.


中文说明

贡献者

贡献者 角色
myst_ly1 维护者、发布者
DeepSeek V4.1 Flash AI 贡献者:实现、验证与文档
DeepSeek Harness 本工作所用的智能体运行时环境

它做什么

在不升级任何组件的前提下,让 DSH(以及任意 Node.js / Electron 应用)不再信任被攻陷的 eMudhra / emSign 中间 CA(CVE-2026-111943)。覆盖 https、http2、fetch/undici、裸 tls; 在 Electron 主进程内还覆盖 Chromium 的证书校验路径。

为什么需要

DSH 0.2.1-rc.2 内置 Electron 44.7.0 / Node 24.21.0,CA 库仍含 emSign ECC Root CA - G3。 实测官方 12 台验证主机中 6 台可握手成功(即可被冒充)。装上本插件后为 0 台, 普通站点不受影响。

老版本 Node 也能用

  • 现代运行时用 spki 模式(按公钥哈希匹配,能拦住所有交叉签名与重签发)
  • 老运行时(Node < 15.6,无 crypto.X509Certificate)自动降级为 der 模式 (按 16 张已公布证书的 DER 哈希匹配)
  • 可用 DSH_EMUDHRA_MODE=auto|spki|der 强制指定
  • 独立使用:NODE_OPTIONS="--require .../preload.cjs" 或 require('.../guard').apply()

安装(DSH)

dsh plugin --profile desktop add dsh-emudhra-guard

装完需完全退出并重启 DSH(对已建立的连接不追溯校验)。

验证

node -e "const g=require('dsh-emudhra-guard/guard');g.apply();console.log(g.isActive(),g.mode(),g.blockedCount())"
# 期望:true spki 8

许可证

AGPL-3.0-or-later(强制开源):修改后若以网络服务形式提供,必须向使用者提供对应源码。 第三方归属见 NOTICE。