8000 SECURITY: node, node-lts: backport CVE-2026-111943 eMudhra fixes (nodejs/node#66871, #66877) by bryan-minimal · Pull Request #946 · gominimal/pkgs · GitHub
[go: up one dir, main page]

Skip to content

SECURITY: node, node-lts: backport CVE-2026-111943 eMudhra fixes (nodejs/node#66871, #66877) - #946

Merged
bryan-minimal merged 1 commit into
mainfrom
security/node-emudhra-cve-2026-111943
Oct 21, 2026
Merged

bryan-minimal merged 1 commit into
mainfrom
security/node-emudhra-cve-2026-111943

Conversation

@bryan-minimal
@bryan-minimal bryan-minimal commented Oct 20, 2026 •
Copy link
Copy Markdown
Member

What

CVE-2026-111943: eight eMudhra/emSign intermediate CA keys (EC P-384) were generated with tiny private scalars and are compromised (Mozilla bug 2083449; Go's fix is golang/go#82345). Each key appears in two certificates: one issued by emSign Root TLS/CS CA - G3, one cross-signed by Sectigo "AAA Certificate Services".

Upstream node merged two fixes to main on 2026-10-20. Neither is in a release yet, so this PR carries both as patches on node 26.10.0 and node-lts 24.19.0:

upstream what node 26.10.0 node-lts 24.19.0
nodejs/node#66871 (d9a3f3d1) SPKI-SHA256 blocklist of the 8 keys, checked by a cert-verify callback installed on every SSL_CTX (SecureContext, QUIC, DTLS). A hit reports CERT_REVOKED. applies, minus the crypto_tls.cc hunks (see below) hand-backported: no src/dtls on v24; the include list, crypto_context.h and QUIC tlscontext.cc context differ, so the same lines went in by hand at the matching spots
nodejs/node#66877 (ae223417), src/node_root_certs.h half removes emSign ECC Root CA - G3, emSign Root CA - C1 and emSign ECC Root CA - C3 from node's bundled root store (NSS 3.131.1) applies cleanly applies cleanly

Correction to the brief this PR was written from: #66877 removes three emSign roots, not only ECC G3. All three are gone because NSS 3.131.1 dropped them. ECC G3 is the Mozilla-store anchor for the compromised intermediates.

The 8 hashes in the patch match Go's distrustedSPKIHashes list exactly (checked programmatically).

Deviations from upstream (also in each patch header)

  • #66871 also moves an OpenSSL >= 4.1 CN name-constraint shim (VerifyCertChain) from crypto_tls.cc into the new callback. That shim doesn't exist in 26.10.0 or 24.19.0, and our openssl is 3.6.5, so the crypto_tls.cc hunks are dropped and the #if OPENSSL_VERSION_PREREQ(4, 1) block is left out of CertVerifyCallback. Nothing else changes.
  • #66877: the tools/certdata.txt hunks are dropped. That file feeds node's root-cert generator, not the build.
  • The upstream regression test test/parallel/test-tls-spki-blocklist.js is included verbatim. It is only documentation, because our build doesn't run node's test suite.

Why both patches are needed

tls trusts node's bundled store by default (we configure with --shared-openssl but not --use-openssl-ca), so the separate ca-certificates fix doesn't reach it. Removing the roots doesn't close the hole on its own either. A chain through a compromised key still verifies against any store that has a path to it: --use-system-ca / --use-openssl-ca, a caller-supplied ca, or the Sectigo AAA cross-sign (Apple, Microsoft and OpenJDK still trust it). The blocklist covers all of those.

How

  • The patch files are named cve-2026-111943-*.patch and listed as Local build deps. build.sh applies them by name (patch -Np1, never a glob), so a patch that goes missing fails the build. Fail-closed grep checks follow: the kBlockedSPKIs table and its install in SecureContext::Init must be present, and emSign ECC Root CA - G3 must be absent from node_root_certs.h. This is the same convention as the existing LIEF patch.
  • node-lts gains patch as a build dep, which node already had.
  • No -rN revision marker. Nothing in pkgs uses the new stdlib revision/patches[] ledger (minimal#1277) yet, and pkgs CI runs an older minimal. upstream_version is unchanged.

Tests (offline standalone, added to both packages)

  1. emsign_roots_removed: computes the SHA-256 of every tls.rootCertificates entry and requires that none of the 3 removed roots appears, with a guard of at least 100 roots so an empty store can't pass.
  2. spki_blocklist_rejects_emudhra_intermediate: a local TLS server presents a throwaway self-signed localhost leaf plus one extra cert. The leaf is untrusted, so the patched callback scans every peer-sent cert.
    • With the real compromised EM OV TLS CA - G3C intermediate attached (public cert only; no compromised private key is used or published), authorizationError must be CERT_REVOKED.
    • With an unrelated cert attached (the control), it must not be.

Negative control: run against stock node 26.9.0, both tests fail. The first reports still bundled: emSign ECC Root CA - G3, emSign Root CA - C1, emSign ECC Root CA - C3; the second reports control=DEPTH_ZERO_SELF_SIGNED_CERT blocked=DEPTH_ZERO_SELF_SIGNED_CERT.

Patched builds (remote min session, pkgbuild loadout, arm64):

  • min package build --rebuild --verbose node-lts: real ~30 min compile, both patches applied (patching file … in the log), build finished, exit 0.
  • min package build --rebuild --verbose node: same result, with the LIEF patch plus both CVE patches, DTLS hunk included.
  • min check --packages node-lts and min check --packages node: every check passes, including standalone tests…Pass with the build-dependent checks live, not Skip.
  • To prove the tests really execute rather than pass vacuously, I inverted both node-lts tests inside the box and re-ran min check. standalone tests…Fail came back listing both test names.

pkgs CI's minimal-check still Skips standalone tests for these packages, so the green check above is not what verifies them.

Blast radius

MINIMAL_SCIENCE_MODE=1 mip dump --packages --arch amd64, diffed against main: 28 spec hashes change. That is node and node-lts plus 26 dependents, all of them via node-lts (nothing in packages/ imports node directly):

agent-browser, bash-language-server, capy, cf, dev-language-servers, emacs-config-dev1, emscripten, eslint, json-server, lerna, mermaid-cli, next, odin, ols, pi, pnpm, prettier, pyright, raygui, raylib, serve, typescript, typescript-language-server, vlt, wrangler, yarn

Unverified

  • amd64 was not built in a session. Only arm64 was, so amd64 depends on minimal build.
  • The node tests are byte-identical to node-lts'. The inversion run that proves they execute was done on node-lts only.
  • Drop both patches on the first node / node-lts release whose changelog lists tls: reject chains containing blocked public keys nodejs/node#66871 and #66877.

🤖 Generated with Claude Code

Eight eMudhra/emSign intermediate CA keys are compromised (Mozilla bug
2083449). Upstream node merged two fixes to main on 2026-10-20; neither
is in a release yet. Carry both as patches on node 26.10.0 and
node-lts 24.19.0:

- nodejs/node#66871: SPKI-SHA256 blocklist of the 8 compromised keys,
  checked in a cert-verify callback on every SSL_CTX; a hit reports
  CERT_REVOKED. Applies to node 26.10.0 minus the crypto_tls.cc hunks
  (an OpenSSL 4.1 shim our tree lacks); hand-backported to 24.19.0
  (no DTLS, different include/QUIC context).
- nodejs/node#66877 (root-store half): drop emSign ECC Root CA - G3,
  emSign Root CA - C1, emSign ECC Root CA - C3 from node_root_certs.h
  (NSS 3.131.1). Applies cleanly to both.

Both are applied by name with fail-closed grep checks. Adds two offline
standalone tests per package; both fail against stock node 26.9.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bryan-minimal
bryan-minimal requested a review from a team as a code owner October 20, 2026 22:39
@coderabbitai
coderabbitai Bot commented Oct 20, 2026 •
Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: gominimal/pkgs/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: db023c97-fd07-4c22-bb48-c16d610a0123
📥 Commits

Reviewing files that changed from the base of the PR and between 24009b6 and abe2bf8.

📒 Files selected for processing: 8
  • packages/node-lts/build.ncl
  • packages/node-lts/build.sh
  • packages/node-lts/cve-2026-111943-drop-emsign-roots.patch
  • packages/node-lts/cve-2026-111943-tls-spki-blocklist.patch
  • packages/node/build.ncl
  • packages/node/build.sh
  • packages/node/cve-2026-111943-drop-emsign-roots.patch
  • packages/node/cve-2026-111943-tls-spki-blocklist.patch

Included review availability: This review used your included allowance. 0 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 4 reviews per hour.

📝 Walkthrough

The Node and Node LTS builds apply patches that block eight compromised certificate public keys during verification and remove three emSign roots from the bundled trust store. The changes also add build checks and TLS tests for blocked certificates, unrelated certificates, and the updated root store.

Changes: Node TLS certificate protections

Layer / File(s) Summary
SPKI blocklist verification
packages/node/cve-2026-111943-tls-spki-blocklist.patch, packages/node-lts/cve-2026-111943-tls-spki-blocklist.patch
The patches define eight blocked SPKI hashes and add certificate encoding, hashing, stack scanning, and verification logic. A blocked key produces CERT_REVOKED; encoding or hashing failures produce an unspecified verification error.
SSL context wiring and regression tests
packages/node/cve-2026-111943-tls-spki-blocklist.patch, packages/node-lts/cve-2026-111943-tls-spki-blocklist.patch
The patches install the verification callback on the covered SSL contexts and add test hooks. Regression tests cover server and client certificates, peer-sent certificates, verification errors, and test-hook validation.
Bundled trust store updates
packages/node/cve-2026-111943-drop-emsign-roots.patch, packages/node-lts/cve-2026-111943-drop-emsign-roots.patch, packages/node/build.ncl, packages/node-lts/build.ncl
The patches remove three emSign roots. Standalone tests check that their fingerprints are absent and that at least 100 roots remain.
Build application and TLS probes
packages/node/build.ncl, packages/node/build.sh, packages/node-lts/build.ncl, packages/node-lts/build.sh
The build definitions include both patches and the patch tool. Build scripts apply the patches and check for expected changes. Standalone TLS tests check that a compromised intermediate returns CERT_REVOKED and an unrelated certificate does not.

Priority: ⬆️ High

Estimated code review effort: 4 (Complex) | ~60 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant TLSPeer
  participant SSLContext
  participant SPKIVerificationCallback
  participant CertificateStack
  TLSPeer->>SSLContext: present certificate chain
  SSLContext->>SPKIVerificationCallback: verify certificates
  SPKIVerificationCallback->>CertificateStack: encode and hash certificate SPKIs
  CertificateStack-->>SPKIVerificationCallback: SPKI hashes
  SPKIVerificationCallback-->>SSLContext: verification result or CERT_REVOKED
Loading

Suggested reviewers: mitodrummer

Merge Risk: ⚪ Minimal · up to abe2b

The Node and Node LTS packages now reject certificates that chain to eight compromised eMudhra/emSign intermediate keys and drop three emSign roots from the bundled trust store. Review found no outstanding defects: the hand-backport's hashing call is valid for v24, the blocked key hashes match upstream, and the build provides the patch tool. CI should still confirm that both packages compile and that the new TLS tests pass.

Pre-merge checks: Passed 5
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly identifies the security change, affected packages, CVE, and eMudhra fixes. It accurately summarizes the main purpose of the pull request.
Docstring Coverage Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2 files. (6 skipped: 6 unsupported.)

✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@bryan-minimal
bryan-minimal added this pull request to the merge queue Oct 21, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to no response for status checks Oct 21, 2026
@bryan-minimal
bryan-minimal added this pull request to the merge queue Oct 21, 2026
Merged via the queue into main with commit 180b651 Oct 21, 2026
10 checks passed
@bryan-minimal
bryan-minimal deleted the security/node-emudhra-cve-2026-111943 branch October 21, 2026 04:24
bryan-minimal added a commit that referenced this pull request Oct 21, 2026
Brings in #946 (CVE-2026-111943 eMudhra backports for node and node-lts).
Both sides kept: the two carried patches, their fail-closed greps and the
standalone tests from #946, then this branch's llhttp wasm rebuild. The
patches apply before the wasm census, which they do not touch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0