diff --git a/deps/ada/ada.cpp b/deps/ada/ada.cpp index 732a5245ef0426..6c2db6f9e30a5c 100644 --- a/deps/ada/ada.cpp +++ b/deps/ada/ada.cpp @@ -1,4 +1,4 @@ -/* auto-generated on 2026-01-30 13:29:04 -0500. Do not edit! */ +/* auto-generated on 2026-02-23 21:29:24 -0500. Do not edit! */ /* begin file src/ada.cpp */ #include "ada.h" /* begin file src/checkers.cpp */ @@ -14495,6 +14495,12 @@ bool url_aggregator::set_pathname(const std::string_view input) { if (get_pathname().starts_with("//") && !has_authority() && !has_dash_dot()) { buffer.insert(components.pathname_start, "/."); components.pathname_start += 2; + if (components.search_start != url_components::omitted) { + components.search_start += 2; + } + if (components.hash_start != url_components::omitted) { + components.hash_start += 2; + } } ADA_ASSERT_TRUE(validate()); return true; diff --git a/deps/ada/ada.h b/deps/ada/ada.h index 898c7dd3042618..1210d7ddb7a123 100644 --- a/deps/ada/ada.h +++ b/deps/ada/ada.h @@ -1,4 +1,4 @@ -/* auto-generated on 2026-01-30 13:29:04 -0500. Do not edit! */ +/* auto-generated on 2026-02-23 21:29:24 -0500. Do not edit! */ /* begin file include/ada.h */ /** * @file ada.h @@ -8091,6 +8091,12 @@ inline void url_aggregator::update_base_pathname(const std::string_view input) { // output. buffer.insert(components.pathname_start, "/."); components.pathname_start += 2; + if (components.search_start != url_components::omitted) { + components.search_start += 2; + } + if (components.hash_start != url_components::omitted) { + components.hash_start += 2; + } } uint32_t difference = replace_and_resize( @@ -9530,13 +9536,14 @@ url_pattern_component::create_component_match_result( auto result = url_pattern_component_result{.input = std::move(input), .groups = {}}; - // Optimization: Let's reserve the size. - result.groups.reserve(exec_result.size()); - // We explicitly start iterating from 0 even though the spec // says we should start from 1. This case is handled by the - // std_regex_provider. - for (size_t index = 0; index < exec_result.size(); index++) { + // std_regex_provider which removes the full match from index 0. + // Use min() to guard against potential mismatches between + // exec_result size and group_name_list size. + const size_t size = std::min(exec_result.size(), group_name_list.size()); + result.groups.reserve(size); + for (size_t index = 0; index < size; index++) { result.groups.emplace(group_name_list[index], std::move(exec_result[index])); } @@ -11221,14 +11228,14 @@ constructor_string_parser::parse(std::string_view input) { #ifndef ADA_ADA_VERSION_H #define ADA_ADA_VERSION_H -#define ADA_VERSION "3.4.2" +#define ADA_VERSION "3.4.3" namespace ada { enum { ADA_VERSION_MAJOR = 3, ADA_VERSION_MINOR = 4, - ADA_VERSION_REVISION = 2, + ADA_VERSION_REVISION = 3, }; } // namespace ada diff --git a/doc/api/cli.md b/doc/api/cli.md index 5e34051f1ce0e3..a8baeabd95cb24 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -3742,6 +3742,7 @@ V8 options that are allowed are: * `--expose-gc` * `--interpreted-frames-native-stack` * `--jitless` +* `--max-heap-size` * `--max-old-space-size` * `--max-semi-space-size` * `--perf-basic-prof-only-functions` @@ -4102,6 +4103,12 @@ documented here: ### `--jitless` +### `--max-heap-size` + +Specifies the maximum heap size (in megabytes) for the process. + +This option is typically used to limit the amount of memory the process can use for its JavaScript heap. + diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index 186cb2381935cc..892ecb9f2b4fc6 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -2665,11 +2665,15 @@ future release. +# Index diff --git a/doc/api/url.md b/doc/api/url.md index 4cea4b15059098..d5dc06055b6cc3 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -1839,7 +1839,7 @@ changes: `url.format(urlString)` is shorthand for `url.format(url.parse(urlString))`. -Because it invokes the deprecated [`url.parse()`][], passing a string argument +Because it invokes the deprecated [`url.parse()`][] internally, passing a string argument to `url.format()` is itself deprecated. Canonicalizing a URL string can be performed using the WHATWG URL API, by @@ -1956,6 +1956,10 @@ npx codemod@latest @nodejs/node-url-to-whatwg-url +> Stability: 0 - Deprecated: Use the WHATWG URL API instead. + * `from` {string} The base URL to use if `to` is a relative URL. * `to` {string} The target URL to resolve. @@ -1992,6 +1998,8 @@ url.resolve('http://example.com/', '/one'); // 'http://example.com/one' url.resolve('http://example.com/one', '/two'); // 'http://example.com/two' ``` +Because it invokes the deprecated [`url.parse()`][] internally, `url.resolve()` is itself deprecated. + To achieve the same result using the WHATWG URL API: ```js diff --git a/lib/internal/webstreams/adapters.js b/lib/internal/webstreams/adapters.js index ed9847f250f1ca..83265227a917a9 100644 --- a/lib/internal/webstreams/adapters.js +++ b/lib/internal/webstreams/adapters.js @@ -53,6 +53,10 @@ const { Buffer, } = require('buffer'); +const { + isArrayBuffer, +} = require('internal/util/types'); + const { AbortError, ErrnoException, @@ -213,6 +217,9 @@ function newWritableStreamFromStreamWritable(streamWritable) { start(c) { controller = c; }, write(chunk) { + if (!streamWritable.writableObjectMode && isArrayBuffer(chunk)) { + chunk = new Uint8Array(chunk); + } if (streamWritable.writableNeedDrain || !streamWritable.write(chunk)) { backpressurePromise = PromiseWithResolvers(); return SafePromisePrototypeFinally( diff --git a/src/node_buffer.cc b/src/node_buffer.cc index d4a63cf610ca7f..e40a21288ee79d 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -1252,7 +1252,8 @@ static void IsAscii(const FunctionCallbackInfo& args) { env, "Cannot validate on a detached buffer"); } - args.GetReturnValue().Set(simdutf::validate_ascii(abv.data(), abv.length())); + args.GetReturnValue().Set( + !simdutf::validate_ascii_with_errors(abv.data(), abv.length()).error); } void SetBufferPrototype(const FunctionCallbackInfo& args) { diff --git a/src/node_options.cc b/src/node_options.cc index 82b802cf552e60..526ac0d05e2763 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -1207,6 +1207,7 @@ PerIsolateOptionsParser::PerIsolateOptionsParser( "help system profilers to translate JavaScript interpreted frames", V8Option{}, kAllowedInEnvvar); + AddOption("--max-heap-size", "", V8Option{}, kAllowedInEnvvar); AddOption("--max-old-space-size", "", V8Option{}, kAllowedInEnvvar); AddOption("--max-old-space-size-percentage", "set V8's max old space size as a percentage of available memory " diff --git a/src/node_options.h b/src/node_options.h index 8bbac8d692807e..e617cafd0147d8 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -262,7 +262,6 @@ class EnvironmentOptions : public Options { std::vector preload_esm_modules; bool strip_types = HAVE_AMARO; - bool experimental_transform_types = false; std::vector user_argv; diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index 12b8fdcdebe04b..98612f39695897 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -2421,6 +2421,11 @@ inline bool StatementSync::IsFinalized() { return statement_ == nullptr; } +inline int StatementSync::ResetStatement() { + reset_generation_++; + return sqlite3_reset(statement_); +} + bool StatementSync::BindParams(const FunctionCallbackInfo& args) { int r = sqlite3_clear_bindings(statement_); CHECK_ERROR_OR_THROW(env()->isolate(), db_.get(), r, SQLITE_OK, false); @@ -2812,7 +2817,7 @@ void StatementSync::All(const FunctionCallbackInfo& args) { THROW_AND_RETURN_ON_BAD_STATE( env, stmt->IsFinalized(), "statement has been finalized"); Isolate* isolate = env->isolate(); - int r = sqlite3_reset(stmt->statement_); + int r = stmt->ResetStatement(); CHECK_ERROR_OR_THROW(isolate, stmt->db_.get(), r, SQLITE_OK, void()); if (!stmt->BindParams(args)) { @@ -2838,7 +2843,7 @@ void StatementSync::Iterate(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); THROW_AND_RETURN_ON_BAD_STATE( env, stmt->IsFinalized(), "statement has been finalized"); - int r = sqlite3_reset(stmt->statement_); + int r = stmt->ResetStatement(); CHECK_ERROR_OR_THROW(env->isolate(), stmt->db_.get(), r, SQLITE_OK, void()); if (!stmt->BindParams(args)) { @@ -2861,7 +2866,7 @@ void StatementSync::Get(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); THROW_AND_RETURN_ON_BAD_STATE( env, stmt->IsFinalized(), "statement has been finalized"); - int r = sqlite3_reset(stmt->statement_); + int r = stmt->ResetStatement(); CHECK_ERROR_OR_THROW(env->isolate(), stmt->db_.get(), r, SQLITE_OK, void()); if (!stmt->BindParams(args)) { @@ -2885,7 +2890,7 @@ void StatementSync::Run(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); THROW_AND_RETURN_ON_BAD_STATE( env, stmt->IsFinalized(), "statement has been finalized"); - int r = sqlite3_reset(stmt->statement_); + int r = stmt->ResetStatement(); CHECK_ERROR_OR_THROW(env->isolate(), stmt->db_.get(), r, SQLITE_OK, void()); if (!stmt->BindParams(args)) { @@ -3430,6 +3435,7 @@ StatementSyncIterator::StatementSyncIterator(Environment* env, : BaseObject(env, object), stmt_(std::move(stmt)) { MakeWeak(); done_ = false; + statement_reset_generation_ = stmt_->reset_generation_; } StatementSyncIterator::~StatementSyncIterator() {} @@ -3488,6 +3494,11 @@ void StatementSyncIterator::Next(const FunctionCallbackInfo& args) { return; } + THROW_AND_RETURN_ON_BAD_STATE( + env, + iter->statement_reset_generation_ != iter->stmt_->reset_generation_, + "iterator was invalidated"); + int r = sqlite3_step(iter->stmt_->statement_); if (r != SQLITE_ROW) { CHECK_ERROR_OR_THROW( diff --git a/src/node_sqlite.h b/src/node_sqlite.h index 9b8fcec2699379..3ee79cc10ec562 100644 --- a/src/node_sqlite.h +++ b/src/node_sqlite.h @@ -291,7 +291,9 @@ class StatementSync : public BaseObject { bool use_big_ints_; bool allow_bare_named_params_; bool allow_unknown_named_params_; + uint64_t reset_generation_ = 0; std::optional> bare_named_params_; + inline int ResetStatement(); bool BindParams(const v8::FunctionCallbackInfo& args); bool BindValue(const v8::Local& value, const int index); @@ -321,6 +323,7 @@ class StatementSyncIterator : public BaseObject { ~StatementSyncIterator() override; BaseObjectPtr stmt_; bool done_; + uint64_t statement_reset_generation_; }; using Sqlite3ChangesetGenFunc = int (*)(sqlite3_session*, int*, void**); diff --git a/test/common/v8-max-heap-size-option.js b/test/common/v8-max-heap-size-option.js new file mode 100644 index 00000000000000..4487b011a97cdf --- /dev/null +++ b/test/common/v8-max-heap-size-option.js @@ -0,0 +1,49 @@ +'use strict'; + +const assert = require('assert'); +const { spawnSync } = require('child_process'); +const path = require('path'); +const fs = require('fs'); +const tmpdir = require('./tmpdir'); + +const testScript = ` + const v8 = require('v8'); + const stats = v8.getHeapStatistics(); + const maxHeapSizeMB = Math.round(stats.heap_size_limit / 1024 / 1024); + console.log(maxHeapSizeMB); +`; + +tmpdir.refresh(); +const scriptPath = path.join(tmpdir.path, 'heap-limit-test.js'); +fs.writeFileSync(scriptPath, testScript); + +const child = spawnSync( + process.execPath, + [scriptPath], + { + encoding: 'utf8', + env: { + ...process.env, + NODE_OPTIONS: '--max-heap-size=750', + }, + }, +); + +assert.strictEqual( + child.status, + 0, + [ + `Child process did not exit cleanly.`, + ` Exit code: ${child.status}`, + child.stderr ? ` Stderr: ${child.stderr.toString()}` : '', + child.stdout ? ` Stdout: ${child.stdout.toString()}` : '', + ].filter(Boolean).join('\n'), +); +const output = child.stdout.trim(); +const heapLimit = Number(output); + +assert.strictEqual( + heapLimit, + 750, + `max heap size is ${heapLimit}MB, expected 750MB`, +); diff --git a/test/fixtures/wpt/README.md b/test/fixtures/wpt/README.md index 2ce7de20ded95f..20c7df576e73b6 100644 --- a/test/fixtures/wpt/README.md +++ b/test/fixtures/wpt/README.md @@ -34,7 +34,7 @@ Last update: - wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/cde25e7e3c/wasm/jsapi - wasm/webapi: https://github.com/web-platform-tests/wpt/tree/fd1b23eeaa/wasm/webapi - web-locks: https://github.com/web-platform-tests/wpt/tree/10a122a6bc/web-locks -- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/7cbe7e8ed9/WebCryptoAPI +- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/42e47329fd/WebCryptoAPI - webidl: https://github.com/web-platform-tests/wpt/tree/63ca529a02/webidl - webidl/ecmascript-binding/es-exceptions: https://github.com/web-platform-tests/wpt/tree/2f96fa1996/webidl/ecmascript-binding/es-exceptions - webmessaging/broadcastchannel: https://github.com/web-platform-tests/wpt/tree/6495c91853/webmessaging/broadcastchannel diff --git a/test/fixtures/wpt/WebCryptoAPI/digest/cshake.tentative.https.any.js b/test/fixtures/wpt/WebCryptoAPI/digest/cshake.tentative.https.any.js index e793722d8be598..d5f790e42b4982 100644 --- a/test/fixtures/wpt/WebCryptoAPI/digest/cshake.tentative.https.any.js +++ b/test/fixtures/wpt/WebCryptoAPI/digest/cshake.tentative.https.any.js @@ -171,26 +171,42 @@ Object.keys(digestedData).forEach(function (alg) { }); }, alg + ' with ' + length + ' bit output and ' + size + ' source data'); - promise_test(function (test) { - var buffer = new Uint8Array(sourceData[size]); - return crypto.subtle - .digest({ name: alg, length: length }, buffer) - .then(function (result) { - // Alter the buffer after calling digest - if (buffer.length > 0) { + if (sourceData[size].length > 0) { + promise_test(function (test) { + var buffer = new Uint8Array(sourceData[size]); + // Alter the buffer before calling digest + buffer[0] = ~buffer[0]; + return crypto.subtle + .digest({ + get name() { + // Alter the buffer back while calling digest + buffer[0] = sourceData[size][0]; + return alg; + }, + length + }, buffer) + .then(function (result) { + assert_true( + equalBuffers(result, digestedData[alg][length][size]), + 'digest matches expected' + ); + }); + }, alg + ' with ' + length + ' bit output and ' + size + ' source data and altered buffer during call'); + + promise_test(function (test) { + var buffer = new Uint8Array(sourceData[size]); + return crypto.subtle + .digest({ name: alg, length: length }, buffer) + .then(function (result) { + // Alter the buffer after calling digest buffer[0] = ~buffer[0]; - } - assert_true( - equalBuffers(result, digestedData[alg][length][size]), - 'digest matches expected' - ); - }); - }, alg + - ' with ' + - length + - ' bit output and ' + - size + - ' source data and altered buffer after call'); + assert_true( + equalBuffers(result, digestedData[alg][length][size]), + 'digest matches expected' + ); + }); + }, alg + ' with ' + length + ' bit output and ' + size + ' source data and altered buffer after call'); + } }); }); }); diff --git a/test/fixtures/wpt/WebCryptoAPI/digest/digest.https.any.js b/test/fixtures/wpt/WebCryptoAPI/digest/digest.https.any.js index 3b0972b1f2bf7d..e0c85f8f6b30e4 100644 --- a/test/fixtures/wpt/WebCryptoAPI/digest/digest.https.any.js +++ b/test/fixtures/wpt/WebCryptoAPI/digest/digest.https.any.js @@ -83,19 +83,37 @@ return promise; }, mixedCase + " with " + size + " source data"); - promise_test(function(test) { - var copiedBuffer = copyBuffer(sourceData[size]); - var promise = subtle.digest({name: upCase}, copiedBuffer) - .then(function(result) { - assert_true(equalBuffers(result, digestedData[alg][size]), "digest() yielded expected result for " + alg + ":" + size); - }, function(err) { - assert_unreached("digest() threw an error for " + alg + ":" + size + " - " + err.message); - }); - - copiedBuffer[0] = 255 - copiedBuffer; - return promise; - }, upCase + " with " + size + " source data and altered buffer after call"); - + if (sourceData[size].length > 0) { + promise_test(function(test) { + var copiedBuffer = copyBuffer(sourceData[size]); + copiedBuffer[0] = 255 - copiedBuffer[0]; + var promise = subtle.digest({ + get name() { + copiedBuffer[0] = sourceData[size][0]; + return upCase; + } + }, copiedBuffer) + .then(function(result) { + assert_true(equalBuffers(result, digestedData[alg][size]), "digest() yielded expected result for " + alg + ":" + size); + }, function(err) { + assert_unreached("digest() threw an error for " + alg + ":" + size + " - " + err.message); + }); + return promise; + }, upCase + " with " + size + " source data and altered buffer during call"); + + promise_test(function(test) { + var copiedBuffer = copyBuffer(sourceData[size]); + var promise = subtle.digest({name: upCase}, copiedBuffer) + .then(function(result) { + assert_true(equalBuffers(result, digestedData[alg][size]), "digest() yielded expected result for " + alg + ":" + size); + }, function(err) { + assert_unreached("digest() threw an error for " + alg + ":" + size + " - " + err.message); + }); + + copiedBuffer[0] = 255 - copiedBuffer[0]; + return promise; + }, upCase + " with " + size + " source data and altered buffer after call"); + } }); }); diff --git a/test/fixtures/wpt/WebCryptoAPI/digest/sha3.tentative.https.any.js b/test/fixtures/wpt/WebCryptoAPI/digest/sha3.tentative.https.any.js index 8f84ad08ca1a25..fc33608e07ab14 100644 --- a/test/fixtures/wpt/WebCryptoAPI/digest/sha3.tentative.https.any.js +++ b/test/fixtures/wpt/WebCryptoAPI/digest/sha3.tentative.https.any.js @@ -109,19 +109,39 @@ Object.keys(sourceData).forEach(function (size) { }); }, alg + ' with ' + size + ' source data'); - promise_test(function (test) { - var buffer = new Uint8Array(sourceData[size]); - return crypto.subtle.digest(alg, buffer).then(function (result) { - // Alter the buffer after calling digest - if (buffer.length > 0) { + if (sourceData[size].length > 0) { + promise_test(function (test) { + var buffer = new Uint8Array(sourceData[size]); + // Alter the buffer before calling digest + buffer[0] = ~buffer[0]; + return crypto.subtle + .digest({ + get name() { + // Alter the buffer back while calling digest + buffer[0] = sourceData[size][0]; + return alg; + } + }, buffer) + .then(function (result) { + assert_true( + equalBuffers(result, digestedData[alg][size]), + 'digest matches expected' + ); + }); + }, alg + ' with ' + size + ' source data and altered buffer during call'); + + promise_test(function (test) { + var buffer = new Uint8Array(sourceData[size]); + return crypto.subtle.digest(alg, buffer).then(function (result) { + // Alter the buffer after calling digest buffer[0] = ~buffer[0]; - } - assert_true( - equalBuffers(result, digestedData[alg][size]), - 'digest matches expected' - ); - }); - }, alg + ' with ' + size + ' source data and altered buffer after call'); + assert_true( + equalBuffers(result, digestedData[alg][size]), + 'digest matches expected' + ); + }); + }, alg + ' with ' + size + ' source data and altered buffer after call'); + } }); }); diff --git a/test/fixtures/wpt/WebCryptoAPI/encrypt_decrypt/aes.js b/test/fixtures/wpt/WebCryptoAPI/encrypt_decrypt/aes.js index d93b60f8b87569..b157a94a0dc4fb 100644 --- a/test/fixtures/wpt/WebCryptoAPI/encrypt_decrypt/aes.js +++ b/test/fixtures/wpt/WebCryptoAPI/encrypt_decrypt/aes.js @@ -35,6 +35,38 @@ function run_test() { all_promises.push(promise); }); + // Check for successful encryption even if the buffer is changed while calling encrypt. + passingVectors.forEach(function(vector) { + var plaintext = copyBuffer(vector.plaintext); + plaintext[0] = 255 - plaintext[0]; + var promise = importVectorKey(vector, ["encrypt", "decrypt"]) + .then(function(vector) { + promise_test(function(test) { + var operation = subtle.encrypt({ + ...vector.algorithm, + get name() { + plaintext[0] = vector.plaintext[0]; + return vector.algorithm.name; + } + }, vector.key, plaintext) + .then(function(result) { + assert_true(equalBuffers(result, vector.result), "Should return expected result"); + }, function(err) { + assert_unreached("encrypt error for test " + vector.name + ": " + err.message); + }); + return operation; + }, vector.name + " with altered plaintext during call"); + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importKey failed for " + vector.name); + }, "importKey step: " + vector.name + " with altered plaintext during call"); + }); + + all_promises.push(promise); + }); + // Check for successful encryption even if the buffer is changed after calling encrypt. passingVectors.forEach(function(vector) { var plaintext = copyBuffer(vector.plaintext); @@ -49,13 +81,13 @@ function run_test() { }); plaintext[0] = 255 - plaintext[0]; return operation; - }, vector.name + " with altered plaintext"); + }, vector.name + " with altered plaintext after call"); }, function(err) { // We need a failed test if the importVectorKey operation fails, so // we know we never tested encryption promise_test(function(test) { assert_unreached("importKey failed for " + vector.name); - }, "importKey step: " + vector.name + " with altered plaintext"); + }, "importKey step: " + vector.name + " with altered plaintext after call"); }); all_promises.push(promise); @@ -84,7 +116,39 @@ function run_test() { all_promises.push(promise); }); - // Check for successful decryption even if ciphertext is altered. + // Check for successful decryption even if ciphertext is altered while calling encrypt. + passingVectors.forEach(function(vector) { + var ciphertext = copyBuffer(vector.result); + ciphertext[0] = 255 - ciphertext[0]; + var promise = importVectorKey(vector, ["encrypt", "decrypt"]) + .then(function(vector) { + promise_test(function(test) { + var operation = subtle.decrypt({ + ...vector.algorithm, + get name() { + ciphertext[0] = vector.result[0]; + return vector.algorithm.name; + } + }, vector.key, ciphertext) + .then(function(result) { + assert_true(equalBuffers(result, vector.plaintext), "Should return expected result"); + }, function(err) { + assert_unreached("decrypt error for test " + vector.name + ": " + err.message); + }); + return operation; + }, vector.name + " decryption with altered ciphertext during call"); + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importKey failed for " + vector.name); + }, "importKey step for decryption: " + vector.name + " with altered ciphertext during call"); + }); + + all_promises.push(promise); + }); + + // Check for successful decryption even if ciphertext is altered after calling encrypt. passingVectors.forEach(function(vector) { var ciphertext = copyBuffer(vector.result); var promise = importVectorKey(vector, ["encrypt", "decrypt"]) @@ -98,13 +162,13 @@ function run_test() { }); ciphertext[0] = 255 - ciphertext[0]; return operation; - }, vector.name + " decryption with altered ciphertext"); + }, vector.name + " decryption with altered ciphertext after call"); }, function(err) { // We need a failed test if the importVectorKey operation fails, so // we know we never tested encryption promise_test(function(test) { assert_unreached("importKey failed for " + vector.name); - }, "importKey step for decryption: " + vector.name + " with altered ciphertext"); + }, "importKey step for decryption: " + vector.name + " with altered ciphertext after call"); }); all_promises.push(promise); diff --git a/test/fixtures/wpt/WebCryptoAPI/encrypt_decrypt/rsa.js b/test/fixtures/wpt/WebCryptoAPI/encrypt_decrypt/rsa.js index 5eae06e474564a..76c90809783205 100644 --- a/test/fixtures/wpt/WebCryptoAPI/encrypt_decrypt/rsa.js +++ b/test/fixtures/wpt/WebCryptoAPI/encrypt_decrypt/rsa.js @@ -40,6 +40,44 @@ function run_test() { all_promises.push(promise); }); + // Test decryption with an altered buffer during call + passingVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) + .then(function(vectors) { + // Get a one byte longer plaintext to encrypt + if (!("ciphertext" in vector)) { + return; + } + + promise_test(function(test) { + var ciphertext = copyBuffer(vector.ciphertext); + ciphertext[0] = 255 - ciphertext[0]; + var operation = subtle.decrypt({ + ...vector.algorithm, + get name() { + ciphertext[0] = vector.ciphertext[0]; + return vector.algorithm.name; + } + }, vector.privateKey, ciphertext) + .then(function(plaintext) { + assert_true(equalBuffers(plaintext, vector.plaintext, "Decryption works")); + }, function(err) { + assert_unreached("Decryption should not throw error " + vector.name + ": " + err.message + "'"); + }); + return operation; + }, vector.name + " decryption with altered ciphertext during call"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " decryption with altered ciphertext during call"); + }); + + all_promises.push(promise); + }); + // Test decryption with an altered buffer passingVectors.forEach(function(vector) { var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) @@ -59,14 +97,14 @@ function run_test() { }); ciphertext[0] = 255 - ciphertext[0]; return operation; - }, vector.name + " decryption with altered ciphertext"); + }, vector.name + " decryption with altered ciphertext after call"); }, function(err) { // We need a failed test if the importVectorKey operation fails, so // we know we never tested encryption promise_test(function(test) { assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); - }, "importVectorKeys step: " + vector.name + " decryption with altered ciphertext"); + }, "importVectorKeys step: " + vector.name + " decryption with altered ciphertext after call"); }); all_promises.push(promise); @@ -125,6 +163,57 @@ function run_test() { }); + // Check for successful encryption even if plaintext is altered during call. + passingVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) + .then(function(vectors) { + promise_test(function(test) { + var plaintext = copyBuffer(vector.plaintext); + plaintext[0] = 255 - plaintext[0]; + var operation = subtle.encrypt({ + ...vector.algorithm, + get name() { + plaintext[0] = vector.plaintext[0]; + return vector.algorithm.name; + } + }, vector.publicKey, plaintext) + .then(function(ciphertext) { + assert_equals(ciphertext.byteLength * 8, vector.privateKey.algorithm.modulusLength, "Ciphertext length matches modulus length"); + // Can we get the original plaintext back via decrypt? + return subtle.decrypt(vector.algorithm, vector.privateKey, ciphertext) + .then(function(result) { + assert_true(equalBuffers(result, vector.plaintext), "Round trip returns original plaintext"); + return ciphertext; + }, function(err) { + assert_unreached("decrypt error for test " + vector.name + ": " + err.message + "'"); + }); + }) + .then(function(priorCiphertext) { + // Will a second encrypt give us different ciphertext, as it should? + return subtle.encrypt(vector.algorithm, vector.publicKey, vector.plaintext) + .then(function(ciphertext) { + assert_false(equalBuffers(priorCiphertext, ciphertext), "Two encrypts give different results") + }, function(err) { + assert_unreached("second time encrypt error for test " + vector.name + ": '" + err.message + "'"); + }); + }, function(err) { + assert_unreached("decrypt error for test " + vector.name + ": '" + err.message + "'"); + }); + + return operation; + }, vector.name + " with altered plaintext during call"); + + }, function(err) { + // We need a failed test if the importVectorKey operation fails, so + // we know we never tested encryption + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " with altered plaintext during call"); + }); + + all_promises.push(promise); + }); + // Check for successful encryption even if plaintext is altered after call. passingVectors.forEach(function(vector) { var promise = importVectorKeys(vector, ["encrypt"], ["decrypt"]) @@ -157,14 +246,14 @@ function run_test() { plaintext[0] = 255 - plaintext[0]; return operation; - }, vector.name + " with altered plaintext"); + }, vector.name + " with altered plaintext after call"); }, function(err) { // We need a failed test if the importVectorKey operation fails, so // we know we never tested encryption promise_test(function(test) { assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); - }, "importVectorKeys step: " + vector.name + " with altered plaintext"); + }, "importVectorKeys step: " + vector.name + " with altered plaintext after call"); }); all_promises.push(promise); diff --git a/test/fixtures/wpt/WebCryptoAPI/sign_verify/ecdsa.js b/test/fixtures/wpt/WebCryptoAPI/sign_verify/ecdsa.js index 6bf662adcc547f..9b47868fe32bfb 100644 --- a/test/fixtures/wpt/WebCryptoAPI/sign_verify/ecdsa.js +++ b/test/fixtures/wpt/WebCryptoAPI/sign_verify/ecdsa.js @@ -39,6 +39,38 @@ function run_test() { all_promises.push(promise); }); + // Test verification with an altered buffer during call + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + promise_test(function(test) { + var signature = copyBuffer(vector.signature); + signature[0] = 255 - signature[0]; + var algorithm = { + hash: vector.hashName, + get name() { + signature[0] = vector.signature[0]; + return vector.algorithmName; + } + }; + var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Signature verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification with altered signature during call"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification with altered signature during call"); + }); + + all_promises.push(promise); + }); + // Test verification with an altered buffer after call testVectors.forEach(function(vector) { var promise = importVectorKeys(vector, ["verify"], ["sign"]) @@ -65,6 +97,38 @@ function run_test() { all_promises.push(promise); }); + // Check for successful verification even if plaintext is altered during call. + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + promise_test(function(test) { + var plaintext = copyBuffer(vector.plaintext); + plaintext[0] = 255 - plaintext[0]; + var algorithm = { + hash: vector.hashName, + get name() { + plaintext[0] = vector.plaintext[0]; + return vector.algorithmName; + } + }; + var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Signature verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " with altered plaintext during call"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " with altered plaintext during call"); + }); + + all_promises.push(promise); + }); + // Check for successful verification even if plaintext is altered after call. testVectors.forEach(function(vector) { var promise = importVectorKeys(vector, ["verify"], ["sign"]) diff --git a/test/fixtures/wpt/WebCryptoAPI/sign_verify/eddsa.js b/test/fixtures/wpt/WebCryptoAPI/sign_verify/eddsa.js index 4024674e3c3d7f..4bf1887b2ae9bb 100644 --- a/test/fixtures/wpt/WebCryptoAPI/sign_verify/eddsa.js +++ b/test/fixtures/wpt/WebCryptoAPI/sign_verify/eddsa.js @@ -23,6 +23,27 @@ function run_test(algorithmName) { assert_true(isVerified, "Signature verified"); }, vector.name + " verification"); + // Test verification with an altered buffer during call + promise_test(async() => { + let isVerified = false; + let key; + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); + var signature = copyBuffer(vector.signature); + signature[0] = 255 - signature[0]; + isVerified = await subtle.verify({ + get name() { + signature[0] = vector.signature[0]; + return vector.algorithmName; + } + }, key, signature, vector.data); + } catch (err) { + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }; + assert_true(isVerified, "Signature verified"); + }, vector.name + " verification with altered signature during call"); + // Test verification with an altered buffer after call promise_test(async() => { let isVerified = false; @@ -41,6 +62,27 @@ function run_test(algorithmName) { assert_true(isVerified, "Signature verified"); }, vector.name + " verification with altered signature after call"); + // Check for successful verification even if data is altered during call. + promise_test(async() => { + let isVerified = false; + let key; + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); + var data = copyBuffer(vector.data); + data[0] = 255 - data[0]; + isVerified = await subtle.verify({ + get name() { + data[0] = vector.data[0]; + return vector.algorithmName; + } + }, key, vector.signature, data); + } catch (err) { + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }; + assert_true(isVerified, "Signature verified"); + }, vector.name + " with altered data during call"); + // Check for successful verification even if data is altered after call. promise_test(async() => { let isVerified = false; diff --git a/test/fixtures/wpt/WebCryptoAPI/sign_verify/hmac.js b/test/fixtures/wpt/WebCryptoAPI/sign_verify/hmac.js index f5e2ad2769cdd8..dff8c994d83de0 100644 --- a/test/fixtures/wpt/WebCryptoAPI/sign_verify/hmac.js +++ b/test/fixtures/wpt/WebCryptoAPI/sign_verify/hmac.js @@ -37,6 +37,37 @@ function run_test() { all_promises.push(promise); }); + // Test verification with an altered buffer during call + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify", "sign"]) + .then(function(vector) { + promise_test(function(test) { + var signature = copyBuffer(vector.signature); + signature[0] = 255 - signature[0]; + var operation = subtle.verify({ + hash: vector.hash, + get name() { + signature[0] = vector.signature[0]; + return "HMAC"; + } + }, vector.key, signature, vector.plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Signature is not verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification with altered signature during call"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification with altered signature during call"); + }); + + all_promises.push(promise); + }); + // Test verification with an altered buffer after call testVectors.forEach(function(vector) { var promise = importVectorKeys(vector, ["verify", "sign"]) @@ -62,6 +93,37 @@ function run_test() { all_promises.push(promise); }); + // Check for successful verification even if plaintext is altered during call. + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify", "sign"]) + .then(function(vector) { + promise_test(function(test) { + var plaintext = copyBuffer(vector.plaintext); + plaintext[0] = 255 - plaintext[0]; + var operation = subtle.verify({ + hash: vector.hash, + get name() { + plaintext[0] = vector.plaintext[0]; + return "HMAC"; + } + }, vector.key, vector.signature, plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Signature verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " with altered plaintext during call"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " with altered plaintext during call"); + }); + + all_promises.push(promise); + }); + // Check for successful verification even if plaintext is altered after call. testVectors.forEach(function(vector) { var promise = importVectorKeys(vector, ["verify", "sign"]) @@ -81,7 +143,7 @@ function run_test() { }, function(err) { promise_test(function(test) { assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); - }, "importVectorKeys step: " + vector.name + " with altered plaintext"); + }, "importVectorKeys step: " + vector.name + " with altered plaintext after call"); }); all_promises.push(promise); diff --git a/test/fixtures/wpt/WebCryptoAPI/sign_verify/kmac.js b/test/fixtures/wpt/WebCryptoAPI/sign_verify/kmac.js index 037dbec1097ddc..57c9dc6f34790a 100644 --- a/test/fixtures/wpt/WebCryptoAPI/sign_verify/kmac.js +++ b/test/fixtures/wpt/WebCryptoAPI/sign_verify/kmac.js @@ -40,6 +40,41 @@ function run_test() { all_promises.push(promise); }); + // Test verification with an altered buffer during call + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify", "sign"]) + .then(function(vector) { + promise_test(function(test) { + var signature = copyBuffer(vector.signature); + signature[0] = 255 - signature[0]; + var algorithmParams = { + length: vector.length, + get name() { + signature[0] = vector.signature[0]; + return vector.algorithm; + } + }; + if (vector.customization !== undefined) { + algorithmParams.customization = vector.customization; + } + var operation = subtle.verify(algorithmParams, vector.key, signature, vector.plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Signature is not verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification with altered signature during call"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification with altered signature during call"); + }); + + all_promises.push(promise); + }); + // Test verification with an altered buffer after call testVectors.forEach(function(vector) { var promise = importVectorKeys(vector, ["verify", "sign"]) @@ -69,6 +104,41 @@ function run_test() { all_promises.push(promise); }); + // Check for successful verification even if plaintext is altered during call. + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify", "sign"]) + .then(function(vector) { + promise_test(function(test) { + var plaintext = copyBuffer(vector.plaintext); + plaintext[0] = 255 - plaintext[0]; + var algorithmParams = { + length: vector.length, + get name() { + plaintext[0] = vector.plaintext[0]; + return vector.algorithm; + } + }; + if (vector.customization !== undefined) { + algorithmParams.customization = vector.customization; + } + var operation = subtle.verify(algorithmParams, vector.key, vector.signature, plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Signature verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " with altered plaintext during call"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " with altered plaintext during call"); + }); + + all_promises.push(promise); + }); + // Check for successful verification even if plaintext is altered after call. testVectors.forEach(function(vector) { var promise = importVectorKeys(vector, ["verify", "sign"]) @@ -92,7 +162,7 @@ function run_test() { }, function(err) { promise_test(function(test) { assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); - }, "importVectorKeys step: " + vector.name + " with altered plaintext"); + }, "importVectorKeys step: " + vector.name + " with altered plaintext after call"); }); all_promises.push(promise); diff --git a/test/fixtures/wpt/WebCryptoAPI/sign_verify/mldsa.js b/test/fixtures/wpt/WebCryptoAPI/sign_verify/mldsa.js index e31d36362b0dc9..88143a33879ed7 100644 --- a/test/fixtures/wpt/WebCryptoAPI/sign_verify/mldsa.js +++ b/test/fixtures/wpt/WebCryptoAPI/sign_verify/mldsa.js @@ -55,6 +55,61 @@ function run_test() { all_promises.push(promise); }); + // Test verification with an altered buffer during call + testVectors.forEach(function (vector) { + var promise = importVectorKeys(vector, ['verify'], ['sign']).then( + function (vectors) { + promise_test(function (test) { + var signature = copyBuffer(vector.signature); + signature[0] = 255 - signature[0]; + var operation = subtle + .verify( + { + get name() { + signature[0] = vector.signature[0]; + return vector.algorithmName; + }, + }, + vector.publicKey, + signature, + vector.data + ) + .then( + function (is_verified) { + assert_true(is_verified, 'Signature verified'); + }, + function (err) { + assert_unreached( + 'Verification should not throw error ' + + vector.name + + ': ' + + err.message + + "'" + ); + } + ); + + return operation; + }, vector.name + ' verification with altered signature during call'); + }, + function (err) { + promise_test(function (test) { + assert_unreached( + 'importVectorKeys failed for ' + + vector.name + + ". Message: ''" + + err.message + + "''" + ); + }, 'importVectorKeys step: ' + + vector.name + + ' verification with altered signature during call'); + } + ); + + all_promises.push(promise); + }); + // Test verification with an altered buffer after call testVectors.forEach(function (vector) { var promise = importVectorKeys(vector, ['verify'], ['sign']).then( @@ -101,6 +156,61 @@ function run_test() { all_promises.push(promise); }); + // Check for successful verification even if plaintext is altered during call. + testVectors.forEach(function (vector) { + var promise = importVectorKeys(vector, ['verify'], ['sign']).then( + function (vectors) { + promise_test(function (test) { + var plaintext = copyBuffer(vector.data); + plaintext[0] = 255 - plaintext[0]; + var operation = subtle + .verify( + { + get name() { + plaintext[0] = vector.data[0]; + return vector.algorithmName; + }, + }, + vector.publicKey, + vector.signature, + plaintext + ) + .then( + function (is_verified) { + assert_true(is_verified, 'Signature verified'); + }, + function (err) { + assert_unreached( + 'Verification should not throw error ' + + vector.name + + ': ' + + err.message + + "'" + ); + } + ); + + return operation; + }, vector.name + ' with altered plaintext during call'); + }, + function (err) { + promise_test(function (test) { + assert_unreached( + 'importVectorKeys failed for ' + + vector.name + + ". Message: ''" + + err.message + + "''" + ); + }, 'importVectorKeys step: ' + + vector.name + + ' with altered plaintext during call'); + } + ); + + all_promises.push(promise); + }); + // Check for successful verification even if plaintext is altered after call. testVectors.forEach(function (vector) { var promise = importVectorKeys(vector, ['verify'], ['sign']).then( diff --git a/test/fixtures/wpt/WebCryptoAPI/sign_verify/rsa.js b/test/fixtures/wpt/WebCryptoAPI/sign_verify/rsa.js index 5abadd3d4b8629..f808714cfb11d4 100644 --- a/test/fixtures/wpt/WebCryptoAPI/sign_verify/rsa.js +++ b/test/fixtures/wpt/WebCryptoAPI/sign_verify/rsa.js @@ -37,6 +37,37 @@ function run_test() { all_promises.push(promise); }); + // Test verification with an altered buffer during call + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + promise_test(function(test) { + var signature = copyBuffer(vector.signature); + signature[0] = 255 - signature[0]; + var operation = subtle.verify({ + ...vector.algorithm, + get name() { + signature[0] = vector.signature[0]; + return vector.algorithm.name; + } + }, vector.publicKey, signature, vector.plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Signature verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " verification with altered signature during call"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " verification with altered signature during call"); + }); + + all_promises.push(promise); + }); + // Test verification with an altered buffer after call testVectors.forEach(function(vector) { var promise = importVectorKeys(vector, ["verify"], ["sign"]) @@ -62,6 +93,37 @@ function run_test() { all_promises.push(promise); }); + // Check for successful verification even if plaintext is altered during call. + testVectors.forEach(function(vector) { + var promise = importVectorKeys(vector, ["verify"], ["sign"]) + .then(function(vectors) { + promise_test(function(test) { + var plaintext = copyBuffer(vector.plaintext); + plaintext[0] = 255 - plaintext[0]; + var operation = subtle.verify({ + ...vector.algorithm, + get name() { + plaintext[0] = vector.plaintext[0]; + return vector.algorithm.name; + } + }, vector.publicKey, vector.signature, plaintext) + .then(function(is_verified) { + assert_true(is_verified, "Signature verified"); + }, function(err) { + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }); + + return operation; + }, vector.name + " with altered plaintext during call"); + }, function(err) { + promise_test(function(test) { + assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); + }, "importVectorKeys step: " + vector.name + " with altered plaintext during call"); + }); + + all_promises.push(promise); + }); + // Check for successful verification even if plaintext is altered after call. testVectors.forEach(function(vector) { var promise = importVectorKeys(vector, ["verify"], ["sign"]) diff --git a/test/fixtures/wpt/versions.json b/test/fixtures/wpt/versions.json index 50173e71b1b9d7..db5222f4fefdca 100644 --- a/test/fixtures/wpt/versions.json +++ b/test/fixtures/wpt/versions.json @@ -96,7 +96,7 @@ "path": "web-locks" }, "WebCryptoAPI": { - "commit": "7cbe7e8ed962eac692ba4ad2e6ce3b9daafb65c0", + "commit": "42e47329fdc92c80d58c2816eb66cb2cf2b32a89", "path": "WebCryptoAPI" }, "webidl": { diff --git a/test/parallel/test-runner-run-files-undefined.mjs b/test/parallel/test-runner-run-files-undefined.mjs index 9d08b10a4550cd..a0981b307d1999 100644 --- a/test/parallel/test-runner-run-files-undefined.mjs +++ b/test/parallel/test-runner-run-files-undefined.mjs @@ -1,61 +1,31 @@ -import * as common from '../common/index.mjs'; +import '../common/index.mjs'; import tmpdir from '../common/tmpdir.js'; -import { describe, it, run, beforeEach } from 'node:test'; -import { dot, spec, tap } from 'node:test/reporters'; -import { fork } from 'node:child_process'; -import assert from 'node:assert'; - -if (common.hasCrypto) { - console.log('1..0 # Skipped: no crypto'); - process.exit(0); -} - -if (process.env.CHILD === 'true') { - describe('require(\'node:test\').run with no files', { concurrency: true }, () => { - beforeEach(() => { - tmpdir.refresh(); - process.chdir(tmpdir.path); +import { spawnSyncAndExitWithoutError } from '../common/child_process.js'; + +async function runTests(run, reporters) { + for (const reporterName of Object.keys(reporters)) { + if (reporterName === 'default') continue; + console.log({ reporterName }); + + const stream = run({ + files: undefined + }).compose(reporters[reporterName]); + stream.on('test:fail', () => { + throw new Error('Received test:fail with ' + reporterName); }); - - it('should neither pass or fail', async () => { - const stream = run({ - files: undefined - }).compose(tap); - stream.on('test:fail', common.mustNotCall()); - stream.on('test:pass', common.mustNotCall()); - - // eslint-disable-next-line no-unused-vars - for await (const _ of stream); + stream.on('test:pass', () => { + throw new Error('Received test:pass with ' + reporterName); }); - it('can use the spec reporter', async () => { - const stream = run({ - files: undefined - }).compose(spec); - stream.on('test:fail', common.mustNotCall()); - stream.on('test:pass', common.mustNotCall()); - - // eslint-disable-next-line no-unused-vars - for await (const _ of stream); - }); + // eslint-disable-next-line no-unused-vars + for await (const _ of stream); + } +} - it('can use the dot reporter', async () => { - const stream = run({ - files: undefined - }).compose(dot); - stream.on('test:fail', common.mustNotCall()); - stream.on('test:pass', common.mustNotCall()); +tmpdir.refresh(); +spawnSyncAndExitWithoutError(process.execPath, ['--input-type=module', '-e', ` + import { run } from 'node:test'; + import * as reporters from 'node:test/reporters'; - // eslint-disable-next-line no-unused-vars - for await (const _ of stream); - }); - }); -} else if (common.isAIX) { - console.log('1..0 # Skipped: test runner without specifying files fails on AIX'); -} else { - fork(import.meta.filename, [], { - env: { CHILD: 'true' } - }).on('exit', common.mustCall((code) => { - assert.strictEqual(code, 0); - })); -} + await (${runTests})(run, reporters); +`], { cwd: tmpdir.path }); diff --git a/test/parallel/test-sqlite-statement-sync.js b/test/parallel/test-sqlite-statement-sync.js index 62e95363f1c46a..aa7a3a73ae6649 100644 --- a/test/parallel/test-sqlite-statement-sync.js +++ b/test/parallel/test-sqlite-statement-sync.js @@ -171,6 +171,61 @@ suite('StatementSync.prototype.iterate()', () => { { __proto__: null, done: true, value: null }, ); }); + + test('iterator is invalidated when statement is reset by get/all/run/iterate', (t) => { + const db = new DatabaseSync(':memory:'); + db.exec('CREATE TABLE test (value INTEGER NOT NULL)'); + for (let i = 0; i < 5; i++) { + db.prepare('INSERT INTO test (value) VALUES (?)').run(i); + } + const stmt = db.prepare('SELECT * FROM test'); + + // Invalidated by stmt.get() + let it = stmt.iterate(); + it.next(); + stmt.get(); + t.assert.throws(() => { it.next(); }, { + code: 'ERR_INVALID_STATE', + message: /iterator was invalidated/, + }); + + // Invalidated by stmt.all() + it = stmt.iterate(); + it.next(); + stmt.all(); + t.assert.throws(() => { it.next(); }, { + code: 'ERR_INVALID_STATE', + message: /iterator was invalidated/, + }); + + // Invalidated by stmt.run() + it = stmt.iterate(); + it.next(); + stmt.run(); + t.assert.throws(() => { it.next(); }, { + code: 'ERR_INVALID_STATE', + message: /iterator was invalidated/, + }); + + // Invalidated by a new stmt.iterate() + it = stmt.iterate(); + it.next(); + const it2 = stmt.iterate(); + t.assert.throws(() => { it.next(); }, { + code: 'ERR_INVALID_STATE', + message: /iterator was invalidated/, + }); + + // New iterator works fine + t.assert.strictEqual(it2.next().done, false); + + // Reset on a different statement does NOT invalidate this iterator + const stmt2 = db.prepare('SELECT * FROM test'); + it = stmt.iterate(); + it.next(); + stmt2.get(); + it.next(); + }); }); suite('StatementSync.prototype.run()', () => { diff --git a/test/parallel/test-webstreams-compression-buffer-source.js b/test/parallel/test-webstreams-compression-buffer-source.js new file mode 100644 index 00000000000000..3304a8e64f3175 --- /dev/null +++ b/test/parallel/test-webstreams-compression-buffer-source.js @@ -0,0 +1,42 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const test = require('node:test'); +const { DecompressionStream, CompressionStream } = require('stream/web'); + +// Minimal gzip-compressed bytes for "hello" +const compressedGzip = new Uint8Array([ + 31, 139, 8, 0, 0, 0, 0, 0, 0, 3, + 203, 72, 205, 201, 201, 7, 0, 134, 166, 16, 54, 5, 0, 0, 0, +]); + +test('DecompressionStream accepts ArrayBuffer chunks', async () => { + const ds = new DecompressionStream('gzip'); + const writer = ds.writable.getWriter(); + + const writePromise = writer.write(compressedGzip.buffer); + writer.close(); + + const chunks = await Array.fromAsync(ds.readable); + await writePromise; + const out = Buffer.concat(chunks.map((c) => Buffer.from(c))); + assert.strictEqual(out.toString(), 'hello'); +}); + +test('CompressionStream round-trip with ArrayBuffer input', async () => { + const cs = new CompressionStream('gzip'); + const ds = new DecompressionStream('gzip'); + + const csWriter = cs.writable.getWriter(); + + const input = new TextEncoder().encode('hello').buffer; + + await csWriter.write(input); + csWriter.close(); + + await cs.readable.pipeTo(ds.writable); + + const out = await Array.fromAsync(ds.readable); + const result = Buffer.concat(out.map((c) => Buffer.from(c))); + assert.strictEqual(result.toString(), 'hello'); +}); diff --git a/test/parallel/test-whatwg-webstreams-adapters-to-writablestream.js b/test/parallel/test-whatwg-webstreams-adapters-to-writablestream.js index 1527610c513333..23e6319563daa0 100644 --- a/test/parallel/test-whatwg-webstreams-adapters-to-writablestream.js +++ b/test/parallel/test-whatwg-webstreams-adapters-to-writablestream.js @@ -165,3 +165,35 @@ class TestWritable extends Writable { const writer = writableStream.getWriter(); writer.closed.then(common.mustCall()); } + +{ + const duplex = new PassThrough(); + const writableStream = newWritableStreamFromStreamWritable(duplex); + const ec = new TextEncoder(); + const arrayBuffer = ec.encode('hello').buffer; + writableStream + .getWriter() + .write(arrayBuffer) + .then(common.mustCall()); + + duplex.on('data', common.mustCall((chunk) => { + assert(chunk instanceof Buffer); + assert(chunk.equals(Buffer.from('hello'))); + })); +} + +{ + const duplex = new PassThrough({ objectMode: true }); + const writableStream = newWritableStreamFromStreamWritable(duplex); + const ec = new TextEncoder(); + const arrayBuffer = ec.encode('hello').buffer; + writableStream + .getWriter() + .write(arrayBuffer) + .then(common.mustCall()); + + duplex.on('data', common.mustCall((chunk) => { + assert(chunk instanceof ArrayBuffer); + assert.strictEqual(chunk, arrayBuffer); + })); +} diff --git a/test/wpt/status/compression.json b/test/wpt/status/compression.json index be073427810f0d..619add6fbc25a9 100644 --- a/test/wpt/status/compression.json +++ b/test/wpt/status/compression.json @@ -5,9 +5,6 @@ "decompression-bad-chunks.tentative.any.js": { "skip": "Execution \"hangs\", ArrayBuffer and TypedArray is not accepted and throws, instead of rejects during writer.write" }, - "decompression-buffersource.tentative.any.js": { - "skip": "ArrayBuffer and TypedArray is not accepted and throws, instead of rejects during writer.write" - }, "compression-with-detach.tentative.window.js": { "requires": ["crypto"] },