From 25efa447997f74d5881edd144525c3fd7db945a4 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Thu, 11 Jun 2026 11:24:59 +0200 Subject: [PATCH] fix(cookies): preserve values and parse SameSite strictly Signed-off-by: Matteo Collina (cherry picked from commit 5655ea43af4add559ee5f94b6cd72e08a55aa621) --- lib/web/cookies/parse.js | 39 +++++++++++++++--------------------- test/cookie/cookies.js | 43 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 23 deletions(-) diff --git a/lib/web/cookies/parse.js b/lib/web/cookies/parse.js index 3c48c26b93f..b3c25fb9423 100644 --- a/lib/web/cookies/parse.js +++ b/lib/web/cookies/parse.js @@ -275,32 +275,25 @@ function parseUnparsedAttributes (unparsedAttributes, cookieAttributeList = {}) // If the attribute-name case-insensitively matches the string // "SameSite", the user agent MUST process the cookie-av as follows: - // 1. Let enforcement be "Default". - let enforcement = 'Default' - const attributeValueLowercase = attributeValue.toLowerCase() - // 2. If cookie-av's attribute-value is a case-insensitive match for - // "None", set enforcement to "None". - if (attributeValueLowercase.includes('none')) { - enforcement = 'None' - } - // 3. If cookie-av's attribute-value is a case-insensitive match for - // "Strict", set enforcement to "Strict". - if (attributeValueLowercase.includes('strict')) { - enforcement = 'Strict' + // 1. If cookie-av's attribute-value is a case-insensitive match for + // "None", append an attribute to the cookie-attribute-list with an + // attribute-name of "SameSite" and an attribute-value of "None". + if (attributeValueLowercase === 'none') { + cookieAttributeList.sameSite = 'None' + } else if (attributeValueLowercase === 'strict') { + // 2. If cookie-av's attribute-value is a case-insensitive match for + // "Strict", append an attribute to the cookie-attribute-list with + // an attribute-name of "SameSite" and an attribute-value of + // "Strict". + cookieAttributeList.sameSite = 'Strict' + } else if (attributeValueLowercase === 'lax') { + // 3. If cookie-av's attribute-value is a case-insensitive match for + // "Lax", append an attribute to the cookie-attribute-list with an + // attribute-name of "SameSite" and an attribute-value of "Lax". + cookieAttributeList.sameSite = 'Lax' } - - // 4. If cookie-av's attribute-value is a case-insensitive match for - // "Lax", set enforcement to "Lax". - if (attributeValueLowercase.includes('lax')) { - enforcement = 'Lax' - } - - // 5. Append an attribute to the cookie-attribute-list with an - // attribute-name of "SameSite" and an attribute-value of - // enforcement. - cookieAttributeList.sameSite = enforcement } else { cookieAttributeList.unparsed ??= [] diff --git a/test/cookie/cookies.js b/test/cookie/cookies.js index 711f26a0351..4f459b331eb 100644 --- a/test/cookie/cookies.js +++ b/test/cookie/cookies.js @@ -600,6 +600,49 @@ test('Set-Cookie parser', () => { assert.deepEqual(getSetCookies(headers), []) }) +test('Set-Cookie parser does not percent-decode cookie values', () => { + assert.deepEqual( + getSetCookies(new Headers({ + 'set-cookie': 'token=legit%0d%0aSet-Cookie:%20evil=injected%3B%20Path%3D/' + })), + [{ + name: 'token', + value: 'legit%0d%0aSet-Cookie:%20evil=injected%3B%20Path%3D/' + }] + ) + + assert.deepEqual(getSetCookies(new Headers({ + 'set-cookie': 'data=prefix%00suffix' + })), [{ + name: 'data', + value: 'prefix%00suffix' + }]) +}) + +test('Set-Cookie parser only accepts exact SameSite values', () => { + assert.deepEqual(getSetCookies(new Headers({ + 'set-cookie': 'a=b; SameSite=none' + })), [{ + name: 'a', + value: 'b', + sameSite: 'None' + }]) + + assert.deepEqual(getSetCookies(new Headers({ + 'set-cookie': 'a=b; SameSite=StrictLax' + })), [{ + name: 'a', + value: 'b' + }]) + + assert.deepEqual(getSetCookies(new Headers({ + 'set-cookie': 'a=b; SameSite=NoneOfYourBusiness' + })), [{ + name: 'a', + value: 'b' + }]) +}) + test('Cookie setCookie throws if headers is not of type Headers', () => { class Headers { [Symbol.toStringTag] = 'CustomHeaders'