From e0f77777b2036dafd4e2324a8b26b444b6ff209a Mon Sep 17 00:00:00 2001 From: Jonathan Stewmon Date: Fri, 26 Apr 2019 18:02:28 -0500 Subject: [PATCH 1/7] return promise if callback is not passed to async methods --- lib/cookie.js | 15 ++++++++++ lib/memstore.js | 15 +++++++++- package.json | 3 +- test/api_test.js | 76 ++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 104 insertions(+), 5 deletions(-) diff --git a/lib/cookie.js b/lib/cookie.js index 0dadcf37..d3be92a7 100644 --- a/lib/cookie.js +++ b/lib/cookie.js @@ -38,6 +38,7 @@ 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 ";" @@ -1523,6 +1524,20 @@ class CookieJar { } 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) { diff --git a/lib/memstore.js b/lib/memstore.js index 80896790..912eead3 100644 --- a/lib/memstore.js +++ b/lib/memstore.js @@ -29,6 +29,7 @@ * 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; @@ -85,7 +86,6 @@ class MemoryCookieStore extends Store { Object.keys(domainIndex).forEach(cookiePath => { if (pathMatch(path, cookiePath)) { const pathIndex = domainIndex[cookiePath]; - for (const key in pathIndex) { results.push(pathIndex[key]); } @@ -174,4 +174,17 @@ class MemoryCookieStore extends Store { } } +[ + "findCookie", + "findCookies", + "putCookie", + "updateCookie", + "removeCookie", + "removeCookies", + "removeAllCookies", + "getAllCookies" +].forEach(name => { + MemoryCookieStore[name] = fromCallback(MemoryCookieStore.prototype[name]); +}); + exports.MemoryCookieStore = MemoryCookieStore; diff --git a/package.json b/package.json index 190a714f..b7f6eba0 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,7 @@ "dependencies": { "ip-regex": "^2.1.0", "psl": "^1.1.33", - "punycode": "^2.1.1" + "punycode": "^2.1.1", + "universalify": "^0.1.2" } } diff --git a/test/api_test.js b/test/api_test.js index 53577cb6..461f4295 100644 --- a/test/api_test.js +++ b/test/api_test.js @@ -29,15 +29,12 @@ * POSSIBILITY OF SUCH DAMAGE. */ "use strict"; -const util = require("util"); const vows = require("vows"); const assert = require("assert"); const async = require("async"); const tough = require("../lib/cookie"); const Cookie = tough.Cookie; const CookieJar = tough.CookieJar; -const Store = tough.Store; -const MemoryCookieStore = tough.MemoryCookieStore; const atNow = Date.now(); @@ -84,6 +81,79 @@ vows } } }) + .addBatch({ + "CookieJar Promises": { + topic: () => new CookieJar(), + setCookie: { + topic(jar) { + jar + .setCookie("foo=bar", "http://example.com") + .then(c => this.callback(null, c), this.callback); + }, + "resolves to a Cookie"(cookie) { + assert.ok(cookie instanceof Cookie); + assert.strictEqual(cookie.key, "foo"); + assert.strictEqual(cookie.value, "bar"); + } + }, + getCookies: { + topic(jar) { + jar + .getCookies("http://example.com") + .then(cookies => this.callback(null, cookies), this.callback); + }, + "resolves to an array of cookies"(cookies) { + assert.ok(Array.isArray(cookies), "not an array"); + assert.ok(cookies.length > 0, "array is empty"); + for (const cookie of cookies) { + assert.ok(cookie instanceof Cookie, "not instanceof Cookie"); + } + } + }, + getCookieString: { + topic(jar) { + jar + .getCookieString("http://example.com") + .then(cookies => this.callback(null, cookies), this.callback); + }, + "resolves to a string"(cookies) { + assert.ok(typeof cookies === "string", "not a string"); + } + }, + getSetCookieStrings: { + topic(jar) { + jar + .getSetCookieStrings("http://example.com") + .then(cookies => this.callback(null, cookies), this.callback); + }, + "resolves to a an array of strings"(cookies) { + assert.ok(Array.isArray(cookies), "not an array"); + assert.ok(cookies.length > 0, "array is empty"); + for (const cookie of cookies) { + assert.ok(typeof cookie === "string", "not a string"); + } + } + }, + removeAllCookies: { + topic(jar) { + jar.removeAllCookies().then(this.callback, this.callback); + }, + "resolves to undefined"(arg) { + assert.ok(arg === undefined, "was not undefined"); + } + }, + serialize: { + topic(jar) { + jar + .serialize() + .then(data => this.callback(null, data), this.callback); + }, + "resolves to an object"(data) { + assert.ok(data instanceof Object, "not an object"); + } + } + } + }) .addBatch({ "expiry option": { topic: function() { From b4d15f79351229a69245d1db7a5754f12c36e6a0 Mon Sep 17 00:00:00 2001 From: Michael de Libero Date: Mon, 27 Apr 2020 14:43:18 -0700 Subject: [PATCH 2/7] Fix for Issue 145 --- CHANGELOG.md | 5 +++++ lib/cookie.js | 7 +++++++ test/cookie_jar_test.js | 17 +++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5328f244..ee52d243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to this project will be documented in this file. +## 4.X.X + +### Mintor Changes +- Added in parameter checking to setCookie to error out when no URL was passed in + ## 4.0.0 ### Breaking Changes (Major Version) diff --git a/lib/cookie.js b/lib/cookie.js index a042893e..1a467d76 100644 --- a/lib/cookie.js +++ b/lib/cookie.js @@ -1077,6 +1077,13 @@ class CookieJar { setCookie(cookie, url, options, cb) { let err; + + if (typeof(url) === "function") { + cb = url + err = new Error("No URL was specified"); + return cb(err); + } + const context = getCookieContext(url); if (typeof options === "function") { cb = options; diff --git a/test/cookie_jar_test.js b/test/cookie_jar_test.js index 9a5d3821..32ccaf29 100644 --- a/test/cookie_jar_test.js +++ b/test/cookie_jar_test.js @@ -669,4 +669,21 @@ vows } } }) + .addBatch({ + "Issue #145 - Missing parameter validation on setCookie function causes TypeError": { + "with missing parameters": { + topic: function() { + const jar = new tough.CookieJar(); + jar.setCookie( + new String("x=y; Domain=example.com; Path=/"), + this.callback + ); + }, + "results in a error being returned because of missing parameters": function(err, cookies) { + assert(err != null); + assert.equal(err.message, "No URL was specified"); + } + } + } + }) .export(module); From 58dd89a20165adf52ed72fa04d42273e3b4e8c9f Mon Sep 17 00:00:00 2001 From: Michael de Libero Date: Mon, 27 Apr 2020 20:10:52 -0700 Subject: [PATCH 3/7] Made changes suggested by Andrew --- CHANGELOG.md | 4 ++-- lib/cookie.js | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee52d243..8fb1426f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,8 @@ All notable changes to this project will be documented in this file. ## 4.X.X -### Mintor Changes -- Added in parameter checking to setCookie to error out when no URL was passed in +### Minor Changes +- Added parameter checking to setCookie so as to error out when no URL was passed in ## 4.0.0 diff --git a/lib/cookie.js b/lib/cookie.js index 1a467d76..8cce6a71 100644 --- a/lib/cookie.js +++ b/lib/cookie.js @@ -1079,9 +1079,8 @@ class CookieJar { let err; if (typeof(url) === "function") { - cb = url - err = new Error("No URL was specified"); - return cb(err); + cb = url; + return cb(new Error("No URL was specified")); } const context = getCookieContext(url); From 715aa79f7a7b782817dc921eea08fd21ca108143 Mon Sep 17 00:00:00 2001 From: Michael de Libero Date: Wed, 27 May 2020 14:27:28 -0700 Subject: [PATCH 4/7] Added validation and other changes to help with parameter checking --- lib/cookie.js | 95 ++++++++++++++++++++++++++++++++++++++--- test/cookie_jar_test.js | 2 +- 2 files changed, 90 insertions(+), 7 deletions(-) diff --git a/lib/cookie.js b/lib/cookie.js index 8cce6a71..76f31d8c 100644 --- a/lib/cookie.js +++ b/lib/cookie.js @@ -1,5 +1,5 @@ /*! - * Copyright (c) 2015, Salesforce.com, Inc. + * Copyright (c) 2015-2020, Salesforce.com, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -78,7 +78,64 @@ 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"'; +/* Validation functions copied from check-types package - https://www.npmjs.com/package/check-types */ +function isFunction(data) { + return typeof data === 'function'; +} + +function isNonEmptyString(data) { + return isString(data) && data !== ''; +} + +function isDate(data) { + return isInstanceStrict(data, Date) && isInteger(data.getTime()); +} + +function isEmptyString(data) { + return data === ''; +} + +function isString(data) { + return typeof data === 'string'; +} + +function isObject(data) { + return toString.call(data) === '[object Object]'; +} +function isInstanceStrict(data, prototype) { + try { + return data instanceof prototype; + } catch (error) { + return false; + } +} + +function isInteger(data) { + return typeof data === 'number' && data % 1 === 0; +} + + +/* End validation functions */ + + + +function validate(bool, cb, options) { + if (!isFunction(cb)) { + options = cb; + cb = null; + } + if (!isObject(options)) options = { Error: "Failed Check" }; + if (!bool) { + if (cb) { + cb(new ParameterError(options)); + } else { + throw new ParameterError(options); + } + } +} + function checkSameSiteContext(value) { + validate(isNonEmptyString(value), value); const context = String(value).toLowerCase(); if (context === "none" || context === "lax" || context === "strict") { return context; @@ -97,7 +154,7 @@ const PrefixSecurityEnum = Object.freeze({ // * 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}|:)))$)/; +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}|:)))$)/; /* * Parses a Natural number (i.e., non-negative integer) with either the @@ -301,6 +358,7 @@ function parseDate(str) { } function formatDate(date) { + validate(isDate(date), date); return date.toUTCString(); } @@ -403,6 +461,7 @@ function defaultPath(path) { } function trimTerminator(str) { + if (isEmptyString(str)) return str; for (let t = 0; t < TERMINATORS.length; t++) { const terminatorIdx = str.indexOf(TERMINATORS[t]); if (terminatorIdx !== -1) { @@ -415,6 +474,7 @@ function trimTerminator(str) { function parseCookiePair(cookiePair, looseMode) { cookiePair = trimTerminator(cookiePair); + validate(isString(cookiePair), cookiePair); let firstEq = cookiePair.indexOf("="); if (looseMode) { @@ -616,6 +676,7 @@ function parse(str, options) { * @returns boolean */ function isSecurePrefixConditionMet(cookie) { + validate(isObject(cookie), cookie); return !cookie.key.startsWith("__Secure-") || cookie.secure; } @@ -631,6 +692,7 @@ function isSecurePrefixConditionMet(cookie) { * @returns boolean */ function isHostPrefixConditionMet(cookie) { + validate(isObject(cookie)); return ( !cookie.key.startsWith("__Host-") || (cookie.secure && @@ -698,6 +760,8 @@ function fromJSON(str) { */ function cookieCompare(a, b) { + validate(isObject(a), a); + validate(isObject(b), b); let cmp = 0; // descending for length: b CMP a @@ -725,6 +789,7 @@ function cookieCompare(a, b) { // 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) { + validate(isString(path)); if (path === "/") { return ["/"]; } @@ -775,6 +840,12 @@ const cookieDefaults = { sameSite: "none" }; +class ParameterError extends Error { + constructor(...params) { + super(...params); + } +}; + class Cookie { constructor(options = {}) { if (util.inspect.custom) { @@ -1060,6 +1131,7 @@ class CookieJar { if (typeof options === "boolean") { options = { rejectPublicSuffixes: options }; } + validate(isObject(options), options); this.rejectPublicSuffixes = options.rejectPublicSuffixes; this.enableLooseMode = !!options.looseMode; this.allowSpecialUseDomain = !!options.allowSpecialUseDomain; @@ -1076,18 +1148,20 @@ class CookieJar { } setCookie(cookie, url, options, cb) { + validate(isNonEmptyString(url), cb, options); let err; - if (typeof(url) === "function") { + if (isFunction(url)) { cb = url; return cb(new Error("No URL was specified")); } const context = getCookieContext(url); - if (typeof options === "function") { + if (isFunction(options)) { cb = options; options = {}; } + validate(isFunction(cb), cb); const host = canonicalDomain(context.hostname); const loose = options.loose || this.enableLooseMode; @@ -1255,11 +1329,14 @@ class CookieJar { // RFC6365 S5.4 getCookies(url, options, cb) { + validate(isNonEmptyString(url), cb, url); const context = getCookieContext(url); - if (typeof options === "function") { + if (isFunction(options)) { cb = options; options = {}; } + validate(isObject(options), cb, options); + validate(isFunction(cb), cb); const host = canonicalDomain(context.hostname); const path = context.pathname || "/"; @@ -1375,6 +1452,7 @@ class CookieJar { getCookieString(...args) { const cb = args.pop(); + validate(isFunction(cb), cb); const next = function(err, cookies) { if (err) { cb(err); @@ -1394,6 +1472,7 @@ class CookieJar { getSetCookieStrings(...args) { const cb = args.pop(); + validate(isFunction(cb), cb); const next = function(err, cookies) { if (err) { cb(err); @@ -1411,8 +1490,9 @@ class CookieJar { } serialize(cb) { + validate(isFunction(cb), cb); let type = this.store.constructor.name; - if (type === "Object") { + if (isObject(type)) { type = null; } @@ -1530,6 +1610,7 @@ class CookieJar { } removeAllCookies(cb) { + validate(isFunction(cb), cb); const store = this.store; // Check that the store implements its own removeAllCookies(). The default @@ -1583,6 +1664,7 @@ class CookieJar { cb = store; store = null; } + validate(isFunction(cb), cb); let serialized; if (typeof strOrObj === "string") { @@ -1675,3 +1757,4 @@ exports.permuteDomain = require("./permuteDomain").permuteDomain; exports.permutePath = permutePath; exports.canonicalDomain = canonicalDomain; exports.PrefixSecurityEnum = PrefixSecurityEnum; +exports.ParameterError = ParameterError; diff --git a/test/cookie_jar_test.js b/test/cookie_jar_test.js index 32ccaf29..8b8af0a5 100644 --- a/test/cookie_jar_test.js +++ b/test/cookie_jar_test.js @@ -681,7 +681,7 @@ vows }, "results in a error being returned because of missing parameters": function(err, cookies) { assert(err != null); - assert.equal(err.message, "No URL was specified"); + assert(err instanceof tough.ParameterError); } } } From a4122f1f11a57e2108ca0859c4062e3dd041cf5b Mon Sep 17 00:00:00 2001 From: Michael de Libero Date: Wed, 27 May 2020 15:24:07 -0700 Subject: [PATCH 5/7] Moved the validators to a separate file --- lib/cookie.js | 113 +++++++++++----------------------------------- lib/validators.js | 98 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 87 deletions(-) create mode 100644 lib/validators.js diff --git a/lib/cookie.js b/lib/cookie.js index 76f31d8c..f7c491e7 100644 --- a/lib/cookie.js +++ b/lib/cookie.js @@ -36,6 +36,7 @@ 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"); @@ -78,64 +79,8 @@ 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"'; -/* Validation functions copied from check-types package - https://www.npmjs.com/package/check-types */ -function isFunction(data) { - return typeof data === 'function'; -} - -function isNonEmptyString(data) { - return isString(data) && data !== ''; -} - -function isDate(data) { - return isInstanceStrict(data, Date) && isInteger(data.getTime()); -} - -function isEmptyString(data) { - return data === ''; -} - -function isString(data) { - return typeof data === 'string'; -} - -function isObject(data) { - return toString.call(data) === '[object Object]'; -} -function isInstanceStrict(data, prototype) { - try { - return data instanceof prototype; - } catch (error) { - return false; - } -} - -function isInteger(data) { - return typeof data === 'number' && data % 1 === 0; -} - - -/* End validation functions */ - - - -function validate(bool, cb, options) { - if (!isFunction(cb)) { - options = cb; - cb = null; - } - if (!isObject(options)) options = { Error: "Failed Check" }; - if (!bool) { - if (cb) { - cb(new ParameterError(options)); - } else { - throw new ParameterError(options); - } - } -} - function checkSameSiteContext(value) { - validate(isNonEmptyString(value), value); + validators.validate(validators.isNonEmptyString(value), value); const context = String(value).toLowerCase(); if (context === "none" || context === "lax" || context === "strict") { return context; @@ -358,7 +303,7 @@ function parseDate(str) { } function formatDate(date) { - validate(isDate(date), date); + validators.validate(validators.isDate(date), date); return date.toUTCString(); } @@ -461,7 +406,7 @@ function defaultPath(path) { } function trimTerminator(str) { - if (isEmptyString(str)) return str; + if (validators.isEmptyString(str)) return str; for (let t = 0; t < TERMINATORS.length; t++) { const terminatorIdx = str.indexOf(TERMINATORS[t]); if (terminatorIdx !== -1) { @@ -474,7 +419,7 @@ function trimTerminator(str) { function parseCookiePair(cookiePair, looseMode) { cookiePair = trimTerminator(cookiePair); - validate(isString(cookiePair), cookiePair); + validators.validate(validators.isString(cookiePair), cookiePair); let firstEq = cookiePair.indexOf("="); if (looseMode) { @@ -676,7 +621,7 @@ function parse(str, options) { * @returns boolean */ function isSecurePrefixConditionMet(cookie) { - validate(isObject(cookie), cookie); + validators.validate(validators.isObject(cookie), cookie); return !cookie.key.startsWith("__Secure-") || cookie.secure; } @@ -692,7 +637,7 @@ function isSecurePrefixConditionMet(cookie) { * @returns boolean */ function isHostPrefixConditionMet(cookie) { - validate(isObject(cookie)); + validators.validate(validators.isObject(cookie)); return ( !cookie.key.startsWith("__Host-") || (cookie.secure && @@ -760,8 +705,8 @@ function fromJSON(str) { */ function cookieCompare(a, b) { - validate(isObject(a), a); - validate(isObject(b), b); + validators.validate(validators.isObject(a), a); + validators.validate(validators.isObject(b), b); let cmp = 0; // descending for length: b CMP a @@ -789,7 +734,7 @@ function cookieCompare(a, b) { // 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) { - validate(isString(path)); + validators.validate(validators.isString(path)); if (path === "/") { return ["/"]; } @@ -840,12 +785,6 @@ const cookieDefaults = { sameSite: "none" }; -class ParameterError extends Error { - constructor(...params) { - super(...params); - } -}; - class Cookie { constructor(options = {}) { if (util.inspect.custom) { @@ -1131,7 +1070,7 @@ class CookieJar { if (typeof options === "boolean") { options = { rejectPublicSuffixes: options }; } - validate(isObject(options), options); + validators.validate(validators.isObject(options), options); this.rejectPublicSuffixes = options.rejectPublicSuffixes; this.enableLooseMode = !!options.looseMode; this.allowSpecialUseDomain = !!options.allowSpecialUseDomain; @@ -1148,20 +1087,20 @@ class CookieJar { } setCookie(cookie, url, options, cb) { - validate(isNonEmptyString(url), cb, options); + validators.validate(validators.isNonEmptyString(url), cb, options); let err; - if (isFunction(url)) { + if (validators.isFunction(url)) { cb = url; return cb(new Error("No URL was specified")); } const context = getCookieContext(url); - if (isFunction(options)) { + if (validators.isFunction(options)) { cb = options; options = {}; } - validate(isFunction(cb), cb); + validators.validate(validators.isFunction(cb), cb); const host = canonicalDomain(context.hostname); const loose = options.loose || this.enableLooseMode; @@ -1329,14 +1268,14 @@ class CookieJar { // RFC6365 S5.4 getCookies(url, options, cb) { - validate(isNonEmptyString(url), cb, url); + validators.validate(validators.isNonEmptyString(url), cb, url); const context = getCookieContext(url); - if (isFunction(options)) { + if (validators.isFunction(options)) { cb = options; options = {}; } - validate(isObject(options), cb, options); - validate(isFunction(cb), cb); + validators.validate(validators.isObject(options), cb, options); + validators.validate(validators.isFunction(cb), cb); const host = canonicalDomain(context.hostname); const path = context.pathname || "/"; @@ -1452,7 +1391,7 @@ class CookieJar { getCookieString(...args) { const cb = args.pop(); - validate(isFunction(cb), cb); + validators.validate(validators.isFunction(cb), cb); const next = function(err, cookies) { if (err) { cb(err); @@ -1472,7 +1411,7 @@ class CookieJar { getSetCookieStrings(...args) { const cb = args.pop(); - validate(isFunction(cb), cb); + validators.validate(validators.isFunction(cb), cb); const next = function(err, cookies) { if (err) { cb(err); @@ -1490,9 +1429,9 @@ class CookieJar { } serialize(cb) { - validate(isFunction(cb), cb); + validators.validate(validators.isFunction(cb), cb); let type = this.store.constructor.name; - if (isObject(type)) { + if (validators.isObject(type)) { type = null; } @@ -1610,7 +1549,7 @@ class CookieJar { } removeAllCookies(cb) { - validate(isFunction(cb), cb); + validators.validate(validators.isFunction(cb), cb); const store = this.store; // Check that the store implements its own removeAllCookies(). The default @@ -1664,7 +1603,7 @@ class CookieJar { cb = store; store = null; } - validate(isFunction(cb), cb); + validators.validate(validators.isFunction(cb), cb); let serialized; if (typeof strOrObj === "string") { @@ -1757,4 +1696,4 @@ exports.permuteDomain = require("./permuteDomain").permuteDomain; exports.permutePath = permutePath; exports.canonicalDomain = canonicalDomain; exports.PrefixSecurityEnum = PrefixSecurityEnum; -exports.ParameterError = ParameterError; +exports.ParameterError = validators.ParameterError; diff --git a/lib/validators.js b/lib/validators.js new file mode 100644 index 00000000..d401c427 --- /dev/null +++ b/lib/validators.js @@ -0,0 +1,98 @@ +/*! + * Copyright (c) 2015-2020, 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"; + +/* Validation functions copied from check-types package - https://www.npmjs.com/package/check-types */ +function isFunction(data) { + return typeof data === 'function'; +} + +function isNonEmptyString(data) { + return isString(data) && data !== ''; +} + +function isDate(data) { + return isInstanceStrict(data, Date) && isInteger(data.getTime()); +} + +function isEmptyString(data) { + return data === ''; +} + +function isString(data) { + return typeof data === 'string'; +} + +function isObject(data) { + return toString.call(data) === '[object Object]'; +} +function isInstanceStrict(data, prototype) { + try { + return data instanceof prototype; + } catch (error) { + return false; + } +} + +function isInteger(data) { + return typeof data === 'number' && data % 1 === 0; +} +/* End validation functions */ + +function validate(bool, cb, options) { + if (!isFunction(cb)) { + options = cb; + cb = null; + } + if (!isObject(options)) options = { Error: "Failed Check" }; + if (!bool) { + if (cb) { + cb(new ParameterError(options)); + } else { + throw new ParameterError(options); + } + } +} + +class ParameterError extends Error { + constructor(...params) { + super(...params); + } +}; + +exports.ParameterError = ParameterError; +exports.isFunction = isFunction; +exports.isNonEmptyString = isNonEmptyString; +exports.isDate = isDate; +exports.isEmptyString = isEmptyString; +exports.isString = isString; +exports.isObject = isObject; +exports.validate = validate; \ No newline at end of file From e3a663ad618d9d8c7e22a6d7291758e71e598144 Mon Sep 17 00:00:00 2001 From: Michael de Libero Date: Mon, 15 Jun 2020 14:08:07 -0700 Subject: [PATCH 6/7] Removed SFDC copyright from validator --- lib/validators.js | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/lib/validators.js b/lib/validators.js index d401c427..07b0e81c 100644 --- a/lib/validators.js +++ b/lib/validators.js @@ -1,33 +1,3 @@ -/*! - * Copyright (c) 2015-2020, 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"; /* Validation functions copied from check-types package - https://www.npmjs.com/package/check-types */ From ffdb6b1eaa68ba9271ef4078d893cd29037d36ad Mon Sep 17 00:00:00 2001 From: Michael de Libero Date: Thu, 23 Jul 2020 12:46:54 -0700 Subject: [PATCH 7/7] Added in the MIT license to the validators.js file --- lib/validators.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/validators.js b/lib/validators.js index 07b0e81c..fd46034c 100644 --- a/lib/validators.js +++ b/lib/validators.js @@ -1,3 +1,30 @@ +/* ************************************************************************************ +Extracted from check-types.js +https://gitlab.com/philbooth/check-types.js + +MIT License + +Copyright (c) 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 Phil Booth + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +************************************************************************************ */ "use strict"; /* Validation functions copied from check-types package - https://www.npmjs.com/package/check-types */