Skip to content

Commit 9dc2163

Browse files
committed
lib: throw from localStorage getter on missing storage path
1 parent 340e619 commit 9dc2163

File tree

7 files changed

+54
-33
lines changed

7 files changed

+54
-33
lines changed

doc/api/errors.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3354,6 +3354,17 @@ added: v18.1.0
33543354
The `Response` that has been passed to `WebAssembly.compileStreaming` or to
33553355
`WebAssembly.instantiateStreaming` is not a valid WebAssembly response.
33563356

3357+
<a id="ERR_WEBSTORAGE_MISSING_STORAGE_PATH"></a>
3358+
3359+
### `ERR_WEBSTORAGE_MISSING_STORAGE_PATH`
3360+
3361+
<!-- YAML
3362+
added: REPLACEME
3363+
-->
3364+
3365+
An attempt was made to access the global [`localStorage`][] object, but the
3366+
[`--localstorage-file`][] command-line argument was not provided.
3367+
33573368
<a id="ERR_WORKER_INIT_FAILED"></a>
33583369

33593370
### `ERR_WORKER_INIT_FAILED`
@@ -4370,6 +4381,7 @@ An error occurred trying to allocate memory. This should never happen.
43704381
[`'uncaughtException'`]: process.md#event-uncaughtexception
43714382
[`--disable-proto=throw`]: cli.md#--disable-protomode
43724383
[`--force-fips`]: cli.md#--force-fips
4384+
[`--localstorage-file`]: cli.md#--localstorage-filefile
43734385
[`--no-addons`]: cli.md#--no-addons
43744386
[`--unhandled-rejections`]: cli.md#--unhandled-rejectionsmode
43754387
[`Class: assert.AssertionError`]: assert.md#class-assertassertionerror
@@ -4412,6 +4424,7 @@ An error occurred trying to allocate memory. This should never happen.
44124424
[`http`]: http.md
44134425
[`https`]: https.md
44144426
[`libuv Error handling`]: https://docs.libuv.org/en/v1.x/errors.html
4427+
[`localStorage`]: globals.md#localstorage
44154428
[`net.Socket.write()`]: net.md#socketwritedata-encoding-callback
44164429
[`net`]: net.md
44174430
[`new URL(input)`]: url.md#new-urlinput-base

