Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0defc34
Implement NODE_USE_SYSTEM_CA=1 feature with dynamic Security framewor…
Aug 15, 2025
a85b88e
Refactor NODE_USE_SYSTEM_CA implementation: extract platform-specific…
Aug 15, 2025
10c4a1f
Add --use-system-ca CLI flag matching Node.js
Aug 15, 2025
0965c1e
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 15, 2025
1ea1cd1
Try this
Jarred-Sumner Aug 15, 2025
6dceed3
try
Jarred-Sumner Aug 15, 2025
3ea3e25
Replace tests with Node.js-style verification tests
Jarred-Sumner Aug 15, 2025
fe8b3f1
Add Node.js system CA tests directly
Jarred-Sumner Aug 15, 2025
bf3947e
Add tests for --use-system-ca flag and NODE_USE_SYSTEM_CA
Jarred-Sumner Aug 15, 2025
e06cdd2
Implement tls.getCACertificates() API
Jarred-Sumner Aug 16, 2025
f58eae1
Fix system certificate loading on macOS
Jarred-Sumner Aug 16, 2025
104dbad
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 16, 2025
88d3a05
Fix
Jarred-Sumner Aug 16, 2025
b8b7c8d
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 16, 2025
d2e1d06
Update BuildBun.cmake
Jarred-Sumner Aug 16, 2025
5173a04
various cleanup
Jarred-Sumner Aug 16, 2025
1e4d648
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 16, 2025
babae37
fix tests
cirospaciari Aug 30, 2025
54cfbc4
Address all PR review comments for system CA implementation
Aug 30, 2025
4724207
Fix critical system CA handling issues
Aug 30, 2025
73911ea
Implement Arguments.zig enum-based CA store selection
Aug 30, 2025
e56b8e0
[autofix.ci] apply automated fixes
autofix-ci[bot] Aug 30, 2025
c1b3e3e
[autofix.ci] apply automated fixes (attempt 2/3)
autofix-ci[bot] Sep 22, 2025
40d2637
[autofix.ci] apply automated fixes (attempt 3/3)
autofix-ci[bot] Sep 22, 2025
8d79fad
remove tests that needs CI changes for now
cirospaciari Sep 22, 2025
11cccf1
[autofix.ci] apply automated fixes
autofix-ci[bot] Sep 22, 2025
e594101
[autofix.ci] apply automated fixes (attempt 2/3)
autofix-ci[bot] Sep 22, 2025
d7e2b81
[autofix.ci] apply automated fixes (attempt 3/3)
autofix-ci[bot] Sep 22, 2025
261b03b
Delete cmake/sources/CxxSources.txt
Jarred-Sumner Sep 25, 2025
257de05
Delete slop tests
Jarred-Sumner Sep 25, 2025
5f959c2
Delete pr_comments.json
Jarred-Sumner Sep 25, 2025
328f1e5
Merge branch 'main' into ciro/claude-system-ca
Jarred-Sumner Sep 25, 2025
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
2 changes: 2 additions & 0 deletions cmake/targets/BuildBun.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,7 @@ if(WIN32)
/delayload:WSOCK32.dll
/delayload:ADVAPI32.dll
/delayload:IPHLPAPI.dll
/delayload:CRYPT32.dll
)
endif()
endif()
Expand Down Expand Up @@ -1188,6 +1189,7 @@ if(WIN32)
ntdll
userenv
dbghelp
crypt32
wsock32 # ws2_32 required by TransmitFile aka sendfile on windows
delayimp.lib
)
Expand Down
129 changes: 122 additions & 7 deletions packages/bun-usockets/src/crypto/root_certs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,46 @@
#include <atomic>
#include <string.h>
#include "./default_ciphers.h"

// System-specific includes for certificate loading
#include "./root_certs_platform.h"
#ifdef _WIN32
#include <windows.h>
#include <wincrypt.h>
#else
// Linux/Unix includes
#include <dirent.h>
#include <stdio.h>
#include <limits.h>
#endif
static const int root_certs_size = sizeof(root_certs) / sizeof(root_certs[0]);

extern "C" void BUN__warn__extra_ca_load_failed(const char* filename, const char* error_msg);

// Forward declarations for platform-specific functions
// (Actual implementations are in platform-specific files)

// External variable from Zig CLI arguments
extern "C" bool Bun__Node__UseSystemCA;

// Helper function to check if system CA should be used
// Checks both CLI flag (--use-system-ca) and environment variable (NODE_USE_SYSTEM_CA=1)
static bool us_should_use_system_ca() {
// Check CLI flag first
if (Bun__Node__UseSystemCA) {
return true;
}

// Check environment variable
const char *use_system_ca = getenv("NODE_USE_SYSTEM_CA");
return use_system_ca && strcmp(use_system_ca, "1") == 0;
}

// Platform-specific system certificate loading implementations are separated:
// - macOS: root_certs_darwin.cpp (Security framework with dynamic loading)
// - Windows: root_certs_windows.cpp (Windows CryptoAPI)
// - Linux/Unix: us_load_system_certificates_linux() below

// This callback is used to avoid the default passphrase callback in OpenSSL
// which will typically prompt for the passphrase. The prompting is designed
// for the OpenSSL CLI, but works poorly for this case because it involves
Expand Down Expand Up @@ -101,7 +137,8 @@ static STACK_OF(X509) *us_ssl_ctx_load_all_certs_from_file(const char *filename)

