Skip to content

Commit

Permalink
Merge branch 'master' into add-codeowners
Browse files Browse the repository at this point in the history
  • Loading branch information
wjhsf committed Mar 8, 2022
2 parents 5963af9 + 2921fbd commit 26ccd20
Show file tree
Hide file tree
Showing 14 changed files with 484 additions and 98 deletions.
14 changes: 11 additions & 3 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"extends": ["plugin:prettier/recommended"],
"parserOptions": {
"ecmaVersion": 6
},
Expand All @@ -7,7 +8,14 @@
"no-var": "error",
"prefer-arrow-callback": "error",
"prefer-const": "error",
"prefer-template": "error"
},
"extends": ["plugin:prettier/recommended"]
"prefer-template": "error",
"no-restricted-modules": ["error",
// we can't rely on node standard modules in "native" or "browser" environments
// - exceptions:
// "punycode": since it's a package.json dependency
"assert", "buffer", "child_process", "cluster", "crypto", "dgram", "dns", "domain", "events", "freelist", "fs",
"http", "https", "module", "net", "os", "path", "querystring", "readline", "repl", "smalloc", "stream",
"string_decoder", "sys", "timers", "tls", "tracing", "tty", "url", "util", "vm", "zlib"
]
}
}
43 changes: 27 additions & 16 deletions lib/cookie.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@
*/
"use strict";
const punycode = require("punycode");
const urlParse = require("url").parse;
const util = require("util");
const urlParse = require("url-parse");
const pubsuffix = require("./pubsuffix-psl");
const Store = require("./store").Store;
const MemoryCookieStore = require("./memstore").MemoryCookieStore;
const pathMatch = require("./pathMatch").pathMatch;
const validators = require("./validators.js");
const VERSION = require("./version");
const { fromCallback } = require("universalify");
const { getCustomInspectSymbol } = require("./utilHelper");

