mirror of
https://github.com/oven-sh/bun.git
synced 2026-10-25 21:40:30 +08:00
### What does this PR do? Makes `--use-openssl-ca` do what it says. The flag was parsed and then ignored: `us_get_default_ca_store()` always added the bundled Mozilla roots, so pointing `SSL_CERT_FILE` / `SSL_CERT_DIR` at a restricted set of CAs could add trust but never remove any. With the flag, the default store now holds only OpenSSL's default certificate file and directory (`$SSL_CERT_FILE` else `/etc/ssl/cert.pem`, `$SSL_CERT_DIR` else `/etc/ssl/certs`) and `NODE_EXTRA_CA_CERTS`, as in Node ([`NewRootCertStore`](https://github.com/nodejs/node/blob/v26.3.0/src/crypto/crypto_context.cc#L1099)). `tls.getCACertificates("default")` lists only the extra certificates ([`lib/tls.js`](https://github.com/nodejs/node/blob/v26.3.0/lib/tls.js#L157)). `tls.rootCertificates` and `getCACertificates("bundled")` are unchanged. | `--use-openssl-ca`, empty OpenSSL paths | Node 26.3.0 | Bun, before | Bun, after | | --- | --- | --- | --- | | `tls.getCACertificates("default").length` | 0 | 118 | 0 | | a server that presents a bundled root | not run | trusted | `DEPTH_ZERO_SELF_SIGNED_CERT` | | a server whose private CA is in `SSL_CERT_FILE` | authorized | authorized | authorized | #### The fix - `bun_core::Global::CAStore { Bundled, System, OpenSSL }` replaces the `Bun__Node__UseSystemCA` bool. It is one choice out of three, and `root_certs.cpp` and `node:tls` both read it, so "OpenSSL and system" cannot be represented. - `root_certs.cpp`: `us_get_default_ca_store()` skips the bundled set for `OpenSSL`. Every default store (sockets, `fetch`, `bun install`, WebSocket, QUIC, `addCACert`) is built there. The loader of the default certificate file drops certificates that the bundled set already holds; with no bundled set in the store it keeps the whole file. - `tls.ts`: `getCACertificates("default")` follows the same choice. #### Every command, and the processes they spawn A flag only reaches the process it is passed to, and several commands leave their downloads to a second `bun`. They now pass on the flag they were given (`Global::ca_store_flag()`). This applies to `--use-system-ca` and `--use-bundled-ca` as well, which had the same gaps. | | before | now | | --- | --- | --- | | `bun test --parallel` workers | flag lost | forwarded | | `bun install`: the security scanner process | flag lost | forwarded | | `bunx` → `bun add` | skipped as "a flag of Bun's with no value" | forwarded, and two stores are rejected even when nothing is installed | | `bun create` → `bun add` / `bun install`, and its own GitHub download | dropped | honoured and forwarded | | `bun init` → `bun install` | dropped | forwarded | | `bun upgrade`, `bun build` (macros, `--compile --target` download) | accepted, ignored | honoured: the three flags moved from the runtime parameters to the ones every command has | #### Deliberate difference from Node Node 26 exits when `NODE_USE_SYSTEM_CA=1` comes with `--use-openssl-ca` ([`node_options.cc`](https://github.com/nodejs/node/blob/v26.3.0/src/node_options.cc#L212)). Here the flag wins, which is what `set_ca_store_from_flags` already documented ("falls back to `NODE_USE_SYSTEM_CA` if none is [passed]"). Bun loads `.env` by itself and hands its values to child processes as their real environment, so with an error the parent would start and its test workers, security scanner and lifecycle scripts would exit. The flag winning only ever narrows trust. #### Not changed Same as on main, checked with a 16-row matrix (4 flags x `NODE_USE_SYSTEM_CA` unset/`0`/`1`/`true`, listing and a real handshake): the 12 rows without `--use-openssl-ca` are identical. - `--use-bundled-ca` is still Bun's default store, which also trusts OpenSSL's default paths, and `NODE_USE_SYSTEM_CA=1` still adds the system store to it (in Node too). - `getCACertificates("default")` still counts a `NODE_USE_SYSTEM_CA=1` that only `.env` sets, which the native store does not see. - `--no-use-system-ca` is still ignored. - `bun run --parallel` / `--sequential` with a script file (`./a.ts`) starts `bun ./a.ts` with none of the runtime flags, so these three are dropped there like `--smol` or `--preload`. Whether it should pass runtime flags on is a question for all of them. A `package.json` script or any other command can be anything and must not get the flag. Same goal as #46302, written independently. ### How did you verify your code works? New tests, each next to the code it covers. On the canary build of `b6a5ecee5` (two commits before this branch's base) every one that depends on the flag fails and the control rows pass. `bun-upgrade.test.ts` does not load on a release build, so its new test was checked with a mutation instead. - `test/js/node/tls/test-use-system-ca.test.ts`: a local TLS 1.2 server answers with ServerHello, Certificate, ServerHelloDone, which takes no private key, so the certificate can be a real bundled root. A client that trusts it fails on a later check, one that does not fails on the chain. Covered: `fetch` and `tls.connect`; the flag in argv and in `BUN_OPTIONS`; the same root given back through `SSL_CERT_FILE`, a hashed `SSL_CERT_DIR` and `NODE_EXTRA_CA_CERTS`; the same server as the registry of `bun install`, `bun x`, `bun create` (npm, local template, `.tsx`) and `bun init` (blank, React); a macro of `bun build`; a request that succeeds; the flag against `NODE_USE_SYSTEM_CA=1` in a real handshake; and the lists of `getCACertificates`. - `parallel.test.ts` (with `NODE_USE_SYSTEM_CA=1` in `.env`), `bun-install-security-provider.test.ts`, `bunx.test.ts`, `bun-upgrade.test.ts`. Mutation checks on a debug build, one at a time: removing the bundled-set gate, the file-loader gate, the nesting in `tls.ts`, the handling in `bun upgrade`, or the forwarding in the scanner or `bunx` each failed exactly the tests written for it. The two forwarding mutations were run before `ca_store_flag()` was simplified to return the flag that was passed; the tests were rerun after that, the mutations were not. The same `-e` scripts print the same output on Node 26.3.0 and on this build for the `getCACertificates` lists and for a handshake with a private CA, with and without it in `SSL_CERT_FILE`. `bun run rust:check-all` passed for `x86_64-pc-windows-msvc` and `aarch64-apple-darwin` partway through; the later edits are not platform-gated. The tests were only run on Linux. --- _Generated by [Claude Code](https://claude.ai/code/session_01EtFydK4WUe1Sg3hyHva2Pr)_ --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>