Repository navigation
SECURITY: bun, plannotator: drop emSign roots from bun's compiled-in store (CVE-2026-111943) - #951
Merged
Merged
Conversation
… store (CVE-2026-111943) bun 1.3.14 compiles 145 Mozilla roots into packages/bun-usockets/src/crypto/ root_certs.h and always loads them; --use-system-ca / NODE_USE_SYSTEM_CA only add the system store on top. So the ca-certificates fix never reaches bun, and emSign ECC Root CA - G3 (the anchor for the compromised eMudhra intermediates, Mozilla bug 2083449) stays trusted. Carry a patch removing exactly the three roots NSS 3.131.1 / curl cacert 2026-10-20 dropped: emSign ECC Root CA - G3, emSign Root CA - C1 and emSign ECC Root CA - C3 (145 -> 142, nothing else changes). Upstream oven-sh/bun#46159 does the same on main (unreleased) and also drops emSign Root CA - G1, which NSS still ships. Fail-closed: the patch is applied by name, the labels must be gone from root_certs.h, and a SHA-256 check of tls.rootCertificates must flag the stock bootstrap bun (control) and pass on the built binary. A standalone test repeats the fingerprint check on the installed bun. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(CVE-2026-111943) On amd64 plannotator compiled for bun-linux-x64-baseline. That is not our bun's default target, so `bun build --compile` downloaded @oven/bun-linux-x64-baseline from npm at build time (unpinned) and embedded upstream's runtime, which still bundles emSign ECC Root CA - G3. The packages/bun root-store fix would never have reached the amd64 binary. Compile for bun-linux-x64, which equals our bun's default target, so bun embeds itself (as on arm64 already). Baseline buys nothing in minimal: glibc and bun are built with -march=x86-64-v3. The build now runs the shipped binary as bun (BUN_BE_BUN=1) and fails if its compiled-in store still has the removed roots. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
Warning Review limit reached
This review includes 4 billable files and costs up to $1.00.
Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing. Or wait 3 minutes for your next included review. View limit details
Comment |
jtnkminimal
approved these changes
Oct 20, 2026
This was referenced Oct 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
CVE-2026-111943. The private keys of eight eMudhra intermediate CAs are recoverable from their public certificates (Mozilla bug 2083449). In Mozilla-derived stores, those intermediates chain to emSign ECC Root CA - G3, through its cross-sign of emSign Root TLS CA - G3.
bun 1.3.14 compiles 145 Mozilla roots into
packages/bun-usockets/src/crypto/root_certs.h, including emSign ECC Root CA - G3, and always loads them:us_get_default_ca_store()inroot_certs.cppadds everyroot_certs[]entry unconditionally.--use-system-ca/NODE_USE_SYSTEM_CA=1only add the system store on top of that.So neither the ca-certificates fix nor an env switch removes the root. It has to come out of the binary.
This PR carries
cve-2026-111943-remove-emsign-roots.patch, which removes exactly the three roots NSS 3.131.1 dropped. They are also exactly the curlcacert2026-09-25 → 2026-10-20 diff:86a1ecba…a36bbe6b125609aa…c8c8608fbc4d809b…dc0d7eb3Root count goes from 145 to 142. Nothing is added, and the other 142 entries are byte-identical: I parsed the old and new header and compared every DER by SHA-256.
Upstream: oven-sh/bun#46159 (
ea6a2c7b, merged 2026-10-20) is not in any release yet; the latest is v1.4.3.root_certs.deron main, so it doesn't apply to 1.3.14'sroot_certs.h.plannotator: the fix wouldn't have reached its amd64 binary
bun build --compileembeds the running bun only when--targetequals bun's own default target (CompileTarget.exePath→selfExePath). For any other target, it downloads@oven/bun-<target>from the npm registry at build time.plannotator's amd64 build used
--target=bun-linux-x64-baseline, and our bun is a non-baseline build. So its amd64 binary has embedded upstream's stock 1.3.14 baseline runtime: fetched unpinned, and still bundling emSign G3. This PR switches it tobun-linux-x64, which equals our default target, so bun embeds itself the same way it already does on arm64.Baseline buys nothing in minimal: glibc and bun are both built with
-march=x86-64-v3.The other consumers of
../bun/build.nclalready embed our bun:hunk:--compilewith no--target.opencode:script/build.ts --singlepicksbun-linux-<arch>(non-baseline, no abi), which is the default. ItsexecArgvalso passes--use-system-ca, which adds the system store on top of the bundled roots.Fail-closed checks
bun/build.sh:patch 8000 code> is a new build dep./* <label> */blocks may remain inroot_certs.h, and at least 100 roots must remain.tls.rootCertificates(the compiled-in store) with SHA-256. It requires at least 100 roots and none of the three.build/release/bun.emsign_roots_removedruns the same fingerprint check on the installed bun.plannotator/build.shruns the shipped./plannotatoras plain bun (BUN_BE_BUN=1) through the same check. It fails if the embedded runtime still has the roots, for example if a target change makes bun fetch a stock runtime again.Verified, and how
patch -Np1. The result is byte-identical to the intended header, removes 51 lines and adds none.roots=121 still bundled: emSign ECC Root CA - G3, emSign Root CA - C1, emSign ECC Root CA - C3, exit 1;roots=145 still bundled: …, exit 1, matching the control pattern exactly;roots=142 ok, exit 0.MINIMAL_SCIENCE_MODE=1 mip dump --packages --arch amd64, diffed against main, changes 4 spec hashes:bun,hunk,opencode,plannotator.nickel formatleavesbun/build.nclunchanged, andsh -npasses on both scripts.Unverified
build_cost_multiple = 2, and arm64 has a history of slow cold builds.BUN_BE_BUN=1running a script inside a compiled 1.3.14 executable is read from source (src/cli/cli.zig), not executed.emsign_roots_removedpasses.Not covered here
NODE_EXTRA_CA_CERTSor a customca. AAA isn't in bun's bundle. With--use-system-ca, the system store is clean once the ca-certificates fix lands.claude-code,varlock) don't consumepackages/bunand are unaffected by this PR.🤖 Generated with Claude Code