Skip to content

fix(network): Include subdomains of localhost when including cookies #35771

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

simenbrekken
Copy link

Replaced the direct check for parsedURL.hostname === 'localhost' with a new helper function isLocalHostname, which also considers hostnames ending with .localhost as local. This improves flexibility and correctness when handling local hostnames.

See also RFC 6761 section 6.3 on handling of localhost.

This comment has been minimized.

@dgozman dgozman requested a review from yury-s April 28, 2025 14:14
Copy link
Member

@yury-s yury-s left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change looks good, but please add a test. Also would be nice to have an issue which describes the problem the PR is fixing.

@simenbrekken
Copy link
Author

@yury-s I added a failing test but I couldn't figure out why it's failing, any ideas?

This comment has been minimized.

Copy link
Member

@yury-s yury-s left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new test is failing across platforms, please fix.

res.setHeader('Set-Cookie', ['a=v; secure', 'b=v']);
res.end();
});
await request.get(`${`http://a.b.localhost:${server.PORT}`}/setcookie.html`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
await request.get(`${`http://a.b.localhost:${server.PORT}`}/setcookie.html`);
await request.get(`http://a.b.localhost:${server.PORT}/setcookie.html`);

@simenbrekken
Copy link
Author

@microsoft-github-policy-service agree

@simen-ardoq
Copy link

@yury-s I'm not sure if I've added isLocalHostname to the wrong place, I am pretty sure my tests are correct but I could use some help figuring if there where the localhost detection happens and I'd be happy to give it another go.

@yury-s
Copy link
Member

yury-s commented May 8, 2025

@yury-s I'm not sure if I've added isLocalHostname to the wrong place, I am pretty sure my tests are correct but I could use some help figuring if there where the localhost detection happens and I'd be happy to give it another go.

Global fetch uses its own cookie store, so you'll need something like this:

diff --git i/packages/playwright-core/src/server/cookieStore.ts w/packages/playwright-core/src/server/cookieStore.ts
index 34ebc7287..0ba6bd7d6 100644
--- i/packages/playwright-core/src/server/cookieStore.ts
+++ w/packages/playwright-core/src/server/cookieStore.ts
@@ -30,7 +30,7 @@ class Cookie {
 
   // https://datatracker.ietf.org/doc/html/rfc6265#section-5.4
   matches(url: URL): boolean {
-    if (this._raw.secure && (url.protocol !== 'https:' && url.hostname !== 'localhost'))
+    if (this._raw.secure && (url.protocol !== 'https:' && url.hostname !== 'localhost' && !url.hostname.endsWith('.localhost')))
       return false;
     if (!domainMatches(url.hostname, this._raw.domain))
       return false;

@yury-s
Copy link
Member

yury-s commented May 8, 2025

See also RFC 6761 section 6.3 on handling of localhost.

Do all browsers actually treat '*.localhost' subdomains as secure?

@simenbrekken
Copy link
Author

Do all browsers actually treat '*.localhost' subdomains as secure?

I created a test repo today that shows Safari, Webkit and Chromium treat subdomains of localhost as secure, at least when it comes to cookies:

https://github.com/simenbrekken/playwright-localhost-secure-cookie-test

Copy link
Contributor

github-actions bot commented May 8, 2025

Test results for "tests 1"

1 failed
❌ [firefox-page] › page/page-evaluate.spec.ts:424:3 › should throw for too deep reference chain @firefox-ubuntu-22.04-node18

4 flaky ⚠️ [firefox-library] › library/inspector/cli-codegen-1.spec.ts:986:7 › cli codegen › should not throw csp directive violation errors @firefox-ubuntu-22.04-node18
⚠️ [installation tests] › playwright-electron-should-work.spec.ts:21:5 › electron should work @package-installations-macos-latest
⚠️ [webkit-library] › library/ignorehttpserrors.spec.ts:30:3 › should isolate contexts @webkit-ubuntu-22.04-node18
⚠️ [playwright-test] › ui-mode-test-watch.spec.ts:145:5 › should watch all @windows-latest-node18-1

39161 passed, 803 skipped
✔️✔️✔️

Merge workflow run.

@yury-s
Copy link
Member

yury-s commented May 10, 2025

This is the behavior that I see with the test page from your repo:

Cookie Type Site Chrome Firefox Safari
HttpOnly http://localhost ✅ Set & Sent ✅ Set & Sent ✅ Set & Sent
HttpOnly http://non-localhost ✅ Set & Sent ✅ Set & Sent ✅ Set & Sent
HttpOnly + Secure http://localhost ✅ Set & Sent ✅ Set & Sent ❌ Ignored
HttpOnly + Secure http://test.localhost ✅ Set & Sent ✅ Set & Sent ❌ Ignored
HttpOnly + Secure http://non-localhost ❌ Ignored ❌ Ignored ❌ Ignored

@@ -43,14 +43,18 @@ export function filterCookies(cookies: channels.NetworkCookie[], urls: string[])
continue;
if (!parsedURL.pathname.startsWith(c.path))
continue;
if (parsedURL.protocol !== 'https:' && parsedURL.hostname !== 'localhost' && c.secure)
if (parsedURL.protocol !== 'https:' && !isLocalHostname(parsedURL.hostname) && c.secure)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you are changing logic for browsers too, please add a test for the cookies received by the browsers from the server. You can use proxyServer fixture, see this test for an example, you'll just need to use page.goto() for navigation. My understanding is that before this change BrowserContext.cookies('http://subdomain.localhost:3000/') would not return secure cookies and now it does.

Copy link
Member

@yury-s yury-s left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change looks good otherwise, thanks!

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.

3 participants