From 7ae6c00ffed8290b2343ea46e38ed8f9bb424cbb Mon Sep 17 00:00:00 2001 From: Colin Casey Date: Thu, 25 Aug 2022 14:43:51 -0300 Subject: [PATCH] fix: allow set cookies with localhost Adding more tests to cover the breaking use cases noted in #246. e.g.;. * `new CookieJar().setCookieSync("settingThisShouldPass=true; Domain=localhost; Path=/;", "http://localhost")` Also modifies the assertion for a test introduced in #221 that may be incorrect. --- lib/pubsuffix-psl.js | 26 +++++++------- test/api_test.js | 76 ++++++++++++++++++++++------------------- test/regression_test.js | 18 +++++++++- 3 files changed, 70 insertions(+), 50 deletions(-) diff --git a/lib/pubsuffix-psl.js b/lib/pubsuffix-psl.js index 83398555..b6649346 100644 --- a/lib/pubsuffix-psl.js +++ b/lib/pubsuffix-psl.js @@ -40,28 +40,28 @@ const SPECIAL_USE_DOMAINS = [ "test" ]; +const SPECIAL_TREATMENT_DOMAINS = ["localhost", "invalid"]; + function getPublicSuffix(domain, options = {}) { const domainParts = domain.split("."); const topLevelDomain = domainParts[domainParts.length - 1]; const allowSpecialUseDomain = !!options.allowSpecialUseDomain; const ignoreError = !!options.ignoreError; - if ( - allowSpecialUseDomain && - domainParts.length > 1 && - SPECIAL_USE_DOMAINS.includes(topLevelDomain) - ) { - // If the right-most label in the name is a special-use domain (e.g. bananas.apple.localhost), - // then don't use PSL. This is because most special-use domains are not listed on PSL. - const secondLevelDomain = domainParts[domainParts.length - 2]; - // In aforementioned example, the eTLD/pubSuf will be apple.localhost - return `${secondLevelDomain}.${topLevelDomain}`; + if (allowSpecialUseDomain && SPECIAL_USE_DOMAINS.includes(topLevelDomain)) { + if (domainParts.length > 1) { + const secondLevelDomain = domainParts[domainParts.length - 2]; + // In aforementioned example, the eTLD/pubSuf will be apple.localhost + return `${secondLevelDomain}.${topLevelDomain}`; + } else if (SPECIAL_TREATMENT_DOMAINS.includes(topLevelDomain)) { + // For a single word special use domain, e.g. 'localhost' or 'invalid', per RFC 6761, + // "Application software MAY recognize {localhost/invalid} names as special, or + // MAY pass them to name resolution APIs as they would for other domain names." + return `${topLevelDomain}`; + } } if (!ignoreError && SPECIAL_USE_DOMAINS.includes(topLevelDomain)) { - if (allowSpecialUseDomain) { - return topLevelDomain; - } throw new Error( `Cookie has domain set to the public suffix "${topLevelDomain}" which is a special use domain. To allow this, configure your CookieJar with {allowSpecialUseDomain:true, rejectPublicSuffixes: false}.` ); diff --git a/test/api_test.js b/test/api_test.js index fae25378..73fd3ed6 100644 --- a/test/api_test.js +++ b/test/api_test.js @@ -591,46 +591,50 @@ function allowSpecialUseOptionVows() { "test" ]; + const specialTreatmentDomains = ["localhost", "invalid"]; + return specialUseDomains.reduce((vows, specialUseDomain) => { - vows[ - `cookie jar with allowSpecialUseDomain set to the default value and domain is "${specialUseDomain}"` - ] = { - topic: function() { - const cb = this.callback; - const cj = new CookieJar(); - cj.setCookie( - `settingThisShouldPass=true; Domain=${specialUseDomain}; Path=/;`, - `http://${specialUseDomain}`, - at(-1), - (err, cookie) => { - cb(err, { cj: cj, cookie: cookie }); - } - ); - }, - "set the cookie": function(t) { - assert.ok(t.cookie, "didn't set?!"); - assert.equal(t.cookie.key, "settingThisShouldPass"); - }, - "then, retrieving": { - topic: function(t) { + if (specialTreatmentDomains.includes(specialUseDomain)) { + vows[ + `cookie jar with allowSpecialUseDomain set to the default value and domain is "${specialUseDomain}"` + ] = { + topic: function() { const cb = this.callback; - setTimeout(() => { - t.cj.getCookies( - `http://${specialUseDomain}`, - { http: true }, - (err, cookies) => { - t.cookies = cookies; - cb(err, t); - } - ); - }, 2000); + const cj = new CookieJar(); + cj.setCookie( + `settingThisShouldPass=true; Domain=${specialUseDomain}; Path=/;`, + `http://${specialUseDomain}`, + at(-1), + (err, cookie) => { + cb(err, { cj: cj, cookie: cookie }); + } + ); }, - "got the cookie": function(t) { - assert.lengthOf(t.cookies, 1); - assert.equal(t.cookies[0].key, "settingThisShouldPass"); + "set the cookie": function(t) { + assert.ok(t.cookie, "didn't set?!"); + assert.equal(t.cookie.key, "settingThisShouldPass"); + }, + "then, retrieving": { + topic: function(t) { + const cb = this.callback; + setTimeout(() => { + t.cj.getCookies( + `http://${specialUseDomain}`, + { http: true }, + (err, cookies) => { + t.cookies = cookies; + cb(err, t); + } + ); + }, 2000); + }, + "got the cookie": function(t) { + assert.lengthOf(t.cookies, 1); + assert.equal(t.cookies[0].key, "settingThisShouldPass"); + } } - } - }; + }; + } vows[ `cookie jar with allowSpecialUseDomain set to the default value and domain is "dev.${specialUseDomain}"` diff --git a/test/regression_test.js b/test/regression_test.js index 44f9b83e..f9ff15b9 100644 --- a/test/regression_test.js +++ b/test/regression_test.js @@ -197,10 +197,26 @@ vows return cookieJar.setCookieSync( "a=b; Domain=localhost", "http://localhost" - ); + ); // Users are free to use localhost names as they would any other domain names. [RFC 6761, Sec. 6.3.1] + }, + works: function(err, c) { + assert.instanceOf(c, Cookie); + assert.match(c, /Domain=localhost/); + } + } + }, + { + "setCookie with localhost (localhost. domain) (GH-215)": { + topic: function() { + const cookieJar = new CookieJar(); + return cookieJar.setCookieSync( + "a=b; Domain=localhost.", + "http://localhost." + ); // Users are free to use localhost names as they would any other domain names. [RFC 6761, Sec. 6.3.1] }, works: function(err, c) { assert.instanceOf(c, Cookie); + assert.match(c, /Domain=localhost/); } } },