From b2aa380da10e073b1226a5c5999a8fd909b1bbbb Mon Sep 17 00:00:00 2001 From: "Kamat, Trivikram" <16024985+trivikr@users.noreply.github.com> Date: Sun, 12 Jul 2026 17:29:53 -0700 Subject: [PATCH] stream: reject strings in ReadableStream.from Use Web IDL async_sequence semantics for ReadableStream.from(), which require the input to be an object. This causes primitive strings to throw a TypeError instead of being iterated by code point. Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: openai:gpt-5.6-sol --- lib/internal/webstreams/readablestream.js | 6 ++++++ test/fixtures/wpt/streams/readable-streams/from.any.js | 6 +----- test/parallel/test-webstream-readable-from.js | 5 +++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/internal/webstreams/readablestream.js b/lib/internal/webstreams/readablestream.js index d0e3bfafc19fcd..33a86b46ae430c 100644 --- a/lib/internal/webstreams/readablestream.js +++ b/lib/internal/webstreams/readablestream.js @@ -1448,6 +1448,12 @@ function createReadableStreamState() { } function readableStreamFromIterable(iterable) { + // Web IDL async_sequence conversion only accepts ECMAScript objects. + // In particular, strings must not be treated as iterable inputs here. + if ((typeof iterable !== 'object' || iterable === null) && + typeof iterable !== 'function') { + throw new ERR_ARG_NOT_ITERABLE(iterable); + } let stream; const iteratorGetter = iterable[SymbolAsyncIterator] ?? iterable[SymbolIterator]; if (iteratorGetter == null || typeof iteratorGetter !== 'function') { diff --git a/test/fixtures/wpt/streams/readable-streams/from.any.js b/test/fixtures/wpt/streams/readable-streams/from.any.js index 2b28a05fee398c..29f5472d8a69e3 100644 --- a/test/fixtures/wpt/streams/readable-streams/from.any.js +++ b/test/fixtures/wpt/streams/readable-streams/from.any.js @@ -18,11 +18,6 @@ const iterableFactories = [ return ['a', 'b'][Symbol.iterator](); }], - ['a string', () => { - // This iterates over the code points of the string. - return 'ab'; - }], - ['a Set', () => { return new Set(['a', 'b']); }], @@ -144,6 +139,7 @@ const badIterables = [ ['Object.create(null)', Object.create(null)], ['a function', () => 42], ['a symbol', Symbol()], + ['a string', 'ab'], ['an object with a non-callable @@iterator method', { [Symbol.iterator]: 42 }], diff --git a/test/parallel/test-webstream-readable-from.js b/test/parallel/test-webstream-readable-from.js index 0a0c6943c72556..599e10a8e6284e 100644 --- a/test/parallel/test-webstream-readable-from.js +++ b/test/parallel/test-webstream-readable-from.js @@ -8,6 +8,11 @@ assert.throws( { code: 'ERR_ARG_NOT_ITERABLE', name: 'TypeError' }, ); +assert.throws( + () => ReadableStream.from('abc'), + { code: 'ERR_ARG_NOT_ITERABLE', name: 'TypeError' }, +); + const invalidIterators = [ { [Symbol.iterator]: () => 42 }, { [Symbol.asyncIterator]: () => 42 },