lib/internal/errors.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1904,6 +1904,8 @@ E('ERR_VM_MODULE_NOT_MODULE',
19041904
E('ERR_VM_MODULE_STATUS', 'Module status %s', Error);
19051905
E('ERR_WASI_ALREADY_STARTED', 'WASI instance has already started', Error);
19061906
E('ERR_WEBASSEMBLY_RESPONSE', 'WebAssembly response %s', TypeError);
1907+
E('ERR_WEBSTORAGE_MISSING_STORAGE_PATH',
1908+
'Cannot initialize local storage without a `--localstorage-file` path', Error);
19071909
E('ERR_WORKER_INIT_FAILED', 'Worker initialization failure: %s', Error);
19081910
E('ERR_WORKER_INVALID_EXEC_ARGV', (errors, msg = 'invalid execArgv flags') =>
19091911
`Initiated Worker with ${msg}: ${ArrayPrototypeJoin(errors, ', ')}`,

lib/internal/webstorage.js

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
'use strict';
22
const {
33
ObjectDefineProperties,
4-
Proxy,
54
} = primordials;
65
const { getOptionValue } = require('internal/options');
6+
const {
7+
codes: {
8+
ERR_WEBSTORAGE_MISSING_STORAGE_PATH,
9+
},
10+
} = require('internal/errors');
711
const { kConstructorKey, Storage } = internalBinding('webstorage');
812
const { getValidatedPath } = require('internal/fs/utils');
913
const kInMemoryPath = ':memory:';
@@ -21,34 +25,14 @@ ObjectDefineProperties(module.exports, {
2125
enumerable: true,
2226
get() {
2327
if (lazyLocalStorage === undefined) {
28+
// For consistency with the web specification, throw from the accessor
29+
// if the local storage path is not provided.
2430
const location = getOptionValue('--localstorage-file');
25-
2631
if (location === '') {
27-
let warningEmitted = false;
28-
const handler = {
29-
__proto__: null,
30-
get(target, prop) {
31-
if (!warningEmitted) {
32-
process.emitWarning('`--localstorage-file` was provided without a valid path');
33-
warningEmitted = true;
34-
}
35-
36-
return undefined;
37-
},
38-
set(target, prop, value) {
39-
if (!warningEmitted) {
40-
process.emitWarning('`--localstorage-file` was provided without a valid path');
41-
warningEmitted = true;
42-
}
43-
44-
return false;
45-
},
46-
};
47-
48-
lazyLocalStorage = new Proxy({}, handler);
49-
} else {
50-
lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location));
32+
throw new ERR_WEBSTORAGE_MISSING_STORAGE_PATH();
5133
}
34+
35+
lazyLocalStorage = new Storage(kConstructorKey, getValidatedPath(location));
5236
}
5337

5438
return lazyLocalStorage;

test/common/index.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ const hasSQLite = Boolean(process.versions.sqlite);
5959

6060
const hasQuic = hasCrypto && !!process.features.quic;
6161

62+
const hasLocalStorage = (() => {
63+
try {
64+
return hasSQLite && globalThis.localStorage !== undefined;
65+
} catch {
66+
return false;
67+
}
68+
})();
69+
6270
/**
6371
* Parse test metadata from the specified file.
6472
* @param {string} filename - The name of the file to parse.
@@ -350,14 +358,17 @@ const knownGlobals = new Set([
350358
'CompressionStream',
351359
'DecompressionStream',
352360
'Storage',
353-
'localStorage',
354361
'sessionStorage',
355362
].forEach((i) => {
356363
if (globalThis[i] !== undefined) {
357364
knownGlobals.add(globalThis[i]);
358365
}
359366
});
360367

368+
if (hasLocalStorage) {
369+
knownGlobals.add(globalThis.localStorage);
370+
}
371+
361372
if (hasCrypto) {
362373
knownGlobals.add(globalThis.crypto);
363374
knownGlobals.add(globalThis.Crypto);
@@ -389,6 +400,11 @@ if (process.env.NODE_TEST_KNOWN_GLOBALS !== '0') {
389400
if (val === 'crypto' && !hasCrypto) {
390401
continue;
391402
}
403+
// globalThis.localStorage is a getter that throws if Node.js was
404+
// executed without a --localstorage-file path.
405+
if (val === 'localStorage' && !hasLocalStorage) {
406+
continue;
407+
}
392408
if (!knownGlobals.has(globalThis[val])) {
393409
leaked.push(val);
394410
}
@@ -933,6 +949,7 @@ const common = {
933949
hasQuic,
934950
hasInspector,
935951
hasSQLite,
952+
hasLocalStorage,
936953
invalidArgTypeHelper,
937954
isAlive,
938955
isASan,

test/common/index.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const {
1919
hasQuic,
2020
hasInspector,
2121
hasSQLite,
22+
hasLocalStorage,
2223
hasIntl,
2324
hasIPv6,
2425
isAIX,
@@ -71,6 +72,7 @@ export {
7172
hasQuic,
7273
hasInspector,
7374
hasSQLite,
75+
hasLocalStorage,
7476
hasIntl,
7577
hasIPv6,
7678
isAIX,

test/parallel/test-assert-checktag.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use strict';
2-
const { hasCrypto } = require('../common');
2+
const { hasCrypto, hasLocalStorage } = require('../common');
33
const { test } = require('node:test');
44
const assert = require('assert');
55

@@ -12,7 +12,7 @@ const assert = require('assert');
1212
if (process.stdout.isTTY)
1313
process.env.NODE_DISABLE_COLORS = '1';
1414

15-
test('', { skip: !hasCrypto }, () => {
15+
test({ skip: !hasCrypto || !hasLocalStorage }, () => {
1616
// See https://github.com/nodejs/node/issues/10258
1717
{
1818
const date = new Date('2016');

test/parallel/test-webstorage.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,16 @@ test('sessionStorage is not persisted', async () => {
4141
assert.strictEqual((await readdir(tmpdir.path)).length, 0);
4242
});
4343

44-
test('localStorage emits a warning when used without --localstorage-file ', async () => {
44+
test('localStorage throws without --localstorage-file', async () => {
4545
const cp = await spawnPromisified(process.execPath, [
46-
'-pe', 'localStorage.length',
46+
'-e', 'localStorage',
4747
]);
48-
assert.strictEqual(cp.code, 0);
48+
assert.strictEqual(cp.code, 1);
4949
assert.strictEqual(cp.signal, null);
50-
assert.match(cp.stderr, /Warning: `--localstorage-file` was provided without a valid path/);
50+
assert.match(
51+
cp.stderr,
52+
/Error \[ERR_WEBSTORAGE_MISSING_STORAGE_PATH\]: Cannot initialize local storage without a `--localstorage-file` path/,
53+
);
5154
});
5255

5356
test('localStorage is not persisted if it is unused', async () => {

0 commit comments

Comments
 (0)