dsh-emudhra-guard
Verifieddsh-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 (needscrypto.X509Certificate, Node ≥ 15.6).der— SHA-256 of the certificate DER against the 16 published certificates. Works on any runtime withcrypto(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 ofERR_EMUDHRA_DISTRUSTED. The security criterion is 0 checker hosts trusted.
How it works
- Wraps
tls.checkServerIdentityandtls.connect(wrapping caller-suppliedcheckServerIdentitytoo), then callsmodule.syncBuiltinESMExports()so ESM named imports (import { connect } from 'node:tls') see the patched functions. Covershttps,http2,fetch/undici and rawtls. - Walks the presented chain (
issuerCertificate/issuerCert), bounded and cycle-safe, and rejects when any certificate matches the compromised set. - In the Electron main process, installs
session.setCertificateVerifyProcreturning-2(net::ERR_FAILED) for blocked chains,-3(Chromium's own verdict) otherwise. - 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: falsealready accept anything; they are unaffected. new tls.TLSSocket()used directly, withouttls.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-guardand remove theemudhra-guardinsert block from that profile'scordis.patch.yml. - Any app: remove the
--requirepreload / therequire(...)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。