diff --git a/CHANGELOG.md b/CHANGELOG.md index 83cecb5a..94427e49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,36 +1,5 @@ # Changelog -All notable changes to this project will be documented in this file. - -## 4.X.X - -### Minor Changes -- Added parameter checking to setCookie so as to error out when no URL was passed in - -## X.Y.Z - -### Minor Changes -- Added loose mode to the serialized options. Now a serialized cookie jar with loose mode enabled will honor that flag when deserialized. -- Added allowSpecialUseDomain and prefixSecurity to the serialized options. Now any options accepted passed in to the cookie jar will be honored when serialized and deserialized. -- Added handling of IPv6 host names so that they would work with tough cookie. - -## 4.0.0 - -### Breaking Changes (Major Version) - -- Modernized JS Syntax - - Use ESLint and Prettier to apply consistent, modern formatting (add dependency on `universalify`, `eslint` and `prettier`) -- Upgraded version dependencies for `psl` and `async` -- Re-order parameters for `findCookies()` - callback fn has to be last in order to comply with `universalify` -- Use Classes instead of function prototypes to define classes - - Might break people using `.call()` to do inheritance using function prototypes - -### Minor Changes -- SameSite cookie support -- Cookie prefix support -- Support for promises -- '.local' support -- Numerous bug fixes! - - +All notable changes to this project can be found at on the [Releases](https://github.com/salesforce/tough-cookie/releases) +page. diff --git a/lib/pubsuffix-psl.js b/lib/pubsuffix-psl.js index d5555523..b6649346 100644 --- a/lib/pubsuffix-psl.js +++ b/lib/pubsuffix-psl.js @@ -40,22 +40,25 @@ 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)) { diff --git a/lib/version.js b/lib/version.js index 1b1faea1..0cba2a81 100644 --- a/lib/version.js +++ b/lib/version.js @@ -1,2 +1,2 @@ // generated by genversion -module.exports = '4.1.1' +module.exports = '4.1.2' diff --git a/package.json b/package.json index e56c2386..4bc8ccea 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "RFC6265", "RFC2965" ], - "version": "4.1.1", + "version": "4.1.2", "homepage": "https://github.com/salesforce/tough-cookie", "repository": { "type": "git", diff --git a/test/api_test.js b/test/api_test.js index 42e6166e..73fd3ed6 100644 --- a/test/api_test.js +++ b/test/api_test.js @@ -591,9 +591,53 @@ function allowSpecialUseOptionVows() { "test" ]; + const specialTreatmentDomains = ["localhost", "invalid"]; + return specialUseDomains.reduce((vows, specialUseDomain) => { + if (specialTreatmentDomains.includes(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) { + 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 "${specialUseDomain}"` + `cookie jar with allowSpecialUseDomain set to the default value and domain is "dev.${specialUseDomain}"` ] = { topic: function() { const cb = this.callback; @@ -633,7 +677,7 @@ function allowSpecialUseOptionVows() { }; vows[ - `cookie jar with allowSpecialUseDomain enabled and domain is "${specialUseDomain}"` + `cookie jar with allowSpecialUseDomain enabled and domain is "dev.${specialUseDomain}"` ] = { topic: function() { const cb = this.callback; @@ -676,7 +720,7 @@ function allowSpecialUseOptionVows() { }; vows[ - `cookie jar with allowSpecialUseDomain disabled and domain is "${specialUseDomain}"` + `cookie jar with allowSpecialUseDomain disabled and domain is "dev.${specialUseDomain}"` ] = { topic: function() { const cj = new CookieJar(new tough.MemoryCookieStore(), { diff --git a/test/regression_test.js b/test/regression_test.js index 29d21c0e..f9ff15b9 100644 --- a/test/regression_test.js +++ b/test/regression_test.js @@ -197,12 +197,26 @@ vows return cookieJar.setCookieSync( "a=b; Domain=localhost", "http://localhost" - ); // when domain set to 'localhost', will throw 'Error: Cookie has domain set to a public suffix' + ); // Users are free to use localhost names as they would any other domain names. [RFC 6761, Sec. 6.3.1] }, works: function(err, c) { - // localhost as domain throws an error, cookie should not be defined - assert.instanceOf(err, Error); - assert.isUndefined(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/); } } }, @@ -210,7 +224,7 @@ vows "setCookie with localhost (GH-215) (null domain)": { topic: function() { const cookieJar = new CookieJar(); - return cookieJar.setCookieSync("a=b; Domain=", "http://localhost"); // when domain set to 'localhost', will throw 'Error: Cookie has domain set to a public suffix' + return cookieJar.setCookieSync("a=b; Domain=", "http://localhost"); }, works: function(c) { assert.instanceOf(c, Cookie);