Version
v24.15.0 (also v25 per anthropics/claude-code#53660 measurements)
Platform
Darwin (macOS 26.4.1, Apple Silicon Mac16,12). Reproduces on macOS Sonoma/Sequoia per the linked downstream reports.
Subsystem
tls, crypto
What steps will reproduce the bug?
On a Mac with any active network-flow filter (corporate NetworkExtension, content filter, or even a userland VPN-style daemon like ZeroTier), and a moderately-sized login keychain:
$ node -e 'const t0=process.hrtime.bigint();
const c = require("tls").getCACertificates("system");
console.log(`returned=${c.length} elapsed=${(Number(process.hrtime.bigint()-t0)/1e6).toFixed(0)}ms`)'
returned=34 elapsed=5287ms
The same call against "bundled" returns in ~0.1ms. HTTPS request times via --use-system-ca are ~100× slower than --use-bundled-ca (5093ms vs 103ms to api.github.com).
The bug is in node::crypto::ReadMacOSKeychainCertificates (src/crypto/crypto_x509.cc / wherever the macOS branch lives). It calls SecTrustEvaluateWithError with a revocation-enabled policy when collecting trust anchors, which causes trustd to attempt AIA/OCSP/CRL fetches for every cert. When a NetworkExtension content filter is present, each flow goes through per-flow cryptographic signing (~1-2ms × hundreds of certs = 5-10 seconds total) before being denied. Without a flow filter the cost is smaller but still present.
How often does it reproduce?
Every invocation. Stable across reboots and Node versions in the v24.x / v25 line.
What is the expected behavior? Why is that the expected behavior?
tls.getCACertificates("system") should be fast (single-digit milliseconds for a typical keychain) because it's collecting trust anchors, not validating a server chain. Revocation checks are pointless during anchor collection — a trust anchor's revocation status is determined by user/admin trust settings, not by an OCSP responder.
What do you see instead?
5-10+ seconds of latency, predominantly in synchronous XPC round-trips to trustd. Effectively blocks Node CLI startup any time the CLI's CA loader touches the system store. Several Node-based CLIs are affected in practice:
Additional information
Proposed fix
Per the anthropics/claude-code#53660 analysis, the call should use SecPolicyCreateBasicX509() (no revocation) when enumerating system CAs to use as trust anchors. Alternatively, the function could skip trust evaluation entirely and call SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainAdmin/System) to collect anchors without validation. Either approach removes revocation from the hot startup path.
Reproducer kit
# Standalone measurement
node -e 'const t0=process.hrtime.bigint();
require("tls").getCACertificates("system");
console.log(`${(Number(process.hrtime.bigint()-t0)/1e6).toFixed(0)}ms`)'
# HTTPS A/B
node --use-bundled-ca -e 'require("https").get("https://api.github.com/zen",r=>r.on("data",()=>{}).on("end",()=>{}))' # ~100ms
node --use-system-ca -e 'require("https").get("https://api.github.com/zen",r=>r.on("data",()=>{}).on("end",()=>{}))' # ~5s
# Sample to confirm the syscall pattern
node --use-system-ca -e 'require("tls").getCACertificates("system")' &
PID=$!; sleep 0.1; sample $PID 3 -mayDie
Sample output during the wait deterministically shows:
node::crypto::ReadMacOSKeychainCertificates
→ node::crypto::IsCertificateTrustedForPolicy
→ SecTrustEvaluateWithError
→ securityd_send_sync_and_do (XPC to trustd)
Why this matters beyond corporate networks
Anthropic's analysis (and the original v24 design assumption) treats this as a "corporate Macs only" problem. In practice, any userland flow-handling daemon — ZeroTier, Tailscale, Cloudflare WARP, certain VPN clients — places similar code in the path. The fix doesn't depend on detecting the filter; it's simply correct to skip revocation when enumerating trust anchors.
Related Node-side tracking
Version
v24.15.0 (also v25 per anthropics/claude-code#53660 measurements)
Platform
Darwin (macOS 26.4.1, Apple Silicon Mac16,12). Reproduces on macOS Sonoma/Sequoia per the linked downstream reports.
Subsystem
tls, crypto
What steps will reproduce the bug?
On a Mac with any active network-flow filter (corporate NetworkExtension, content filter, or even a userland VPN-style daemon like ZeroTier), and a moderately-sized login keychain:
The same call against
"bundled"returns in ~0.1ms. HTTPS request times via--use-system-caare ~100× slower than--use-bundled-ca(5093ms vs 103ms toapi.github.com).The bug is in
node::crypto::ReadMacOSKeychainCertificates(src/crypto/crypto_x509.cc/ wherever the macOS branch lives). It callsSecTrustEvaluateWithErrorwith a revocation-enabled policy when collecting trust anchors, which causestrustdto attempt AIA/OCSP/CRL fetches for every cert. When a NetworkExtension content filter is present, each flow goes through per-flow cryptographic signing (~1-2ms × hundreds of certs = 5-10 seconds total) before being denied. Without a flow filter the cost is smaller but still present.How often does it reproduce?
Every invocation. Stable across reboots and Node versions in the v24.x / v25 line.
What is the expected behavior? Why is that the expected behavior?
tls.getCACertificates("system")should be fast (single-digit milliseconds for a typical keychain) because it's collecting trust anchors, not validating a server chain. Revocation checks are pointless during anchor collection — a trust anchor's revocation status is determined by user/admin trust settings, not by an OCSP responder.What do you see instead?
5-10+ seconds of latency, predominantly in synchronous XPC round-trips to
trustd. Effectively blocks Node CLI startup any time the CLI's CA loader touches the system store. Several Node-based CLIs are affected in practice:tls.getCACertificates("system")blocks startup ~9-10s on corporate-managed Macs (regression in 2.1.119) anthropics/claude-code#53660 — comprehensive root-cause analysis. Their measurement on a corporate Mac: 9.7s for 18 certs (530ms/cert), vs 0.3ms/cert for direct C calls usingSecPolicyCreateBasicX509. They shipped aCLAUDE_CODE_CERT_STORE=bundledenv var workaround.tls.getCACertificates("system")call adds 5+ seconds to every CLI invocation github/copilot-cli#3330 (filing concurrently) — same call costs 5+ seconds on everycopilotinvocation. The CLI's code explicitly asks for"system"certs in addition to"bundled".Additional information
Proposed fix
Per the anthropics/claude-code#53660 analysis, the call should use
SecPolicyCreateBasicX509()(no revocation) when enumerating system CAs to use as trust anchors. Alternatively, the function could skip trust evaluation entirely and callSecTrustSettingsCopyCertificates(kSecTrustSettingsDomainAdmin/System)to collect anchors without validation. Either approach removes revocation from the hot startup path.Reproducer kit
Sample output during the wait deterministically shows:
Why this matters beyond corporate networks
Anthropic's analysis (and the original v24 design assumption) treats this as a "corporate Macs only" problem. In practice, any userland flow-handling daemon — ZeroTier, Tailscale, Cloudflare WARP, certain VPN clients — places similar code in the path. The fix doesn't depend on detecting the filter; it's simply correct to skip revocation when enumerating trust anchors.
Related Node-side tracking
--use-system-casupport for intermediate certificates on Windows #57163 —--use-system-caintermediate-cert issues on Windows.--use-system-ca.