// From RFC6265 S4.1.1
// note that it excludes \x3B ";"
Expand Down Expand Up @@ -99,7 +99,7 @@ const PrefixSecurityEnum = Object.freeze({
// * all capturing groups converted to non-capturing -- "(?:)"
// * 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_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}|:)|
Expand All @@ -111,9 +111,11 @@ const IP_V6_REGEX = `
(?:[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}$`)

`
.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 @@ -380,7 +382,7 @@ function domainMatch(str, domStr, canonicalize) {

/* " * The last character of the string that is not included in the
* domain string is a %x2E (".") character." */
if (str.substr(idx-1,1) !== '.') {
if (str.substr(idx - 1, 1) !== ".") {
return false; // doesn't align on "."
}

Expand Down Expand Up @@ -479,7 +481,7 @@ function parse(str, options) {
}

if (validators.isEmptyString(str) || !validators.isString(str)) {
return null
return null;
}

str = str.trim();
Expand Down Expand Up @@ -810,8 +812,9 @@ const cookieDefaults = {

class Cookie {
constructor(options = {}) {
if (util.inspect.custom) {
this[util.inspect.custom] = this.inspect;
const customInspectSymbol = getCustomInspectSymbol();
if (customInspectSymbol) {
this[customInspectSymbol] = this.inspect;
}

Object.assign(this, cookieDefaults, options);
Expand Down Expand Up @@ -1110,7 +1113,7 @@ class CookieJar {
}

setCookie(cookie, url, options, cb) {
validators.validate(validators.isNonEmptyString(url), cb, options);
validators.validate(validators.isNonEmptyString(url), cb, options);
let err;

if (validators.isFunction(url)) {
Expand All @@ -1126,7 +1129,12 @@ class CookieJar {

validators.validate(validators.isFunction(cb), cb);

if(!validators.isNonEmptyString(cookie) && !validators.isObject(cookie) && ( cookie instanceof String && cookie.length == 0)) {
if (
!validators.isNonEmptyString(cookie) &&
!validators.isObject(cookie) &&
cookie instanceof String &&
cookie.length == 0
) {
return cb(null);
}

Expand Down Expand Up @@ -1166,7 +1174,10 @@ class CookieJar {

// S5.3 step 5: public suffixes
if (this.rejectPublicSuffixes && cookie.domain) {
const suffix = pubsuffix.getPublicSuffix(cookie.cdomain());
const suffix = pubsuffix.getPublicSuffix(cookie.cdomain(), {
allowSpecialUseDomain: this.allowSpecialUseDomain,
ignoreError: options.ignoreError
});
if (suffix == null && !IP_V6_REGEX_OBJECT.test(cookie.domain)) {
// e.g. "com"
err = new Error("Cookie has domain set to a public suffix");
Expand Down Expand Up @@ -1646,7 +1657,7 @@ class CookieJar {
serialized = strOrObj;
}

const jar = new CookieJar(store, {
const jar = new CookieJar(store, {
rejectPublicSuffixes: serialized.rejectPublicSuffixes,
looseMode: serialized.enableLooseMode,
allowSpecialUseDomain: serialized.allowSpecialUseDomain,
Expand All @@ -1663,9 +1674,9 @@ class CookieJar {
static deserializeSync(strOrObj, store) {
const serialized =
typeof strOrObj === "string" ? JSON.parse(strOrObj) : strOrObj;
const jar = new CookieJar(store, {
const jar = new CookieJar(store, {
rejectPublicSuffixes: serialized.rejectPublicSuffixes,
looseMode: serialized.enableLooseMode
looseMode: serialized.enableLooseMode
});

// catch this mistake early:
Expand Down
56 changes: 53 additions & 3 deletions lib/memstore.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,21 @@ const { fromCallback } = require("universalify");
const Store = require("./store").Store;
const permuteDomain = require("./permuteDomain").permuteDomain;
const pathMatch = require("./pathMatch").pathMatch;
const util = require("util");
const { getCustomInspectSymbol, getUtilInspect } = require("./utilHelper");

class MemoryCookieStore extends Store {
constructor() {
super();
this.synchronous = true;
this.idx = {};
if (util.inspect.custom) {
this[util.inspect.custom] = this.inspect;
const customInspectSymbol = getCustomInspectSymbol();
if (customInspectSymbol) {
this[customInspectSymbol] = this.inspect;
}
}

inspect() {
const util = { inspect: getUtilInspect(inspectFallback) };
return `{ idx: ${util.inspect(this.idx, false, 2)} }`;
}

Expand Down Expand Up @@ -190,3 +192,51 @@ class MemoryCookieStore extends Store {
});

exports.MemoryCookieStore = MemoryCookieStore;

function inspectFallback(val) {
const domains = Object.keys(val);
if (domains.length === 0) {
return "{}";
}
let result = "{\n";
Object.keys(val).forEach((domain, i) => {
result += formatDomain(domain, val[domain]);
if (i < domains.length - 1) {
result += ",";
}
result += "\n";
});
result += "}";
return result;
}

function formatDomain(domainName, domainValue) {
const indent = " ";
let result = `${indent}'${domainName}': {\n`;
Object.keys(domainValue).forEach((path, i, paths) => {
result += formatPath(path, domainValue[path]);
if (i < paths.length - 1) {
result += ",";
}
result += "\n";
});
result += `${indent}}`;
return result;
}

function formatPath(pathName, pathValue) {
const indent = " ";
let result = `${indent}'${pathName}': {\n`;
Object.keys(pathValue).forEach((cookieName, i, cookieNames) => {
const cookie = pathValue[cookieName];
result += ` ${cookieName}: ${cookie.inspect()}`;
if (i < cookieNames.length - 1) {
result += ",";
}
result += "\n";
});
result += `${indent}}`;
return result;
}

exports.inspectFallback = inspectFallback;
30 changes: 4 additions & 26 deletions lib/permuteDomain.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,32 +34,10 @@ const pubsuffix = require("./pubsuffix-psl");
// Gives the permutation of all possible domainMatch()es of a given domain. The
// array is in shortest-to-longest order. Handy for indexing.

// RFC 6761
const SPECIAL_USE_DOMAINS = [
"local",
"example",
"invalid",
"localhost",
"test"
];

function permuteDomain(domain, allowSpecialUseDomain) {
let pubSuf = null;
if (allowSpecialUseDomain) {
const domainParts = domain.split(".");
// 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 topLevelDomain = domainParts[domainParts.length - 1];
if (SPECIAL_USE_DOMAINS.includes(topLevelDomain)) {
const secondLevelDomain = domainParts[domainParts.length - 2];
// In aforementioned example, the eTLD/pubSuf will be apple.localhost
pubSuf = `${secondLevelDomain}.${topLevelDomain}`;
} else {
pubSuf = pubsuffix.getPublicSuffix(domain);
}
} else {
pubSuf = pubsuffix.getPublicSuffix(domain);
}
const pubSuf = pubsuffix.getPublicSuffix(domain, {
allowSpecialUseDomain: allowSpecialUseDomain
});

if (!pubSuf) {
return null;
Expand All @@ -70,7 +48,7 @@ function permuteDomain(domain, allowSpecialUseDomain) {

// Nuke trailing dot
if (domain.slice(-1) == ".") {
domain = domain.slice(0, -1)
domain = domain.slice(0, -1);
}

const prefix = domain.slice(0, -(pubSuf.length + 1)); // ".example.com"
Expand Down
34 changes: 33 additions & 1 deletion lib/pubsuffix-psl.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,39 @@
"use strict";
const psl = require("psl");

function getPublicSuffix(domain) {
// RFC 6761
const SPECIAL_USE_DOMAINS = [
"local",
"example",
"invalid",
"localhost",
"test"
];

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 (!ignoreError && SPECIAL_USE_DOMAINS.includes(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}.`
);
}

return psl.get(domain);
}

Expand Down
39 changes: 39 additions & 0 deletions lib/utilHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
function requireUtil() {
try {
// eslint-disable-next-line no-restricted-modules
return require("util");
} catch (e) {
return null;
}
}

// for v10.12.0+
function lookupCustomInspectSymbol() {
return Symbol.for("nodejs.util.inspect.custom");
}

// for older node environments
function tryReadingCustomSymbolFromUtilInspect(options) {
const _requireUtil = options.requireUtil || requireUtil;
const util = _requireUtil();
return util ? util.inspect.custom : null;
}

exports.getUtilInspect = function getUtilInspect(fallback, options = {}) {
const _requireUtil = options.requireUtil || requireUtil;
const util = _requireUtil();
return function inspect(value, showHidden, depth) {
return util ? util.inspect(value, showHidden, depth) : fallback(value);
};
};

exports.getCustomInspectSymbol = function getCustomInspectSymbol(options = {}) {
const _lookupCustomInspectSymbol =
options.lookupCustomInspectSymbol || lookupCustomInspectSymbol;

// get custom inspect symbol for node environments
return (
_lookupCustomInspectSymbol() ||
tryReadingCustomSymbolFromUtilInspect(options)
);
};

0 comments on commit 26ccd20

Please sign in to comment.