Skip to content

Commit 15392d4

Browse files
authored
Merge pull request #6313 from cloudflare/yagiz/fix-x509-certificate
fix X509Certificate::parse() throwing internal error for invalid input
2 parents 329f6d4 + 1b5ccb1 commit 15392d4

4 files changed

Lines changed: 45 additions & 4 deletions

File tree

src/node/internal/crypto.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export interface CheckOptions {
3131
}
3232

3333
export class X509Certificate {
34-
static parse(data: ArrayBuffer | ArrayBufferView): X509Certificate;
34+
static parse(data: ArrayBuffer | ArrayBufferView): X509Certificate | null;
3535
get subject(): string | undefined;
3636
get subjectAltName(): string | undefined;
3737
get infoAccess(): string | undefined;

src/node/internal/crypto_x509.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,15 @@ export class X509Certificate {
114114
buffer
115115
);
116116
}
117-
this.#handle = cryptoImpl.X509Certificate.parse(buffer);
117+
const handle = cryptoImpl.X509Certificate.parse(buffer);
118+
if (handle == null) {
119+
throw new ERR_INVALID_ARG_VALUE(
120+
'buffer',
121+
buffer,
122+
'is not a valid certificate'
123+
);
124+
}
125+
this.#handle = handle;
118126
}
119127

120128
get subject() {

src/workerd/api/crypto/x509.c++

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -514,15 +514,17 @@ constexpr StackOfXASN1Disposer stackOfXASN1Disposer;
514514

515515
kj::Maybe<jsg::Ref<X509Certificate>> X509Certificate::parse(
516516
jsg::Lock& js, kj::Array<const kj::byte> raw) {
517-
ClearErrorOnReturn ClearErrorOnReturn;
517+
ClearErrorOnReturn clearErrorOnReturn;
518518
KJ_IF_SOME(bio, loadBio(raw)) {
519519
auto ptr = PEM_read_bio_X509_AUX(bio.get(), nullptr, NoPasswordCallback, nullptr);
520520
if (ptr == nullptr) {
521521
MarkPopErrorOnReturn mark_here;
522522
auto data = raw.begin();
523523
ptr = d2i_X509(nullptr, &data, raw.size());
524524
if (ptr == nullptr) {
525-
throwOpensslError(__FILE__, __LINE__, "X509Certificate::parse()");
525+
// Invalid certificate data is a user input error, not an internal error.
526+
// Return kj::none and let the JS layer throw a user-facing error.
527+
return kj::none;
526528
}
527529
}
528530
return js.alloc<X509Certificate>(ptr);

src/workerd/api/node/tests/crypto_x509-test.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,37 @@ export const test_ok = {
347347
// }
348348
// };
349349

350+
// Verify that invalid certificate data throws a user-facing error (not an internal error).
351+
export const test_invalid_cert = {
352+
test() {
353+
// Completely invalid data
354+
throws(() => new X509Certificate('not a certificate'), {
355+
code: 'ERR_INVALID_ARG_VALUE',
356+
});
357+
358+
// Invalid base64 in PEM wrapper
359+
throws(
360+
() =>
361+
new X509Certificate(
362+
'-----BEGIN CERTIFICATE-----\n!!!invalid!!!\n-----END CERTIFICATE-----'
363+
),
364+
{
365+
code: 'ERR_INVALID_ARG_VALUE',
366+
}
367+
);
368+
369+
// Empty buffer
370+
throws(() => new X509Certificate(Buffer.alloc(0)), {
371+
code: 'ERR_INVALID_ARG_VALUE',
372+
});
373+
374+
// Random bytes (not a valid DER or PEM certificate)
375+
throws(() => new X509Certificate(Buffer.from([0x01, 0x02, 0x03])), {
376+
code: 'ERR_INVALID_ARG_VALUE',
377+
});
378+
},
379+
};
380+
350381
// Ref: https://github.com/unjs/unenv/pull/310
351382
export const shouldImportCertificate = {
352383
test() {

0 commit comments

Comments
 (0)