Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for issue #153 #210

Merged
merged 3 commits into from Sep 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file.
### 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

Expand Down
20 changes: 19 additions & 1 deletion lib/cookie.js
Expand Up @@ -100,6 +100,20 @@ const PrefixSecurityEnum = Object.freeze({
// * support for IPv6 Scoped Literal ("%eth1") removed
// * lowercase hexadecimal only
const IP_REGEX_LOWERCASE =/(?:^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$)|(?:^(?:(?:[a-f\d]{1,4}:){7}(?:[a-f\d]{1,4}|:)|(?:[a-f\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|:[a-f\d]{1,4}|:)|(?:[a-f\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,2}|:)|(?:[a-f\d]{1,4}:){4}(?:(?::[a-f\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,3}|:)|(?:[a-f\d]{1,4}:){3}(?:(?::[a-f\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,4}|:)|(?:[a-f\d]{1,4}:){2}(?:(?::[a-f\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,5}|:)|(?:[a-f\d]{1,4}:){1}(?:(?::[a-f\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,6}|:)|(?::(?:(?::[a-f\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,7}|:)))$)/;
const IP_V6_REGEX = `
\\[?(?:
(?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|
(?:[a-fA-F\\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|:[a-fA-F\\d]{1,4}|:)|
(?:[a-fA-F\\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,2}|:)|
(?:[a-fA-F\\d]{1,4}:){4}(?:(?::[a-fA-F\\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,3}|:)|
(?:[a-fA-F\\d]{1,4}:){3}(?:(?::[a-fA-F\\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,4}|:)|
(?:[a-fA-F\\d]{1,4}:){2}(?:(?::[a-fA-F\\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,5}|:)|
(?:[a-fA-F\\d]{1,4}:){1}(?:(?::[a-fA-F\\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,6}|:)|
(?::(?:(?::[a-fA-F\\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,7}|:))
)(?:%[0-9a-zA-Z]{1,})?\\]?
`.replace(/\s*\/\/.*$/gm, '').replace(/\n/g, '').trim();
const IP_V6_REGEX_OBJECT = new RegExp(`^${IP_V6_REGEX}$`)


/*
* Parses a Natural number (i.e., non-negative integer) with either the
Expand Down Expand Up @@ -314,6 +328,10 @@ function canonicalDomain(str) {
}
str = str.trim().replace(/^\./, ""); // S4.1.2.3 & S5.2.3: ignore leading .

if (IP_V6_REGEX_OBJECT.test(str)) {
str = str.replace("[", "").replace("]", "");
}

// convert to IDN if any non-ASCII characters
if (punycode && /[^\u0001-\u007f]/.test(str)) {
str = punycode.toASCII(str);
Expand Down Expand Up @@ -1149,7 +1167,7 @@ class CookieJar {
// S5.3 step 5: public suffixes
if (this.rejectPublicSuffixes && cookie.domain) {
const suffix = pubsuffix.getPublicSuffix(cookie.cdomain());
if (suffix == null) {
if (suffix == null && !IP_V6_REGEX_OBJECT.test(cookie.domain)) {
// e.g. "com"
err = new Error("Cookie has domain set to a public suffix");
return cb(options.ignoreError ? null : err);
Expand Down
90 changes: 89 additions & 1 deletion test/cookie_jar_test.js
Expand Up @@ -193,7 +193,95 @@ vows
assert.match(err.message, /HttpOnly/i);
assert.ok(!c);
}
}
},
"Setting a basic IPv6 cookie": {
topic: function() {
const cj = new CookieJar();
const c = Cookie.parse("a=b; Domain=[::1]; Path=/");
assert.strictEqual(c.hostOnly, null);
assert.instanceOf(c.creation, Date);
assert.strictEqual(c.lastAccessed, null);
c.creation = new Date(Date.now() - 10000);
cj.setCookie(c, "http://[::1]/", this.callback);
},
works: function(c) {
assert.instanceOf(c, Cookie);
}, // C is for Cookie, good enough for me
"gets timestamped": function(c) {
assert.ok(c.creation);
assert.ok(Date.now() - c.creation.getTime() < 5000); // recently stamped
assert.ok(c.lastAccessed);
assert.equal(c.creation, c.lastAccessed);
assert.equal(c.TTL(), Infinity);
assert.ok(!c.isPersistent());
}
},
"Setting a prefix IPv6 cookie": {
topic: function() {
const cj = new CookieJar();
const c = Cookie.parse("a=b; Domain=[::ffff:127.0.0.1]; Path=/");
assert.strictEqual(c.hostOnly, null);
assert.instanceOf(c.creation, Date);
assert.strictEqual(c.lastAccessed, null);
c.creation = new Date(Date.now() - 10000);
cj.setCookie(c, "http://[::ffff:127.0.0.1]/", this.callback);
},
works: function(c) {
assert.instanceOf(c, Cookie);
}, // C is for Cookie, good enough for me
"gets timestamped": function(c) {
assert.ok(c.creation);
assert.ok(Date.now() - c.creation.getTime() < 5000); // recently stamped
assert.ok(c.lastAccessed);
assert.equal(c.creation, c.lastAccessed);
assert.equal(c.TTL(), Infinity);
assert.ok(!c.isPersistent());
}
},
"Setting a classic IPv6 cookie": {
topic: function() {
const cj = new CookieJar();
const c = Cookie.parse("a=b; Domain=[2001:4860:4860::8888]; Path=/");
assert.strictEqual(c.hostOnly, null);
assert.instanceOf(c.creation, Date);
assert.strictEqual(c.lastAccessed, null);
c.creation = new Date(Date.now() - 10000);
cj.setCookie(c, "http://[2001:4860:4860::8888]/", this.callback);
},
works: function(c) {
assert.instanceOf(c, Cookie);
}, // C is for Cookie, good enough for me
"gets timestamped": function(c) {
assert.ok(c.creation);
assert.ok(Date.now() - c.creation.getTime() < 5000); // recently stamped
assert.ok(c.lastAccessed);
assert.equal(c.creation, c.lastAccessed);
assert.equal(c.TTL(), Infinity);
assert.ok(!c.isPersistent());
}
},
"Setting a short IPv6 cookie": {
topic: function() {
const cj = new CookieJar();
const c = Cookie.parse("a=b; Domain=[2600::]; Path=/");
assert.strictEqual(c.hostOnly, null);
assert.instanceOf(c.creation, Date);
assert.strictEqual(c.lastAccessed, null);
c.creation = new Date(Date.now() - 10000);
cj.setCookie(c, "http://[2600::]/", this.callback);
},
works: function(c) {
assert.instanceOf(c, Cookie);
}, // C is for Cookie, good enough for me
"gets timestamped": function(c) {
assert.ok(c.creation);
assert.ok(Date.now() - c.creation.getTime() < 5000); // recently stamped
assert.ok(c.lastAccessed);
assert.equal(c.creation, c.lastAccessed);
assert.equal(c.TTL(), Infinity);
assert.ok(!c.isPersistent());
}
},
})
.addBatch({
"Store eight cookies": {
Expand Down