diff --git a/CHANGELOG.md b/CHANGELOG.md index ce5caa58ee7642..36aa3280779cf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,8 @@ release. -25.2.0
+25.2.1
+25.2.0
25.1.0
25.0.0
diff --git a/common.gypi b/common.gypi index b1858065820877..b915af23ad13a8 100644 --- a/common.gypi +++ b/common.gypi @@ -38,7 +38,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.13', + 'v8_embedder_string': '-node.14', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/DEPS b/deps/v8/DEPS index c6b16a0d770bf1..e2809308a05f6b 100644 --- a/deps/v8/DEPS +++ b/deps/v8/DEPS @@ -141,7 +141,7 @@ deps = { } ], 'dep_type': 'cipd', - 'condition': 'host_os == "linux" and host_cpu != "s390" and host_os != "zos" and host_cpu != "ppc"', + 'condition': 'host_os == "linux" and host_cpu != "s390x" and host_os != "zos" and host_cpu != "ppc64"', }, 'buildtools/mac': { 'packages': [ @@ -171,7 +171,7 @@ deps = { } ], 'dep_type': 'cipd', - 'condition': '(host_os == "linux" or host_os == "mac" or host_os == "win") and host_cpu != "s390" and host_os != "zos" and host_cpu != "ppc" and (host_cpu != "arm64" or host_os == "mac")', + 'condition': '(host_os == "linux" or host_os == "mac" or host_os == "win") and host_cpu != "s390x" and host_os != "zos" and host_cpu != "ppc64" and (host_cpu != "arm64" or host_os == "mac")', }, 'test/benchmarks/data': Var('chromium_url') + '/v8/deps/third_party/benchmarks.git' + '@' + '05d7188267b4560491ff9155c5ee13e207ecd65f', @@ -481,7 +481,7 @@ deps = { } ], 'dep_type': 'cipd', - 'condition': 'host_cpu != "s390" and host_os != "zos" and host_cpu != "ppc"' + 'condition': 'host_cpu != "s390x" and host_os != "zos" and host_cpu != "ppc64"' }, 'third_party/partition_alloc': { 'url': Var('chromium_url') + '/chromium/src/base/allocator/partition_allocator.git@' + Var('partition_alloc_version'), @@ -545,7 +545,7 @@ deps = { } ], 'dep_type': 'cipd', - 'condition': 'not build_with_chromium and host_cpu != "s390" and host_os != "zos" and host_cpu != "ppc"', + 'condition': 'not build_with_chromium and host_cpu != "s390x" and host_os != "zos" and host_cpu != "ppc64"', }, 'third_party/zlib': Var('chromium_url') + '/chromium/src/third_party/zlib.git'+ '@' + 'caf4afa1afc92e16fef429f182444bed98a46a6c', diff --git a/doc/api/cli.md b/doc/api/cli.md index 28de8b72b5eb27..a53c45a16d4edc 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1741,10 +1741,11 @@ surface on other platforms, but the performance impact may be severe. added: v22.4.0 --> +> Stability: 1.2 - Release candidate. + The file used to store `localStorage` data. If the file does not exist, it is created the first time `localStorage` is accessed. The same file may be shared -between multiple Node.js processes concurrently. This flag is a no-op if -Node.js is started with the `--no-webstorage` (or `--no-experimental-webstorage`) flag. +between multiple Node.js processes concurrently. ### `--max-http-header-size=size` @@ -1906,6 +1907,20 @@ added: v22.0.0 Disable exposition of {WebSocket} on the global scope. +### `--no-experimental-webstorage` + + + +> Stability: 1.2 - Release candidate. + +Disable [`Web Storage`][] support. + ### `--no-extra-info-on-fatal-exception` - -Disable [`Web Storage`][] support. - ### `--node-memory-debug` +> Stability: 1.2 - Release candidate. Disable this API with [`--no-experimental-webstorage`][]. + A browser-compatible implementation of [`localStorage`][]. Data is stored unencrypted in the file specified by the [`--localstorage-file`][] CLI flag. The maximum amount of data that can be stored is 10 MB. Any modification of this data outside of the Web Storage API is not supported. -Disable this API with the [`--no-webstorage`][] (or its alias `--no-experimental-webstorage`) CLI flag. `localStorage` data is not stored per user or per request when used in the context of a server, it is shared across all users and requests. @@ -1073,9 +1083,13 @@ A browser-compatible implementation of {Request}. -> Stability: 1.0 - Early development. +> Stability: 1.2 - Release candidate. Disable this API with [`--no-experimental-webstorage`][]. A browser-compatible implementation of [`sessionStorage`][]. Data is stored in memory, with a storage quota of 10 MB. `sessionStorage` data persists only within @@ -1111,10 +1125,9 @@ added: v0.0.1 added: v22.4.0 --> -> Stability: 1.0 - Early development. +> Stability: 1.2 - Release candidate. Disable this API with [`--no-experimental-webstorage`][]. -A browser-compatible implementation of {Storage}. Disable this API with the -[`--no-webstorage`][] (or its alias `--no-experimental-webstorage`) CLI flag. +A browser-compatible implementation of {Storage}. ## `structuredClone(value[, options])` @@ -1328,7 +1341,7 @@ A browser-compatible implementation of [`WritableStreamDefaultWriter`][]. [`--localstorage-file`]: cli.md#--localstorage-filefile [`--no-experimental-global-navigator`]: cli.md#--no-experimental-global-navigator [`--no-experimental-websocket`]: cli.md#--no-experimental-websocket -[`--no-webstorage`]: cli.md#--no-webstorage +[`--no-experimental-webstorage`]: cli.md#--no-experimental-webstorage [`ByteLengthQueuingStrategy`]: webstreams.md#class-bytelengthqueuingstrategy [`CompressionStream`]: webstreams.md#class-compressionstream [`CountQueuingStrategy`]: webstreams.md#class-countqueuingstrategy diff --git a/doc/changelogs/CHANGELOG_V25.md b/doc/changelogs/CHANGELOG_V25.md index 3bc217bae117d8..7c235f3f7afdd1 100644 --- a/doc/changelogs/CHANGELOG_V25.md +++ b/doc/changelogs/CHANGELOG_V25.md @@ -8,6 +8,7 @@ +25.2.1
25.2.0
25.1.0
25.0.0
@@ -42,6 +43,19 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + + +## 2025-11-17, Version 25.2.1 (Current), @aduh95 + +### Notable Changes + +### Commits + +* \[[`ff89b7b6c7`](https://github.com/nodejs/node/commit/ff89b7b6c7)] - **crypto**: ensure documented RSA-PSS saltLength default is used (Filip Skokan) [#60662](https://github.com/nodejs/node/pull/60662) +* \[[`5316b580eb`](https://github.com/nodejs/node/commit/5316b580eb)] - **deps**: V8: backport 2e4c5cf9b112 (Michaƫl Zasso) [#60654](https://github.com/nodejs/node/pull/60654) +* \[[`ca878bc90e`](https://github.com/nodejs/node/commit/ca878bc90e)] - **doc,src,lib**: clarify experimental status of Web Storage support (Antoine du Hamel) [#60708](https://github.com/nodejs/node/pull/60708) +* \[[`a4dee613fd`](https://github.com/nodejs/node/commit/a4dee613fd)] - _**Revert**_ "**lib**: throw from localStorage getter on missing storage path" (Antoine du Hamel) [#60750](https://github.com/nodejs/node/pull/60750) + ## 2025-11-11, Version 25.2.0 (Current), @aduh95 diff --git a/doc/node.1 b/doc/node.1 index 72800cd364222b..540ce661a0d182 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -207,8 +207,8 @@ Enable experimental support for the EventSource Web API. .It Fl -no-experimental-websocket Disable experimental support for the WebSocket API. . -.It Fl -no-webstorage -Disable webstorage. +.It Fl -no-experimental-webstorage +Disable experimental support for the Web Storage API. . .It Fl -no-experimental-repl-await Disable top-level await keyword support in REPL. diff --git a/lib/internal/crypto/sig.js b/lib/internal/crypto/sig.js index 091a3f7bdcd6e9..a5bdaaf22b5ef0 100644 --- a/lib/internal/crypto/sig.js +++ b/lib/internal/crypto/sig.js @@ -50,6 +50,8 @@ const { isArrayBufferView, } = require('internal/util/types'); +const constants = internalBinding('constants').crypto; + function Sign(algorithm, options) { if (!(this instanceof Sign)) return new Sign(algorithm, options); @@ -85,7 +87,11 @@ function getPadding(options) { } function getSaltLength(options) { - return getIntOption('saltLength', options); + let saltLength = getIntOption('saltLength', options); + if (options.padding === constants.RSA_PKCS1_PSS_PADDING && saltLength === undefined) { + saltLength = constants.RSA_PSS_SALTLEN_MAX_SIGN; + } + return saltLength; } function getDSASignatureEncoding(options) { diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index 76ec7d821cb4cc..283ec72d388572 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -398,7 +398,7 @@ function setupQuic() { function setupWebStorage() { if (getEmbedderOptions().noBrowserGlobals || - !getOptionValue('--webstorage')) { + !getOptionValue('--experimental-webstorage')) { return; } diff --git a/lib/internal/webstorage.js b/lib/internal/webstorage.js index 989ccb2dd40eea..28b5df3f3da835 100644 --- a/lib/internal/webstorage.js +++ b/lib/internal/webstorage.js @@ -1,9 +1,9 @@ 'use strict'; const { ObjectDefineProperties, + Proxy, } = primordials; const { getOptionValue } = require('internal/options'); -const { lazyDOMException } = require('internal/util'); const { kConstructorKey, Storage } = internalBinding('webstorage'); const { getValidatedPath } = require('internal/fs/utils'); const kInMemoryPath = ':memory:'; @@ -21,17 +21,34 @@ ObjectDefineProperties(module.exports, { enumerable: true, get() { if (lazyLocalStorage === undefined) { - // For consistency with the web specification, throw from the accessor - // if the local storage path is not provided. const location = getOptionValue('--localstorage-file'); + if (location === '') { - throw lazyDOMException( - 'Cannot initialize local storage without a `--localstorage-file` path', - 'SecurityError', - ); - } + let warningEmitted = false; + const handler = { + __proto__: null, + get(target, prop) { + if (!warningEmitted) { + process.emitWarning('`--localstorage-file` was provided without a valid path'); + warningEmitted = true; + } + + return undefined; + }, + set(target, prop, value) { + if (!warningEmitted) { + process.emitWarning('`--localstorage-file` was provided without a valid path'); + warningEmitted = true; + } - lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location)); + return false; + }, + }; + + lazyLocalStorage = new Proxy({}, handler); + } else { + lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location)); + } } return lazyLocalStorage; diff --git a/src/node_options.cc b/src/node_options.cc index 6f5475c3d9bd14..8dd186477c8875 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -567,12 +567,12 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { NoOp{}, #endif kAllowedInEnvvar); - AddOption("--webstorage", - "Web Storage API", + AddOption("--experimental-webstorage", + "experimental Web Storage API", &EnvironmentOptions::webstorage, kAllowedInEnvvar, true); - AddAlias("--experimental-webstorage", "--webstorage"); + AddAlias("--webstorage", "--experimental-webstorage"); AddOption("--localstorage-file", "file used to persist localStorage data", &EnvironmentOptions::localstorage_file, diff --git a/src/node_version.h b/src/node_version.h index f2e0e3fe7529e1..2eb3ac4b17d0c6 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -29,7 +29,7 @@ #define NODE_VERSION_IS_LTS 0 #define NODE_VERSION_LTS_CODENAME "" -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n) diff --git a/test/common/index.js b/test/common/index.js index 3f612afbff5ef9..11465d7b5d77be 100755 --- a/test/common/index.js +++ b/test/common/index.js @@ -59,14 +59,6 @@ const hasSQLite = Boolean(process.versions.sqlite); const hasQuic = hasCrypto && !!process.features.quic; -const hasLocalStorage = (() => { - try { - return hasSQLite && globalThis.localStorage !== undefined; - } catch { - return false; - } -})(); - /** * Parse test metadata from the specified file. * @param {string} filename - The name of the file to parse. @@ -359,6 +351,7 @@ const knownGlobals = new Set([ 'CompressionStream', 'DecompressionStream', 'Storage', + 'localStorage', 'sessionStorage', ].forEach((i) => { if (globalThis[i] !== undefined) { @@ -373,10 +366,6 @@ if (hasCrypto) { knownGlobals.add(globalThis.SubtleCrypto); } -if (hasLocalStorage) { - knownGlobals.add(globalThis.localStorage); -} - const { Worker } = require('node:worker_threads'); knownGlobals.add(Worker); @@ -401,11 +390,6 @@ if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') { if (val === 'crypto' && !hasCrypto) { continue; } - // globalThis.localStorage is a getter that throws if Node.js was - // executed without a --localstorage-file path. - if (val === 'localStorage' && !hasLocalStorage) { - continue; - } if (!knownGlobals.has(globalThis[val])) { leaked.push(val); } @@ -956,7 +940,6 @@ const common = { hasQuic, hasInspector, hasSQLite, - hasLocalStorage, invalidArgTypeHelper, isAlive, isASan, diff --git a/test/common/index.mjs b/test/common/index.mjs index 7516eb68a53b7a..471d58b7dbbfe0 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -19,7 +19,6 @@ const { hasQuic, hasInspector, hasSQLite, - hasLocalStorage, hasIntl, hasIPv6, isAIX, @@ -73,7 +72,6 @@ export { hasQuic, hasInspector, hasSQLite, - hasLocalStorage, hasIntl, hasIPv6, isAIX, diff --git a/test/parallel/test-assert-checktag.js b/test/parallel/test-assert-checktag.js index b1c3660ab4a693..b86a1bde7f096d 100644 --- a/test/parallel/test-assert-checktag.js +++ b/test/parallel/test-assert-checktag.js @@ -1,5 +1,5 @@ 'use strict'; -const { hasCrypto, hasLocalStorage } = require('../common'); +const { hasCrypto } = require('../common'); const { test } = require('node:test'); const assert = require('assert'); @@ -12,7 +12,7 @@ const assert = require('assert'); if (process.stdout.isTTY) process.env.NODE_DISABLE_COLORS = '1'; -test({ skip: !hasCrypto || !hasLocalStorage }, () => { +test('', { skip: !hasCrypto }, () => { // See https://github.com/nodejs/node/issues/10258 { const date = new Date('2016'); diff --git a/test/parallel/test-crypto-rsa-pss-default-salt-length.js b/test/parallel/test-crypto-rsa-pss-default-salt-length.js new file mode 100644 index 00000000000000..fc86b52931fc74 --- /dev/null +++ b/test/parallel/test-crypto-rsa-pss-default-salt-length.js @@ -0,0 +1,37 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); + +const fixtures = require('../common/fixtures'); + +const privateKey = crypto.createPrivateKey(fixtures.readKey('rsa_private.pem', 'ascii')); +const publicKey = crypto.createPublicKey(fixtures.readKey('rsa_public.pem', 'ascii')); + +const data = crypto.randomBytes(32); + +for (const digest of ['sha256', 'sha384', 'sha512']) { + const hLen = crypto.hash(digest, data, 'buffer').byteLength; + const maxSaltLength = + privateKey.asymmetricKeyDetails.modulusLength / 8 - hLen - 2; + + const sig = crypto.sign(digest, data, { + key: privateKey, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + // No "saltLength" provided, documented default RSA_PSS_SALTLEN_MAX_SIGN expected + }); + + assert.strictEqual(crypto.verify( + digest, + data, + { + key: publicKey, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: maxSaltLength, + }, + sig + ), true); +} diff --git a/test/parallel/test-webstorage.js b/test/parallel/test-webstorage.js index cc5f0366f7116e..bc48c9643773b5 100644 --- a/test/parallel/test-webstorage.js +++ b/test/parallel/test-webstorage.js @@ -41,13 +41,13 @@ test('sessionStorage is not persisted', async () => { assert.strictEqual((await readdir(tmpdir.path)).length, 0); }); -test('localStorage throws without --localstorage-file', async () => { +test('localStorage emits a warning when used without --localstorage-file ', async () => { const cp = await spawnPromisified(process.execPath, [ - '-e', 'localStorage', + '-pe', 'localStorage.length', ]); - assert.strictEqual(cp.code, 1); + assert.strictEqual(cp.code, 0); assert.strictEqual(cp.signal, null); - assert.match(cp.stderr, /SecurityError:/); + assert.match(cp.stderr, /Warning: `--localstorage-file` was provided without a valid path/); }); test('localStorage is not persisted if it is unused', async () => {