From f86755a44ae88fba64df8e814a03174bb6f72e9c Mon Sep 17 00:00:00 2001 From: Renegade334 Date: Sun, 16 Nov 2025 17:23:44 +0000 Subject: [PATCH] lib: do not provide an empty Proxy from localStorage getter --- lib/internal/webstorage.js | 35 ++++++--------------------- test/common/index.js | 14 ++++++++++- test/common/index.mjs | 2 ++ test/parallel/test-assert-checktag.js | 4 +-- test/parallel/test-webstorage.js | 5 ++-- 5 files changed, 28 insertions(+), 32 deletions(-) diff --git a/lib/internal/webstorage.js b/lib/internal/webstorage.js index 28b5df3f3da835..76d55bb0d9b170 100644 --- a/lib/internal/webstorage.js +++ b/lib/internal/webstorage.js @@ -1,7 +1,6 @@ 'use strict'; const { ObjectDefineProperties, - Proxy, } = primordials; const { getOptionValue } = require('internal/options'); const { kConstructorKey, Storage } = internalBinding('webstorage'); @@ -13,6 +12,8 @@ module.exports = { Storage }; let lazyLocalStorage; let lazySessionStorage; +let localStorageWarningEmitted = false; + ObjectDefineProperties(module.exports, { __proto__: null, localStorage: { @@ -20,35 +21,15 @@ ObjectDefineProperties(module.exports, { configurable: true, enumerable: true, get() { - if (lazyLocalStorage === undefined) { + if (lazyLocalStorage === undefined && !localStorageWarningEmitted) { const location = getOptionValue('--localstorage-file'); - if (location === '') { - 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; - } - - return false; - }, - }; - - lazyLocalStorage = new Proxy({}, handler); - } else { - lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location)); + process.emitWarning('Cannot initialize local storage without a `--localstorage-file` path'); + localStorageWarningEmitted = true; + return undefined; } + + lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location)); } return lazyLocalStorage; diff --git a/test/common/index.js b/test/common/index.js index 11465d7b5d77be..83e7b8f58f1194 100755 --- a/test/common/index.js +++ b/test/common/index.js @@ -59,6 +59,9 @@ const hasSQLite = Boolean(process.versions.sqlite); const hasQuic = hasCrypto && !!process.features.quic; +const hasLocalStorage = hasSQLite && + process.execArgv.find((arg) => arg.startsWith('--localstorage-file=')) !== undefined; + /** * Parse test metadata from the specified file. * @param {string} filename - The name of the file to parse. @@ -351,7 +354,6 @@ const knownGlobals = new Set([ 'CompressionStream', 'DecompressionStream', 'Storage', - 'localStorage', 'sessionStorage', ].forEach((i) => { if (globalThis[i] !== undefined) { @@ -366,6 +368,10 @@ if (hasCrypto) { knownGlobals.add(globalThis.SubtleCrypto); } +if (hasLocalStorage) { + knownGlobals.add(globalThis.localStorage); +} + const { Worker } = require('node:worker_threads'); knownGlobals.add(Worker); @@ -390,6 +396,11 @@ if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') { if (val === 'crypto' && !hasCrypto) { continue; } + // globalThis.localStorage is a getter that warns if Node.js was + // executed without a --localstorage-file path. + if (val === 'localStorage' && !hasLocalStorage) { + continue; + } if (!knownGlobals.has(globalThis[val])) { leaked.push(val); } @@ -940,6 +951,7 @@ const common = { hasQuic, hasInspector, hasSQLite, + hasLocalStorage, invalidArgTypeHelper, isAlive, isASan, diff --git a/test/common/index.mjs b/test/common/index.mjs index 471d58b7dbbfe0..7516eb68a53b7a 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -19,6 +19,7 @@ const { hasQuic, hasInspector, hasSQLite, + hasLocalStorage, hasIntl, hasIPv6, isAIX, @@ -72,6 +73,7 @@ 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 b86a1bde7f096d..b1c3660ab4a693 100644 --- a/test/parallel/test-assert-checktag.js +++ b/test/parallel/test-assert-checktag.js @@ -1,5 +1,5 @@ 'use strict'; -const { hasCrypto } = require('../common'); +const { hasCrypto, hasLocalStorage } = 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 }, () => { +test({ skip: !hasCrypto || !hasLocalStorage }, () => { // See https://github.com/nodejs/node/issues/10258 { const date = new Date('2016'); diff --git a/test/parallel/test-webstorage.js b/test/parallel/test-webstorage.js index bc48c9643773b5..0644698d94dc24 100644 --- a/test/parallel/test-webstorage.js +++ b/test/parallel/test-webstorage.js @@ -43,11 +43,12 @@ test('sessionStorage is not persisted', async () => { test('localStorage emits a warning when used without --localstorage-file ', async () => { const cp = await spawnPromisified(process.execPath, [ - '-pe', 'localStorage.length', + '-pe', 'localStorage', ]); assert.strictEqual(cp.code, 0); assert.strictEqual(cp.signal, null); - assert.match(cp.stderr, /Warning: `--localstorage-file` was provided without a valid path/); + assert.match(cp.stdout, /undefined/); + assert.match(cp.stderr, /Warning: Cannot initialize local storage without a `--localstorage-file` path/); }); test('localStorage is not persisted if it is unused', async () => {