diff options
| author | Joel Kronqvist <joel.h.kronqvist@gmail.com> | 2022-03-05 19:02:27 +0200 | 
|---|---|---|
| committer | Joel Kronqvist <joel.h.kronqvist@gmail.com> | 2022-03-05 19:02:27 +0200 | 
| commit | 5d309ff52cd399a6b71968a6b9a70c8ac0b98981 (patch) | |
| tree | 360f7eb50f956e2367ef38fa1fc6ac7ac5258042 /node_modules/tough-cookie/lib | |
| parent | b500a50f1b97d93c98b36ed9a980f8188d648147 (diff) | |
| download | LYLLRuoka-5d309ff52cd399a6b71968a6b9a70c8ac0b98981.tar.gz LYLLRuoka-5d309ff52cd399a6b71968a6b9a70c8ac0b98981.zip  | |
Added node_modules for the updating to work properly.
Diffstat (limited to 'node_modules/tough-cookie/lib')
| -rw-r--r-- | node_modules/tough-cookie/lib/cookie.js | 1671 | ||||
| -rw-r--r-- | node_modules/tough-cookie/lib/memstore.js | 190 | ||||
| -rw-r--r-- | node_modules/tough-cookie/lib/pathMatch.js | 61 | ||||
| -rw-r--r-- | node_modules/tough-cookie/lib/permuteDomain.js | 70 | ||||
| -rw-r--r-- | node_modules/tough-cookie/lib/pubsuffix-psl.js | 38 | ||||
| -rw-r--r-- | node_modules/tough-cookie/lib/store.js | 76 | ||||
| -rw-r--r-- | node_modules/tough-cookie/lib/version.js | 2 | 
7 files changed, 2108 insertions, 0 deletions
diff --git a/node_modules/tough-cookie/lib/cookie.js b/node_modules/tough-cookie/lib/cookie.js new file mode 100644 index 0000000..a042893 --- /dev/null +++ b/node_modules/tough-cookie/lib/cookie.js @@ -0,0 +1,1671 @@ +/*! + * Copyright (c) 2015, Salesforce.com, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Salesforce.com nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +"use strict"; +const punycode = require("punycode"); +const urlParse = require("url").parse; +const util = require("util"); +const pubsuffix = require("./pubsuffix-psl"); +const Store = require("./store").Store; +const MemoryCookieStore = require("./memstore").MemoryCookieStore; +const pathMatch = require("./pathMatch").pathMatch; +const VERSION = require("./version"); +const { fromCallback } = require("universalify"); + +// From RFC6265 S4.1.1 +// note that it excludes \x3B ";" +const COOKIE_OCTETS = /^[\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]+$/; + +const CONTROL_CHARS = /[\x00-\x1F]/; + +// From Chromium // '\r', '\n' and '\0' should be treated as a terminator in +// the "relaxed" mode, see: +// https://github.com/ChromiumWebApps/chromium/blob/b3d3b4da8bb94c1b2e061600df106d590fda3620/net/cookies/parsed_cookie.cc#L60 +const TERMINATORS = ["\n", "\r", "\0"]; + +// RFC6265 S4.1.1 defines path value as 'any CHAR except CTLs or ";"' +// Note ';' is \x3B +const PATH_VALUE = /[\x20-\x3A\x3C-\x7E]+/; + +// date-time parsing constants (RFC6265 S5.1.1) + +const DATE_DELIM = /[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]/; + +const MONTH_TO_NUM = { +  jan: 0, +  feb: 1, +  mar: 2, +  apr: 3, +  may: 4, +  jun: 5, +  jul: 6, +  aug: 7, +  sep: 8, +  oct: 9, +  nov: 10, +  dec: 11 +}; + +const MAX_TIME = 2147483647000; // 31-bit max +const MIN_TIME = 0; // 31-bit min +const SAME_SITE_CONTEXT_VAL_ERR = +  'Invalid sameSiteContext option for getCookies(); expected one of "strict", "lax", or "none"'; + +function checkSameSiteContext(value) { +  const context = String(value).toLowerCase(); +  if (context === "none" || context === "lax" || context === "strict") { +    return context; +  } else { +    return null; +  } +} + +const PrefixSecurityEnum = Object.freeze({ +  SILENT: "silent", +  STRICT: "strict", +  DISABLED: "unsafe-disabled" +}); + +// Dumped from ip-regex@4.0.0, with the following changes: +// * all capturing groups converted to non-capturing -- "(?:)" +// * support for IPv6 Scoped Literal ("%eth1") removed +// * lowercase hexadecimal only +var 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}|:)))$)/; + +/* + * Parses a Natural number (i.e., non-negative integer) with either the + *    <min>*<max>DIGIT ( non-digit *OCTET ) + * or + *    <min>*<max>DIGIT + * grammar (RFC6265 S5.1.1). + * + * The "trailingOK" boolean controls if the grammar accepts a + * "( non-digit *OCTET )" trailer. + */ +function parseDigits(token, minDigits, maxDigits, trailingOK) { +  let count = 0; +  while (count < token.length) { +    const c = token.charCodeAt(count); +    // "non-digit = %x00-2F / %x3A-FF" +    if (c <= 0x2f || c >= 0x3a) { +      break; +    } +    count++; +  } + +  // constrain to a minimum and maximum number of digits. +  if (count < minDigits || count > maxDigits) { +    return null; +  } + +  if (!trailingOK && count != token.length) { +    return null; +  } + +  return parseInt(token.substr(0, count), 10); +} + +function parseTime(token) { +  const parts = token.split(":"); +  const result = [0, 0, 0]; + +  /* RF6256 S5.1.1: +   *      time            = hms-time ( non-digit *OCTET ) +   *      hms-time        = time-field ":" time-field ":" time-field +   *      time-field      = 1*2DIGIT +   */ + +  if (parts.length !== 3) { +    return null; +  } + +  for (let i = 0; i < 3; i++) { +    // "time-field" must be strictly "1*2DIGIT", HOWEVER, "hms-time" can be +    // followed by "( non-digit *OCTET )" so therefore the last time-field can +    // have a trailer +    const trailingOK = i == 2; +    const num = parseDigits(parts[i], 1, 2, trailingOK); +    if (num === null) { +      return null; +    } +    result[i] = num; +  } + +  return result; +} + +function parseMonth(token) { +  token = String(token) +    .substr(0, 3) +    .toLowerCase(); +  const num = MONTH_TO_NUM[token]; +  return num >= 0 ? num : null; +} + +/* + * RFC6265 S5.1.1 date parser (see RFC for full grammar) + */ +function parseDate(str) { +  if (!str) { +    return; +  } + +  /* RFC6265 S5.1.1: +   * 2. Process each date-token sequentially in the order the date-tokens +   * appear in the cookie-date +   */ +  const tokens = str.split(DATE_DELIM); +  if (!tokens) { +    return; +  } + +  let hour = null; +  let minute = null; +  let second = null; +  let dayOfMonth = null; +  let month = null; +  let year = null; + +  for (let i = 0; i < tokens.length; i++) { +    const token = tokens[i].trim(); +    if (!token.length) { +      continue; +    } + +    let result; + +    /* 2.1. If the found-time flag is not set and the token matches the time +     * production, set the found-time flag and set the hour- value, +     * minute-value, and second-value to the numbers denoted by the digits in +     * the date-token, respectively.  Skip the remaining sub-steps and continue +     * to the next date-token. +     */ +    if (second === null) { +      result = parseTime(token); +      if (result) { +        hour = result[0]; +        minute = result[1]; +        second = result[2]; +        continue; +      } +    } + +    /* 2.2. If the found-day-of-month flag is not set and the date-token matches +     * the day-of-month production, set the found-day-of- month flag and set +     * the day-of-month-value to the number denoted by the date-token.  Skip +     * the remaining sub-steps and continue to the next date-token. +     */ +    if (dayOfMonth === null) { +      // "day-of-month = 1*2DIGIT ( non-digit *OCTET )" +      result = parseDigits(token, 1, 2, true); +      if (result !== null) { +        dayOfMonth = result; +        continue; +      } +    } + +    /* 2.3. If the found-month flag is not set and the date-token matches the +     * month production, set the found-month flag and set the month-value to +     * the month denoted by the date-token.  Skip the remaining sub-steps and +     * continue to the next date-token. +     */ +    if (month === null) { +      result = parseMonth(token); +      if (result !== null) { +        month = result; +        continue; +      } +    } + +    /* 2.4. If the found-year flag is not set and the date-token matches the +     * year production, set the found-year flag and set the year-value to the +     * number denoted by the date-token.  Skip the remaining sub-steps and +     * continue to the next date-token. +     */ +    if (year === null) { +      // "year = 2*4DIGIT ( non-digit *OCTET )" +      result = parseDigits(token, 2, 4, true); +      if (result !== null) { +        year = result; +        /* From S5.1.1: +         * 3.  If the year-value is greater than or equal to 70 and less +         * than or equal to 99, increment the year-value by 1900. +         * 4.  If the year-value is greater than or equal to 0 and less +         * than or equal to 69, increment the year-value by 2000. +         */ +        if (year >= 70 && year <= 99) { +          year += 1900; +        } else if (year >= 0 && year <= 69) { +          year += 2000; +        } +      } +    } +  } + +  /* RFC 6265 S5.1.1 +   * "5. Abort these steps and fail to parse the cookie-date if: +   *     *  at least one of the found-day-of-month, found-month, found- +   *        year, or found-time flags is not set, +   *     *  the day-of-month-value is less than 1 or greater than 31, +   *     *  the year-value is less than 1601, +   *     *  the hour-value is greater than 23, +   *     *  the minute-value is greater than 59, or +   *     *  the second-value is greater than 59. +   *     (Note that leap seconds cannot be represented in this syntax.)" +   * +   * So, in order as above: +   */ +  if ( +    dayOfMonth === null || +    month === null || +    year === null || +    second === null || +    dayOfMonth < 1 || +    dayOfMonth > 31 || +    year < 1601 || +    hour > 23 || +    minute > 59 || +    second > 59 +  ) { +    return; +  } + +  return new Date(Date.UTC(year, month, dayOfMonth, hour, minute, second)); +} + +function formatDate(date) { +  return date.toUTCString(); +} + +// S5.1.2 Canonicalized Host Names +function canonicalDomain(str) { +  if (str == null) { +    return null; +  } +  str = str.trim().replace(/^\./, ""); // S4.1.2.3 & S5.2.3: ignore leading . + +  // convert to IDN if any non-ASCII characters +  if (punycode && /[^\u0001-\u007f]/.test(str)) { +    str = punycode.toASCII(str); +  } + +  return str.toLowerCase(); +} + +// S5.1.3 Domain Matching +function domainMatch(str, domStr, canonicalize) { +  if (str == null || domStr == null) { +    return null; +  } +  if (canonicalize !== false) { +    str = canonicalDomain(str); +    domStr = canonicalDomain(domStr); +  } + +  /* +   * S5.1.3: +   * "A string domain-matches a given domain string if at least one of the +   * following conditions hold:" +   * +   * " o The domain string and the string are identical. (Note that both the +   * domain string and the string will have been canonicalized to lower case at +   * this point)" +   */ +  if (str == domStr) { +    return true; +  } + +  /* " o All of the following [three] conditions hold:" */ + +  /* "* The domain string is a suffix of the string" */ +  const idx = str.indexOf(domStr); +  if (idx <= 0) { +    return false; // it's a non-match (-1) or prefix (0) +  } + +  // next, check it's a proper suffix +  // e.g., "a.b.c".indexOf("b.c") === 2 +  // 5 === 3+2 +  if (str.length !== domStr.length + idx) { +    return false; // it's not a suffix +  } + +  /* "  * The last character of the string that is not included in the +   * domain string is a %x2E (".") character." */ +  if (str.substr(idx-1,1) !== '.') { +    return false; // doesn't align on "." +  } + +  /* "  * The string is a host name (i.e., not an IP address)." */ +  if (IP_REGEX_LOWERCASE.test(str)) { +    return false; // it's an IP address +  } + +  return true; +} + +// RFC6265 S5.1.4 Paths and Path-Match + +/* + * "The user agent MUST use an algorithm equivalent to the following algorithm + * to compute the default-path of a cookie:" + * + * Assumption: the path (and not query part or absolute uri) is passed in. + */ +function defaultPath(path) { +  // "2. If the uri-path is empty or if the first character of the uri-path is not +  // a %x2F ("/") character, output %x2F ("/") and skip the remaining steps. +  if (!path || path.substr(0, 1) !== "/") { +    return "/"; +  } + +  // "3. If the uri-path contains no more than one %x2F ("/") character, output +  // %x2F ("/") and skip the remaining step." +  if (path === "/") { +    return path; +  } + +  const rightSlash = path.lastIndexOf("/"); +  if (rightSlash === 0) { +    return "/"; +  } + +  // "4. Output the characters of the uri-path from the first character up to, +  // but not including, the right-most %x2F ("/")." +  return path.slice(0, rightSlash); +} + +function trimTerminator(str) { +  for (let t = 0; t < TERMINATORS.length; t++) { +    const terminatorIdx = str.indexOf(TERMINATORS[t]); +    if (terminatorIdx !== -1) { +      str = str.substr(0, terminatorIdx); +    } +  } + +  return str; +} + +function parseCookiePair(cookiePair, looseMode) { +  cookiePair = trimTerminator(cookiePair); + +  let firstEq = cookiePair.indexOf("="); +  if (looseMode) { +    if (firstEq === 0) { +      // '=' is immediately at start +      cookiePair = cookiePair.substr(1); +      firstEq = cookiePair.indexOf("="); // might still need to split on '=' +    } +  } else { +    // non-loose mode +    if (firstEq <= 0) { +      // no '=' or is at start +      return; // needs to have non-empty "cookie-name" +    } +  } + +  let cookieName, cookieValue; +  if (firstEq <= 0) { +    cookieName = ""; +    cookieValue = cookiePair.trim(); +  } else { +    cookieName = cookiePair.substr(0, firstEq).trim(); +    cookieValue = cookiePair.substr(firstEq + 1).trim(); +  } + +  if (CONTROL_CHARS.test(cookieName) || CONTROL_CHARS.test(cookieValue)) { +    return; +  } + +  const c = new Cookie(); +  c.key = cookieName; +  c.value = cookieValue; +  return c; +} + +function parse(str, options) { +  if (!options || typeof options !== "object") { +    options = {}; +  } +  str = str.trim(); + +  // We use a regex to parse the "name-value-pair" part of S5.2 +  const firstSemi = str.indexOf(";"); // S5.2 step 1 +  const cookiePair = firstSemi === -1 ? str : str.substr(0, firstSemi); +  const c = parseCookiePair(cookiePair, !!options.loose); +  if (!c) { +    return; +  } + +  if (firstSemi === -1) { +    return c; +  } + +  // S5.2.3 "unparsed-attributes consist of the remainder of the set-cookie-string +  // (including the %x3B (";") in question)." plus later on in the same section +  // "discard the first ";" and trim". +  const unparsed = str.slice(firstSemi + 1).trim(); + +  // "If the unparsed-attributes string is empty, skip the rest of these +  // steps." +  if (unparsed.length === 0) { +    return c; +  } + +  /* +   * S5.2 says that when looping over the items "[p]rocess the attribute-name +   * and attribute-value according to the requirements in the following +   * subsections" for every item.  Plus, for many of the individual attributes +   * in S5.3 it says to use the "attribute-value of the last attribute in the +   * cookie-attribute-list".  Therefore, in this implementation, we overwrite +   * the previous value. +   */ +  const cookie_avs = unparsed.split(";"); +  while (cookie_avs.length) { +    const av = cookie_avs.shift().trim(); +    if (av.length === 0) { +      // happens if ";;" appears +      continue; +    } +    const av_sep = av.indexOf("="); +    let av_key, av_value; + +    if (av_sep === -1) { +      av_key = av; +      av_value = null; +    } else { +      av_key = av.substr(0, av_sep); +      av_value = av.substr(av_sep + 1); +    } + +    av_key = av_key.trim().toLowerCase(); + +    if (av_value) { +      av_value = av_value.trim(); +    } + +    switch (av_key) { +      case "expires": // S5.2.1 +        if (av_value) { +          const exp = parseDate(av_value); +          // "If the attribute-value failed to parse as a cookie date, ignore the +          // cookie-av." +          if (exp) { +            // over and underflow not realistically a concern: V8's getTime() seems to +            // store something larger than a 32-bit time_t (even with 32-bit node) +            c.expires = exp; +          } +        } +        break; + +      case "max-age": // S5.2.2 +        if (av_value) { +          // "If the first character of the attribute-value is not a DIGIT or a "-" +          // character ...[or]... If the remainder of attribute-value contains a +          // non-DIGIT character, ignore the cookie-av." +          if (/^-?[0-9]+$/.test(av_value)) { +            const delta = parseInt(av_value, 10); +            // "If delta-seconds is less than or equal to zero (0), let expiry-time +            // be the earliest representable date and time." +            c.setMaxAge(delta); +          } +        } +        break; + +      case "domain": // S5.2.3 +        // "If the attribute-value is empty, the behavior is undefined.  However, +        // the user agent SHOULD ignore the cookie-av entirely." +        if (av_value) { +          // S5.2.3 "Let cookie-domain be the attribute-value without the leading %x2E +          // (".") character." +          const domain = av_value.trim().replace(/^\./, ""); +          if (domain) { +            // "Convert the cookie-domain to lower case." +            c.domain = domain.toLowerCase(); +          } +        } +        break; + +      case "path": // S5.2.4 +        /* +         * "If the attribute-value is empty or if the first character of the +         * attribute-value is not %x2F ("/"): +         *   Let cookie-path be the default-path. +         * Otherwise: +         *   Let cookie-path be the attribute-value." +         * +         * We'll represent the default-path as null since it depends on the +         * context of the parsing. +         */ +        c.path = av_value && av_value[0] === "/" ? av_value : null; +        break; + +      case "secure": // S5.2.5 +        /* +         * "If the attribute-name case-insensitively matches the string "Secure", +         * the user agent MUST append an attribute to the cookie-attribute-list +         * with an attribute-name of Secure and an empty attribute-value." +         */ +        c.secure = true; +        break; + +      case "httponly": // S5.2.6 -- effectively the same as 'secure' +        c.httpOnly = true; +        break; + +      case "samesite": // RFC6265bis-02 S5.3.7 +        const enforcement = av_value ? av_value.toLowerCase() : ""; +        switch (enforcement) { +          case "strict": +            c.sameSite = "strict"; +            break; +          case "lax": +            c.sameSite = "lax"; +            break; +          default: +            // RFC6265bis-02 S5.3.7 step 1: +            // "If cookie-av's attribute-value is not a case-insensitive match +            //  for "Strict" or "Lax", ignore the "cookie-av"." +            // This effectively sets it to 'none' from the prototype. +            break; +        } +        break; + +      default: +        c.extensions = c.extensions || []; +        c.extensions.push(av); +        break; +    } +  } + +  return c; +} + +/** + *  If the cookie-name begins with a case-sensitive match for the + *  string "__Secure-", abort these steps and ignore the cookie + *  entirely unless the cookie's secure-only-flag is true. + * @param cookie + * @returns boolean + */ +function isSecurePrefixConditionMet(cookie) { +  return !cookie.key.startsWith("__Secure-") || cookie.secure; +} + +/** + *  If the cookie-name begins with a case-sensitive match for the + *  string "__Host-", abort these steps and ignore the cookie + *  entirely unless the cookie meets all the following criteria: + *    1.  The cookie's secure-only-flag is true. + *    2.  The cookie's host-only-flag is true. + *    3.  The cookie-attribute-list contains an attribute with an + *        attribute-name of "Path", and the cookie's path is "/". + * @param cookie + * @returns boolean + */ +function isHostPrefixConditionMet(cookie) { +  return ( +    !cookie.key.startsWith("__Host-") || +    (cookie.secure && +      cookie.hostOnly && +      cookie.path != null && +      cookie.path === "/") +  ); +} + +// avoid the V8 deoptimization monster! +function jsonParse(str) { +  let obj; +  try { +    obj = JSON.parse(str); +  } catch (e) { +    return e; +  } +  return obj; +} + +function fromJSON(str) { +  if (!str) { +    return null; +  } + +  let obj; +  if (typeof str === "string") { +    obj = jsonParse(str); +    if (obj instanceof Error) { +      return null; +    } +  } else { +    // assume it's an Object +    obj = str; +  } + +  const c = new Cookie(); +  for (let i = 0; i < Cookie.serializableProperties.length; i++) { +    const prop = Cookie.serializableProperties[i]; +    if (obj[prop] === undefined || obj[prop] === cookieDefaults[prop]) { +      continue; // leave as prototype default +    } + +    if (prop === "expires" || prop === "creation" || prop === "lastAccessed") { +      if (obj[prop] === null) { +        c[prop] = null; +      } else { +        c[prop] = obj[prop] == "Infinity" ? "Infinity" : new Date(obj[prop]); +      } +    } else { +      c[prop] = obj[prop]; +    } +  } + +  return c; +} + +/* Section 5.4 part 2: + * "*  Cookies with longer paths are listed before cookies with + *     shorter paths. + * + *  *  Among cookies that have equal-length path fields, cookies with + *     earlier creation-times are listed before cookies with later + *     creation-times." + */ + +function cookieCompare(a, b) { +  let cmp = 0; + +  // descending for length: b CMP a +  const aPathLen = a.path ? a.path.length : 0; +  const bPathLen = b.path ? b.path.length : 0; +  cmp = bPathLen - aPathLen; +  if (cmp !== 0) { +    return cmp; +  } + +  // ascending for time: a CMP b +  const aTime = a.creation ? a.creation.getTime() : MAX_TIME; +  const bTime = b.creation ? b.creation.getTime() : MAX_TIME; +  cmp = aTime - bTime; +  if (cmp !== 0) { +    return cmp; +  } + +  // break ties for the same millisecond (precision of JavaScript's clock) +  cmp = a.creationIndex - b.creationIndex; + +  return cmp; +} + +// Gives the permutation of all possible pathMatch()es of a given path. The +// array is in longest-to-shortest order.  Handy for indexing. +function permutePath(path) { +  if (path === "/") { +    return ["/"]; +  } +  const permutations = [path]; +  while (path.length > 1) { +    const lindex = path.lastIndexOf("/"); +    if (lindex === 0) { +      break; +    } +    path = path.substr(0, lindex); +    permutations.push(path); +  } +  permutations.push("/"); +  return permutations; +} + +function getCookieContext(url) { +  if (url instanceof Object) { +    return url; +  } +  // NOTE: decodeURI will throw on malformed URIs (see GH-32). +  // Therefore, we will just skip decoding for such URIs. +  try { +    url = decodeURI(url); +  } catch (err) { +    // Silently swallow error +  } + +  return urlParse(url); +} + +const cookieDefaults = { +  // the order in which the RFC has them: +  key: "", +  value: "", +  expires: "Infinity", +  maxAge: null, +  domain: null, +  path: null, +  secure: false, +  httpOnly: false, +  extensions: null, +  // set by the CookieJar: +  hostOnly: null, +  pathIsDefault: null, +  creation: null, +  lastAccessed: null, +  sameSite: "none" +}; + +class Cookie { +  constructor(options = {}) { +    if (util.inspect.custom) { +      this[util.inspect.custom] = this.inspect; +    } + +    Object.assign(this, cookieDefaults, options); +    this.creation = this.creation || new Date(); + +    // used to break creation ties in cookieCompare(): +    Object.defineProperty(this, "creationIndex", { +      configurable: false, +      enumerable: false, // important for assert.deepEqual checks +      writable: true, +      value: ++Cookie.cookiesCreated +    }); +  } + +  inspect() { +    const now = Date.now(); +    const hostOnly = this.hostOnly != null ? this.hostOnly : "?"; +    const createAge = this.creation +      ? `${now - this.creation.getTime()}ms` +      : "?"; +    const accessAge = this.lastAccessed +      ? `${now - this.lastAccessed.getTime()}ms` +      : "?"; +    return `Cookie="${this.toString()}; hostOnly=${hostOnly}; aAge=${accessAge}; cAge=${createAge}"`; +  } + +  toJSON() { +    const obj = {}; + +    for (const prop of Cookie.serializableProperties) { +      if (this[prop] === cookieDefaults[prop]) { +        continue; // leave as prototype default +      } + +      if ( +        prop === "expires" || +        prop === "creation" || +        prop === "lastAccessed" +      ) { +        if (this[prop] === null) { +          obj[prop] = null; +        } else { +          obj[prop] = +            this[prop] == "Infinity" // intentionally not === +              ? "Infinity" +              : this[prop].toISOString(); +        } +      } else if (prop === "maxAge") { +        if (this[prop] !== null) { +          // again, intentionally not === +          obj[prop] = +            this[prop] == Infinity || this[prop] == -Infinity +              ? this[prop].toString() +              : this[prop]; +        } +      } else { +        if (this[prop] !== cookieDefaults[prop]) { +          obj[prop] = this[prop]; +        } +      } +    } + +    return obj; +  } + +  clone() { +    return fromJSON(this.toJSON()); +  } + +  validate() { +    if (!COOKIE_OCTETS.test(this.value)) { +      return false; +    } +    if ( +      this.expires != Infinity && +      !(this.expires instanceof Date) && +      !parseDate(this.expires) +    ) { +      return false; +    } +    if (this.maxAge != null && this.maxAge <= 0) { +      return false; // "Max-Age=" non-zero-digit *DIGIT +    } +    if (this.path != null && !PATH_VALUE.test(this.path)) { +      return false; +    } + +    const cdomain = this.cdomain(); +    if (cdomain) { +      if (cdomain.match(/\.$/)) { +        return false; // S4.1.2.3 suggests that this is bad. domainMatch() tests confirm this +      } +      const suffix = pubsuffix.getPublicSuffix(cdomain); +      if (suffix == null) { +        // it's a public suffix +        return false; +      } +    } +    return true; +  } + +  setExpires(exp) { +    if (exp instanceof Date) { +      this.expires = exp; +    } else { +      this.expires = parseDate(exp) || "Infinity"; +    } +  } + +  setMaxAge(age) { +    if (age === Infinity || age === -Infinity) { +      this.maxAge = age.toString(); // so JSON.stringify() works +    } else { +      this.maxAge = age; +    } +  } + +  cookieString() { +    let val = this.value; +    if (val == null) { +      val = ""; +    } +    if (this.key === "") { +      return val; +    } +    return `${this.key}=${val}`; +  } + +  // gives Set-Cookie header format +  toString() { +    let str = this.cookieString(); + +    if (this.expires != Infinity) { +      if (this.expires instanceof Date) { +        str += `; Expires=${formatDate(this.expires)}`; +      } else { +        str += `; Expires=${this.expires}`; +      } +    } + +    if (this.maxAge != null && this.maxAge != Infinity) { +      str += `; Max-Age=${this.maxAge}`; +    } + +    if (this.domain && !this.hostOnly) { +      str += `; Domain=${this.domain}`; +    } +    if (this.path) { +      str += `; Path=${this.path}`; +    } + +    if (this.secure) { +      str += "; Secure"; +    } +    if (this.httpOnly) { +      str += "; HttpOnly"; +    } +    if (this.sameSite && this.sameSite !== "none") { +      const ssCanon = Cookie.sameSiteCanonical[this.sameSite.toLowerCase()]; +      str += `; SameSite=${ssCanon ? ssCanon : this.sameSite}`; +    } +    if (this.extensions) { +      this.extensions.forEach(ext => { +        str += `; ${ext}`; +      }); +    } + +    return str; +  } + +  // TTL() partially replaces the "expiry-time" parts of S5.3 step 3 (setCookie() +  // elsewhere) +  // S5.3 says to give the "latest representable date" for which we use Infinity +  // For "expired" we use 0 +  TTL(now) { +    /* RFC6265 S4.1.2.2 If a cookie has both the Max-Age and the Expires +     * attribute, the Max-Age attribute has precedence and controls the +     * expiration date of the cookie. +     * (Concurs with S5.3 step 3) +     */ +    if (this.maxAge != null) { +      return this.maxAge <= 0 ? 0 : this.maxAge * 1000; +    } + +    let expires = this.expires; +    if (expires != Infinity) { +      if (!(expires instanceof Date)) { +        expires = parseDate(expires) || Infinity; +      } + +      if (expires == Infinity) { +        return Infinity; +      } + +      return expires.getTime() - (now || Date.now()); +    } + +    return Infinity; +  } + +  // expiryTime() replaces the "expiry-time" parts of S5.3 step 3 (setCookie() +  // elsewhere) +  expiryTime(now) { +    if (this.maxAge != null) { +      const relativeTo = now || this.creation || new Date(); +      const age = this.maxAge <= 0 ? -Infinity : this.maxAge * 1000; +      return relativeTo.getTime() + age; +    } + +    if (this.expires == Infinity) { +      return Infinity; +    } +    return this.expires.getTime(); +  } + +  // expiryDate() replaces the "expiry-time" parts of S5.3 step 3 (setCookie() +  // elsewhere), except it returns a Date +  expiryDate(now) { +    const millisec = this.expiryTime(now); +    if (millisec == Infinity) { +      return new Date(MAX_TIME); +    } else if (millisec == -Infinity) { +      return new Date(MIN_TIME); +    } else { +      return new Date(millisec); +    } +  } + +  // This replaces the "persistent-flag" parts of S5.3 step 3 +  isPersistent() { +    return this.maxAge != null || this.expires != Infinity; +  } + +  // Mostly S5.1.2 and S5.2.3: +  canonicalizedDomain() { +    if (this.domain == null) { +      return null; +    } +    return canonicalDomain(this.domain); +  } + +  cdomain() { +    return this.canonicalizedDomain(); +  } +} + +Cookie.cookiesCreated = 0; +Cookie.parse = parse; +Cookie.fromJSON = fromJSON; +Cookie.serializableProperties = Object.keys(cookieDefaults); +Cookie.sameSiteLevel = { +  strict: 3, +  lax: 2, +  none: 1 +}; + +Cookie.sameSiteCanonical = { +  strict: "Strict", +  lax: "Lax" +}; + +function getNormalizedPrefixSecurity(prefixSecurity) { +  if (prefixSecurity != null) { +    const normalizedPrefixSecurity = prefixSecurity.toLowerCase(); +    /* The three supported options */ +    switch (normalizedPrefixSecurity) { +      case PrefixSecurityEnum.STRICT: +      case PrefixSecurityEnum.SILENT: +      case PrefixSecurityEnum.DISABLED: +        return normalizedPrefixSecurity; +    } +  } +  /* Default is SILENT */ +  return PrefixSecurityEnum.SILENT; +} + +class CookieJar { +  constructor(store, options = { rejectPublicSuffixes: true }) { +    if (typeof options === "boolean") { +      options = { rejectPublicSuffixes: options }; +    } +    this.rejectPublicSuffixes = options.rejectPublicSuffixes; +    this.enableLooseMode = !!options.looseMode; +    this.allowSpecialUseDomain = !!options.allowSpecialUseDomain; +    this.store = store || new MemoryCookieStore(); +    this.prefixSecurity = getNormalizedPrefixSecurity(options.prefixSecurity); +    this._cloneSync = syncWrap("clone"); +    this._importCookiesSync = syncWrap("_importCookies"); +    this.getCookiesSync = syncWrap("getCookies"); +    this.getCookieStringSync = syncWrap("getCookieString"); +    this.getSetCookieStringsSync = syncWrap("getSetCookieStrings"); +    this.removeAllCookiesSync = syncWrap("removeAllCookies"); +    this.setCookieSync = syncWrap("setCookie"); +    this.serializeSync = syncWrap("serialize"); +  } + +  setCookie(cookie, url, options, cb) { +    let err; +    const context = getCookieContext(url); +    if (typeof options === "function") { +      cb = options; +      options = {}; +    } + +    const host = canonicalDomain(context.hostname); +    const loose = options.loose || this.enableLooseMode; + +    let sameSiteContext = null; +    if (options.sameSiteContext) { +      sameSiteContext = checkSameSiteContext(options.sameSiteContext); +      if (!sameSiteContext) { +        return cb(new Error(SAME_SITE_CONTEXT_VAL_ERR)); +      } +    } + +    // S5.3 step 1 +    if (typeof cookie === "string" || cookie instanceof String) { +      cookie = Cookie.parse(cookie, { loose: loose }); +      if (!cookie) { +        err = new Error("Cookie failed to parse"); +        return cb(options.ignoreError ? null : err); +      } +    } else if (!(cookie instanceof Cookie)) { +      // If you're seeing this error, and are passing in a Cookie object, +      // it *might* be a Cookie object from another loaded version of tough-cookie. +      err = new Error( +        "First argument to setCookie must be a Cookie object or string" +      ); +      return cb(options.ignoreError ? null : err); +    } + +    // S5.3 step 2 +    const now = options.now || new Date(); // will assign later to save effort in the face of errors + +    // S5.3 step 3: NOOP; persistent-flag and expiry-time is handled by getCookie() + +    // S5.3 step 4: NOOP; domain is null by default + +    // S5.3 step 5: public suffixes +    if (this.rejectPublicSuffixes && cookie.domain) { +      const suffix = pubsuffix.getPublicSuffix(cookie.cdomain()); +      if (suffix == null) { +        // e.g. "com" +        err = new Error("Cookie has domain set to a public suffix"); +        return cb(options.ignoreError ? null : err); +      } +    } + +    // S5.3 step 6: +    if (cookie.domain) { +      if (!domainMatch(host, cookie.cdomain(), false)) { +        err = new Error( +          `Cookie not in this host's domain. Cookie:${cookie.cdomain()} Request:${host}` +        ); +        return cb(options.ignoreError ? null : err); +      } + +      if (cookie.hostOnly == null) { +        // don't reset if already set +        cookie.hostOnly = false; +      } +    } else { +      cookie.hostOnly = true; +      cookie.domain = host; +    } + +    //S5.2.4 If the attribute-value is empty or if the first character of the +    //attribute-value is not %x2F ("/"): +    //Let cookie-path be the default-path. +    if (!cookie.path || cookie.path[0] !== "/") { +      cookie.path = defaultPath(context.pathname); +      cookie.pathIsDefault = true; +    } + +    // S5.3 step 8: NOOP; secure attribute +    // S5.3 step 9: NOOP; httpOnly attribute + +    // S5.3 step 10 +    if (options.http === false && cookie.httpOnly) { +      err = new Error("Cookie is HttpOnly and this isn't an HTTP API"); +      return cb(options.ignoreError ? null : err); +    } + +    // 6252bis-02 S5.4 Step 13 & 14: +    if (cookie.sameSite !== "none" && sameSiteContext) { +      // "If the cookie's "same-site-flag" is not "None", and the cookie +      //  is being set from a context whose "site for cookies" is not an +      //  exact match for request-uri's host's registered domain, then +      //  abort these steps and ignore the newly created cookie entirely." +      if (sameSiteContext === "none") { +        err = new Error( +          "Cookie is SameSite but this is a cross-origin request" +        ); +        return cb(options.ignoreError ? null : err); +      } +    } + +    /* 6265bis-02 S5.4 Steps 15 & 16 */ +    const ignoreErrorForPrefixSecurity = +      this.prefixSecurity === PrefixSecurityEnum.SILENT; +    const prefixSecurityDisabled = +      this.prefixSecurity === PrefixSecurityEnum.DISABLED; +    /* If prefix checking is not disabled ...*/ +    if (!prefixSecurityDisabled) { +      let errorFound = false; +      let errorMsg; +      /* Check secure prefix condition */ +      if (!isSecurePrefixConditionMet(cookie)) { +        errorFound = true; +        errorMsg = "Cookie has __Secure prefix but Secure attribute is not set"; +      } else if (!isHostPrefixConditionMet(cookie)) { +        /* Check host prefix condition */ +        errorFound = true; +        errorMsg = +          "Cookie has __Host prefix but either Secure or HostOnly attribute is not set or Path is not '/'"; +      } +      if (errorFound) { +        return cb( +          options.ignoreError || ignoreErrorForPrefixSecurity +            ? null +            : new Error(errorMsg) +        ); +      } +    } + +    const store = this.store; + +    if (!store.updateCookie) { +      store.updateCookie = function(oldCookie, newCookie, cb) { +        this.putCookie(newCookie, cb); +      }; +    } + +    function withCookie(err, oldCookie) { +      if (err) { +        return cb(err); +      } + +      const next = function(err) { +        if (err) { +          return cb(err); +        } else { +          cb(null, cookie); +        } +      }; + +      if (oldCookie) { +        // S5.3 step 11 - "If the cookie store contains a cookie with the same name, +        // domain, and path as the newly created cookie:" +        if (options.http === false && oldCookie.httpOnly) { +          // step 11.2 +          err = new Error("old Cookie is HttpOnly and this isn't an HTTP API"); +          return cb(options.ignoreError ? null : err); +        } +        cookie.creation = oldCookie.creation; // step 11.3 +        cookie.creationIndex = oldCookie.creationIndex; // preserve tie-breaker +        cookie.lastAccessed = now; +        // Step 11.4 (delete cookie) is implied by just setting the new one: +        store.updateCookie(oldCookie, cookie, next); // step 12 +      } else { +        cookie.creation = cookie.lastAccessed = now; +        store.putCookie(cookie, next); // step 12 +      } +    } + +    store.findCookie(cookie.domain, cookie.path, cookie.key, withCookie); +  } + +  // RFC6365 S5.4 +  getCookies(url, options, cb) { +    const context = getCookieContext(url); +    if (typeof options === "function") { +      cb = options; +      options = {}; +    } + +    const host = canonicalDomain(context.hostname); +    const path = context.pathname || "/"; + +    let secure = options.secure; +    if ( +      secure == null && +      context.protocol && +      (context.protocol == "https:" || context.protocol == "wss:") +    ) { +      secure = true; +    } + +    let sameSiteLevel = 0; +    if (options.sameSiteContext) { +      const sameSiteContext = checkSameSiteContext(options.sameSiteContext); +      sameSiteLevel = Cookie.sameSiteLevel[sameSiteContext]; +      if (!sameSiteLevel) { +        return cb(new Error(SAME_SITE_CONTEXT_VAL_ERR)); +      } +    } + +    let http = options.http; +    if (http == null) { +      http = true; +    } + +    const now = options.now || Date.now(); +    const expireCheck = options.expire !== false; +    const allPaths = !!options.allPaths; +    const store = this.store; + +    function matchingCookie(c) { +      // "Either: +      //   The cookie's host-only-flag is true and the canonicalized +      //   request-host is identical to the cookie's domain. +      // Or: +      //   The cookie's host-only-flag is false and the canonicalized +      //   request-host domain-matches the cookie's domain." +      if (c.hostOnly) { +        if (c.domain != host) { +          return false; +        } +      } else { +        if (!domainMatch(host, c.domain, false)) { +          return false; +        } +      } + +      // "The request-uri's path path-matches the cookie's path." +      if (!allPaths && !pathMatch(path, c.path)) { +        return false; +      } + +      // "If the cookie's secure-only-flag is true, then the request-uri's +      // scheme must denote a "secure" protocol" +      if (c.secure && !secure) { +        return false; +      } + +      // "If the cookie's http-only-flag is true, then exclude the cookie if the +      // cookie-string is being generated for a "non-HTTP" API" +      if (c.httpOnly && !http) { +        return false; +      } + +      // RFC6265bis-02 S5.3.7 +      if (sameSiteLevel) { +        const cookieLevel = Cookie.sameSiteLevel[c.sameSite || "none"]; +        if (cookieLevel > sameSiteLevel) { +          // only allow cookies at or below the request level +          return false; +        } +      } + +      // deferred from S5.3 +      // non-RFC: allow retention of expired cookies by choice +      if (expireCheck && c.expiryTime() <= now) { +        store.removeCookie(c.domain, c.path, c.key, () => {}); // result ignored +        return false; +      } + +      return true; +    } + +    store.findCookies( +      host, +      allPaths ? null : path, +      this.allowSpecialUseDomain, +      (err, cookies) => { +        if (err) { +          return cb(err); +        } + +        cookies = cookies.filter(matchingCookie); + +        // sorting of S5.4 part 2 +        if (options.sort !== false) { +          cookies = cookies.sort(cookieCompare); +        } + +        // S5.4 part 3 +        const now = new Date(); +        for (const cookie of cookies) { +          cookie.lastAccessed = now; +        } +        // TODO persist lastAccessed + +        cb(null, cookies); +      } +    ); +  } + +  getCookieString(...args) { +    const cb = args.pop(); +    const next = function(err, cookies) { +      if (err) { +        cb(err); +      } else { +        cb( +          null, +          cookies +            .sort(cookieCompare) +            .map(c => c.cookieString()) +            .join("; ") +        ); +      } +    }; +    args.push(next); +    this.getCookies.apply(this, args); +  } + +  getSetCookieStrings(...args) { +    const cb = args.pop(); +    const next = function(err, cookies) { +      if (err) { +        cb(err); +      } else { +        cb( +          null, +          cookies.map(c => { +            return c.toString(); +          }) +        ); +      } +    }; +    args.push(next); +    this.getCookies.apply(this, args); +  } + +  serialize(cb) { +    let type = this.store.constructor.name; +    if (type === "Object") { +      type = null; +    } + +    // update README.md "Serialization Format" if you change this, please! +    const serialized = { +      // The version of tough-cookie that serialized this jar. Generally a good +      // practice since future versions can make data import decisions based on +      // known past behavior. When/if this matters, use `semver`. +      version: `tough-cookie@${VERSION}`, + +      // add the store type, to make humans happy: +      storeType: type, + +      // CookieJar configuration: +      rejectPublicSuffixes: !!this.rejectPublicSuffixes, + +      // this gets filled from getAllCookies: +      cookies: [] +    }; + +    if ( +      !( +        this.store.getAllCookies && +        typeof this.store.getAllCookies === "function" +      ) +    ) { +      return cb( +        new Error( +          "store does not support getAllCookies and cannot be serialized" +        ) +      ); +    } + +    this.store.getAllCookies((err, cookies) => { +      if (err) { +        return cb(err); +      } + +      serialized.cookies = cookies.map(cookie => { +        // convert to serialized 'raw' cookies +        cookie = cookie instanceof Cookie ? cookie.toJSON() : cookie; + +        // Remove the index so new ones get assigned during deserialization +        delete cookie.creationIndex; + +        return cookie; +      }); + +      return cb(null, serialized); +    }); +  } + +  toJSON() { +    return this.serializeSync(); +  } + +  // use the class method CookieJar.deserialize instead of calling this directly +  _importCookies(serialized, cb) { +    let cookies = serialized.cookies; +    if (!cookies || !Array.isArray(cookies)) { +      return cb(new Error("serialized jar has no cookies array")); +    } +    cookies = cookies.slice(); // do not modify the original + +    const putNext = err => { +      if (err) { +        return cb(err); +      } + +      if (!cookies.length) { +        return cb(err, this); +      } + +      let cookie; +      try { +        cookie = fromJSON(cookies.shift()); +      } catch (e) { +        return cb(e); +      } + +      if (cookie === null) { +        return putNext(null); // skip this cookie +      } + +      this.store.putCookie(cookie, putNext); +    }; + +    putNext(); +  } + +  clone(newStore, cb) { +    if (arguments.length === 1) { +      cb = newStore; +      newStore = null; +    } + +    this.serialize((err, serialized) => { +      if (err) { +        return cb(err); +      } +      CookieJar.deserialize(serialized, newStore, cb); +    }); +  } + +  cloneSync(newStore) { +    if (arguments.length === 0) { +      return this._cloneSync(); +    } +    if (!newStore.synchronous) { +      throw new Error( +        "CookieJar clone destination store is not synchronous; use async API instead." +      ); +    } +    return this._cloneSync(newStore); +  } + +  removeAllCookies(cb) { +    const store = this.store; + +    // Check that the store implements its own removeAllCookies(). The default +    // implementation in Store will immediately call the callback with a "not +    // implemented" Error. +    if ( +      typeof store.removeAllCookies === "function" && +      store.removeAllCookies !== Store.prototype.removeAllCookies +    ) { +      return store.removeAllCookies(cb); +    } + +    store.getAllCookies((err, cookies) => { +      if (err) { +        return cb(err); +      } + +      if (cookies.length === 0) { +        return cb(null); +      } + +      let completedCount = 0; +      const removeErrors = []; + +      function removeCookieCb(removeErr) { +        if (removeErr) { +          removeErrors.push(removeErr); +        } + +        completedCount++; + +        if (completedCount === cookies.length) { +          return cb(removeErrors.length ? removeErrors[0] : null); +        } +      } + +      cookies.forEach(cookie => { +        store.removeCookie( +          cookie.domain, +          cookie.path, +          cookie.key, +          removeCookieCb +        ); +      }); +    }); +  } + +  static deserialize(strOrObj, store, cb) { +    if (arguments.length !== 3) { +      // store is optional +      cb = store; +      store = null; +    } + +    let serialized; +    if (typeof strOrObj === "string") { +      serialized = jsonParse(strOrObj); +      if (serialized instanceof Error) { +        return cb(serialized); +      } +    } else { +      serialized = strOrObj; +    } + +    const jar = new CookieJar(store, serialized.rejectPublicSuffixes); +    jar._importCookies(serialized, err => { +      if (err) { +        return cb(err); +      } +      cb(null, jar); +    }); +  } + +  static deserializeSync(strOrObj, store) { +    const serialized = +      typeof strOrObj === "string" ? JSON.parse(strOrObj) : strOrObj; +    const jar = new CookieJar(store, serialized.rejectPublicSuffixes); + +    // catch this mistake early: +    if (!jar.store.synchronous) { +      throw new Error( +        "CookieJar store is not synchronous; use async API instead." +      ); +    } + +    jar._importCookiesSync(serialized); +    return jar; +  } +} +CookieJar.fromJSON = CookieJar.deserializeSync; + +[ +  "_importCookies", +  "clone", +  "getCookies", +  "getCookieString", +  "getSetCookieStrings", +  "removeAllCookies", +  "serialize", +  "setCookie" +].forEach(name => { +  CookieJar.prototype[name] = fromCallback(CookieJar.prototype[name]); +}); +CookieJar.deserialize = fromCallback(CookieJar.deserialize); + +// Use a closure to provide a true imperative API for synchronous stores. +function syncWrap(method) { +  return function(...args) { +    if (!this.store.synchronous) { +      throw new Error( +        "CookieJar store is not synchronous; use async API instead." +      ); +    } + +    let syncErr, syncResult; +    this[method](...args, (err, result) => { +      syncErr = err; +      syncResult = result; +    }); + +    if (syncErr) { +      throw syncErr; +    } +    return syncResult; +  }; +} + +exports.version = VERSION; +exports.CookieJar = CookieJar; +exports.Cookie = Cookie; +exports.Store = Store; +exports.MemoryCookieStore = MemoryCookieStore; +exports.parseDate = parseDate; +exports.formatDate = formatDate; +exports.parse = parse; +exports.fromJSON = fromJSON; +exports.domainMatch = domainMatch; +exports.defaultPath = defaultPath; +exports.pathMatch = pathMatch; +exports.getPublicSuffix = pubsuffix.getPublicSuffix; +exports.cookieCompare = cookieCompare; +exports.permuteDomain = require("./permuteDomain").permuteDomain; +exports.permutePath = permutePath; +exports.canonicalDomain = canonicalDomain; +exports.PrefixSecurityEnum = PrefixSecurityEnum; diff --git a/node_modules/tough-cookie/lib/memstore.js b/node_modules/tough-cookie/lib/memstore.js new file mode 100644 index 0000000..912eead --- /dev/null +++ b/node_modules/tough-cookie/lib/memstore.js @@ -0,0 +1,190 @@ +/*! + * Copyright (c) 2015, Salesforce.com, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Salesforce.com nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +"use strict"; +const { fromCallback } = require("universalify"); +const Store = require("./store").Store; +const permuteDomain = require("./permuteDomain").permuteDomain; +const pathMatch = require("./pathMatch").pathMatch; +const util = require("util"); + +class MemoryCookieStore extends Store { +  constructor() { +    super(); +    this.synchronous = true; +    this.idx = {}; +    if (util.inspect.custom) { +      this[util.inspect.custom] = this.inspect; +    } +  } + +  inspect() { +    return `{ idx: ${util.inspect(this.idx, false, 2)} }`; +  } + +  findCookie(domain, path, key, cb) { +    if (!this.idx[domain]) { +      return cb(null, undefined); +    } +    if (!this.idx[domain][path]) { +      return cb(null, undefined); +    } +    return cb(null, this.idx[domain][path][key] || null); +  } +  findCookies(domain, path, allowSpecialUseDomain, cb) { +    const results = []; +    if (typeof allowSpecialUseDomain === "function") { +      cb = allowSpecialUseDomain; +      allowSpecialUseDomain = false; +    } +    if (!domain) { +      return cb(null, []); +    } + +    let pathMatcher; +    if (!path) { +      // null means "all paths" +      pathMatcher = function matchAll(domainIndex) { +        for (const curPath in domainIndex) { +          const pathIndex = domainIndex[curPath]; +          for (const key in pathIndex) { +            results.push(pathIndex[key]); +          } +        } +      }; +    } else { +      pathMatcher = function matchRFC(domainIndex) { +        //NOTE: we should use path-match algorithm from S5.1.4 here +        //(see : https://github.com/ChromiumWebApps/chromium/blob/b3d3b4da8bb94c1b2e061600df106d590fda3620/net/cookies/canonical_cookie.cc#L299) +        Object.keys(domainIndex).forEach(cookiePath => { +          if (pathMatch(path, cookiePath)) { +            const pathIndex = domainIndex[cookiePath]; +            for (const key in pathIndex) { +              results.push(pathIndex[key]); +            } +          } +        }); +      }; +    } + +    const domains = permuteDomain(domain, allowSpecialUseDomain) || [domain]; +    const idx = this.idx; +    domains.forEach(curDomain => { +      const domainIndex = idx[curDomain]; +      if (!domainIndex) { +        return; +      } +      pathMatcher(domainIndex); +    }); + +    cb(null, results); +  } + +  putCookie(cookie, cb) { +    if (!this.idx[cookie.domain]) { +      this.idx[cookie.domain] = {}; +    } +    if (!this.idx[cookie.domain][cookie.path]) { +      this.idx[cookie.domain][cookie.path] = {}; +    } +    this.idx[cookie.domain][cookie.path][cookie.key] = cookie; +    cb(null); +  } +  updateCookie(oldCookie, newCookie, cb) { +    // updateCookie() may avoid updating cookies that are identical.  For example, +    // lastAccessed may not be important to some stores and an equality +    // comparison could exclude that field. +    this.putCookie(newCookie, cb); +  } +  removeCookie(domain, path, key, cb) { +    if ( +      this.idx[domain] && +      this.idx[domain][path] && +      this.idx[domain][path][key] +    ) { +      delete this.idx[domain][path][key]; +    } +    cb(null); +  } +  removeCookies(domain, path, cb) { +    if (this.idx[domain]) { +      if (path) { +        delete this.idx[domain][path]; +      } else { +        delete this.idx[domain]; +      } +    } +    return cb(null); +  } +  removeAllCookies(cb) { +    this.idx = {}; +    return cb(null); +  } +  getAllCookies(cb) { +    const cookies = []; +    const idx = this.idx; + +    const domains = Object.keys(idx); +    domains.forEach(domain => { +      const paths = Object.keys(idx[domain]); +      paths.forEach(path => { +        const keys = Object.keys(idx[domain][path]); +        keys.forEach(key => { +          if (key !== null) { +            cookies.push(idx[domain][path][key]); +          } +        }); +      }); +    }); + +    // Sort by creationIndex so deserializing retains the creation order. +    // When implementing your own store, this SHOULD retain the order too +    cookies.sort((a, b) => { +      return (a.creationIndex || 0) - (b.creationIndex || 0); +    }); + +    cb(null, cookies); +  } +} + +[ +  "findCookie", +  "findCookies", +  "putCookie", +  "updateCookie", +  "removeCookie", +  "removeCookies", +  "removeAllCookies", +  "getAllCookies" +].forEach(name => { +  MemoryCookieStore[name] = fromCallback(MemoryCookieStore.prototype[name]); +}); + +exports.MemoryCookieStore = MemoryCookieStore; diff --git a/node_modules/tough-cookie/lib/pathMatch.js b/node_modules/tough-cookie/lib/pathMatch.js new file mode 100644 index 0000000..16ff63e --- /dev/null +++ b/node_modules/tough-cookie/lib/pathMatch.js @@ -0,0 +1,61 @@ +/*! + * Copyright (c) 2015, Salesforce.com, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Salesforce.com nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +"use strict"; +/* + * "A request-path path-matches a given cookie-path if at least one of the + * following conditions holds:" + */ +function pathMatch(reqPath, cookiePath) { +  // "o  The cookie-path and the request-path are identical." +  if (cookiePath === reqPath) { +    return true; +  } + +  const idx = reqPath.indexOf(cookiePath); +  if (idx === 0) { +    // "o  The cookie-path is a prefix of the request-path, and the last +    // character of the cookie-path is %x2F ("/")." +    if (cookiePath.substr(-1) === "/") { +      return true; +    } + +    // " o  The cookie-path is a prefix of the request-path, and the first +    // character of the request-path that is not included in the cookie- path +    // is a %x2F ("/") character." +    if (reqPath.substr(cookiePath.length, 1) === "/") { +      return true; +    } +  } + +  return false; +} + +exports.pathMatch = pathMatch; diff --git a/node_modules/tough-cookie/lib/permuteDomain.js b/node_modules/tough-cookie/lib/permuteDomain.js new file mode 100644 index 0000000..78e6cad --- /dev/null +++ b/node_modules/tough-cookie/lib/permuteDomain.js @@ -0,0 +1,70 @@ +/*! + * Copyright (c) 2015, Salesforce.com, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Salesforce.com nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +"use strict"; +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. +const SPECIAL_USE_DOMAINS = ["local"]; // RFC 6761 +function permuteDomain(domain, allowSpecialUseDomain) { +  let pubSuf = null; +  if (allowSpecialUseDomain) { +    const domainParts = domain.split("."); +    if (SPECIAL_USE_DOMAINS.includes(domainParts[domainParts.length - 1])) { +      pubSuf = `${domainParts[domainParts.length - 2]}.${ +        domainParts[domainParts.length - 1] +      }`; +    } else { +      pubSuf = pubsuffix.getPublicSuffix(domain); +    } +  } else { +    pubSuf = pubsuffix.getPublicSuffix(domain); +  } + +  if (!pubSuf) { +    return null; +  } +  if (pubSuf == domain) { +    return [domain]; +  } + +  const prefix = domain.slice(0, -(pubSuf.length + 1)); // ".example.com" +  const parts = prefix.split(".").reverse(); +  let cur = pubSuf; +  const permutations = [cur]; +  while (parts.length) { +    cur = `${parts.shift()}.${cur}`; +    permutations.push(cur); +  } +  return permutations; +} + +exports.permuteDomain = permuteDomain; diff --git a/node_modules/tough-cookie/lib/pubsuffix-psl.js b/node_modules/tough-cookie/lib/pubsuffix-psl.js new file mode 100644 index 0000000..93a8577 --- /dev/null +++ b/node_modules/tough-cookie/lib/pubsuffix-psl.js @@ -0,0 +1,38 @@ +/*! + * Copyright (c) 2018, Salesforce.com, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Salesforce.com nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +"use strict"; +const psl = require("psl"); + +function getPublicSuffix(domain) { +  return psl.get(domain); +} + +exports.getPublicSuffix = getPublicSuffix; diff --git a/node_modules/tough-cookie/lib/store.js b/node_modules/tough-cookie/lib/store.js new file mode 100644 index 0000000..2ed0259 --- /dev/null +++ b/node_modules/tough-cookie/lib/store.js @@ -0,0 +1,76 @@ +/*! + * Copyright (c) 2015, Salesforce.com, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of Salesforce.com nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +"use strict"; +/*jshint unused:false */ + +class Store { +  constructor() { +    this.synchronous = false; +  } + +  findCookie(domain, path, key, cb) { +    throw new Error("findCookie is not implemented"); +  } + +  findCookies(domain, path, allowSpecialUseDomain, cb) { +    throw new Error("findCookies is not implemented"); +  } + +  putCookie(cookie, cb) { +    throw new Error("putCookie is not implemented"); +  } + +  updateCookie(oldCookie, newCookie, cb) { +    // recommended default implementation: +    // return this.putCookie(newCookie, cb); +    throw new Error("updateCookie is not implemented"); +  } + +  removeCookie(domain, path, key, cb) { +    throw new Error("removeCookie is not implemented"); +  } + +  removeCookies(domain, path, cb) { +    throw new Error("removeCookies is not implemented"); +  } + +  removeAllCookies(cb) { +    throw new Error("removeAllCookies is not implemented"); +  } + +  getAllCookies(cb) { +    throw new Error( +      "getAllCookies is not implemented (therefore jar cannot be serialized)" +    ); +  } +} + +exports.Store = Store; diff --git a/node_modules/tough-cookie/lib/version.js b/node_modules/tough-cookie/lib/version.js new file mode 100644 index 0000000..e52f25b --- /dev/null +++ b/node_modules/tough-cookie/lib/version.js @@ -0,0 +1,2 @@ +// generated by genversion +module.exports = '4.0.0'  | 
