From e0924ef226696f34de5feab381323a5e02449a99 Mon Sep 17 00:00:00 2001 From: pfg Date: Fri, 13 Jun 2025 17:30:04 -0700 Subject: [PATCH] Implement `require('tls').getCACertificates()` (#20364) Co-authored-by: pfgithub <6010774+pfgithub@users.noreply.github.com> --- packages/bun-usockets/src/crypto/openssl.c | 5 +- .../bun-usockets/src/crypto/root_certs.cpp | 27 ++++++-- .../src/crypto/root_certs_header.h | 14 ++++ src/bun.js/bindings/NodeTLS.cpp | 55 +++++++++++++--- src/bun.js/bindings/NodeTLS.h | 4 +- src/js/internal-for-testing.ts | 2 +- src/js/node/tls.ts | 64 ++++++++++++++++++- test/js/node/test/common/tls.js | 13 ++++ .../tls-check-extra-ca-certificates.js | 17 +++++ .../test/fixtures/tls-get-ca-certificates.js | 11 ++++ ...-tls-get-ca-certificates-bundled-subset.js | 17 +++++ .../test-tls-get-ca-certificates-bundled.js | 20 ++++++ .../test-tls-get-ca-certificates-default.js | 20 ++++++ .../test-tls-get-ca-certificates-error.js | 20 ++++++ ...est-tls-get-ca-certificates-extra-empty.js | 29 +++++++++ ...st-tls-get-ca-certificates-extra-subset.js | 16 +++++ .../test-tls-get-ca-certificates-extra.js | 29 +++++++++ test/js/node/tls/node-tls-internals.test.ts | 5 +- 18 files changed, 341 insertions(+), 27 deletions(-) create mode 100644 packages/bun-usockets/src/crypto/root_certs_header.h create mode 100644 test/js/node/test/fixtures/tls-check-extra-ca-certificates.js create mode 100644 test/js/node/test/fixtures/tls-get-ca-certificates.js create mode 100644 test/js/node/test/parallel/test-tls-get-ca-certificates-bundled-subset.js create mode 100644 test/js/node/test/parallel/test-tls-get-ca-certificates-bundled.js create mode 100644 test/js/node/test/parallel/test-tls-get-ca-certificates-default.js create mode 100644 test/js/node/test/parallel/test-tls-get-ca-certificates-error.js create mode 100644 test/js/node/test/parallel/test-tls-get-ca-certificates-extra-empty.js create mode 100644 test/js/node/test/parallel/test-tls-get-ca-certificates-extra-subset.js create mode 100644 test/js/node/test/parallel/test-tls-get-ca-certificates-extra.js diff --git a/packages/bun-usockets/src/crypto/openssl.c b/packages/bun-usockets/src/crypto/openssl.c index 49b535d818..dff3399fb6 100644 --- a/packages/bun-usockets/src/crypto/openssl.c +++ b/packages/bun-usockets/src/crypto/openssl.c @@ -44,10 +44,7 @@ void *sni_find(void *sni, const char *hostname); #include #endif -#include "./root_certs.h" - -/* These are in root_certs.cpp */ -extern X509_STORE *us_get_default_ca_store(); +#include "./root_certs_header.h" struct loop_ssl_data { char *ssl_read_input, *ssl_read_output; diff --git a/packages/bun-usockets/src/crypto/root_certs.cpp b/packages/bun-usockets/src/crypto/root_certs.cpp index f218ad7c5a..877685a8c8 100644 --- a/packages/bun-usockets/src/crypto/root_certs.cpp +++ b/packages/bun-usockets/src/crypto/root_certs.cpp @@ -1,10 +1,9 @@ // MSVC doesn't support C11 stdatomic.h propertly yet. // so we use C++ std::atomic instead. #include "./root_certs.h" +#include "./root_certs_header.h" #include "./internal/internal.h" #include -#include -#include #include static const int root_certs_size = sizeof(root_certs) / sizeof(root_certs[0]); @@ -134,6 +133,23 @@ extern "C" int us_internal_raw_root_certs(struct us_cert_string_t **out) { return root_certs_size; } +struct us_default_ca_certificates { + X509 *root_cert_instances[root_certs_size]; + STACK_OF(X509) *root_extra_cert_instances; +}; + +us_default_ca_certificates* us_get_default_ca_certificates() { + static us_default_ca_certificates default_ca_certificates = {{NULL}, NULL}; + + us_internal_init_root_certs(default_ca_certificates.root_cert_instances, default_ca_certificates.root_extra_cert_instances); + + return &default_ca_certificates; +} + +STACK_OF(X509) *us_get_root_extra_cert_instances() { + return us_get_default_ca_certificates()->root_extra_cert_instances; +} + extern "C" X509_STORE *us_get_default_ca_store() { X509_STORE *store = X509_STORE_new(); if (store == NULL) { @@ -145,10 +161,9 @@ extern "C" X509_STORE *us_get_default_ca_store() { return NULL; } - static X509 *root_cert_instances[root_certs_size] = {NULL}; - static STACK_OF(X509) *root_extra_cert_instances = NULL; - - us_internal_init_root_certs(root_cert_instances, root_extra_cert_instances); + 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; // load all root_cert_instances on the default ca store for (size_t i = 0; i < root_certs_size; i++) { diff --git a/packages/bun-usockets/src/crypto/root_certs_header.h b/packages/bun-usockets/src/crypto/root_certs_header.h new file mode 100644 index 0000000000..92515fcbfe --- /dev/null +++ b/packages/bun-usockets/src/crypto/root_certs_header.h @@ -0,0 +1,14 @@ +#include +#include + +#ifdef __cplusplus +#define CPPDECL extern "C" + +STACK_OF(X509) *us_get_root_extra_cert_instances(); + +#else +#define CPPDECL extern +#endif + +CPPDECL X509_STORE *us_get_default_ca_store(); + diff --git a/src/bun.js/bindings/NodeTLS.cpp b/src/bun.js/bindings/NodeTLS.cpp index 4c4102180f..36ef165e22 100644 --- a/src/bun.js/bindings/NodeTLS.cpp +++ b/src/bun.js/bindings/NodeTLS.cpp @@ -6,22 +6,23 @@ #include "libusockets.h" #include "ZigGlobalObject.h" +#include "ErrorCode.h" +#include "openssl/base.h" +#include "openssl/bio.h" +#include "../../packages/bun-usockets/src/crypto/root_certs_header.h" namespace Bun { using namespace JSC; -BUN_DECLARE_HOST_FUNCTION(Bun__canonicalizeIP); - -JSC::JSValue createNodeTLSBinding(Zig::GlobalObject* globalObject) +JSC_DEFINE_HOST_FUNCTION(getBundledRootCertificates, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { VM& vm = globalObject->vm(); - auto* obj = constructEmptyObject(globalObject); struct us_cert_string_t* out; auto size = us_raw_root_certs(&out); if (size < 0) { - return jsUndefined(); + return JSValue::encode(jsUndefined()); } auto rootCertificates = JSC::JSArray::create(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), size); for (auto i = 0; i < size; i++) { @@ -29,12 +30,46 @@ JSC::JSValue createNodeTLSBinding(Zig::GlobalObject* globalObject) auto str = WTF::String::fromUTF8(std::span { raw.str, raw.len }); rootCertificates->putDirectIndex(globalObject, i, JSC::jsString(vm, str)); } - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "rootCertificates"_s)), JSC::objectConstructorFreeze(globalObject, rootCertificates), 0); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "canonicalizeIP"_s)), JSC::JSFunction::create(vm, globalObject, 1, "canonicalizeIP"_s, Bun__canonicalizeIP, ImplementationVisibility::Public, NoIntrinsic), 0); - return obj; + return JSValue::encode(JSC::objectConstructorFreeze(globalObject, rootCertificates)); +} + +JSC_DEFINE_HOST_FUNCTION(getExtraCACertificates, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); + VM& vm = globalObject->vm(); + + STACK_OF(X509)* root_extra_cert_instances = us_get_root_extra_cert_instances(); + + auto size = sk_X509_num(root_extra_cert_instances); + if (size < 0) size = 0; // root_extra_cert_instances is nullptr + + auto rootCertificates = JSC::JSArray::create(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), size); + for (auto i = 0; i < size; i++) { + BIO* bio = BIO_new(BIO_s_mem()); + if (!bio) { + throwOutOfMemoryError(globalObject, scope); + return {}; + } + + if (PEM_write_bio_X509(bio, sk_X509_value(root_extra_cert_instances, i)) != 1) { + BIO_free(bio); + return throwError(globalObject, scope, ErrorCode::ERR_CRYPTO_OPERATION_FAILED, "X509 to PEM conversion"_str); + } + + char* bioData = nullptr; + long bioLen = BIO_get_mem_data(bio, &bioData); + if (bioLen <= 0 || !bioData) { + BIO_free(bio); + return throwError(globalObject, scope, ErrorCode::ERR_CRYPTO_OPERATION_FAILED, "Reading PEM data"_str); + } + + auto str = WTF::String::fromUTF8(std::span { bioData, static_cast(bioLen) }); + rootCertificates->putDirectIndex(globalObject, i, JSC::jsString(vm, str)); + BIO_free(bio); + } + + return JSValue::encode(JSC::objectConstructorFreeze(globalObject, rootCertificates)); } } // namespace Bun diff --git a/src/bun.js/bindings/NodeTLS.h b/src/bun.js/bindings/NodeTLS.h index 2650f0cfaf..296c28c76a 100644 --- a/src/bun.js/bindings/NodeTLS.h +++ b/src/bun.js/bindings/NodeTLS.h @@ -3,6 +3,8 @@ namespace Bun { -JSC::JSValue createNodeTLSBinding(Zig::GlobalObject*); +BUN_DECLARE_HOST_FUNCTION(Bun__canonicalizeIP); +JSC_DECLARE_HOST_FUNCTION(getBundledRootCertificates); +JSC_DECLARE_HOST_FUNCTION(getExtraCACertificates); } diff --git a/src/js/internal-for-testing.ts b/src/js/internal-for-testing.ts index fa784a3ae9..d922de05b7 100644 --- a/src/js/internal-for-testing.ts +++ b/src/js/internal-for-testing.ts @@ -12,7 +12,7 @@ const fmtBinding = $bindgenFn("fmt.bind.ts", "fmtString"); export const highlightJavaScript = (code: string) => fmtBinding(code, "highlight-javascript"); export const escapePowershell = (code: string) => fmtBinding(code, "escape-powershell"); -export const TLSBinding = $cpp("NodeTLS.cpp", "createNodeTLSBinding"); +export const canonicalizeIP = $newCppFunction("NodeTLS.cpp", "Bun__canonicalizeIP", 1); export const SQL = $cpp("JSSQLStatement.cpp", "createJSSQLStatementConstructor"); diff --git a/src/js/node/tls.ts b/src/js/node/tls.ts index 946e4e2b7e..06f7a81e44 100644 --- a/src/js/node/tls.ts +++ b/src/js/node/tls.ts @@ -5,10 +5,13 @@ const { Duplex } = require("node:stream"); const addServerName = $newZigFunction("socket.zig", "jsAddServerName", 3); const { throwNotImplemented } = require("internal/shared"); const { throwOnInvalidTLSArray, DEFAULT_CIPHERS, validateCiphers } = require("internal/tls"); +const { validateString } = require("internal/validators"); const { Server: NetServer, Socket: NetSocket } = net; -const { rootCertificates, canonicalizeIP } = $cpp("NodeTLS.cpp", "createNodeTLSBinding"); +const getBundledRootCertificates = $newCppFunction("NodeTLS.cpp", "getBundledRootCertificates", 1); +const getExtraCACertificates = $newCppFunction("NodeTLS.cpp", "getExtraCACertificates", 1); +const canonicalizeIP = $newCppFunction("NodeTLS.cpp", "Bun__canonicalizeIP", 1); const SymbolReplace = Symbol.replace; const RegExpPrototypeSymbolReplace = RegExp.prototype[SymbolReplace]; @@ -31,6 +34,9 @@ const ArrayPrototypeForEach = Array.prototype.forEach; const ArrayPrototypePush = Array.prototype.push; const ArrayPrototypeSome = Array.prototype.some; const ArrayPrototypeReduce = Array.prototype.reduce; + +const ObjectFreeze = Object.freeze; + function parseCertString() { // Removed since JAN 2022 Node v18.0.0+ https://github.com/nodejs/node/pull/41479 throwNotImplemented("Not implemented"); @@ -724,6 +730,59 @@ function convertALPNProtocols(protocols, out) { } } +let bundledRootCertificates: string[] | undefined; +function cacheBundledRootCertificates(): string[] { + bundledRootCertificates ||= getBundledRootCertificates() as string[]; + return bundledRootCertificates; +} +let defaultCACertificates: string[] | undefined; +function cacheDefaultCACertificates() { + if (defaultCACertificates) return defaultCACertificates; + defaultCACertificates = []; + + const bundled = cacheBundledRootCertificates(); + for (let i = 0; i < bundled.length; ++i) { + ArrayPrototypePush.$call(defaultCACertificates, bundled[i]); + } + + if (process.env.NODE_EXTRA_CA_CERTS) { + const extra = cacheExtraCACertificates(); + for (let i = 0; i < extra.length; ++i) { + ArrayPrototypePush.$call(defaultCACertificates, extra[i]); + } + } + + ObjectFreeze(defaultCACertificates); + return defaultCACertificates; +} + +function cacheSystemCACertificates(): string[] { + throw new Error("getCACertificates('system') is not yet implemented in Bun"); +} + +let extraCACertificates: string[] | undefined; +function cacheExtraCACertificates(): string[] { + extraCACertificates ||= getExtraCACertificates() as string[]; + return extraCACertificates; +} + +function getCACertificates(type = "default") { + validateString(type, "type"); + + switch (type) { + case "default": + return cacheDefaultCACertificates(); + case "bundled": + return cacheBundledRootCertificates(); + case "system": + return cacheSystemCACertificates(); + case "extra": + return cacheExtraCACertificates(); + default: + throw $ERR_INVALID_ARG_VALUE("type", type); + } +} + export default { CLIENT_RENEG_LIMIT, CLIENT_RENEG_WINDOW, @@ -742,6 +801,7 @@ export default { TLSSocket, checkServerIdentity, get rootCertificates() { - return rootCertificates; + return cacheBundledRootCertificates(); }, + getCACertificates, } as any as typeof import("node:tls"); diff --git a/test/js/node/test/common/tls.js b/test/js/node/test/common/tls.js index d212fbe076..bbc37df19c 100644 --- a/test/js/node/test/common/tls.js +++ b/test/js/node/test/common/tls.js @@ -3,6 +3,7 @@ 'use strict'; const crypto = require('crypto'); const net = require('net'); +const assert = require('assert'); exports.ccs = Buffer.from('140303000101', 'hex'); @@ -173,4 +174,16 @@ function P_hash(algo, secret, seed, size) { return result; } +exports.assertIsCAArray = function assertIsCAArray(certs) { + assert(Array.isArray(certs)); + assert(certs.length > 0); + + // The certificates looks PEM-encoded. + for (const cert of certs) { + const trimmed = cert.trim(); + assert.match(trimmed, /^-----BEGIN CERTIFICATE-----/); + assert.match(trimmed, /-----END CERTIFICATE-----$/); + } +}; + exports.TestTLSSocket = TestTLSSocket; diff --git a/test/js/node/test/fixtures/tls-check-extra-ca-certificates.js b/test/js/node/test/fixtures/tls-check-extra-ca-certificates.js new file mode 100644 index 0000000000..ee12992604 --- /dev/null +++ b/test/js/node/test/fixtures/tls-check-extra-ca-certificates.js @@ -0,0 +1,17 @@ +'use strict'; + +const tls = require('tls'); +const assert = require('assert'); + +const defaultSet = new Set(tls.getCACertificates('default')); +const extraSet = new Set(tls.getCACertificates('extra')); +console.log(defaultSet.size, 'default certificates'); +console.log(extraSet.size, 'extra certificates') + +// Parent process is supposed to call this with +// NODE_EXTRA_CA_CERTS set to test/fixtures/keys/ca1-cert.pem. +assert.strictEqual(extraSet.size, 1); + +// Check that default set is a super set of extra set. +assert.deepStrictEqual(defaultSet.intersection(extraSet), + extraSet); diff --git a/test/js/node/test/fixtures/tls-get-ca-certificates.js b/test/js/node/test/fixtures/tls-get-ca-certificates.js new file mode 100644 index 0000000000..e21f06b81b --- /dev/null +++ b/test/js/node/test/fixtures/tls-get-ca-certificates.js @@ -0,0 +1,11 @@ +'use strict'; +// This fixture just writes tls.getCACertificates() outputs to process.env.CA_OUT +const tls = require('tls'); +const fs = require('fs'); +const assert = require('assert'); +assert(process.env.CA_TYPE); +assert(process.env.CA_OUT); + +const certs = tls.getCACertificates(process.env.CA_TYPE); + +fs.writeFileSync(process.env.CA_OUT, JSON.stringify(certs), 'utf8'); diff --git a/test/js/node/test/parallel/test-tls-get-ca-certificates-bundled-subset.js b/test/js/node/test/parallel/test-tls-get-ca-certificates-bundled-subset.js new file mode 100644 index 0000000000..b9fe43b965 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-get-ca-certificates-bundled-subset.js @@ -0,0 +1,17 @@ +'use strict'; +// Flags: --no-use-openssl-ca +// This tests that tls.getCACertificates() returns the bundled +// certificates correctly. + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); + +const defaultSet = new Set(tls.getCACertificates('default')); +const bundledSet = new Set(tls.getCACertificates('bundled')); + +// When --use-openssl-ca is false (i.e. bundled CA is sued), +// default is a superset of bundled certificates. +assert.deepStrictEqual(defaultSet.intersection(bundledSet), bundledSet); diff --git a/test/js/node/test/parallel/test-tls-get-ca-certificates-bundled.js b/test/js/node/test/parallel/test-tls-get-ca-certificates-bundled.js new file mode 100644 index 0000000000..5dbe254bca --- /dev/null +++ b/test/js/node/test/parallel/test-tls-get-ca-certificates-bundled.js @@ -0,0 +1,20 @@ +'use strict'; +// This tests that tls.getCACertificates() returns the bundled +// certificates correctly. + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); +const { assertIsCAArray } = require('../common/tls'); + +const certs = tls.getCACertificates('bundled'); +assertIsCAArray(certs); + +// It's the same as tls.rootCertificates - both are +// Mozilla CA stores across platform. +assert.strictEqual(certs, tls.rootCertificates); + +// It's cached on subsequent accesses. +assert.strictEqual(certs, tls.getCACertificates('bundled')); diff --git a/test/js/node/test/parallel/test-tls-get-ca-certificates-default.js b/test/js/node/test/parallel/test-tls-get-ca-certificates-default.js new file mode 100644 index 0000000000..29fb2a29a8 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-get-ca-certificates-default.js @@ -0,0 +1,20 @@ +'use strict'; + +// This tests that tls.getCACertificates() returns the default +// certificates correctly. + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); +const { assertIsCAArray } = require('../common/tls'); + +const certs = tls.getCACertificates(); +assertIsCAArray(certs); + +const certs2 = tls.getCACertificates('default'); +assert.strictEqual(certs, certs2); + +// It's cached on subsequent accesses. +assert.strictEqual(certs, tls.getCACertificates('default')); diff --git a/test/js/node/test/parallel/test-tls-get-ca-certificates-error.js b/test/js/node/test/parallel/test-tls-get-ca-certificates-error.js new file mode 100644 index 0000000000..da40b8fdf4 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-get-ca-certificates-error.js @@ -0,0 +1,20 @@ +'use strict'; + +// This tests that tls.getCACertificates() throws error when being +// passed an invalid argument. + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); + +for (const invalid of [1, null, () => {}, true]) { + assert.throws(() => tls.getCACertificates(invalid), { + code: 'ERR_INVALID_ARG_TYPE' + }); +} + +assert.throws(() => tls.getCACertificates('test'), { + code: 'ERR_INVALID_ARG_VALUE' +}); diff --git a/test/js/node/test/parallel/test-tls-get-ca-certificates-extra-empty.js b/test/js/node/test/parallel/test-tls-get-ca-certificates-extra-empty.js new file mode 100644 index 0000000000..f099a03962 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-get-ca-certificates-extra-empty.js @@ -0,0 +1,29 @@ +'use strict'; +// This tests that tls.getCACertificates('extra') returns an empty +// array if NODE_EXTRA_CA_CERTS is empty. + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const tmpdir = require('../common/tmpdir'); +const fs = require('fs'); + +const assert = require('assert'); +const { spawnSyncAndExitWithoutError } = require('../common/child_process'); +const fixtures = require('../common/fixtures'); + +tmpdir.refresh(); +const certsJSON = tmpdir.resolve('certs.json'); + +// If NODE_EXTRA_CA_CERTS is not set, it should be an empty array. +spawnSyncAndExitWithoutError(process.execPath, [fixtures.path('tls-get-ca-certificates.js')], { + env: { + ...process.env, + NODE_EXTRA_CA_CERTS: undefined, + CA_TYPE: 'extra', + CA_OUT: certsJSON, + } +}); + +const parsed = JSON.parse(fs.readFileSync(certsJSON, 'utf-8')); +assert.deepStrictEqual(parsed, []); diff --git a/test/js/node/test/parallel/test-tls-get-ca-certificates-extra-subset.js b/test/js/node/test/parallel/test-tls-get-ca-certificates-extra-subset.js new file mode 100644 index 0000000000..5fd3da47bd --- /dev/null +++ b/test/js/node/test/parallel/test-tls-get-ca-certificates-extra-subset.js @@ -0,0 +1,16 @@ +'use strict'; +// This tests that tls.getCACertificates('defulat') returns a superset +// of tls.getCACertificates('extra') when NODE_EXTRA_CA_CERTS is used. + +const common = require('../common'); +if (!common.hasCrypto) common.skip('missing crypto'); + +const { spawnSyncAndExitWithoutError } = require('../common/child_process'); +const fixtures = require('../common/fixtures'); + +spawnSyncAndExitWithoutError(process.execPath, [fixtures.path('tls-check-extra-ca-certificates.js')], { + env: { + ...process.env, + NODE_EXTRA_CA_CERTS: fixtures.path('keys', 'ca1-cert.pem'), + } +}); diff --git a/test/js/node/test/parallel/test-tls-get-ca-certificates-extra.js b/test/js/node/test/parallel/test-tls-get-ca-certificates-extra.js new file mode 100644 index 0000000000..1ff6a51079 --- /dev/null +++ b/test/js/node/test/parallel/test-tls-get-ca-certificates-extra.js @@ -0,0 +1,29 @@ +'use strict'; +// This tests that tls.getCACertificates('extra') returns the extra +// certificates from NODE_EXTRA_CA_CERTS correctly. + +const common = require('../common'); + +if (!common.hasCrypto) common.skip('missing crypto'); + +const tmpdir = require('../common/tmpdir'); +const fs = require('fs'); +const assert = require('assert'); +const { spawnSyncAndExitWithoutError } = require('../common/child_process'); +const fixtures = require('../common/fixtures'); + +tmpdir.refresh(); +const certsJSON = tmpdir.resolve('certs.json'); + +// If NODE_EXTRA_CA_CERTS is set, it should contain a list of certificates. +spawnSyncAndExitWithoutError(process.execPath, [fixtures.path('tls-get-ca-certificates.js')], { + env: { + ...process.env, + NODE_EXTRA_CA_CERTS: fixtures.path('keys', 'ca1-cert.pem'), + CA_TYPE: 'extra', + CA_OUT: certsJSON, + } +}); + +const parsed = JSON.parse(fs.readFileSync(certsJSON, 'utf-8')); +assert.deepStrictEqual(parsed, [fixtures.readKey('ca1-cert.pem', 'utf8')]); diff --git a/test/js/node/tls/node-tls-internals.test.ts b/test/js/node/tls/node-tls-internals.test.ts index 9b342cd22b..8a7e021504 100644 --- a/test/js/node/tls/node-tls-internals.test.ts +++ b/test/js/node/tls/node-tls-internals.test.ts @@ -1,9 +1,8 @@ -import { TLSBinding } from "bun:internal-for-testing"; +import { canonicalizeIP } from "bun:internal-for-testing"; import { createTest } from "node-harness"; +import { rootCertificates } from "tls"; const { describe, expect } = createTest(import.meta.path); -const { canonicalizeIP, rootCertificates } = TLSBinding; - describe("NodeTLS.cpp", () => { test("canonicalizeIP", () => { expect(canonicalizeIP("127.0.0.1")).toBe("127.0.0.1");