diff --git a/src/bun.js/bindings/JSX509CertificatePrototype.cpp b/src/bun.js/bindings/JSX509CertificatePrototype.cpp index 8df864d95e..d8a1e0a35f 100644 --- a/src/bun.js/bindings/JSX509CertificatePrototype.cpp +++ b/src/bun.js/bindings/JSX509CertificatePrototype.cpp @@ -560,6 +560,8 @@ JSC_DEFINE_CUSTOM_GETTER(jsX509CertificateGetter_infoAccess, (JSGlobalObject * g return JSValue::encode(jsUndefined()); BUF_MEM* bptr = bio; + if (!bptr) + return JSValue::encode(jsUndefined()); return JSValue::encode(undefinedIfEmpty(jsString(vm, String::fromUTF8(std::span(bptr->data, bptr->length))))); } @@ -602,20 +604,10 @@ JSC_DEFINE_CUSTOM_GETTER(jsX509CertificateGetter_issuerCertificate, (JSGlobalObj return {}; } - auto issuerCert = thisObject->view().getIssuer(); - if (!issuerCert) - return JSValue::encode(jsUndefined()); - - auto bio = issuerCert.get(); - - BUF_MEM* bptr = nullptr; - BIO_get_mem_ptr(bio, &bptr); - std::span span(reinterpret_cast(bptr->data), bptr->length); - auto* zigGlobalObject = defaultGlobalObject(globalObject); - auto* structure = zigGlobalObject->m_JSX509CertificateClassStructure.get(zigGlobalObject); - auto jsIssuerCert = JSX509Certificate::create(vm, structure, globalObject, span); - RETURN_IF_EXCEPTION(scope, {}); - return JSValue::encode(jsIssuerCert); + // issuerCertificate is only available when the certificate was obtained from + // a TLS connection with a peer certificate chain. For certificates parsed + // directly from PEM/DER data, it is always undefined (matching Node.js behavior). + return JSValue::encode(jsUndefined()); } JSC_DEFINE_CUSTOM_GETTER(jsX509CertificateGetter_publicKey, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName)) diff --git a/test/regression/issue/27025.test.ts b/test/regression/issue/27025.test.ts new file mode 100644 index 0000000000..b31a2a52ac --- /dev/null +++ b/test/regression/issue/27025.test.ts @@ -0,0 +1,31 @@ +import { expect, test } from "bun:test"; +import { X509Certificate } from "crypto"; +import { readFileSync } from "fs"; +import { join } from "path"; + +const certPem = readFileSync(join(import.meta.dir, "../../js/node/test/fixtures/keys/agent1-cert.pem")); + +test("issuerCertificate should return undefined for directly-parsed certificates without crashing", () => { + const cert = new X509Certificate(certPem); + + // issuerCertificate is only populated for certificates obtained from TLS + // connections with a peer certificate chain. For directly parsed certs, + // it should be undefined (matching Node.js behavior). + expect(cert.issuerCertificate).toBeUndefined(); +}); + +test("X509Certificate properties should not crash on valid certificates", () => { + const cert = new X509Certificate(certPem); + + // These should all work without segfaulting + expect(cert.subject).toBeDefined(); + expect(cert.issuer).toBeDefined(); + expect(cert.validFrom).toBeDefined(); + expect(cert.validTo).toBeDefined(); + expect(cert.fingerprint).toBeDefined(); + expect(cert.fingerprint256).toBeDefined(); + expect(cert.fingerprint512).toBeDefined(); + expect(cert.serialNumber).toBeDefined(); + expect(cert.raw).toBeInstanceOf(Uint8Array); + expect(cert.ca).toBe(false); +});