| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -80,6 +80,33 @@ Arguments: | |||
| 80 | 80 | ||
| 81 | 81 | Returns: `Cookie[]` | |
| 82 | 82 | ||
| 83 | + ## `parseCookie(cookie)` | ||
| 84 | + | ||
| 85 | + Parses a single `Set-Cookie` header value into a `Cookie` object. | ||
| 86 | + | ||
| 87 | + ```js | ||
| 88 | + import { parseCookie } from 'undici' | ||
| 89 | + | ||
| 90 | + console.log(parseCookie('undici=getSetCookies; Secure; SameSite=Lax')) | ||
| 91 | + // { | ||
| 92 | + // name: 'undici', | ||
| 93 | + // value: 'getSetCookies', | ||
| 94 | + // secure: true, | ||
| 95 | + // sameSite: 'Lax' | ||
| 96 | + // } | ||
| 97 | + ``` | ||
| 98 | + | ||
| 99 | + Notes: | ||
| 100 | + | ||
| 101 | + * The cookie value is returned as it appears in the header. Percent-encoded sequences such as `%20` or `%0D%0A` are **not** decoded. | ||
| 102 | + * `sameSite` is only set for exact case-insensitive matches of `Strict`, `Lax`, or `None`. | ||
| 103 | + | ||
| 104 | + Arguments: | ||
| 105 | + | ||
| 106 | + * **cookie** `string` | ||
| 107 | + | ||
| 108 | + Returns: `Cookie | null` | ||
| 109 | + | ||
| 83 | 110 | ## `setCookie(headers, cookie)` | |
| 84 | 111 | ||
| 85 | 112 | Appends a cookie to the `Set-Cookie` header. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,7 +4,6 @@ const { collectASequenceOfCodePointsFast } = require('../infra') | |||
| 4 | 4 | const { maxNameValuePairSize, maxAttributeValueSize } = require('./constants') | |
| 5 | 5 | const { isCTLExcludingHtab } = require('./util') | |
| 6 | 6 | const assert = require('node:assert') | |
| 7 | - const { unescape: qsUnescape } = require('node:querystring') | ||
| 8 | 7 | ||
| 9 | 8 | /** | |
| 10 | 9 | * @description Parses the field-value attributes of a set-cookie header string. | |
@@ -82,7 +81,7 @@ function parseSetCookie (header) { | |||
| 82 | 81 | // store arbitrary data in a cookie-value SHOULD encode that data, for | |
| 83 | 82 | // example, using Base64 [RFC4648]. | |
| 84 | 83 | return { | |
| 85 | - name, value: qsUnescape(value), ...parseUnparsedAttributes(unparsedAttributes) | ||
| 84 | + name, value, ...parseUnparsedAttributes(unparsedAttributes) | ||
| 86 | 85 | } | |
| 87 | 86 | } | |
| 88 | 87 | ||
@@ -280,32 +279,25 @@ function parseUnparsedAttributes (unparsedAttributes, cookieAttributeList = {}) | |||
| 280 | 279 | // If the attribute-name case-insensitively matches the string | |
| 281 | 280 | // "SameSite", the user agent MUST process the cookie-av as follows: | |
| 282 | 281 | ||
| 283 | - // 1. Let enforcement be "Default". | ||
| 284 | - let enforcement = 'Default' | ||
| 285 | - | ||
| 286 | 282 | const attributeValueLowercase = attributeValue.toLowerCase() | |
| 287 | - // 2. If cookie-av's attribute-value is a case-insensitive match for | ||
| 288 | - // "None", set enforcement to "None". | ||
| 289 | - if (attributeValueLowercase.includes('none')) { | ||
| 290 | - enforcement = 'None' | ||
| 291 | - } | ||
| 292 | 283 | ||
| 293 | - // 3. If cookie-av's attribute-value is a case-insensitive match for | ||
| 294 | - // "Strict", set enforcement to "Strict". | ||
| 295 | - if (attributeValueLowercase.includes('strict')) { | ||
| 296 | - enforcement = 'Strict' | ||
| 284 | + // 1. If cookie-av's attribute-value is a case-insensitive match for | ||
| 285 | + // "None", append an attribute to the cookie-attribute-list with an | ||
| 286 | + // attribute-name of "SameSite" and an attribute-value of "None". | ||
| 287 | + if (attributeValueLowercase === 'none') { | ||
| 288 | + cookieAttributeList.sameSite = 'None' | ||
| 289 | + } else if (attributeValueLowercase === 'strict') { | ||
| 290 | + // 2. If cookie-av's attribute-value is a case-insensitive match for | ||
| 291 | + // "Strict", append an attribute to the cookie-attribute-list with | ||
| 292 | + // an attribute-name of "SameSite" and an attribute-value of | ||
| 293 | + // "Strict". | ||
| 294 | + cookieAttributeList.sameSite = 'Strict' | ||
| 295 | + } else if (attributeValueLowercase === 'lax') { | ||
| 296 | + // 3. If cookie-av's attribute-value is a case-insensitive match for | ||
| 297 | + // "Lax", append an attribute to the cookie-attribute-list with an | ||
| 298 | + // attribute-name of "SameSite" and an attribute-value of "Lax". | ||
| 299 | + cookieAttributeList.sameSite = 'Lax' | ||
| 297 | 300 | } | |
| 298 | - | ||
| 299 | - // 4. If cookie-av's attribute-value is a case-insensitive match for | ||
| 300 | - // "Lax", set enforcement to "Lax". | ||
| 301 | - if (attributeValueLowercase.includes('lax')) { | ||
| 302 | - enforcement = 'Lax' | ||
| 303 | - } | ||
| 304 | - | ||
| 305 | - // 5. Append an attribute to the cookie-attribute-list with an | ||
| 306 | - // attribute-name of "SameSite" and an attribute-value of | ||
| 307 | - // enforcement. | ||
| 308 | - cookieAttributeList.sameSite = enforcement | ||
| 309 | 301 | } else { | |
| 310 | 302 | cookieAttributeList.unparsed ??= [] | |
| 311 | 303 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -28,6 +28,7 @@ const { | |||
| 28 | 28 | deleteCookie, | |
| 29 | 29 | getCookies, | |
| 30 | 30 | getSetCookies, | |
| 31 | + parseCookie, | ||
| 31 | 32 | setCookie, | |
| 32 | 33 | Headers | |
| 33 | 34 | } = require('../..') | |
@@ -600,6 +601,41 @@ test('Set-Cookie parser', () => { | |||
| 600 | 601 | assert.deepEqual(getSetCookies(headers), []) | |
| 601 | 602 | }) | |
| 602 | 603 | ||
| 604 | + test('Set-Cookie parser does not percent-decode cookie values', () => { | ||
| 605 | + assert.deepEqual( | ||
| 606 | + parseCookie( | ||
| 607 | + 'token=legit%0d%0aSet-Cookie:%20evil=injected%3B%20Path%3D/' | ||
| 608 | + ), | ||
| 609 | + { | ||
| 610 | + name: 'token', | ||
| 611 | + value: 'legit%0d%0aSet-Cookie:%20evil=injected%3B%20Path%3D/' | ||
| 612 | + } | ||
| 613 | + ) | ||
| 614 | + | ||
| 615 | + assert.deepEqual(parseCookie('data=prefix%00suffix'), { | ||
| 616 | + name: 'data', | ||
| 617 | + value: 'prefix%00suffix' | ||
| 618 | + }) | ||
| 619 | + }) | ||
| 620 | + | ||
| 621 | + test('Set-Cookie parser only accepts exact SameSite values', () => { | ||
| 622 | + assert.deepEqual(parseCookie('a=b; SameSite=none'), { | ||
| 623 | + name: 'a', | ||
| 624 | + value: 'b', | ||
| 625 | + sameSite: 'None' | ||
| 626 | + }) | ||
| 627 | + | ||
| 628 | + assert.deepEqual(parseCookie('a=b; SameSite=StrictLax'), { | ||
| 629 | + name: 'a', | ||
| 630 | + value: 'b' | ||
| 631 | + }) | ||
| 632 | + | ||
| 633 | + assert.deepEqual(parseCookie('a=b; SameSite=NoneOfYourBusiness'), { | ||
| 634 | + name: 'a', | ||
| 635 | + value: 'b' | ||
| 636 | + }) | ||
| 637 | + }) | ||
| 638 | + | ||
| 603 | 639 | test('Cookie setCookie throws if headers is not of type Headers', () => { | |
| 604 | 640 | class Headers { | |
| 605 | 641 | [Symbol.toStringTag] = 'CustomHeaders' | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -51,13 +51,16 @@ describe('parseCookie(str)', function () { | |||
| 51 | 51 | assert.deepStrictEqual(parseCookie('f=;b='), { name: 'f', value: '', unparsed: ['b='] }) | |
| 52 | 52 | }) | |
| 53 | 53 | ||
| 54 | - it('should URL-decode values', function () { | ||
| 54 | + it('should preserve encoded values', function () { | ||
| 55 | 55 | assert.deepStrictEqual(parseCookie('foo="bar=123456789&name=Magic+Mouse"'), { | |
| 56 | 56 | name: 'foo', | |
| 57 | 57 | value: '"bar=123456789&name=Magic+Mouse"' | |
| 58 | 58 | }) | |
| 59 | 59 | ||
| 60 | - assert.deepStrictEqual(parseCookie('email=%20%22%2c%3b%2f'), { name: 'email', value: ' ",;/' }) | ||
| 60 | + assert.deepStrictEqual(parseCookie('email=%20%22%2c%3b%2f'), { | ||
| 61 | + name: 'email', | ||
| 62 | + value: '%20%22%2c%3b%2f' | ||
| 63 | + }) | ||
| 61 | 64 | }) | |
| 62 | 65 | ||
| 63 | 66 | it('should trim whitespace around key and value', function () { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments