fix(cookies): properly filter cookies for subdomain (#36109)

This commit is contained in:
Yury Semikhatsky 2025-05-27 14:27:25 -07:00 committed by GitHub
parent 64e2b5989e
commit 9d70a305a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 38 additions and 3 deletions

View File

@ -18,7 +18,7 @@ import { kMaxCookieExpiresDateInSeconds } from './network';
import type * as channels from '@protocol/channels'; import type * as channels from '@protocol/channels';
class Cookie { export class Cookie {
private _raw: channels.NetworkCookie; private _raw: channels.NetworkCookie;
constructor(data: channels.NetworkCookie) { constructor(data: channels.NetworkCookie) {
this._raw = data; this._raw = data;

View File

@ -24,7 +24,7 @@ import { assert, constructURLBasedOnBaseURL, createProxyAgent, eventsHelper, mon
import { createGuid } from './utils/crypto'; import { createGuid } from './utils/crypto';
import { getUserAgent } from './utils/userAgent'; import { getUserAgent } from './utils/userAgent';
import { BrowserContext, verifyClientCertificates } from './browserContext'; import { BrowserContext, verifyClientCertificates } from './browserContext';
import { CookieStore, domainMatches, parseRawCookie } from './cookieStore'; import { Cookie, CookieStore, domainMatches, parseRawCookie } from './cookieStore';
import { MultipartFormData } from './formData'; import { MultipartFormData } from './formData';
import { SdkObject } from './instrumentation'; import { SdkObject } from './instrumentation';
import { ProgressController } from './progress'; import { ProgressController } from './progress';
@ -255,7 +255,11 @@ export abstract class APIRequestContext extends SdkObject {
private async _updateRequestCookieHeader(url: URL, headers: HeadersObject) { private async _updateRequestCookieHeader(url: URL, headers: HeadersObject) {
if (getHeader(headers, 'cookie') !== undefined) if (getHeader(headers, 'cookie') !== undefined)
return; return;
const cookies = await this._cookies(url); const contextCookies = await this._cookies(url);
// Browser context returns cookies with domain matching both .example.com and
// example.com. Those without leading dot are only sent when domain is strictly
// matching example.com, but not for sub.example.com.
const cookies = contextCookies.filter(c => new Cookie(c).matches(url));
if (cookies.length) { if (cookies.length) {
const valueArray = cookies.map(c => `${c.name}=${c.value}`); const valueArray = cookies.map(c => `${c.name}=${c.value}`);
setHeader(headers, 'cookie', valueArray.join('; ')); setHeader(headers, 'cookie', valueArray.join('; '));

View File

@ -121,6 +121,37 @@ it('should add session cookies to request', async ({ context, server }) => {
expect(req.headers.cookie).toEqual('username=John Doe'); expect(req.headers.cookie).toEqual('username=John Doe');
}); });
it('should filter cookies by domain', {
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/36069' }
}, async ({ context, server }) => {
await context.addCookies([{
name: 'first',
value: '1',
domain: 'playwright.dev',
path: '/',
expires: -1,
httpOnly: false,
secure: false,
sameSite: 'Lax',
}, {
name: 'second',
value: '2',
domain: '.playwright.dev',
path: '/',
expires: -1,
httpOnly: false,
secure: false,
sameSite: 'Lax',
}]);
const [req] = await Promise.all([
server.waitForRequest('/simple.json'),
context.request.get(`http://my.playwright.dev:${server.PORT}/simple.json`, {
__testHookLookup
} as any),
]);
expect(req.headers.cookie).toEqual('second=2');
});
for (const method of ['fetch', 'delete', 'get', 'head', 'patch', 'post', 'put'] as const) { for (const method of ['fetch', 'delete', 'get', 'head', 'patch', 'post', 'put'] as const) {
it(`${method} should support params passed as object`, async ({ context, server }) => { it(`${method} should support params passed as object`, async ({ context, server }) => {
const url = new URL(server.EMPTY_PAGE); const url = new URL(server.EMPTY_PAGE);