Skip to content

Commit 5772bf2

Browse files
Fix searchParams setter dropping the value when a URL is set (#2454)
1 parent a5b76bf commit 5772bf2

3 files changed

Lines changed: 55 additions & 3 deletions

File tree

source/core/options.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2067,7 +2067,8 @@ export default class Options {
20672067
if (is.string(value)) {
20682068
updated = new URLSearchParams(value);
20692069
} else if (value instanceof URLSearchParams) {
2070-
updated = value;
2070+
// Clone so the caller-owned object is not stored by reference.
2071+
updated = new URLSearchParams(value);
20712072
} else {
20722073
validateSearchParameters(value);
20732074

@@ -2100,9 +2101,10 @@ export default class Options {
21002101
searchParameters.append(key, value);
21012102
}
21022103
} else if (url) {
2103-
url.search = searchParameters.toString();
2104+
// Overrides the query string in the URL.
2105+
url.search = updated.toString();
21042106
} else {
2105-
this.#internals.searchParams = searchParameters;
2107+
this.#internals.searchParams = updated;
21062108
}
21072109
}
21082110

test/hooks.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,44 @@ test('beforeRequest allows modifications', withServer, async (t, server, got) =>
415415
t.is(body.foo, 'bar');
416416
});
417417

418+
test('beforeRequest allows overriding searchParams with an object', withServer, async (t, server, got) => {
419+
server.get('/', (request, response) => {
420+
response.end(request.url);
421+
});
422+
423+
const {body} = await got('', {
424+
searchParams: {old: 'value'},
425+
hooks: {
426+
beforeRequest: [
427+
options => {
428+
options.searchParams = {foo: 'bar'};
429+
},
430+
],
431+
},
432+
});
433+
434+
t.is(body, '/?foo=bar');
435+
});
436+
437+
test('beforeRequest allows overriding searchParams with a string', withServer, async (t, server, got) => {
438+
server.get('/', (request, response) => {
439+
response.end(request.url);
440+
});
441+
442+
const {body} = await got('', {
443+
searchParams: {old: 'value'},
444+
hooks: {
445+
beforeRequest: [
446+
options => {
447+
options.searchParams = 'foo=bar';
448+
},
449+
],
450+
},
451+
});
452+
453+
t.is(body, '/?foo=bar');
454+
});
455+
418456
test('beforeRequest is called with context', withServer, async (t, server, got) => {
419457
server.get('/', echoHeaders);
420458

test/normalize-arguments.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,18 @@ test('searchParams - multiple values for one key', t => {
167167
);
168168
});
169169

170+
test('searchParams - assigning a URLSearchParams clones it', t => {
171+
const searchParameters = new URLSearchParams('foo=bar');
172+
173+
const options = new Options();
174+
options.searchParams = searchParameters;
175+
176+
// Mutating the caller-owned object must not leak into the stored options.
177+
searchParameters.set('foo', 'changed');
178+
179+
t.is(options.searchParams.toString(), 'foo=bar');
180+
});
181+
170182
test('__proto__ in options does not cause prototype pollution', t => {
171183
const malicious = JSON.parse('{"method": "POST", "__proto__": {"injected": true}}');
172184
const options = new Options('https://example.com', malicious);

0 commit comments

Comments
 (0)