github / github/copilot-cli

macOS: explicit `tls.getCACertificates("system")` call adds 5+ seconds to every CLI invocation

Aperta
#3,330 1 commento 0 reazioni 0 assegnatari Vedi su GitHub
area:networking
Lingua principale
Shell
Stelle
11.2k
Fork
1.9k
Merge medio
14h 16m
PR unite (30g)
6

Descrizione

### Describe the bug

GitHub Copilot CLI's CA loader calls `tls.getCACertificates("system")` on every invocation in addition to `"bundled"` and `"extra"`. On macOS, that call iterates every keychain cert and invokes `SecTrustEvaluateWithError` synchronously via XPC to `trustd` — which (because of a Node bug, see References) uses a revocation-enabled trust policy and triggers OCSP/CRL fetches per cert. On machines with any network-flow filter (corporate NetworkExtension, ZeroTier, etc.), each fetch pays a per-flow cost and totals **5-10 seconds of latency on every CLI invocation** — even sub-commands like `--version` that don't make HTTPS requests inherit the cost because the call happens during SEA startup.

The `"system"` arg is also functionally redundant with `"bundled"` for `*.github.com` / `*.githubcopilot.com` endpoints. See related #869 (which reports a separate failure caused by the duplicate-certs concatenation from this same code).

### Affected version

GitHub Copilot CLI 1.0.48 (also reproduced on 1.0.39). macOS 26.4.1, Apple Silicon. Node 24.15.0 inside the SEA.

### Steps to reproduce the behavior

```bash
$ time copilot --allow-all-tools -p "reply with exactly: ack"
ack

real 0m12.43s
user 0m1.98s
sys 0m0.17s
```

Wall-clock vs user CPU mismatch confirms it's I/O wait, not CPU work. `sample(1)` on the grandchild SEA process during the wait:

```
node::crypto::GetSystemCACertificates
node::crypto::ReadMacOSKeychainCertificates
node::crypto::IsCertificateTrustedForPolicy
node::crypto::IsCertificateTrustValid
SecTrustEvaluateWithError
SecTrustEvaluateIfNecessary
securityd_send_sync_and_do ← synchronous XPC to trustd
xpc_connection_send_message_with_reply_sync
mach_msg2_trap
```

Isolated reproduction with standalone Node 24 on the same machine:

```bash
$ node -e 'const t0=process.hrtime.bigint();
require("tls").getCACertificates("system");
console.log(`${(Number(process.hrtime.bigint()-t0)/1e6).toFixed(0)}ms`)'
5287ms
$ node -e 'const t0=process.hrtime.bigint();
require("tls").getCACertificates("bundled");
console.log(`${(Number(process.hrtime.bigint()-t0)/1e6).toFixed(0)}ms`)'
0.1ms
```

HTTPS request to `api.github.com`:
- default (bundled): **53ms**
- `node --use-bundled-ca …`: **103ms**
- `node --use-system-ca …`: **5093ms** — 96× slower

### Expected behavior

CLI startup should not pay multi-second latency for a CA loader that produces certs equivalent to the bundled Mozilla store.

### Root cause in copilot-cli source

Decoded from `app.js` (npm-loader fallback; the SEA's bundled JS contains the same logic):

```js
function buildCAList() {
const envCerts = ["NODE_EXTRA_CA_CERTS","SSL_CERT_FILE","CURL_CA_BUNDLE"].flatMap(readEnvCAFile);
return typeof tls.getCACertificates === "function"
? [...envCerts,
...tls.getCACertificates(), // default (bundled in Node 24+)
...tls.getCACertificates("system"), // ← THIS LINE
...tls.getCACertificates("bundled"), // bundled (already implied above)
...tls.getCACertificates("extra")]
: [...envCerts, ...tls.rootCertificates];
}
```

### Proposed fix

Remove the `tls.getCACertificates("system")` line. One-line patch. If retaining the option for users with private CAs is desired, gate it behind a `COPILOT_USE_SYSTEM_CA=1` env var defaulting OFF. The current always-on cost is borne by every macOS user even when they don't need it. This also resolves the duplicate-certs issue tracked at #869.

### Why this user sees the upper end

My login keychain had 1589 certs (mostly auto-imported S/MIME contact certs from Mail.app over 20 years). After pruning 1478 long-expired certs, the call dropped from ~10s to ~5s — the floor on this machine, set by ~270 still-valid certs each costing ~20 ms in `SecTrustEvaluateWithError`. ZeroTier's `feth1960` virtual interface and `zerotier-one` daemon further amplify per-flow cost for the trustd OCSP traffic.

Users without a flow filter and a smaller keychain see a smaller version of this same tax (1-2s baseline, per anthropics/claude-code#53660's measurements).

### Related

- **anthropics/claude-code#53660** — same Node-side root cause analyzed in detail (revocation-enabled trust policy in `SecTrustEvaluateWithError`). Their workaround was to ship a `CLAUDE_CODE_CERT_STORE=bundled` env var users can set.
- **#1250** — Windows variant of the same call: throws "X509 to PEM conversion" instead of slow execution. Different symptom, same offending line.
- **#869** — duplicate certs from this same `bundled + system` concatenation cause `Failed to list available models` for some users.
- **nodejs/node#58990** — Node tracking issue for custom CA support; needs a sub-issue for the revocation-policy perf bug specifically, which I'm filing separately.

Guida per i contributori

Apri la guida per i contributori

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.