Skip to content

fix(ext/web): reject non-object iterator results in URLSearchParams - #37124

Open
OMD-123 wants to merge 1 commit into
denoland:mainfrom
OMD-123:fix/web/url-search-params-iterator-type-37117
Open

OMD-123 wants to merge 1 commit into
denoland:mainfrom
OMD-123:fix/web/url-search-params-iterator-type-37117

Conversation

@OMD-123

@OMD-123 OMD-123 commented Nov 3, 2026 •

Copy link
Copy Markdown

fix(ext/web): reject non-object iterator results in URLSearchParams

The URLSearchParams constructor built its pair list by calling next() on the input iterator and then reading res.done. It only checked for null/undefined, so a primitive iterator result was accepted.

Web IDL's "create sequence from iterable" uses IteratorStepValue, which calls IteratorNext. IteratorNext throws a TypeError when the iterator result is not an Object, and it does so before done is read. A primitive result whose prototype happens to expose done was therefore treated as a completed iteration instead of raising a TypeError.

Check that the iterator result is an object before reading done, so the constructor matches the Web IDL sequence conversion.

// Before: accepted, and `done` was read
let reads = 0;
Object.defineProperty(Number.prototype, "done", {
  configurable: true,
  get() { reads++; return true; },
});
const iterable = {
  [Symbol.iterator]() {
    return { next() { return 1; } };
  },
};
new URLSearchParams(iterable); // accepted

// After the fix: TypeError, and `reads` stays 0

This matches the behavior of Node.js 26.13.0.

Fixes #37117

Testing

  • Added urlSearchParamsRejectsNonObjectIteratorResult to tests/unit/url_search_params_test.ts. It asserts that a non-object iterator result raises a TypeError and that done is never read on the primitive result.
  • The test was red-verified against the unpatched ext/web/00_url.js: without the fix the constructor accepts the primitive result and reads done once; with the fix it throws a TypeError and reads done zero times.
  • Normal construction paths (array of pairs, Map, generator, and iterators that terminate with done: true) produce identical results before and after the change.

AI usage disclosure

This PR was prepared with AI assistance (Claude / Hermes). The reproduction came from the linked issue, the fix follows the Web IDL and ECMA-262 specifications cited above, and the change was verified by running the affected code path directly before and after the patch.

The `URLSearchParams` constructor built its pair list by calling `next()`
on the input iterator and then reading `res.done`. It only checked for
`null`/`undefined`, so a primitive iterator result was accepted.

Web IDL's "create sequence from iterable" uses `IteratorStepValue`, which
calls `IteratorNext`. `IteratorNext` throws a TypeError when the iterator
result is not an Object, and it does so before `done` is read. A primitive
result whose prototype happens to expose `done` was therefore treated as a
completed iteration instead of raising a TypeError.

Check that the iterator result is an object before reading `done`, so the
constructor matches the Web IDL sequence conversion.

Fixes denoland#37117
@OMD-123 OMD-123 changed the title p fix(ext/web): reject non-object iterator results in URLSearchParams Nov 3, 2026
@OMD-123

OMD-123 commented Nov 3, 2026

Copy link
Copy Markdown
Author

Verified against a real Deno 2.9.8 binary (v8 15.0.245.2-rusty).

Running the issue's reproduction on the stock runtime reproduces the bug:

$ deno run --no-config repro.js
{"outcome":"accepted: \"\"","reads":1}

Node.js 26 rejects it:

$ node repro.js
{"outcome":"TypeError","reads":0}

Running the patched constructor logic from ext/web/00_url.js side by side with the stock global in the same Deno process:

PATCHED logic : {"outcome":"TypeError","reads":0}
STOCK global  : {"outcome":"accepted: {}","reads":1}
normal pairs  : [["a","1"],["b","2"]]
normal map    : [["x","y"]]

So the patched path throws before done is read, matching Node.js, and normal construction paths are unaffected.

NetOpWibby pushed a commit to theende/ende that referenced this pull request Nov 7, 2026
The `URLSearchParams` constructor built its pair list by calling `next()`
on the input iterator and then reading `res.done`. It only checked for
`null`/`undefined`, so a primitive iterator result was accepted.

Web IDL's "create sequence from iterable" uses `IteratorStepValue`, which
calls `IteratorNext`. `IteratorNext` throws a TypeError when the iterator
result is not an Object, and it does so before `done` is read. A primitive
result whose prototype happens to expose `done` was therefore treated as a
completed iteration instead of raising a TypeError.

Check that the iterator result is an object before reading `done`, so the
constructor matches the Web IDL sequence conversion.

Ende change: upstream's `typeof res !== "object"` also rejected functions,
which are Objects; they are accepted again, with a test.

(cherry picked from commit 4225ff7d0ae7fb2e4d6c95412e51bb48693278b2)

Upstream: denoland/deno#37124
@CLAassistant

CLAassistant commented Nov 8, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

URLSearchParams reads done before validating an outer iterator result

2 participants