Skip to content

Commit b83e0af

Browse files
authored
fix: do not allow to override cookie header (#35168)
Behavior before this PR regarding 'Cookie' header already varied between browsers: - Chromium would not respect the 'Cookie' header if there was one with the same name in its cookie jar. If there was no corresponding cooke in the cookie jar, Chromium would apply one from the overrides. - WebKit would always take one from the cookie jar. To override cookies `addCookies` should be used instead. See https://docs.google.com/document/d/1LXMSP4GVxFLYJxA6z4upKqwkgD-TnVCGeX-daS4VQjk/edit?usp=sharing for mode details. Reference #35154
1 parent b9ccc5b commit b83e0af

File tree

7 files changed

+125
-9
lines changed

7 files changed

+125
-9
lines changed

docs/src/api/class-route.md

+4
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ The [`option: headers`] option applies to both the routed request and any redire
106106

107107
[`method: Route.continue`] will immediately send the request to the network, other matching handlers won't be invoked. Use [`method: Route.fallback`] If you want next matching handler in the chain to be invoked.
108108

109+
:::warning
110+
The `Cookie` header cannot be overridden using this method. If a value is provided, it will be ignored, and the cookie will be loaded from the browser's cookie store. To set custom cookies, use [`method: BrowserContext.addCookies`].
111+
:::
112+
109113
### option: Route.continue.url
110114
* since: v1.8
111115
- `url` <[string]>

packages/playwright-client/types/types.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -20786,6 +20786,11 @@ export interface Route {
2078620786
* request to the network, other matching handlers won't be invoked. Use
2078720787
* [route.fallback([options])](https://playwright.dev/docs/api/class-route#route-fallback) If you want next matching
2078820788
* handler in the chain to be invoked.
20789+
*
20790+
* **NOTE** The `Cookie` header cannot be overridden using this method. If a value is provided, it will be ignored,
20791+
* and the cookie will be loaded from the browser's cookie store. To set custom cookies, use
20792+
* [browserContext.addCookies(cookies)](https://playwright.dev/docs/api/class-browsercontext#browser-context-add-cookies).
20793+
*
2078920794
* @param options
2079020795
*/
2079120796
continue(options?: {

packages/playwright-core/src/server/network.ts

+2
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,8 @@ export class Route extends SdkObject {
329329
if (oldUrl.protocol !== newUrl.protocol)
330330
throw new Error('New URL must have same protocol as overridden URL');
331331
}
332+
if (overrides.headers)
333+
overrides.headers = overrides.headers?.filter(header => header.name.toLowerCase() !== 'cookie');
332334
this._request._setOverrides(overrides);
333335
if (!overrides.isFallback)
334336
this._request._context.emit(BrowserContext.Events.RequestContinued, this._request);

packages/playwright-core/types/types.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -20786,6 +20786,11 @@ export interface Route {
2078620786
* request to the network, other matching handlers won't be invoked. Use
2078720787
* [route.fallback([options])](https://playwright.dev/docs/api/class-route#route-fallback) If you want next matching
2078820788
* handler in the chain to be invoked.
20789+
*
20790+
* **NOTE** The `Cookie` header cannot be overridden using this method. If a value is provided, it will be ignored,
20791+
* and the cookie will be loaded from the browser's cookie store. To set custom cookies, use
20792+
* [browserContext.addCookies(cookies)](https://playwright.dev/docs/api/class-browsercontext#browser-context-add-cookies).
20793+
*
2078920794
* @param options
2079020795
*/
2079120796
continue(options?: {

tests/library/har.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ it('should record request overrides', async ({ contextFactory, server }, testInf
295295
expect(request.url).toBe(server.EMPTY_PAGE);
296296
expect(request.method).toBe('POST');
297297
expect(request.headers).toContainEqual({ name: 'custom', value: 'value' });
298-
expect(request.cookies).toContainEqual({ name: 'foo', value: 'bar' });
298+
expect(request.cookies).toEqual([]);
299299
expect(request.postData).toEqual({ 'mimeType': 'text/plain', 'params': [], 'text': 'Hi!' });
300300
});
301301

@@ -508,7 +508,7 @@ it('should record failed request overrides', async ({ contextFactory, server },
508508
expect(request.url).toBe(server.EMPTY_PAGE);
509509
expect(request.method).toBe('POST');
510510
expect(request.headers).toContainEqual({ name: 'custom', value: 'value' });
511-
expect(request.cookies).toContainEqual({ name: 'foo', value: 'bar' });
511+
expect(request.cookies).toEqual([]);
512512
expect(request.postData).toEqual({ 'mimeType': 'text/plain', 'params': [], 'text': 'Hi!' });
513513
});
514514

tests/page/page-request-continue.spec.ts

+102-3
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,96 @@ it('should respect set-cookie in redirect response', {
452452
expect.soft(await page.evaluate(() => document.cookie)).toBe('foo=bar');
453453
});
454454

455+
it('continue should not propagate cookie override to redirects', {
456+
annotation: [
457+
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/35168' },
458+
]
459+
}, async ({ page, server, browserName }) => {
460+
it.fixme(browserName === 'firefox', 'We currently clear all headers during interception in firefox');
461+
server.setRoute('/set-cookie', (request, response) => {
462+
response.writeHead(200, { 'Set-Cookie': 'foo=bar;' });
463+
response.end();
464+
});
465+
await page.goto(server.PREFIX + '/set-cookie');
466+
expect(await page.evaluate(() => document.cookie)).toBe('foo=bar');
467+
server.setRedirect('/redirect', server.PREFIX + '/empty.html');
468+
await page.route('**/redirect', route => {
469+
void route.continue({
470+
headers: {
471+
...route.request().headers(),
472+
cookie: 'override'
473+
}
474+
});
475+
});
476+
const [serverRequest] = await Promise.all([
477+
server.waitForRequest('/empty.html'),
478+
page.goto(server.PREFIX + '/redirect')
479+
]);
480+
expect(serverRequest.headers['cookie']).toBe('foo=bar');
481+
});
482+
483+
it('continue should not override cookie', {
484+
annotation: [
485+
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/35168' },
486+
]
487+
}, async ({ page, server, browserName }) => {
488+
it.fixme(browserName === 'firefox', 'We currently clear all headers during interception in firefox');
489+
server.setRoute('/set-cookie', (request, response) => {
490+
response.writeHead(200, { 'Set-Cookie': 'foo=bar;' });
491+
response.end();
492+
});
493+
await page.goto(server.PREFIX + '/set-cookie');
494+
expect(await page.evaluate(() => document.cookie)).toBe('foo=bar');
495+
await page.route('**', route => {
496+
void route.continue({
497+
headers: {
498+
...route.request().headers(),
499+
cookie: 'override',
500+
custom: 'value'
501+
}
502+
});
503+
});
504+
const [serverRequest] = await Promise.all([
505+
server.waitForRequest('/empty.html'),
506+
page.goto(server.EMPTY_PAGE)
507+
]);
508+
// Original cookie from the browser's cookie jar should be sent.
509+
expect(serverRequest.headers['cookie']).toBe('foo=bar');
510+
expect(serverRequest.headers['custom']).toBe('value');
511+
});
512+
513+
it('redirect after continue should be able to delete cookie', {
514+
annotation: [
515+
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/35168' },
516+
]
517+
}, async ({ page, server }) => {
518+
server.setRoute('/set-cookie', (request, response) => {
519+
response.writeHead(200, { 'Set-Cookie': 'foo=bar;' });
520+
response.end();
521+
});
522+
await page.goto(server.PREFIX + '/set-cookie');
523+
expect(await page.evaluate(() => document.cookie)).toBe('foo=bar');
524+
525+
server.setRoute('/delete-cookie', (request, response) => {
526+
response.writeHead(200, { 'Set-Cookie': 'foo=bar; expires=Thu, 01 Jan 1970 00:00:00 GMT' });
527+
response.end();
528+
});
529+
server.setRedirect('/redirect', '/delete-cookie');
530+
await page.route('**/redirect', route => {
531+
void route.continue({
532+
headers: {
533+
...route.request().headers(),
534+
}
535+
});
536+
});
537+
await page.goto(server.PREFIX + '/redirect');
538+
const [serverRequest] = await Promise.all([
539+
server.waitForRequest('/empty.html'),
540+
page.goto(server.EMPTY_PAGE)
541+
]);
542+
expect(serverRequest.headers['cookie']).toBeFalsy();
543+
});
544+
455545
it('continue should propagate headers to redirects', {
456546
annotation: [
457547
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/28758' },
@@ -536,6 +626,7 @@ it('propagate headers same origin redirect', {
536626
annotation: [
537627
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/13106' },
538628
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/32045' },
629+
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/35154' },
539630
]
540631
}, async ({ page, server }) => {
541632
await page.goto(server.PREFIX + '/empty.html');
@@ -547,7 +638,7 @@ it('propagate headers same origin redirect', {
547638
'Access-Control-Allow-Origin': server.PREFIX,
548639
'Access-Control-Allow-Credentials': 'true',
549640
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, DELETE',
550-
'Access-Control-Allow-Headers': 'authorization,custom',
641+
'Access-Control-Allow-Headers': 'authorization,cookie,custom',
551642
});
552643
response.end();
553644
return;
@@ -557,6 +648,7 @@ it('propagate headers same origin redirect', {
557648
response.end('done');
558649
});
559650
await server.setRedirect('/redirect', '/something');
651+
await page.evaluate(() => document.cookie = 'a=b');
560652
const text = await page.evaluate(async url => {
561653
const data = await fetch(url, {
562654
headers: {
@@ -570,6 +662,7 @@ it('propagate headers same origin redirect', {
570662
expect(text).toBe('done');
571663
const serverRequest = await serverRequestPromise;
572664
expect.soft(serverRequest.headers['authorization']).toBe('credentials');
665+
expect.soft(serverRequest.headers['cookie']).toBe('a=b');
573666
expect.soft(serverRequest.headers['custom']).toBe('foo');
574667
});
575668

@@ -620,6 +713,7 @@ it('propagate headers cross origin redirect', {
620713
annotation: [
621714
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/13106' },
622715
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/32045' },
716+
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/35154' },
623717
]
624718
}, async ({ page, server, isAndroid }) => {
625719
it.fixme(isAndroid, 'receives authorization:credentials header');
@@ -633,7 +727,7 @@ it('propagate headers cross origin redirect', {
633727
'Access-Control-Allow-Origin': server.PREFIX,
634728
'Access-Control-Allow-Credentials': 'true',
635729
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, DELETE',
636-
'Access-Control-Allow-Headers': 'authorization,custom',
730+
'Access-Control-Allow-Headers': 'authorization,cookie,custom',
637731
});
638732
response.end();
639733
return;
@@ -649,6 +743,7 @@ it('propagate headers cross origin redirect', {
649743
response.writeHead(301, { location: `${server.CROSS_PROCESS_PREFIX}/something` });
650744
response.end();
651745
});
746+
await page.evaluate(() => document.cookie = 'a=b');
652747
const text = await page.evaluate(async url => {
653748
const data = await fetch(url, {
654749
headers: {
@@ -663,13 +758,15 @@ it('propagate headers cross origin redirect', {
663758
const serverRequest = await serverRequestPromise;
664759
// Authorization header not propagated to cross-origin redirect.
665760
expect.soft(serverRequest.headers['authorization']).toBeFalsy();
761+
expect.soft(serverRequest.headers['cookie']).toBeFalsy();
666762
expect.soft(serverRequest.headers['custom']).toBe('foo');
667763
});
668764

669765
it('propagate headers cross origin redirect after interception', {
670766
annotation: [
671767
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/13106' },
672768
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/32045' },
769+
{ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/35154' },
673770
]
674771
}, async ({ page, server, browserName }) => {
675772
await page.goto(server.PREFIX + '/empty.html');
@@ -681,7 +778,7 @@ it('propagate headers cross origin redirect after interception', {
681778
'Access-Control-Allow-Origin': server.PREFIX,
682779
'Access-Control-Allow-Credentials': 'true',
683780
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, DELETE',
684-
'Access-Control-Allow-Headers': 'authorization,custom',
781+
'Access-Control-Allow-Headers': 'authorization,cookie,custom',
685782
});
686783
response.end();
687784
return;
@@ -697,6 +794,7 @@ it('propagate headers cross origin redirect after interception', {
697794
response.writeHead(301, { location: `${server.CROSS_PROCESS_PREFIX}/something` });
698795
response.end();
699796
});
797+
await page.evaluate(() => document.cookie = 'a=b');
700798
await page.route('**/redirect', async route => {
701799
await route.continue({
702800
headers: {
@@ -721,6 +819,7 @@ it('propagate headers cross origin redirect after interception', {
721819
expect.soft(serverRequest.headers['authorization']).toBeFalsy();
722820
else
723821
expect.soft(serverRequest.headers['authorization']).toBe('credentials');
822+
expect.soft(serverRequest.headers['cookie']).toBeFalsy();
724823
expect.soft(serverRequest.headers['custom']).toBe('foo');
725824
});
726825

tests/page/page-route.spec.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,9 @@ it('should properly return navigation response when URL has cookies', async ({ p
166166
expect(response.status()).toBe(200);
167167
});
168168

169-
it('should override cookie header', async ({ page, server, browserName }) => {
169+
it('should not override cookie header', async ({ page, server, browserName }) => {
170170
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/16773' });
171-
it.fail(browserName !== 'firefox' && !browserName.includes('bidi'));
171+
it.fixme(browserName === 'firefox', 'We currently clear all headers during interception in firefox');
172172

173173
await page.goto(server.EMPTY_PAGE);
174174
await page.evaluate(() => document.cookie = 'original=value');
@@ -184,8 +184,9 @@ it('should override cookie header', async ({ page, server, browserName }) => {
184184
page.goto(server.EMPTY_PAGE),
185185
]);
186186

187-
expect(cookieValueInRoute).toBe('original=value');
188-
expect(serverReq.headers['cookie']).toBe('overridden=value');
187+
if (browserName !== 'webkit')
188+
expect.soft(cookieValueInRoute).toBe('original=value');
189+
expect.soft(serverReq.headers['cookie']).toBe('original=value');
189190
});
190191

191192
it('should show custom HTTP headers', async ({ page, server }) => {

0 commit comments

Comments
 (0)