From 3b6d8643309d7d2b63a9aa3f14afa713052c168a Mon Sep 17 00:00:00 2001 From: Steve Dudenhoeffer Date: Tue, 17 Mar 2026 02:37:47 +0000 Subject: [PATCH] Preserve cookie security attributes in updateCookies round-trip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chromium's Cookies() API can lose or normalize Secure, SameSite, and HttpOnly attributes during the AddCookies → navigate → Cookies() round-trip. This caused cookies like cf_clearance (set with Secure=true, SameSite=None) to be overwritten with Chromium's defaults (Secure=false, SameSite=Lax). Now updateCookies() looks up existing cookies in the jar first. For cookies that already exist, only Value and Expires are updated — security attributes are preserved from the original. New cookies from the server are still written with all their attributes. Co-Authored-By: Claude Opus 4.6 --- playwright.go | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/playwright.go b/playwright.go index df8fb4b..684c81a 100644 --- a/playwright.go +++ b/playwright.go @@ -219,11 +219,35 @@ func (b playWrightBrowser) updateCookies(_ context.Context, page playwright.Page return fmt.Errorf("error getting cookies from browser: %w", err) } + // Build a lookup of existing cookies so we can preserve their security + // attributes. Chromium's Cookies() API can lose or normalize Secure, + // SameSite, and HttpOnly during the AddCookies → navigate → Cookies() + // round-trip, so we only update Value and Expires for cookies that + // already exist in the jar. + existing, err := b.cookieJar.Get(page.URL()) + if err != nil { + return fmt.Errorf("error getting existing cookies from jar: %w", err) + } + type cookieKey struct{ Name, Path string } + existingMap := make(map[cookieKey]Cookie, len(existing)) + for _, c := range existing { + existingMap[cookieKey{c.Name, c.Path}] = c + } + for _, cookie := range cookies { // TODO: add support for deleting cookies from the jar which are deleted in the browser - err = b.cookieJar.Set(playwrightCookieToCookie(cookie)) + c := playwrightCookieToCookie(cookie) - if err != nil { + if prev, ok := existingMap[cookieKey{c.Name, c.Path}]; ok { + // Preserve the original security attributes; only update + // Value and Expires which are the fields that legitimately + // change during navigation. + c.Secure = prev.Secure + c.HttpOnly = prev.HttpOnly + c.SameSite = prev.SameSite + } + + if err = b.cookieJar.Set(c); err != nil { return fmt.Errorf("error setting cookie in cookie jar: %w", err) } }