From 8a9bbf58de0781a7a6dab799021bcf5bb64ffec3 Mon Sep 17 00:00:00 2001 From: Mohamed Sayed Date: Mon, 18 May 2026 12:25:58 +0300 Subject: [PATCH 1/3] test: cover fromWritable writev input validation Signed-off-by: Mohamed Sayed --- test/parallel/test-stream-iter-validation.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/parallel/test-stream-iter-validation.js b/test/parallel/test-stream-iter-validation.js index 19cde169d6f496..10d1a4dc71536a 100644 --- a/test/parallel/test-stream-iter-validation.js +++ b/test/parallel/test-stream-iter-validation.js @@ -3,8 +3,9 @@ const common = require('../common'); const assert = require('assert'); +const { Writable } = require('stream'); const { - from, fromSync, pull, pullSync, pipeTo, + from, fromSync, fromWritable, pull, pullSync, pipeTo, push, duplex, broadcast, Broadcast, share, shareSync, Share, SyncShare, bytes, bytesSync, text, textSync, @@ -161,6 +162,17 @@ assert.throws(() => broadcast({ backpressure: 'bad' }), { code: 'ERR_INVALID_ARG assert.throws(() => Broadcast.from(42), { code: 'ERR_INVALID_ARG_TYPE' }); assert.throws(() => Broadcast.from('bad'), { code: 'ERR_INVALID_ARG_TYPE' }); +// ============================================================================= +// fromWritable() validation +// ============================================================================= +{ + const writable = new Writable({ write(chunk, encoding, cb) { cb(); } }); + const writer = fromWritable(writable); + assert.throws(() => writer.writev('bad'), { code: 'ERR_INVALID_ARG_TYPE' }); + assert.throws(() => writer.writev(42), { code: 'ERR_INVALID_ARG_TYPE' }); + writer.endSync(); +} + // ============================================================================= // share() / shareSync() validation // ============================================================================= From ec44152f85e98afc45729180f605136fccebe9c6 Mon Sep 17 00:00:00 2001 From: Mohamed Sayed Date: Sun, 24 May 2026 09:11:37 +0300 Subject: [PATCH 2/3] src: fix crash when reading length on Storage.prototype --- src/node_webstorage.cc | 6 ++++-- test/parallel/test-webstorage.js | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/node_webstorage.cc b/src/node_webstorage.cc index 10c3ccd68e49a1..21f846fbeb6225 100644 --- a/src/node_webstorage.cc +++ b/src/node_webstorage.cc @@ -43,6 +43,7 @@ using v8::PropertyAttribute; using v8::PropertyCallbackInfo; using v8::PropertyDescriptor; using v8::PropertyHandlerFlags; +using v8::Signature; using v8::String; using v8::Value; @@ -737,8 +738,9 @@ static void Initialize(Local target, Local(), PropertyHandlerFlags::kHasNoSideEffect)); - Local length_getter = - FunctionTemplate::New(isolate, StorageLengthGetter); + Local length_signature = Signature::New(isolate, ctor_tmpl); + Local length_getter = FunctionTemplate::New( + isolate, StorageLengthGetter, Local(), length_signature); ctor_tmpl->PrototypeTemplate()->SetAccessorProperty(env->length_string(), length_getter, Local(), diff --git a/test/parallel/test-webstorage.js b/test/parallel/test-webstorage.js index 71e0a095163ced..affa9099ed3ec2 100644 --- a/test/parallel/test-webstorage.js +++ b/test/parallel/test-webstorage.js @@ -26,6 +26,21 @@ test('Storage instances cannot be created in userland', async () => { assert.match(cp.stderr, /Error: Illegal constructor/); }); +test('reading "length" on Storage.prototype throws instead of crashing', async () => { + // Refs: the `length` getter is defined on Storage.prototype but used to lack + // a signature, so accessing it on the prototype (not a real Storage instance) + // unwrapped a non-Storage object and segfaulted. + const cp = await spawnPromisified(process.execPath, [ + '-e', + 'globalThis.sessionStorage.setItem.length;globalThis.Storage.prototype.length', + ]); + + assert.strictEqual(cp.signal, null); + assert.strictEqual(cp.code, 1); + assert.strictEqual(cp.stdout, ''); + assert.match(cp.stderr, /Illegal invocation/); +}); + test('sessionStorage is not persisted', async () => { let cp = await spawnPromisified(process.execPath, [ '-pe', 'sessionStorage.foo = "barbaz"', From c822d3adfac809e37d28e607b5b77197e811c1ea Mon Sep 17 00:00:00 2001 From: Mohamed Sayed Date: Sun, 24 May 2026 09:21:58 +0300 Subject: [PATCH 3/3] Revert "src: fix crash when reading length on Storage.prototype" This reverts commit ec44152f85e98afc45729180f605136fccebe9c6. --- src/node_webstorage.cc | 6 ++---- test/parallel/test-webstorage.js | 15 --------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/src/node_webstorage.cc b/src/node_webstorage.cc index 21f846fbeb6225..10c3ccd68e49a1 100644 --- a/src/node_webstorage.cc +++ b/src/node_webstorage.cc @@ -43,7 +43,6 @@ using v8::PropertyAttribute; using v8::PropertyCallbackInfo; using v8::PropertyDescriptor; using v8::PropertyHandlerFlags; -using v8::Signature; using v8::String; using v8::Value; @@ -738,9 +737,8 @@ static void Initialize(Local target, Local(), PropertyHandlerFlags::kHasNoSideEffect)); - Local length_signature = Signature::New(isolate, ctor_tmpl); - Local length_getter = FunctionTemplate::New( - isolate, StorageLengthGetter, Local(), length_signature); + Local length_getter = + FunctionTemplate::New(isolate, StorageLengthGetter); ctor_tmpl->PrototypeTemplate()->SetAccessorProperty(env->length_string(), length_getter, Local(), diff --git a/test/parallel/test-webstorage.js b/test/parallel/test-webstorage.js index affa9099ed3ec2..71e0a095163ced 100644 --- a/test/parallel/test-webstorage.js +++ b/test/parallel/test-webstorage.js @@ -26,21 +26,6 @@ test('Storage instances cannot be created in userland', async () => { assert.match(cp.stderr, /Error: Illegal constructor/); }); -test('reading "length" on Storage.prototype throws instead of crashing', async () => { - // Refs: the `length` getter is defined on Storage.prototype but used to lack - // a signature, so accessing it on the prototype (not a real Storage instance) - // unwrapped a non-Storage object and segfaulted. - const cp = await spawnPromisified(process.execPath, [ - '-e', - 'globalThis.sessionStorage.setItem.length;globalThis.Storage.prototype.length', - ]); - - assert.strictEqual(cp.signal, null); - assert.strictEqual(cp.code, 1); - assert.strictEqual(cp.stdout, ''); - assert.match(cp.stderr, /Illegal invocation/); -}); - test('sessionStorage is not persisted', async () => { let cp = await spawnPromisified(process.execPath, [ '-pe', 'sessionStorage.foo = "barbaz"',