static void us_internal_init_root_certs(
X509 *root_cert_instances[root_certs_size],
STACK_OF(X509) *&root_extra_cert_instances) {
STACK_OF(X509) *&root_extra_cert_instances,
STACK_OF(X509) *&root_system_cert_instances) {
static std::atomic_flag root_cert_instances_lock = ATOMIC_FLAG_INIT;
static std::atomic_bool root_cert_instances_initialized = 0;

Expand All @@ -123,6 +160,17 @@ static void us_internal_init_root_certs(
if (extra_certs && extra_certs[0]) {
root_extra_cert_instances = us_ssl_ctx_load_all_certs_from_file(extra_certs);
}

// load system certificates if NODE_USE_SYSTEM_CA=1
if (us_should_use_system_ca()) {
#ifdef __APPLE__
us_load_system_certificates_macos(&root_system_cert_instances);
#elif defined(_WIN32)
us_load_system_certificates_windows(&root_system_cert_instances);
#else
us_load_system_certificates_linux(&root_system_cert_instances);
#endif
}
}

atomic_flag_clear_explicit(&root_cert_instances_lock,
Expand All @@ -137,12 +185,15 @@ extern "C" int us_internal_raw_root_certs(struct us_cert_string_t **out) {
struct us_default_ca_certificates {
X509 *root_cert_instances[root_certs_size];
STACK_OF(X509) *root_extra_cert_instances;
STACK_OF(X509) *root_system_cert_instances;
};

us_default_ca_certificates* us_get_default_ca_certificates() {
static us_default_ca_certificates default_ca_certificates = {{NULL}, NULL};
static us_default_ca_certificates default_ca_certificates = {{NULL}, NULL, NULL};

us_internal_init_root_certs(default_ca_certificates.root_cert_instances, default_ca_certificates.root_extra_cert_instances);
us_internal_init_root_certs(default_ca_certificates.root_cert_instances,
default_ca_certificates.root_extra_cert_instances,
default_ca_certificates.root_system_cert_instances);

return &default_ca_certificates;
}
Expand All @@ -151,20 +202,33 @@ STACK_OF(X509) *us_get_root_extra_cert_instances() {
return us_get_default_ca_certificates()->root_extra_cert_instances;
}

STACK_OF(X509) *us_get_root_system_cert_instances() {
if (!us_should_use_system_ca())
return NULL;
// Ensure single-path initialization via us_internal_init_root_certs
auto certs = us_get_default_ca_certificates();
return certs->root_system_cert_instances;
}

extern "C" X509_STORE *us_get_default_ca_store() {
X509_STORE *store = X509_STORE_new();
if (store == NULL) {
return NULL;
}

if (!X509_STORE_set_default_paths(store)) {
X509_STORE_free(store);
return NULL;
// Only load system default paths when NODE_USE_SYSTEM_CA=1
// Otherwise, rely on bundled certificates only (like Node.js behavior)
if (us_should_use_system_ca()) {
if (!X509_STORE_set_default_paths(store)) {
X509_STORE_free(store);
return NULL;
}
}

us_default_ca_certificates *default_ca_certificates = us_get_default_ca_certificates();
X509** root_cert_instances = default_ca_certificates->root_cert_instances;
STACK_OF(X509) *root_extra_cert_instances = default_ca_certificates->root_extra_cert_instances;
STACK_OF(X509) *root_system_cert_instances = default_ca_certificates->root_system_cert_instances;

// load all root_cert_instances on the default ca store
for (size_t i = 0; i < root_certs_size; i++) {
Expand All @@ -183,8 +247,59 @@ extern "C" X509_STORE *us_get_default_ca_store() {
}
}

if (us_should_use_system_ca() && root_system_cert_instances) {
for (int i = 0; i < sk_X509_num(root_system_cert_instances); i++) {
X509 *cert = sk_X509_value(root_system_cert_instances, i);
X509_up_ref(cert);
X509_STORE_add_cert(store, cert);
}
}

return store;
}
extern "C" const char *us_get_default_ciphers() {
return DEFAULT_CIPHER_LIST;
}
}

// Platform-specific implementations for loading system certificates

#if defined(_WIN32)
// Windows implementation is split to avoid header conflicts:
// - root_certs_windows.cpp loads raw certificate data (uses Windows headers)
// - This file converts raw data to X509* (uses OpenSSL headers)

#include <vector>

struct RawCertificate {
std::vector<unsigned char> data;
};

// Defined in root_certs_windows.cpp - loads raw certificate data
extern void us_load_system_certificates_windows_raw(
std::vector<RawCertificate>& raw_certs);

// Convert raw Windows certificates to OpenSSL X509 format
void us_load_system_certificates_windows(STACK_OF(X509) **system_certs) {
*system_certs = sk_X509_new_null();
if (*system_certs == NULL) {
return;
}

// Load raw certificates from Windows stores
std::vector<RawCertificate> raw_certs;
us_load_system_certificates_windows_raw(raw_certs);

// Convert each raw certificate to X509
for (const auto& raw_cert : raw_certs) {
const unsigned char* data = raw_cert.data.data();
X509* x509_cert = d2i_X509(NULL, &data, raw_cert.data.size());
if (x509_cert != NULL) {
sk_X509_push(*system_certs, x509_cert);
}
}
}

#else
// Linux and other Unix-like systems - implementation is in root_certs_linux.cpp
extern "C" void us_load_system_certificates_linux(STACK_OF(X509) **system_certs);
#endif
Loading