diff --git a/lib/internal/crypto/aes.js b/lib/internal/crypto/aes.js index 79ca61c5ff20970..41747c2bf9126fc 100644 --- a/lib/internal/crypto/aes.js +++ b/lib/internal/crypto/aes.js @@ -241,11 +241,10 @@ async function aesGenerateKey(algorithm, extractable, keyUsages) { } const key = await generateKey('aes', { length }).catch((err) => { - // TODO(@panva): add err as cause to DOMException throw lazyDOMException( - 'The operation failed for an operation-specific reason' + - `[${err.message}]`, - 'OperationError'); + 'The operation failed for an operation-specific reason', + 'OperationError', + err); }); return new InternalCryptoKey( diff --git a/lib/internal/crypto/cfrg.js b/lib/internal/crypto/cfrg.js index 17358c7ccca23d0..d92200efe1085f1 100644 --- a/lib/internal/crypto/cfrg.js +++ b/lib/internal/crypto/cfrg.js @@ -150,10 +150,10 @@ async function cfrgGenerateKey(algorithm, extractable, keyUsages) { } const keyPair = await generateKeyPair(genKeyType).catch((err) => { - // TODO(@panva): add err as cause to DOMException throw lazyDOMException( 'The operation failed for an operation-specific reason', - 'OperationError'); + 'OperationError', + err); }); let publicUsages; diff --git a/lib/internal/crypto/ec.js b/lib/internal/crypto/ec.js index ed7484dbbb596e7..e48ce1ee5be653a 100644 --- a/lib/internal/crypto/ec.js +++ b/lib/internal/crypto/ec.js @@ -112,10 +112,10 @@ async function ecGenerateKey(algorithm, extractable, keyUsages) { } const keypair = await generateKeyPair('ec', { namedCurve }).catch((err) => { - // TODO(@panva): add err as cause to DOMException throw lazyDOMException( 'The operation failed for an operation-specific reason', - 'OperationError'); + 'OperationError', + err); }); let publicUsages; diff --git a/lib/internal/crypto/mac.js b/lib/internal/crypto/mac.js index 15b3378e2eda640..2b28f73ad3615e3 100644 --- a/lib/internal/crypto/mac.js +++ b/lib/internal/crypto/mac.js @@ -66,10 +66,10 @@ async function hmacGenerateKey(algorithm, extractable, keyUsages) { } const key = await generateKey('hmac', { length }).catch((err) => { - // TODO(@panva): add err as cause to DOMException throw lazyDOMException( 'The operation failed for an operation-specific reason', - 'OperationError'); + 'OperationError', + err); }); return new InternalCryptoKey( diff --git a/lib/internal/crypto/rsa.js b/lib/internal/crypto/rsa.js index fa6928e4d78f06a..108ef7202e1b6f4 100644 --- a/lib/internal/crypto/rsa.js +++ b/lib/internal/crypto/rsa.js @@ -178,10 +178,10 @@ async function rsaKeyGenerate( modulusLength, publicExponent: publicExponentConverted, }).catch((err) => { - // TODO(@panva): add err as cause to DOMException throw lazyDOMException( 'The operation failed for an operation-specific reason', - 'OperationError'); + 'OperationError', + err); }); const keyAlgorithm = { diff --git a/lib/internal/crypto/util.js b/lib/internal/crypto/util.js index dbd816cff99e9fa..e1fd52658358a55 100644 --- a/lib/internal/crypto/util.js +++ b/lib/internal/crypto/util.js @@ -279,10 +279,10 @@ const validateByteSource = hideStackFrames((val, name) => { function onDone(resolve, reject, err, result) { if (err) { - // TODO(@panva): add err as cause to DOMException return reject(lazyDOMException( 'The operation failed for an operation-specific reason', - 'OperationError')); + 'OperationError', + err)); } resolve(result); } diff --git a/lib/internal/per_context/domexception.js b/lib/internal/per_context/domexception.js index 917e2c00bccda94..98cc669ca54ee6d 100644 --- a/lib/internal/per_context/domexception.js +++ b/lib/internal/per_context/domexception.js @@ -49,14 +49,23 @@ const disusedNamesSet = new SafeSet() .add('ValidationError'); class DOMException { - constructor(message = '', name = 'Error') { + constructor(message = '', name = 'Error', cause = undefined) { ErrorCaptureStackTrace(this); internalsMap.set(this, { message: `${message}`, - name: `${name}` + name: `${name}`, + cause }); } + get cause() { + const internals = internalsMap.get(this); + if (internals === undefined) { + throwInvalidThisError(TypeError, 'DOMException'); + } + return internals.cause; + } + get name() { const internals = internalsMap.get(this); if (internals === undefined) { diff --git a/lib/internal/util.js b/lib/internal/util.js index 843c1a6cae4483c..89acb75758c1f6f 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -498,9 +498,9 @@ const lazyDOMExceptionClass = () => { return _DOMException; }; -const lazyDOMException = hideStackFrames((message, name) => { +const lazyDOMException = hideStackFrames((message, name, cause) => { _DOMException ??= internalBinding('messaging').DOMException; - return new _DOMException(message, name); + return new _DOMException(message, name, cause); }); const kEnumerableProperty = ObjectCreate(null); diff --git a/test/parallel/test-webcrypto-encrypt-decrypt-aes.js b/test/parallel/test-webcrypto-encrypt-decrypt-aes.js index 5dfae1c5d55385d..085e1e088a1d1b9 100644 --- a/test/parallel/test-webcrypto-encrypt-decrypt-aes.js +++ b/test/parallel/test-webcrypto-encrypt-decrypt-aes.js @@ -118,8 +118,11 @@ async function testDecrypt({ keyBuffer, algorithm, result }) { }); decryptionFailing.forEach((vector) => { - variations.push(assert.rejects(testDecrypt(vector), { - name: 'OperationError' + variations.push(assert.rejects(testDecrypt(vector), (err) => { + assert.strictEqual(err.name, 'OperationError'); + assert.ok(err.cause instanceof Error); + assert.match(err.cause?.message, /bad decrypt/); + return true; })); }); @@ -157,8 +160,11 @@ async function testDecrypt({ keyBuffer, algorithm, result }) { }); decryptionFailing.forEach((vector) => { - variations.push(assert.rejects(testDecrypt(vector), { - name: 'OperationError' + variations.push(assert.rejects(testDecrypt(vector), (err) => { + assert.strictEqual(err.name, 'OperationError'); + assert.ok(err.cause instanceof Error); + assert.match(err.cause?.message, /foo/); + return true; })); }); @@ -194,8 +200,11 @@ async function testDecrypt({ keyBuffer, algorithm, result }) { }); decryptionFailing.forEach((vector) => { - variations.push(assert.rejects(testDecrypt(vector), { - name: 'OperationError' + variations.push(assert.rejects(testDecrypt(vector), (err) => { + assert.strictEqual(err.name, 'OperationError'); + assert.ok(err.cause instanceof Error); + assert.match(err.cause?.message, /foo/); + return true; })); }); diff --git a/test/parallel/test-webcrypto-encrypt-decrypt-rsa.js b/test/parallel/test-webcrypto-encrypt-decrypt-rsa.js index 6af0fa727969d89..6cfe4cd27d6ecc0 100644 --- a/test/parallel/test-webcrypto-encrypt-decrypt-rsa.js +++ b/test/parallel/test-webcrypto-encrypt-decrypt-rsa.js @@ -126,8 +126,11 @@ async function testEncryptionLongPlaintext({ algorithm, newplaintext[plaintext.byteLength] = 32; return assert.rejects( - subtle.encrypt(algorithm, publicKey, newplaintext), { - name: 'OperationError' + subtle.encrypt(algorithm, publicKey, newplaintext), (err) => { + assert.strictEqual(err.name, 'OperationError'); + assert.ok(err.cause instanceof Error); + assert.match(err.cause?.message, /data too large for key size/); + return true; }); } diff --git a/test/parallel/test-webcrypto-keygen.js b/test/parallel/test-webcrypto-keygen.js index 5acea2debdd2928..71413acfed5186f 100644 --- a/test/parallel/test-webcrypto-keygen.js +++ b/test/parallel/test-webcrypto-keygen.js @@ -377,8 +377,11 @@ const vectors = { modulusLength, publicExponent: new Uint8Array(publicExponent), hash - }, true, usages), { - name: 'OperationError', + }, true, usages), (err) => { + assert.strictEqual(err.name, 'OperationError'); + assert.ok(err.cause instanceof Error); + assert.match(err.cause?.message, /pub exponent out of range/); + return true; }); })); }