8000 SECURITY: deno, claude-code: default to the system trust store (CVE-2026-111943) by bryan-minimal · Pull Request #956 · gominimal/pkgs · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/claude-code/build.ncl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ let git = import "../git/build.ncl" in
let glibc = import "../glibc/build.ncl" in
let openssh = import "../openssh/build.ncl" in
let ripgrep = import "../ripgrep/build.ncl" in
let ca-certificates = import "../ca-certificates/build.ncl" in

let { version, amd64_sha256, arm64_sha256, gcs_bucket } = {
gcs_bucket = "https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases",
Expand Down Expand Up @@ -48,6 +49,9 @@ in
glibc,
ripgrep,
openssh,
# The wrapper defaults CLAUDE_CODE_CERT_STORE=system (CVE-2026-111943), so
# the OS store must be present or Claude Code cannot reach its API.
ca-certificates,
],

cmd = "./build.sh",
Expand Down
9 changes: 9 additions & 0 deletions packages/claude-code/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ if [ -z "\${MINIMAL_CLAUDE_NO_PLUGIN:-}" ] && [ -d "\$min_plugin" ]; then
set -- "--plugin-dir=\$min_plugin" "\$@"
fi

# CVE-2026-111943: trust only the OS store (/etc/ssl/certs, from
# ca-certificates) by default. Claude Code's default is "bundled,system", and
# the bundled set (its Bun runtime's compiled-in Mozilla roots) still carries
# emSign ECC Root CA - G3, the anchor for the compromised eMudhra
# intermediates. A caller's own CLAUDE_CODE_CERT_STORE still wins, and
# NODE_EXTRA_CA_CERTS is still appended.
: "\${CLAUDE_CODE_CERT_STORE:=system}"
export CLAUDE_CODE_CERT_STORE

DISABLE_AUTOUPDATER=1 USE_BUILTIN_RIPGREP=0 exec /usr/share/claude/versions/$MINIMAL_ARG_VERSION "\$@"
EOF

Expand Down
22 changes: 22 additions & 0 deletions packages/deno/build.ncl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ let bash = import "../bash/build.ncl" in
let glibc = import "../glibc/build.ncl" in
let gcc = import "../gcc/build.ncl" in
let python = import "../python/build.ncl" in
let ca-certificates = import "../ca-certificates/build.ncl" in

let version = "2.9.7" in
{
Expand All @@ -30,6 +31,9 @@ let version = "2.9.7" in
glibc,
subsetOf gcc ["libgcc"],
bash, # the /usr/bin wrapper is a #!/bin/sh script (bash provides bin/sh)
# The wrapper defaults DENO_TLS_CA_STORE=system (CVE-2026-111943), so the
# OS store must be present or every TLS connection fails.
ca-certificates,
],

cmd = "./build.sh",
Expand Down Expand Up @@ -67,6 +71,24 @@ let version = "2.9.7" in
["/bin/bash", "-c", "deno eval 'console.log(1+1)' | grep -q '^2$'"],
],
} | Test,
# CVE-2026-111943: the wrapper defaults deno to the OS trust store, and a
# caller's own choice still wins.
tls_ca_store_default =
{
class = 'Standalone,
test_deps = [base],
cmds = [
[
"/bin/bash",
"-c",
m%"
set -eu
test "$(env -u DENO_TLS_CA_STORE deno eval 'console.log(Deno.env.get("DENO_TLS_CA_STORE"))')" = system
test "$(DENO_TLS_CA_STORE=mozilla deno eval 'console.log(Deno.env.get("DENO_TLS_CA_STORE"))')" = mozilla
"%
],
],
} | Test,
# `deno install -g` lands a script in ~/.local/bin, where the session PATH
# resolves it (gominimal/inbox#584).
global_install =
Expand Down
11 changes: 11 additions & 0 deletions packages/deno/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ python3 -m zipfile -e "deno-${DENOARCH}-unknown-linux-gnu.zip" .
# defaults DENO_INSTALL_ROOT so `deno install -g` bins land in ~/.local/bin
# (on the session PATH) rather than the off-PATH ~/.deno/bin default; a
# caller's own DENO_INSTALL_ROOT still wins. See gominimal/inbox#584.
#
# It also defaults DENO_TLS_CA_STORE=system (CVE-2026-111943). deno's default
# store is "mozilla": webpki-roots compiled into the binary (0.26.1 in 2.9.7),
# which still trusts emSign ECC Root CA - G3, the anchor for the compromised
# eMudhra intermediates, and its rustls-webpki (0.103.13) has no SPKI
# blocklist. "system" reads ONLY the OS store (/etc/ssl/certs, from
# ca-certificates), which drops those roots, and leaves the bundled set out.
# A caller's own DENO_TLS_CA_STORE still wins. Revisit on the first deno
# release with denoland/deno#37046 (rustls-webpki 0.103.16, webpki-roots 1.0.10).
mkdir -p $OUTPUT_DIR/usr/bin $OUTPUT_DIR/usr/libexec/deno
install -m 755 deno $OUTPUT_DIR/usr/libexec/deno/deno
cat > "$OUTPUT_DIR/usr/bin/deno" <<'WRAPPER'
Expand All @@ -21,6 +30,8 @@ if [ -n "${HOME:-}" ]; then
: "${DENO_INSTALL_ROOT:=$HOME/.local}"
export DENO_INSTALL_ROOT
fi
: "${DENO_TLS_CA_STORE:=system}"
export DENO_TLS_CA_STORE
exec /usr/libexec/deno/deno "$@"
WRAPPER
chmod +x "$OUTPUT_DIR/usr/bin/deno"
Loading
0