Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: salesforce/tough-cookie
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 671ad413b38d7464352886772f57a7ec417d4760
Choose a base ref
...
head repository: salesforce/tough-cookie
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 4ff4d29f6cefd279a412b8d62a21142ebd410b36
Choose a head ref

Commits on Jun 25, 2018

  1. Copy the full SHA
    e083fc8 View commit details

Commits on Jul 30, 2018

  1. Merge pull request #116 from salesforce/fix-clone

    Fix jar clone and cloneSync methods, fixing #101
    stash authored Jul 30, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    6300174 View commit details
  2. Copy the full SHA
    901d898 View commit details
  3. Copy the full SHA
    55a41c1 View commit details
  4. Merge pull request #118 from salesforce/deps-2018jul30

    Update packages & npm audit fix. Merging based on discussion today.
    awaterma authored Jul 30, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    4cbd182 View commit details
  5. 2.4.4-1

    stash committed Jul 30, 2018
    Copy the full SHA
    3d8570c View commit details
  6. Purposefully don't lock dependencies

    Since this is a module and not an end package, we want CI/etc. to always
    upgrade packages based on semver.
    stash committed Jul 30, 2018
    Copy the full SHA
    550f951 View commit details
  7. Upgrade to punycode 2.1

    stash committed Jul 30, 2018
    Copy the full SHA
    31ad267 View commit details

Commits on Aug 6, 2018

  1. Merge pull request #119 from salesforce/inline-version

    Inline version
    wtfismyip authored Aug 6, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    b897b49 View commit details

Commits on Aug 14, 2018

  1. Merge pull request #120 from salesforce/no-package-lock

    Purposefully don't lock dependencies
    awaterma authored Aug 14, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    d6ea115 View commit details
  2. Merge pull request #121 from salesforce/punycode-2.1

    Upgrade to punycode 2.1
    awaterma authored Aug 14, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    8302ebc View commit details

Commits on Oct 16, 2018

  1. Remove left-over mention of MPL from README

    Closes #129
    stash authored Oct 16, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    8783d46 View commit details

Commits on Nov 26, 2018

  1. remove all cookies from cookie jar at once (#115)

    * remove all cookies from jar
    * fix typo in readme
    * delete cookies one-by-one for old store implementations
    YevhenLukomskyi authored and stash committed Nov 26, 2018
    Copy the full SHA
    62802ef View commit details
  2. Copy the full SHA
    28f0808 View commit details
  3. Copy the full SHA
    5cc9bd2 View commit details
  4. Additional documentation for removeAllCookies

    Covers the cases where removeAllCookies (and even getAllCookies!) isn't
    implemented, in particular what happens when multiple errors are
    returned.
    stash committed Nov 26, 2018
    Copy the full SHA
    1855bf3 View commit details
  5. Copy the full SHA
    9ff4ba5 View commit details
  6. 2.5.0

    stash committed Nov 26, 2018
    Copy the full SHA
    7c1fdf1 View commit details

Commits on Dec 31, 2018

  1. 1
    Copy the full SHA
    eff9118 View commit details

Commits on Jan 7, 2019

  1. setCookie arg must be Cookie or string

    Addresses #132, partially. The error message is clearer, indicating that
    this method only accepts the afformentioned types. The part of 132 that
    it doesn't address is the whole "Cookie instance from another version of
    the `tough-cookie` package" thing, which isn't really solvable unless we
    somehow expose the Cookie class that "belongs to" this CookieJar class,
    which is maybe another patch.
    stash committed Jan 7, 2019
    Copy the full SHA
    84422cd View commit details
  2. Leave a breadcrumb for setCookie error

    In the event that two tough-cookie modules are loaded, it can be confusing when one sees this error, so help folks figure out how to resolve this.
    stash authored Jan 7, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    e213c27 View commit details
  3. Merge pull request #137 from salesforce/issue-132

    setCookie arg must be Cookie or string
    stash authored Jan 7, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    9e2626d View commit details
  4. Merge pull request #136 from gzzhanghao/master

    Use ip-regex instead of net.isIP. Fix #125
    stash authored Jan 7, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    8aaa5c4 View commit details
  5. Copy the full SHA
    16ba441 View commit details
  6. Merge pull request #138 from salesforce/issue-125

    Fixes #125 
    Upgrades minimum supported node.js version to v6 because (a) ip-regexp doesn't work before v4 and (b) request now requires v6+
    stash authored Jan 7, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    0ebed2e View commit details
  7. 3.0.0

    stash committed Jan 7, 2019
    Copy the full SHA
    05b4713 View commit details

Commits on Jan 10, 2019

  1. Copy the full SHA
    e8ce830 View commit details

Commits on Feb 4, 2019

  1. Merge pull request #141 from simonihmig/fix-ip-regex

    Downgrade ip-regex dependency to support node 6
    stash authored Feb 4, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    73920e5 View commit details

Commits on Feb 5, 2019

  1. 3.0.1

    stash committed Feb 5, 2019
    Copy the full SHA
    4350705 View commit details
  2. Copy the full SHA
    a4bf3e2 View commit details
  3. SameSite: set-cookie logic

    stash committed Feb 5, 2019
    Copy the full SHA
    cf3ede8 View commit details
  4. Copy the full SHA
    9582a4f View commit details
  5. Copy the full SHA
    a7a2b5b View commit details
  6. Copy the full SHA
    2b4c8e9 View commit details
  7. SameSite: documentation

    stash committed Feb 5, 2019
    Copy the full SHA
    c84fffe View commit details
  8. Copy the full SHA
    797f0a5 View commit details
  9. Copy the full SHA
    a565060 View commit details
  10. Copy the full SHA
    3abb594 View commit details
  11. Copy the full SHA
    9cdd54e View commit details
  12. .cloneSync should not require object param

    Documentation says: `.cloneSync([store])` 
    So cloneSync does not require `store` param, but it does due to property(`synchronous`) checking
    AlexOwl authored Feb 5, 2019
    Copy the full SHA
    275724b View commit details

Commits on Feb 6, 2019

  1. Copy the full SHA
    23dba9e View commit details

Commits on Feb 7, 2019

  1. Change style 😁👌

    AlexOwl authored Feb 7, 2019
    Copy the full SHA
    ff932cd View commit details

Commits on May 30, 2019

  1. fix(cookie-jar): use typeof for checking type of option

    Works around jestjs/jest#2549 when used in Jest
    SimenB authored May 30, 2019
    Copy the full SHA
    d94532d View commit details

Commits on Aug 21, 2019

  1. use eslint and prettier to apply consistent, modern formatting (#155)

    * use eslint and prettier to apply consistent formatting
    * convert var to let,const
    * prefer template literal over string concatenation
    * prefer arrow function callbacks
    * remove string.prototype.repeat
    * bump async and nyc to latest
    * use Date.prototype.toUTCString to format date
    * reformat async task arrangement to be more compact
    * condense json serialization test with arrow functions
    * require punycode unconditionally
    * update remove cookies test to no assume synchronous store
    * npm test should not run eslint
    jstewmon authored and awaterma committed Aug 21, 2019
    Copy the full SHA
    88618e2 View commit details

Commits on Aug 22, 2019

  1. Copy the full SHA
    c1a791e View commit details
  2. use eslint and prettier to apply consistent, modern formatting (#168)

    * use eslint and prettier to apply consistent formatting
    * convert var to let,const
    * prefer template literal over string concatenation
    * prefer arrow function callbacks
    * remove string.prototype.repeat
    * bump async and nyc to latest
    * use Date.prototype.toUTCString to format date
    * reformat async task arrangement to be more compact
    * condense json serialization test with arrow functions
    * require punycode unconditionally
    * update remove cookies test to no assume synchronous store
    * npm test should not run eslint
    * test: update ietf test dates to impossibly far in the future
    * test: prune unused module var
    jstewmon authored and awaterma committed Aug 22, 2019
    Copy the full SHA
    296a98f View commit details

Commits on Aug 26, 2019

  1. updated PSL

    medelibero-sfdc committed Aug 26, 2019
    Copy the full SHA
    720703f View commit details
  2. Merge pull request #169 from salesforce/deps-20190826

    updated PSL
    wtfismyip authored Aug 26, 2019
    Copy the full SHA
    7b8570c View commit details

Commits on Oct 5, 2019

  1. modify permuteDomain to allow special-use domain 'local'

        this was added because .local domains are commonly used for testing
        purposes inside internal/local networks.
    miggs125 committed Oct 5, 2019
    Copy the full SHA
    873842e View commit details
  2. add allowSpecialUseDomain to MemoryCookieStore.findCookies

        added allowSpecialUseDomain to parameters so that it can be passed to
        permuteDomain, line 103
    miggs125 committed Oct 5, 2019
    Copy the full SHA
    f08f254 View commit details
21 changes: 21 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"extends": ["plugin:prettier/recommended"],
"parserOptions": {
"ecmaVersion": 6
},
"rules": {
"no-debugger": "error",
"no-var": "error",
"prefer-arrow-callback": "error",
"prefer-const": "error",
"prefer-template": "error",
"no-restricted-modules": ["error",
// we can't rely on node standard modules in "native" or "browser" environments
// - exceptions:
// "punycode": since it's a package.json dependency
"assert", "buffer", "child_process", "cluster", "crypto", "dgram", "dns", "domain", "events", "freelist", "fs",
"http", "https", "module", "net", "os", "path", "querystring", "readline", "repl", "smalloc", "stream",
"string_decoder", "sys", "timers", "tls", "tracing", "tty", "url", "util", "vm", "zlib"
]
}
}
26 changes: 26 additions & 0 deletions .github/workflows/integration.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Actions-CI

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
build:

runs-on: ubuntu-latest

strategy:
matrix:
node-version: [14.x, 16.x]

steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies and test
run: npm install
- run: npm test
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -5,3 +5,7 @@ npm-debug.log
docker-compose.override.yml
.nyc_output
coverage
package-lock.json
.npm
.config
.bash_history
70 changes: 0 additions & 70 deletions .jshintrc

This file was deleted.

1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -2,3 +2,4 @@ docker-compose.yml
Dockerfile
.nyc_output
coverage
package-lock.json
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lib/version.js
3 changes: 0 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -4,8 +4,5 @@ node_js:
- "v10.*"
- "v8.*"
- "v6.*"
- "v4.*"
- "v0.12.*"
- "v0.10.*"
matrix:
fast_finish: true
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

All notable changes to this project can be found at on the [Releases](https://github.com/salesforce/tough-cookie/releases)
page.

2 changes: 2 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Comment line immediately above ownership line is reserved for related information. Please be careful while editing.
#ECCN:Open Source
105 changes: 105 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Salesforce Open Source Community Code of Conduct

## About the Code of Conduct

Equality is a core value at Salesforce. We believe a diverse and inclusive
community fosters innovation and creativity, and are committed to building a
culture where everyone feels included.

Salesforce open-source projects are committed to providing a friendly, safe, and
welcoming environment for all, regardless of gender identity and expression,
sexual orientation, disability, physical appearance, body size, ethnicity, nationality,
race, age, religion, level of experience, education, socioeconomic status, or
other similar personal characteristics.

The goal of this code of conduct is to specify a baseline standard of behavior so
that people with different social values and communication styles can work
together effectively, productively, and respectfully in our open source community.
It also establishes a mechanism for reporting issues and resolving conflicts.

All questions and reports of abusive, harassing, or otherwise unacceptable behavior
in a Salesforce open-source project may be reported by contacting the Salesforce
Open Source Conduct Committee at ossconduct@salesforce.com.

## Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of gender
identity and expression, sexual orientation, disability, physical appearance,
body size, ethnicity, nationality, race, age, religion, level of experience, education,
socioeconomic status, or other similar personal characteristics.

## Our Standards

Examples of behavior that contributes to creating a positive environment
include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy toward other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Personal attacks, insulting/derogatory comments, or trolling
* Public or private harassment
* Publishing, or threatening to publish, others' private information—such as
a physical or electronic address—without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
* Advocating for or encouraging any of the above behaviors

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned with this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project email
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the Salesforce Open Source Conduct Committee
at ossconduct@salesforce.com. All complaints will be reviewed and investigated
and will result in a response that is deemed necessary and appropriate to the
circumstances. The committee is obligated to maintain confidentiality with
regard to the reporter of an incident. Further details of specific enforcement
policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership and the Salesforce Open Source Conduct
Committee.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant-home],
version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html.
It includes adaptions and additions from [Go Community Code of Conduct][golang-coc],
[CNCF Code of Conduct][cncf-coc], and [Microsoft Open Source Code of Conduct][microsoft-coc].

This Code of Conduct is licensed under the [Creative Commons Attribution 3.0 License][cc-by-3-us].

[contributor-covenant-home]: https://www.contributor-covenant.org (https://www.contributor-covenant.org/)
[golang-coc]: https://golang.org/conduct
[cncf-coc]: https://github.com/cncf/foundation/blob/master/code-of-conduct.md
[microsoft-coc]: https://opensource.microsoft.com/codeofconduct/
[cc-by-3-us]: https://creativecommons.org/licenses/by/3.0/us/
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node
FROM node:16
MAINTAINER awaterman@salesforce.com
LABEL Description="Vendor=\"Salesforce.com\" Version=\"1.0\""
RUN apt-get update && \
507 changes: 298 additions & 209 deletions README.md

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Security

Please report any security issue to [security@salesforce.com](mailto:security@salesforce.com)
as soon as it is discovered. This library limits its runtime dependencies in
order to reduce the total cost of ownership as much as can be, but all consumers
should remain vigilant and have their security stakeholders review all third-party
products (3PP) like this one and their dependencies.
1,863 changes: 1,094 additions & 769 deletions lib/cookie.js

Large diffs are not rendered by default.

316 changes: 191 additions & 125 deletions lib/memstore.js
Original file line number Diff line number Diff line change
@@ -28,149 +28,215 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
'use strict';
var Store = require('./store').Store;
var permuteDomain = require('./permuteDomain').permuteDomain;
var pathMatch = require('./pathMatch').pathMatch;
var util = require('util');

function MemoryCookieStore() {
Store.call(this);
this.idx = {};
}
util.inherits(MemoryCookieStore, Store);
exports.MemoryCookieStore = MemoryCookieStore;
MemoryCookieStore.prototype.idx = null;

// Since it's just a struct in RAM, this Store is synchronous
MemoryCookieStore.prototype.synchronous = true;

// force a default depth:
MemoryCookieStore.prototype.inspect = function() {
return "{ idx: "+util.inspect(this.idx, false, 2)+' }';
};

// Use the new custom inspection symbol to add the custom inspect function if
// available.
if (util.inspect.custom) {
MemoryCookieStore.prototype[util.inspect.custom] = MemoryCookieStore.prototype.inspect;
}

MemoryCookieStore.prototype.findCookie = function(domain, path, key, cb) {
if (!this.idx[domain]) {
return cb(null,undefined);
"use strict";
const { fromCallback } = require("universalify");
const Store = require("./store").Store;
const permuteDomain = require("./permuteDomain").permuteDomain;
const pathMatch = require("./pathMatch").pathMatch;
const { getCustomInspectSymbol, getUtilInspect } = require("./utilHelper");

class MemoryCookieStore extends Store {
constructor() {
super();
this.synchronous = true;
this.idx = Object.create(null);
const customInspectSymbol = getCustomInspectSymbol();
if (customInspectSymbol) {
this[customInspectSymbol] = this.inspect;
}
}
if (!this.idx[domain][path]) {
return cb(null,undefined);

inspect() {
const util = { inspect: getUtilInspect(inspectFallback) };
return `{ idx: ${util.inspect(this.idx, false, 2)} }`;
}
return cb(null,this.idx[domain][path][key]||null);
};

MemoryCookieStore.prototype.findCookies = function(domain, path, cb) {
var results = [];
if (!domain) {
return cb(null,[]);
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 = true;
}
if (!domain) {
return cb(null, []);
}

var pathMatcher;
if (!path) {
// null means "all paths"
pathMatcher = function matchAll(domainIndex) {
for (var curPath in domainIndex) {
var pathIndex = domainIndex[curPath];
for (var key in pathIndex) {
results.push(pathIndex[key]);
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;
}
};

} 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(function (cookiePath) {
if (pathMatch(path, cookiePath)) {
var pathIndex = domainIndex[cookiePath];

for (var key in pathIndex) {
results.push(pathIndex[key]);
}
}
});
};
pathMatcher(domainIndex);
});

cb(null, results);
}

var domains = permuteDomain(domain) || [domain];
var idx = this.idx;
domains.forEach(function(curDomain) {
var domainIndex = idx[curDomain];
if (!domainIndex) {
return;
putCookie(cookie, cb) {
if (!this.idx[cookie.domain]) {
this.idx[cookie.domain] = Object.create(null);
}
pathMatcher(domainIndex);
});

cb(null,results);
};

MemoryCookieStore.prototype.putCookie = function(cookie, cb) {
if (!this.idx[cookie.domain]) {
this.idx[cookie.domain] = {};
if (!this.idx[cookie.domain][cookie.path]) {
this.idx[cookie.domain][cookie.path] = Object.create(null);
}
this.idx[cookie.domain][cookie.path][cookie.key] = cookie;
cb(null);
}
if (!this.idx[cookie.domain][cookie.path]) {
this.idx[cookie.domain][cookie.path] = {};
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);
}
this.idx[cookie.domain][cookie.path][cookie.key] = cookie;
cb(null);
};

MemoryCookieStore.prototype.updateCookie = function(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);
};

MemoryCookieStore.prototype.removeCookie = function(domain, path, key, cb) {
if (this.idx[domain] && this.idx[domain][path] && this.idx[domain][path][key]) {
delete this.idx[domain][path][key];
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);
}
cb(null);
};

MemoryCookieStore.prototype.removeCookies = function(domain, path, cb) {
if (this.idx[domain]) {
if (path) {
delete this.idx[domain][path];
} else {
delete this.idx[domain];
removeCookies(domain, path, cb) {
if (this.idx[domain]) {
if (path) {
delete this.idx[domain][path];
} else {
delete this.idx[domain];
}
}
return cb(null);
}
return cb(null);
};

MemoryCookieStore.prototype.getAllCookies = function(cb) {
var cookies = [];
var idx = this.idx;

var domains = Object.keys(idx);
domains.forEach(function(domain) {
var paths = Object.keys(idx[domain]);
paths.forEach(function(path) {
var keys = Object.keys(idx[domain][path]);
keys.forEach(function(key) {
if (key !== null) {
cookies.push(idx[domain][path][key]);
}
removeAllCookies(cb) {
this.idx = Object.create(null);
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.prototype[name] = fromCallback(
MemoryCookieStore.prototype[name]
);
});

exports.MemoryCookieStore = MemoryCookieStore;

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

// Sort by creationIndex so deserializing retains the creation order.
// When implementing your own store, this SHOULD retain the order too
cookies.sort(function(a,b) {
return (a.creationIndex||0) - (b.creationIndex||0);
function formatDomain(domainName, domainValue) {
const indent = " ";
let result = `${indent}'${domainName}': [Object: null prototype] {\n`;
Object.keys(domainValue).forEach((path, i, paths) => {
result += formatPath(path, domainValue[path]);
if (i < paths.length - 1) {
result += ",";
}
result += "\n";
});
result += `${indent}}`;
return result;
}

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

cb(null, cookies);
};
exports.inspectFallback = inspectFallback;
4 changes: 2 additions & 2 deletions lib/pathMatch.js
Original file line number Diff line number Diff line change
@@ -33,13 +33,13 @@
* "A request-path path-matches a given cookie-path if at least one of the
* following conditions holds:"
*/
function pathMatch (reqPath, cookiePath) {
function pathMatch(reqPath, cookiePath) {
// "o The cookie-path and the request-path are identical."
if (cookiePath === reqPath) {
return true;
}

var idx = reqPath.indexOf(cookiePath);
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 ("/")."
25 changes: 17 additions & 8 deletions lib/permuteDomain.js
Original file line number Diff line number Diff line change
@@ -29,25 +29,34 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
"use strict";
var pubsuffix = require('./pubsuffix-psl');
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.
function permuteDomain (domain) {
var pubSuf = pubsuffix.getPublicSuffix(domain);

function permuteDomain(domain, allowSpecialUseDomain) {
const pubSuf = pubsuffix.getPublicSuffix(domain, {
allowSpecialUseDomain: allowSpecialUseDomain
});

if (!pubSuf) {
return null;
}
if (pubSuf == domain) {
return [domain];
}

var prefix = domain.slice(0, -(pubSuf.length + 1)); // ".example.com"
var parts = prefix.split('.').reverse();
var cur = pubSuf;
var permutations = [cur];
// Nuke trailing dot
if (domain.slice(-1) == ".") {
domain = domain.slice(0, -1);
}

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;
cur = `${parts.shift()}.${cur}`;
permutations.push(cur);
}
return permutations;
41 changes: 38 additions & 3 deletions lib/pubsuffix-psl.js
Original file line number Diff line number Diff line change
@@ -28,10 +28,45 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
'use strict';
var psl = require('psl');
"use strict";
const psl = require("psl");

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

const SPECIAL_TREATMENT_DOMAINS = ["localhost", "invalid"];

function getPublicSuffix(domain, options = {}) {
const domainParts = domain.split(".");
const topLevelDomain = domainParts[domainParts.length - 1];
const allowSpecialUseDomain = !!options.allowSpecialUseDomain;
const ignoreError = !!options.ignoreError;

if (allowSpecialUseDomain && SPECIAL_USE_DOMAINS.includes(topLevelDomain)) {
if (domainParts.length > 1) {
const secondLevelDomain = domainParts[domainParts.length - 2];
// In aforementioned example, the eTLD/pubSuf will be apple.localhost
return `${secondLevelDomain}.${topLevelDomain}`;
} else if (SPECIAL_TREATMENT_DOMAINS.includes(topLevelDomain)) {
// For a single word special use domain, e.g. 'localhost' or 'invalid', per RFC 6761,
// "Application software MAY recognize {localhost/invalid} names as special, or
// MAY pass them to name resolution APIs as they would for other domain names."
return `${topLevelDomain}`;
}
}

if (!ignoreError && SPECIAL_USE_DOMAINS.includes(topLevelDomain)) {
throw new Error(
`Cookie has domain set to the public suffix "${topLevelDomain}" which is a special use domain. To allow this, configure your CookieJar with {allowSpecialUseDomain:true, rejectPublicSuffixes: false}.`
);
}

function getPublicSuffix(domain) {
return psl.get(domain);
}

67 changes: 36 additions & 31 deletions lib/store.js
Original file line number Diff line number Diff line change
@@ -28,44 +28,49 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
'use strict';
"use strict";
/*jshint unused:false */

function Store() {
}
exports.Store = Store;
class Store {
constructor() {
this.synchronous = false;
}

// Stores may be synchronous, but are still required to use a
// Continuation-Passing Style API. The CookieJar itself will expose a "*Sync"
// API that converts from synchronous-callbacks to imperative style.
Store.prototype.synchronous = false;
findCookie(domain, path, key, cb) {
throw new Error("findCookie is not implemented");
}

Store.prototype.findCookie = function(domain, path, key, cb) {
throw new Error('findCookie is not implemented');
};
findCookies(domain, path, allowSpecialUseDomain, cb) {
throw new Error("findCookies is not implemented");
}

Store.prototype.findCookies = function(domain, path, cb) {
throw new Error('findCookies is not implemented');
};
putCookie(cookie, cb) {
throw new Error("putCookie is not implemented");
}

Store.prototype.putCookie = function(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");
}

Store.prototype.updateCookie = function(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");
}

Store.prototype.removeCookie = function(domain, path, key, cb) {
throw new Error('removeCookie is not implemented');
};
removeCookies(domain, path, cb) {
throw new Error("removeCookies is not implemented");
}

Store.prototype.removeCookies = function(domain, path, cb) {
throw new Error('removeCookies is not implemented');
};
removeAllCookies(cb) {
throw new Error("removeAllCookies is not implemented");
}

Store.prototype.getAllCookies = function(cb) {
throw new Error('getAllCookies is not implemented (therefore jar cannot be serialized)');
};
getAllCookies(cb) {
throw new Error(
"getAllCookies is not implemented (therefore jar cannot be serialized)"
);
}
}

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

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

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

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

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

// get custom inspect symbol for node environments
return (
_lookupCustomInspectSymbol() ||
tryReadingCustomSymbolFromUtilInspect(options)
);
};
95 changes: 95 additions & 0 deletions lib/validators.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/* ************************************************************************************
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 */
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 === "" || (data instanceof String && data.toString() === "");
}

function isString(data) {
return typeof data === "string" || data instanceof 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;
2 changes: 2 additions & 0 deletions lib/version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// generated by genversion
module.exports = '4.1.3'
70 changes: 52 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
@@ -6,28 +6,52 @@
},
"contributors": [
{
"name": "Alexander Savin",
"website": "https://github.com/apsavin"
"name": "Ivan Nikulin",
"website": "https://github.com/inikulin"
},
{
"name": "Shivan Kaul Sahib",
"website": "https://github.com/ShivanKaul"
},
{
"name": "Clint Ruoho",
"website": "https://github.com/ruoho"
},
{
"name": "Ian Livingstone",
"website": "https://github.com/ianlivingstone"
},
{
"name": "Ivan Nikulin",
"website": "https://github.com/inikulin"
"name": "Andrew Waterman",
"website": "https://github.com/awaterma"
},
{
"name": "Lalit Kapoor",
"website": "https://github.com/lalitkapoor"
"name": "Michael de Libero ",
"website": "https://github.com/medelibero-sfdc"
},
{
"name": "Sam Thompson",
"website": "https://github.com/sambthompson"
"name": "Jonathan Stewmon",
"website": "https://github.com/jstewmon"
},
{
"name": "Miguel Roncancio",
"website": "https://github.com/miggs125"
},
{
"name": "Sebastian Mayr",
"website": "https://github.com/Sebmaster"
},
{
"name": "Alexander Savin",
"website": "https://github.com/apsavin"
},
{
"name": "Lalit Kapoor",
"website": "https://github.com/lalitkapoor"
},
{
"name": "Sam Thompson",
"website": "https://github.com/sambthompson"
}
],
"license": "BSD-3-Clause",
@@ -43,7 +67,7 @@
"RFC6265",
"RFC2965"
],
"version": "2.4.3",
"version": "4.1.3",
"homepage": "https://github.com/salesforce/tough-cookie",
"repository": {
"type": "git",
@@ -57,20 +81,30 @@
"lib"
],
"scripts": {
"test": "vows test/*_test.js",
"cover": "nyc --reporter=lcov --reporter=html vows test/*_test.js"
"version": "genversion lib/version.js && git add lib/version.js",
"test": "vows test/*_test.js && npm run eslint",
"cover": "nyc --reporter=lcov --reporter=html vows test/*_test.js",
"eslint": "eslint --env node --ext .js .",
"prettier": "prettier '**/*.{json,ts,yaml,md}'",
"format": "npm run eslint -- --fix"
},
"engines": {
"node": ">=0.8"
"node": ">=6"
},
"devDependencies": {
"async": "^1.4.2",
"nyc": "^11.6.0",
"string.prototype.repeat": "^0.2.0",
"vows": "^0.8.1"
"async": "^2.6.2",
"eslint": "^5.16.0",
"eslint-config-prettier": "^4.2.0",
"eslint-plugin-prettier": "^3.0.1",
"genversion": "^2.1.0",
"nyc": "^14.0.0",
"prettier": "^1.17.0",
"vows": "^0.8.2"
},
"dependencies": {
"psl": "^1.1.24",
"punycode": "^1.4.1"
"psl": "^1.1.33",
"punycode": "^2.1.1",
"universalify": "^0.2.0",
"url-parse": "^1.5.3"
}
}
5 changes: 5 additions & 0 deletions test/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"no-restricted-modules": "off"
}
}
699 changes: 511 additions & 188 deletions test/api_test.js

Large diffs are not rendered by default.

782 changes: 568 additions & 214 deletions test/cookie_jar_test.js

Large diffs are not rendered by default.

249 changes: 249 additions & 0 deletions test/cookie_prefixes_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
/*!
* 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 vows = require("vows");
const assert = require("assert");
const tough = require("../lib/cookie");
const CookieJar = tough.CookieJar;
const PrefixSecurityEnum = tough.PrefixSecurityEnum;

vows
.describe("Cookie Prefixes")
.addBatch({
"Prefix Security Mode": {
"with prefixSecurity = silent": {
"for __Secure prefix": {
topic: function() {
return new CookieJar(null, { prefixSecurity: "silent" });
},
"with no Secure attribute, should fail silently": function(cj) {
assert.equal(PrefixSecurityEnum.SILENT, cj.prefixSecurity);
cj.setCookieSync(
"__Secure-SID=12345; Domain=example.com",
"http://www.example.com",
{}
);
const cookies = cj.getCookiesSync("http://www.example.com");
assert.isEmpty(cookies); // no cookies set
},
"with Secure attribute and over https, should work": function(cj) {
assert.equal(PrefixSecurityEnum.SILENT, cj.prefixSecurity);
cj.setCookieSync(
"__Secure-SID=12345; Domain=example.com; Secure",
"https://www.example.com",
{}
);
const cookies = cj.getCookiesSync("https://www.example.com");
assert.strictEqual(cookies.length, 1);
assert.strictEqual(cookies[0].key, "__Secure-SID");
assert.strictEqual(cookies[0].value, "12345");
},
"with Secure attribute but not over https, should fail silently": function(
cj
) {
assert.equal(PrefixSecurityEnum.SILENT, cj.prefixSecurity);
cj.setCookieSync(
"__Secure-SID=12345; Domain=example.com; Secure",
"http://www.example.com",
{}
);
const cookies = cj.getCookiesSync("http://www.example.com");
assert.isEmpty(cookies); // no cookies set
}
},
"for __Host prefix": {
topic: function() {
return new CookieJar(null, { prefixSecurity: "silent" });
},
"with no Secure attribute or Domain or Path, should fail silently": function(
cj
) {
assert.equal(PrefixSecurityEnum.SILENT, cj.prefixSecurity);
cj.setCookieSync("__Host-SID=12345", "http://www.example.com", {});
const cookies = cj.getCookiesSync("http://www.example.com");
assert.isEmpty(cookies); // no cookies set
},
"with no Domain or Path, should fail silently": function(cj) {
assert.equal(PrefixSecurityEnum.SILENT, cj.prefixSecurity);
cj.setCookieSync(
"__Host-SID=12345; Secure",
"http://www.example.com",
{}
);
const cookies = cj.getCookiesSync("http://www.example.com");
assert.isEmpty(cookies); // no cookies set
},
"with no Path, should fail silently": function(cj) {
assert.equal(PrefixSecurityEnum.SILENT, cj.prefixSecurity);
cj.setCookieSync(
"__Host-SID=12345; Secure; Domain=example.com",
"http://www.example.com",
{}
);
const cookies = cj.getCookiesSync("http://www.example.com");
assert.isEmpty(cookies); // no cookies set
},
"with Domain, should fail silently": function(cj) {
assert.equal(PrefixSecurityEnum.SILENT, cj.prefixSecurity);
cj.setCookieSync(
"__Host-SID=12345; Secure; Domain=example.com; Path=/",
"http://www.example.com",
{}
);
const cookies = cj.getCookiesSync("http://www.example.com");
assert.isEmpty(cookies); // no cookies set
},
"with Secure and Path but no Domain over https, should work": function(
cj
) {
assert.equal(PrefixSecurityEnum.SILENT, cj.prefixSecurity);
cj.setCookieSync(
"__Host-SID=12345; Secure; Path=/",
"https://www.example.com",
{}
);
const cookies = cj.getCookiesSync("https://www.example.com");
assert.strictEqual(cookies.length, 1);
assert.strictEqual(cookies[0].key, "__Host-SID");
assert.strictEqual(cookies[0].value, "12345");
}
}
},
"with prefixSecurity = strict": {
"for __Secure prefix": {
"for valid cookie": {
topic: function() {
return new CookieJar(null, { prefixSecurity: "strict" });
},
passes: function(cj) {
assert.equal(PrefixSecurityEnum.STRICT, cj.prefixSecurity);
cj.setCookieSync(
"__Secure-SID=12345; Secure; Domain=example.com",
"https://www.example.com",
{}
);
const cookies = cj.getCookiesSync("https://www.example.com");
assert.strictEqual(cookies.length, 1);
assert.strictEqual(cookies[0].key, "__Secure-SID");
assert.strictEqual(cookies[0].value, "12345");
}
},
"for invalid cookie": {
topic: function() {
const cj = new CookieJar(null, { prefixSecurity: "strict" });
assert.equal(PrefixSecurityEnum.STRICT, cj.prefixSecurity);
cj.setCookieSync(
"__Secure-SID=12345; Domain=example.com",
"http://www.example.com",
{}
);
},
"fails shrilly": function(err, cookie) {
assert.isNotNull(err);
assert.isUndefined(cookie);
}
}
},
"for __Host prefix": {
"for invalid cookie": {
topic: function() {
const cj = new CookieJar(null, { prefixSecurity: "strict" });
assert.equal(PrefixSecurityEnum.STRICT, cj.prefixSecurity);
cj.setCookieSync(
"__Host-SID=12345; Secure; Domain=example.com",
"https://www.example.com",
{}
);
},
"fails shrilly": function(err, cookie) {
assert.isNotNull(err);
assert.isUndefined(cookie);
}
},
"for valid cookie": {
topic: function() {
return new CookieJar(null, { prefixSecurity: "strict" });
},
passes: function(cj) {
assert.equal(PrefixSecurityEnum.STRICT, cj.prefixSecurity);
cj.setCookieSync(
"__Host-SID=12345; Secure; Path=/",
"https://www.foo.com",
{}
);
const cookies = cj.getCookiesSync("https://www.foo.com");
assert.strictEqual(cookies.length, 1);
assert.strictEqual(cookies[0].key, "__Host-SID");
assert.strictEqual(cookies[0].value, "12345");
}
}
}
},
"with prefixSecurity = disabled": {
"for __Secure prefix": {
topic: function() {
return new CookieJar(null, { prefixSecurity: "unsafe-disabled" });
},
"does not fail": function(cj) {
assert.equal(PrefixSecurityEnum.DISABLED, cj.prefixSecurity);
cj.setCookieSync(
"__Secure-SID=12345; Domain=example.com",
"http://www.example.com",
{}
);
const cookies = cj.getCookiesSync("https://www.example.com");
assert.strictEqual(cookies.length, 1);
assert.strictEqual(cookies[0].key, "__Secure-SID");
assert.strictEqual(cookies[0].value, "12345");
}
},
"for __Host prefix": {
topic: function() {
return new CookieJar(null, { prefixSecurity: "unsafe-disabled" });
},
"does not fail": function(cj) {
assert.equal(PrefixSecurityEnum.DISABLED, cj.prefixSecurity);
/* Failure case because Domain defined */
cj.setCookieSync(
"__Host-SID=12345; Domain=example.com",
"http://www.example.com",
{}
);
const cookies = cj.getCookiesSync("https://www.example.com");
assert.strictEqual(cookies.length, 1);
assert.strictEqual(cookies[0].key, "__Host-SID");
assert.strictEqual(cookies[0].value, "12345");
}
}
}
}
})
.export(module);
106 changes: 57 additions & 49 deletions test/cookie_sorting_test.js
Original file line number Diff line number Diff line change
@@ -28,128 +28,136 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
'use strict';
var vows = require('vows');
var assert = require('assert');
var tough = require('../lib/cookie');
var Cookie = tough.Cookie;
var CookieJar = tough.CookieJar;
"use strict";
const vows = require("vows");
const assert = require("assert");
const tough = require("../lib/cookie");
const Cookie = tough.Cookie;
const CookieJar = tough.CookieJar;

function toKeyArray(cookies) {
return cookies.map(function (c) {
return c.key
return cookies.map(c => {
return c.key;
});
}

vows
.describe('Cookie sorting')
.describe("Cookie sorting")
.addBatch({
"Assumptions:": {
".creationIndex is set during construction": function() {
var now = new Date();
var c1 = new Cookie();
var c2 = new Cookie();
const now = new Date();
const c1 = new Cookie();
const c2 = new Cookie();
assert.isNumber(c1.creationIndex);
assert.isNumber(c2.creationIndex);
assert(c1.creationIndex < c2.creationIndex,
'creationIndex should increase with each construction');
assert(
c1.creationIndex < c2.creationIndex,
"creationIndex should increase with each construction"
);
},

".creationIndex is set during construction (forced ctime)": function() {
var now = new Date();
var c1 = new Cookie({creation: now});
var c2 = new Cookie({creation: now});
const now = new Date();
const c1 = new Cookie({ creation: now });
const c2 = new Cookie({ creation: now });
assert.strictEqual(c1.creation, c2.creation);
assert.isNumber(c1.creationIndex);
assert.isNumber(c2.creationIndex);
assert(c1.creationIndex < c2.creationIndex,
'creationIndex should increase with each construction');
assert(
c1.creationIndex < c2.creationIndex,
"creationIndex should increase with each construction"
);
},

".creationIndex is left alone during new setCookie": function() {
var jar = new CookieJar();
var c = new Cookie({key:'k', value:'v', domain:'example.com'});
var now = new Date();
var beforeDate = c.creation;
const jar = new CookieJar();
const c = new Cookie({ key: "k", value: "v", domain: "example.com" });
const now = new Date();
const beforeDate = c.creation;
assert.instanceOf(beforeDate, Date);
assert.notStrictEqual(now, beforeDate);
var beforeIndex = c.creationIndex;
const beforeIndex = c.creationIndex;
assert.isNumber(c.creationIndex);

jar.setCookieSync(c, 'http://example.com/', {now: now});
jar.setCookieSync(c, "http://example.com/", { now: now });

assert.strictEqual(c.creation, now);
assert.strictEqual(c.creationIndex, beforeIndex);
},

".creationIndex is preserved during update setCookie": function() {
var jar = new CookieJar();
const jar = new CookieJar();

var thisMs = Date.now();
var t1 = new Date(thisMs);
var t2 = new Date(thisMs);
const thisMs = Date.now();
const t1 = new Date(thisMs);
const t2 = new Date(thisMs);
assert.notStrictEqual(t1, t2); // Date objects are distinct

var c = new Cookie({key:'k', value:'v1', domain:'example.com'});
jar.setCookieSync(c, 'http://example.com/', {now: t1});
var originalIndex = c.creationIndex;
let c = new Cookie({ key: "k", value: "v1", domain: "example.com" });
jar.setCookieSync(c, "http://example.com/", { now: t1 });
const originalIndex = c.creationIndex;

assert.strictEqual(c.creation, t1);
assert.strictEqual(c.lastAccessed, t1);

c = new Cookie({key:'k', value:'v2', domain:'example.com'});
c = new Cookie({ key: "k", value: "v2", domain: "example.com" });
assert.notStrictEqual(c.creation, t1); // new timestamp assigned

jar.setCookieSync(c, 'http://example.com/', {now: t2});
jar.setCookieSync(c, "http://example.com/", { now: t2 });

assert.strictEqual(c.creation, t1); // retained
assert.strictEqual(c.lastAccessed, t2); // updated
assert.strictEqual(c.creationIndex, originalIndex); // retained
},
}
}
})
.addBatch({
"Cookie Sorting": {
topic: function () {
var cookies = [];
topic: function() {
let cookies = [];
cookies.push(Cookie.parse("a=0; Domain=example.com"));
cookies.push(Cookie.parse("b=1; Domain=www.example.com"));
cookies.push(Cookie.parse("c=2; Domain=example.com; Path=/pathA"));
cookies.push(Cookie.parse("d=3; Domain=www.example.com; Path=/pathA"));
cookies.push(Cookie.parse("e=4; Domain=example.com; Path=/pathA/pathB"));
cookies.push(Cookie.parse("f=5; Domain=www.example.com; Path=/pathA/pathB"));
cookies.push(
Cookie.parse("e=4; Domain=example.com; Path=/pathA/pathB")
);
cookies.push(
Cookie.parse("f=5; Domain=www.example.com; Path=/pathA/pathB")
);

// weak shuffle:
cookies = cookies.sort(function () {
return Math.random() - 0.5
cookies = cookies.sort(() => {
return Math.random() - 0.5;
});

cookies = cookies.sort(tough.cookieCompare);
return cookies;
},
"got": function (cookies) {
got: function(cookies) {
assert.lengthOf(cookies, 6);
assert.deepEqual(toKeyArray(cookies), ['e', 'f', 'c', 'd', 'a', 'b']);
assert.deepEqual(toKeyArray(cookies), ["e", "f", "c", "d", "a", "b"]);
}
}
})
.addBatch({
"Changing creation date affects sorting": {
topic: function () {
var cookies = [];
var now = Date.now();
topic: function() {
const cookies = [];
const now = Date.now();
cookies.push(Cookie.parse("a=0;"));
cookies.push(Cookie.parse("b=1;"));
cookies.push(Cookie.parse("c=2;"));

cookies.forEach(function (cookie, idx) {
cookies.forEach((cookie, idx) => {
cookie.creation = new Date(now - 100 * idx);
});

return cookies.sort(tough.cookieCompare);
},
"got": function (cookies) {
assert.deepEqual(toKeyArray(cookies), ['c', 'b', 'a']);
got: function(cookies) {
assert.deepEqual(toKeyArray(cookies), ["c", "b", "a"]);
}
}
})
131 changes: 78 additions & 53 deletions test/cookie_to_json_test.js
Original file line number Diff line number Diff line change
@@ -29,64 +29,65 @@
* POSSIBILITY OF SUCH DAMAGE.
*/

'use strict';
var vows = require('vows');
var assert = require('assert');
var tough = require('../lib/cookie');
var Cookie = tough.Cookie;
"use strict";
const vows = require("vows");
const assert = require("assert");
const tough = require("../lib/cookie");
const Cookie = tough.Cookie;

vows
.describe('Cookie.toJSON()')
.describe("Cookie.toJSON()")
.addBatch({
"JSON": {
"serialization": {
JSON: {
serialization: {
topic: function() {
var c = Cookie.parse('alpha=beta; Domain=example.com; Path=/foo; Expires=Tue, 19 Jan 2038 03:14:07 GMT; HttpOnly');
const c = Cookie.parse(
"alpha=beta; Domain=example.com; Path=/foo; Expires=Tue, 19 Jan 2038 03:14:07 GMT; HttpOnly"
);
return JSON.stringify(c);
},
"gives a string": function(str) {
assert.equal(typeof str, "string");
},
"date is in ISO format": function(str) {
assert.match(str, /"expires":"2038-01-19T03:14:07\.000Z"/, 'expires is in ISO format');
assert.match(
str,
/"expires":"2038-01-19T03:14:07\.000Z"/,
"expires is in ISO format"
);
}
},
"deserialization": {
topic: function() {
var json = '{"key":"alpha","value":"beta","domain":"example.com","path":"/foo","expires":"2038-01-19T03:14:07.000Z","httpOnly":true,"lastAccessed":2000000000123}';
return Cookie.fromJSON(json);
},
"works": function(c) {
assert.ok(c);
},
"key": function(c) { assert.equal(c.key, "alpha") },
"value": function(c) { assert.equal(c.value, "beta") },
"domain": function(c) { assert.equal(c.domain, "example.com") },
"path": function(c) { assert.equal(c.path, "/foo") },
"httpOnly": function(c) { assert.strictEqual(c.httpOnly, true) },
"secure": function(c) { assert.strictEqual(c.secure, false) },
"hostOnly": function(c) { assert.strictEqual(c.hostOnly, null) },
"expires is a date object": function(c) {
assert.equal(c.expires.getTime(), 2147483647000);
deserialization: {
topic() {
return Cookie.fromJSON(
'{"key":"alpha","value":"beta","domain":"example.com","path":"/foo","expires":"2038-01-19T03:14:07.000Z","httpOnly":true,"lastAccessed":2000000000123}'
);
},
"lastAccessed is a date object": function(c) {
assert.equal(c.lastAccessed.getTime(), 2000000000123);
},
"creation defaulted": function(c) {
assert.ok(c.creation.getTime());
}
works: c => assert.ok(c),
key: c => assert.equal(c.key, "alpha"),
value: c => assert.equal(c.value, "beta"),
domain: c => assert.equal(c.domain, "example.com"),
path: c => assert.equal(c.path, "/foo"),
httpOnly: c => assert.strictEqual(c.httpOnly, true),
secure: c => assert.strictEqual(c.secure, false),
hostOnly: c => assert.strictEqual(c.hostOnly, null),
"expires is a date object": c =>
assert.equal(c.expires.getTime(), 2147483647000),
"lastAccessed is a date object": c =>
assert.equal(c.lastAccessed.getTime(), 2000000000123),
"creation defaulted": c => assert.ok(c.creation.getTime())
},
"null deserialization": {
topic: function() {
return Cookie.fromJSON(null);
},
"is null": function(cookie) {
assert.equal(cookie,null);
assert.equal(cookie, null);
}
}
},
"expiry deserialization": {
"Infinity": {
Infinity: {
topic: Cookie.fromJSON.bind(null, '{"expires":"Infinity"}'),
"is infinite": function(c) {
assert.strictEqual(c.expires, "Infinity");
@@ -97,64 +98,88 @@ vows
"maxAge serialization": {
topic: function() {
return function(toSet) {
var c = new Cookie();
c.key = 'foo'; c.value = 'bar';
const c = new Cookie();
c.key = "foo";
c.value = "bar";
c.setMaxAge(toSet);
return JSON.stringify(c);
};
},
"zero": {
topic: function(f) { return f(0) },
zero: {
topic: function(f) {
return f(0);
},
"looks good": function(str) {
assert.match(str, /"maxAge":0/);
}
},
"Infinity": {
topic: function(f) { return f(Infinity) },
Infinity: {
topic: function(f) {
return f(Infinity);
},
"looks good": function(str) {
assert.match(str, /"maxAge":"Infinity"/);
}
},
"-Infinity": {
topic: function(f) { return f(-Infinity) },
topic: function(f) {
return f(-Infinity);
},
"looks good": function(str) {
assert.match(str, /"maxAge":"-Infinity"/);
}
},
"null": {
topic: function(f) { return f(null) },
"absent": function(str) {
null: {
topic: function(f) {
return f(null);
},
absent: function(str) {
assert.match(str, /(?!"maxAge":null)/); // NB: negative RegExp
}
}
},
"maxAge deserialization": {
"number": {
topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":123}'),
number: {
topic: Cookie.fromJSON.bind(
null,
'{"key":"foo","value":"bar","maxAge":123}'
),
"is the number": function(c) {
assert.strictEqual(c.maxAge, 123);
}
},
"null": {
topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":null}'),
null: {
topic: Cookie.fromJSON.bind(
null,
'{"key":"foo","value":"bar","maxAge":null}'
),
"is null": function(c) {
assert.strictEqual(c.maxAge, null);
}
},
"less than zero": {
topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":-123}'),
topic: Cookie.fromJSON.bind(
null,
'{"key":"foo","value":"bar","maxAge":-123}'
),
"is -123": function(c) {
assert.strictEqual(c.maxAge, -123);
}
},
"Infinity": {
topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":"Infinity"}'),
Infinity: {
topic: Cookie.fromJSON.bind(
null,
'{"key":"foo","value":"bar","maxAge":"Infinity"}'
),
"is inf-as-string": function(c) {
assert.strictEqual(c.maxAge, "Infinity");
}
},
"-Infinity": {
topic: Cookie.fromJSON.bind(null,'{"key":"foo","value":"bar","maxAge":"-Infinity"}'),
topic: Cookie.fromJSON.bind(
null,
'{"key":"foo","value":"bar","maxAge":"-Infinity"}'
),
"is inf-as-string": function(c) {
assert.strictEqual(c.maxAge, "-Infinity");
}
143 changes: 76 additions & 67 deletions test/cookie_to_string_test.js
Original file line number Diff line number Diff line change
@@ -29,133 +29,142 @@
* POSSIBILITY OF SUCH DAMAGE.
*/

'use strict';
var vows = require('vows');
var assert = require('assert');
var tough = require('../lib/cookie');
var Cookie = tough.Cookie;
"use strict";
const vows = require("vows");
const assert = require("assert");
const tough = require("../lib/cookie");
const Cookie = tough.Cookie;

vows
.describe('Cookie.toString()')
.describe("Cookie.toString()")
.addBatch({
"a simple cookie": {
topic: function () {
var c = new Cookie();
c.key = 'a';
c.value = 'b';
topic: function() {
const c = new Cookie();
c.key = "a";
c.value = "b";
return c;
},
"validates": function (c) {
validates: function(c) {
assert.ok(c.validate());
},
"to string": function (c) {
assert.equal(c.toString(), 'a=b');
"to string": function(c) {
assert.equal(c.toString(), "a=b");
}
},
"a cookie with spaces in the value": {
topic: function () {
var c = new Cookie();
c.key = 'a';
c.value = 'beta gamma';
topic: function() {
const c = new Cookie();
c.key = "a";
c.value = "beta gamma";
return c;
},
"doesn't validate": function (c) {
"doesn't validate": function(c) {
assert.ok(!c.validate());
},
"'garbage in, garbage out'": function (c) {
assert.equal(c.toString(), 'a=beta gamma');
"'garbage in, garbage out'": function(c) {
assert.equal(c.toString(), "a=beta gamma");
}
},
"with an empty value and HttpOnly": {
topic: function () {
var c = new Cookie();
c.key = 'a';
topic: function() {
const c = new Cookie();
c.key = "a";
c.httpOnly = true;
return c;
},
"to string": function (c) {
assert.equal(c.toString(), 'a=; HttpOnly');
"to string": function(c) {
assert.equal(c.toString(), "a=; HttpOnly");
}
},
"with an expiry": {
topic: function () {
var c = new Cookie();
c.key = 'a';
c.value = 'b';
topic: function() {
const c = new Cookie();
c.key = "a";
c.value = "b";
c.setExpires("Oct 18 2011 07:05:03 GMT");
return c;
},
"validates": function (c) {
validates: function(c) {
assert.ok(c.validate());
},
"to string": function (c) {
assert.equal(c.toString(), 'a=b; Expires=Tue, 18 Oct 2011 07:05:03 GMT');
"to string": function(c) {
assert.equal(
c.toString(),
"a=b; Expires=Tue, 18 Oct 2011 07:05:03 GMT"
);
},
"to short string": function (c) {
assert.equal(c.cookieString(), 'a=b');
"to short string": function(c) {
assert.equal(c.cookieString(), "a=b");
}
},
"with a max-age": {
topic: function () {
var c = new Cookie();
c.key = 'a';
c.value = 'b';
topic: function() {
const c = new Cookie();
c.key = "a";
c.value = "b";
c.setExpires("Oct 18 2011 07:05:03 GMT");
c.maxAge = 12345;
return c;
},
"validates": function (c) {
validates: function(c) {
assert.ok(c.validate()); // mabe this one *shouldn't*?
},
"to string": function (c) {
assert.equal(c.toString(), 'a=b; Expires=Tue, 18 Oct 2011 07:05:03 GMT; Max-Age=12345');
"to string": function(c) {
assert.equal(
c.toString(),
"a=b; Expires=Tue, 18 Oct 2011 07:05:03 GMT; Max-Age=12345"
);
}
},
"with a bunch of things": function () {
var c = new Cookie();
c.key = 'a';
c.value = 'b';
"with a bunch of things": function() {
const c = new Cookie();
c.key = "a";
c.value = "b";
c.setExpires("Oct 18 2011 07:05:03 GMT");
c.maxAge = 12345;
c.domain = 'example.com';
c.path = '/foo';
c.domain = "example.com";
c.path = "/foo";
c.secure = true;
c.httpOnly = true;
c.extensions = ['MyExtension'];
assert.equal(c.toString(), 'a=b; Expires=Tue, 18 Oct 2011 07:05:03 GMT; Max-Age=12345; Domain=example.com; Path=/foo; Secure; HttpOnly; MyExtension');
c.extensions = ["MyExtension"];
assert.equal(
c.toString(),
"a=b; Expires=Tue, 18 Oct 2011 07:05:03 GMT; Max-Age=12345; Domain=example.com; Path=/foo; Secure; HttpOnly; MyExtension"
);
},
"a host-only cookie": {
topic: function () {
var c = new Cookie();
c.key = 'a';
c.value = 'b';
topic: function() {
const c = new Cookie();
c.key = "a";
c.value = "b";
c.hostOnly = true;
c.domain = 'shouldnt-stringify.example.com';
c.path = '/should-stringify';
c.domain = "shouldnt-stringify.example.com";
c.path = "/should-stringify";
return c;
},
"validates": function (c) {
validates: function(c) {
assert.ok(c.validate());
},
"to string": function (c) {
assert.equal(c.toString(), 'a=b; Path=/should-stringify');
"to string": function(c) {
assert.equal(c.toString(), "a=b; Path=/should-stringify");
}
},
"minutes are '10'": {
topic: function () {
var c = new Cookie();
c.key = 'a';
c.value = 'b';
topic: function() {
const c = new Cookie();
c.key = "a";
c.value = "b";
c.expires = new Date(1284113410000);
return c;
},
"validates": function (c) {
validates: function(c) {
assert.ok(c.validate());
},
"to string": function (c) {
var str = c.toString();
assert.notEqual(str, 'a=b; Expires=Fri, 010 Sep 2010 010:010:010 GMT');
assert.equal(str, 'a=b; Expires=Fri, 10 Sep 2010 10:10:10 GMT');
"to string": function(c) {
const str = c.toString();
assert.notEqual(str, "a=b; Expires=Fri, 010 Sep 2010 010:010:010 GMT");
assert.equal(str, "a=b; Expires=Fri, 10 Sep 2010 10:10:10 GMT");
}
}
})
228 changes: 116 additions & 112 deletions test/date_test.js
Original file line number Diff line number Diff line change
@@ -29,18 +29,17 @@
* POSSIBILITY OF SUCH DAMAGE.
*/

'use strict';
var vows = require('vows');
var assert = require('assert');
var tough = require('../lib/cookie');
require('string.prototype.repeat'); // polyfill
"use strict";
const vows = require("vows");
const assert = require("assert");
const tough = require("../lib/cookie");

function dateVows(table) {
var theVows = {};
Object.keys(table).forEach(function (date) {
var expect = table[date];
theVows[date] = function () {
var got = tough.parseDate(date) ? true : false;
const theVows = {};
Object.keys(table).forEach(date => {
const expect = table[date];
theVows[date] = function() {
const got = tough.parseDate(date) ? true : false;
if (expect && !got) {
assert.ok(false, "expected valid date but was invalid");
} else if (!expect && got) {
@@ -50,150 +49,155 @@ function dateVows(table) {
}
};
});
return {"date parsing": theVows};
return { "date parsing": theVows };
}

function equivalenceVows(table) {
var theVows = {};
Object.keys(table).forEach(function (thisDate) {
var sameAs = table[thisDate];
var label = "'"+thisDate+"' parses the same as '"+sameAs+"'";
theVows[label] = function () {
var expected = tough.parseDate(sameAs);
var actual = tough.parseDate(thisDate);
const theVows = {};
Object.keys(table).forEach(thisDate => {
const sameAs = table[thisDate];
const label = `'${thisDate}' parses the same as '${sameAs}'`;
theVows[label] = function() {
const expected = tough.parseDate(sameAs);
const actual = tough.parseDate(thisDate);
if (!expected && !actual) {
assert.ok(false, "both dates failed to parse!");
}
assert.equal(actual.toString(), expected.toString());
};
});
return {"equivalence parsing": theVows};
return { "equivalence parsing": theVows };
}

var TOO_MANY_XS = String("x").repeat(65535);
const TOO_MANY_XS = String("x").repeat(65535);

vows
.describe('Date')
.addBatch(dateVows({
"Wed, 09 Jun 2021 10:18:14 GMT": true,
"Wed, 09 JUN 2021 10:18:14 GMT": true,
"Wed, 09 Jun 2021 22:18:14 GMT": true,
"Tue, 18 Oct 2011 07:42:42.123 GMT": true,
"18 Oct 2011 07:42:42 GMT": true,
"8 Oct 2011 7:42:42 GMT": true,
"8 Oct 2011 7:2:42 GMT": true,
"8 Oct 2011 7:2:2 GMT": true,
"Oct 18 2011 07:42:42 GMT": true,
"Tue Oct 18 2011 07:05:03 GMT+0000 (GMT)": true,
"09 Jun 2021 10:18:14 GMT": true,
"99 Jix 3038 48:86:72 ZMT": false,
'01 Jan 1970 00:00:00 GMT': true,
'01 Jan 1600 00:00:00 GMT': false, // before 1601
'01 Jan 1601 00:00:00 GMT': true,
'10 Feb 81 13:00:00 GMT': true, // implicit year
'Thu, 17-Apr-2014 02:12:29 GMT': true, // dashes
'Thu, 17-Apr-2014 02:12:29 UTC': true, // dashes and UTC

// garbage after parts:
"Wedxxx, 09 Jun 2021 10:18:14 GMT": true, // day of week doesn't matter
"Wed, 09e9 Jun 2021 10:18:14 GMT": true, // garbage after day ignored
"Wed, 09 Junxxx 2021 10:18:14 GMT": true, // prefix match on month
"Wed, 09 Jun 2021e9 10:18:14 GMT": true, // garbage after year OK
"Wed, 09 Jun 2021 10e9:18:14 GMT": false, // can't have garbage after HH
"Wed, 09 Jun 2021 10:18e9:14 GMT": false, // can't have garbage after MM
"Wed, 09 Jun 2021 10:18:14e9 GMT": true, // garbage after SS ignored

// extra digit in time parts:
"Thu, 01 Jan 1970 000:00:01 GMT": false,
"Thu, 01 Jan 1970 00:000:01 GMT": false,
"Thu, 01 Jan 1970 00:00:010 GMT": false,

// hex in time
"Wed, 09 Jun 2021 1a:33:44 GMT": false,
"Wed, 09 Jun 2021 a1:33:44 GMT": false,
"Wed, 09 Jun 2021 11:f3:44 GMT": false,
"Wed, 09 Jun 2021 11:3f:44 GMT": false,
"Wed, 09 Jun 2021 11:33:e4 GMT": false,
"Wed, 09 Jun 2021 11:33:4e GMT": true, // garbage after seconds is OK

// negatives in time
"Wed, 09 Jun 2021 -1:33:44 GMT": true, // parses as 1:33; - is a delimiter
"Wed, 09 Jun 2021 11:-3:44 GMT": false,
"Wed, 09 Jun 2021 11:33:-4 GMT": false,

"": false
}))
.describe("Date")
.addBatch(
dateVows({
"Wed, 09 Jun 2021 10:18:14 GMT": true,
"Wed, 09 JUN 2021 10:18:14 GMT": true,
"Wed, 09 Jun 2021 22:18:14 GMT": true,
"Tue, 18 Oct 2011 07:42:42.123 GMT": true,
"18 Oct 2011 07:42:42 GMT": true,
"8 Oct 2011 7:42:42 GMT": true,
"8 Oct 2011 7:2:42 GMT": true,
"8 Oct 2011 7:2:2 GMT": true,
"Oct 18 2011 07:42:42 GMT": true,
"Tue Oct 18 2011 07:05:03 GMT+0000 (GMT)": true,
"09 Jun 2021 10:18:14 GMT": true,
"99 Jix 3038 48:86:72 ZMT": false,
"01 Jan 1970 00:00:00 GMT": true,
"01 Jan 1600 00:00:00 GMT": false, // before 1601
"01 Jan 1601 00:00:00 GMT": true,
"10 Feb 81 13:00:00 GMT": true, // implicit year
"Thu, 17-Apr-2014 02:12:29 GMT": true, // dashes
"Thu, 17-Apr-2014 02:12:29 UTC": true, // dashes and UTC

// garbage after parts:
"Wedxxx, 09 Jun 2021 10:18:14 GMT": true, // day of week doesn't matter
"Wed, 09e9 Jun 2021 10:18:14 GMT": true, // garbage after day ignored
"Wed, 09 Junxxx 2021 10:18:14 GMT": true, // prefix match on month
"Wed, 09 Jun 2021e9 10:18:14 GMT": true, // garbage after year OK
"Wed, 09 Jun 2021 10e9:18:14 GMT": false, // can't have garbage after HH
"Wed, 09 Jun 2021 10:18e9:14 GMT": false, // can't have garbage after MM
"Wed, 09 Jun 2021 10:18:14e9 GMT": true, // garbage after SS ignored

// extra digit in time parts:
"Thu, 01 Jan 1970 000:00:01 GMT": false,
"Thu, 01 Jan 1970 00:000:01 GMT": false,
"Thu, 01 Jan 1970 00:00:010 GMT": false,

// hex in time
"Wed, 09 Jun 2021 1a:33:44 GMT": false,
"Wed, 09 Jun 2021 a1:33:44 GMT": false,
"Wed, 09 Jun 2021 11:f3:44 GMT": false,
"Wed, 09 Jun 2021 11:3f:44 GMT": false,
"Wed, 09 Jun 2021 11:33:e4 GMT": false,
"Wed, 09 Jun 2021 11:33:4e GMT": true, // garbage after seconds is OK

// negatives in time
"Wed, 09 Jun 2021 -1:33:44 GMT": true, // parses as 1:33; - is a delimiter
"Wed, 09 Jun 2021 11:-3:44 GMT": false,
"Wed, 09 Jun 2021 11:33:-4 GMT": false,

"": false
})
)
.addBatch({
"reDos hr": {
topic: function () {
var str = "Wed, 09 Jun 2021 10" + TOO_MANY_XS + ":18:14 GMT";
topic: function() {
const str = `Wed, 09 Jun 2021 10${TOO_MANY_XS}:18:14 GMT`;
return tough.parseDate(str, true) ? true : false;
},
"invalid": function (date) {
invalid: function(date) {
assert.equal(date, false);
}
},
"reDos min": {
topic: function () {
var str = "Wed, 09 Jun 2021 10:18" + TOO_MANY_XS + ":14 GMT";
topic: function() {
const str = `Wed, 09 Jun 2021 10:18${TOO_MANY_XS}:14 GMT`;
return tough.parseDate(str, true) ? true : false;
},
"invalid": function (date) {
invalid: function(date) {
assert.equal(date, false);
}
},
"reDos sec": {
topic: function () {
var str = "Wed, 09 Jun 2021 10:18:14" + TOO_MANY_XS + " GMT";
topic: function() {
const str = `Wed, 09 Jun 2021 10:18:14${TOO_MANY_XS} GMT`;
return tough.parseDate(str, true) ? true : false;
},
"valid": function (date) {
valid: function(date) {
assert.equal(date, true);
}
}
})
.addBatch(equivalenceVows({
// milliseconds ignored
"Tue, 18 Oct 2011 07:42:42.123 GMT": "Tue, 18 Oct 2011 07:42:42 GMT",
.addBatch(
equivalenceVows({
// milliseconds ignored
"Tue, 18 Oct 2011 07:42:42.123 GMT": "Tue, 18 Oct 2011 07:42:42 GMT",

// shorter HH:MM:SS works how you'd expect:
"8 Oct 2011 7:32:42 GMT": "8 Oct 2011 07:32:42 GMT",
"8 Oct 2011 7:2:42 GMT": "8 Oct 2011 07:02:42 GMT",
"8 Oct 2011 7:2:2 GMT": "8 Oct 2011 07:02:02 GMT",
// shorter HH:MM:SS works how you'd expect:
"8 Oct 2011 7:32:42 GMT": "8 Oct 2011 07:32:42 GMT",
"8 Oct 2011 7:2:42 GMT": "8 Oct 2011 07:02:42 GMT",
"8 Oct 2011 7:2:2 GMT": "8 Oct 2011 07:02:02 GMT",

// MDY versus DMY:
"Oct 18 2011 07:42:42 GMT": "18 Oct 2011 07:42:42 GMT",
// MDY versus DMY:
"Oct 18 2011 07:42:42 GMT": "18 Oct 2011 07:42:42 GMT",

// some other messy auto format
"Tue Oct 18 2011 07:05:03 GMT+0000 (GMT)": "Tue, 18 Oct 2011 07:05:03 GMT",
// some other messy auto format
"Tue Oct 18 2011 07:05:03 GMT+0000 (GMT)":
"Tue, 18 Oct 2011 07:05:03 GMT",

// short year
'10 Feb 81 13:00:00 GMT': '10 Feb 1981 13:00:00 GMT',
'10 Feb 17 13:00:00 GMT': '10 Feb 2017 13:00:00 GMT',
// short year
"10 Feb 81 13:00:00 GMT": "10 Feb 1981 13:00:00 GMT",
"10 Feb 17 13:00:00 GMT": "10 Feb 2017 13:00:00 GMT",

// dashes
'Thu, 17-Apr-2014 02:12:29 GMT': 'Thu, 17 Apr 2014 02:12:29 GMT',
// dashes and "UTC" (timezone is always ignored)
'Thu, 17-Apr-2014 02:12:29 UTC': 'Thu, 17 Apr 2014 02:12:29 GMT',
// dashes
"Thu, 17-Apr-2014 02:12:29 GMT": "Thu, 17 Apr 2014 02:12:29 GMT",
// dashes and "UTC" (timezone is always ignored)
"Thu, 17-Apr-2014 02:12:29 UTC": "Thu, 17 Apr 2014 02:12:29 GMT",

// no weekday
"09 Jun 2021 10:18:14 GMT": "Wed, 09 Jun 2021 10:18:14 GMT",
// no weekday
"09 Jun 2021 10:18:14 GMT": "Wed, 09 Jun 2021 10:18:14 GMT",

// garbage after seconds is OK
"Wed, 09 Jun 2021 11:33:4e GMT": "Wed, 09 Jun 2021 11:33:04 GMT",
// garbage after seconds is OK
"Wed, 09 Jun 2021 11:33:4e GMT": "Wed, 09 Jun 2021 11:33:04 GMT",

// - is delimiter in this position
"Wed, 09 Jun 2021 -1:33:44 GMT": "Wed, 09 Jun 2021 01:33:44 GMT",
// - is delimiter in this position
"Wed, 09 Jun 2021 -1:33:44 GMT": "Wed, 09 Jun 2021 01:33:44 GMT",

// prefix match on month
"Wed, 09 Junxxx 2021 10:18:14 GMT": "Wed, 09 Jun 2021 10:18:14 GMT",
"09 November 2021 10:18:14 GMT": "09 Nov 2021 10:18:14 GMT",
// prefix match on month
"Wed, 09 Junxxx 2021 10:18:14 GMT": "Wed, 09 Jun 2021 10:18:14 GMT",
"09 November 2021 10:18:14 GMT": "09 Nov 2021 10:18:14 GMT",

// case of Month
"Wed, 09 JUN 2021 10:18:14 GMT": "Wed, 09 Jun 2021 10:18:14 GMT",
"Wed, 09 jUN 2021 10:18:14 GMT": "Wed, 09 Jun 2021 10:18:14 GMT",
// case of Month
"Wed, 09 JUN 2021 10:18:14 GMT": "Wed, 09 Jun 2021 10:18:14 GMT",
"Wed, 09 jUN 2021 10:18:14 GMT": "Wed, 09 Jun 2021 10:18:14 GMT",

// test the framework :wink:
"Wed, 09 Jun 2021 10:18:14 GMT": "Wed, 09 Jun 2021 10:18:14 GMT"
}))
// test the framework :wink:
"Wed, 09 Jun 2021 10:18:14 GMT": "Wed, 09 Jun 2021 10:18:14 GMT"
})
)
.export(module);
230 changes: 141 additions & 89 deletions test/domain_and_path_test.js
Original file line number Diff line number Diff line change
@@ -29,97 +29,132 @@
* POSSIBILITY OF SUCH DAMAGE.
*/

'use strict';
var vows = require('vows');
var assert = require('assert');
var tough = require('../lib/cookie');
var Cookie = tough.Cookie;
"use strict";
const vows = require("vows");
const assert = require("assert");
const tough = require("../lib/cookie");

function matchVows(func, table) {
var theVows = {};
table.forEach(function (item) {
var str = item[0];
var dom = item[1];
var expect = item[2];
var label = str + (expect ? " matches " : " doesn't match ") + dom;
theVows[label] = function () {
const theVows = {};
table.forEach(item => {
const str = item[0];
const dom = item[1];
const expect = item[2];
const label = str + (expect ? " matches " : " doesn't match ") + dom;
theVows[label] = function() {
assert.equal(func(str, dom), expect);
};
});
return theVows;
}

function defaultPathVows(table) {
var theVows = {};
table.forEach(function (item) {
var str = item[0];
var expect = item[1];
var label = str + " gives " + expect;
theVows[label] = function () {
assert.equal(tough.defaultPath(str), expect);
function transformVows(fn, table) {
const theVows = {};
table.forEach(item => {
const str = item[0];
const expect = item[1];
let label = `${str} gives ${expect}`;
if (item.length >= 3) {
label += ` (${item[2]})`;
}
theVows[label] = function() {
assert.equal(fn(str), expect);
};
});
return theVows;
}

vows
.describe('Domain and Path')
.describe("Domain and Path")
.addBatch({
"domain normalization": {
"simple": function () {
var c = new Cookie();
c.domain = "EXAMPLE.com";
assert.equal(c.canonicalizedDomain(), "example.com");
},
"extra dots": function () {
var c = new Cookie();
c.domain = ".EXAMPLE.com";
assert.equal(c.cdomain(), "example.com");
},
"weird trailing dot": function () {
var c = new Cookie();
c.domain = "EXAMPLE.ca.";
assert.equal(c.canonicalizedDomain(), "example.ca.");
},
"weird internal dots": function () {
var c = new Cookie();
c.domain = "EXAMPLE...ca.";
assert.equal(c.canonicalizedDomain(), "example...ca.");
},
"IDN": function () {
var c = new Cookie();
c.domain = "δοκιμή.δοκιμή"; // "test.test" in greek
assert.equal(c.canonicalizedDomain(), "xn--jxalpdlp.xn--jxalpdlp");
}
}
"domain normalization": transformVows(tough.canonicalDomain, [
["example.com", "example.com", "already canonical"],
["EXAMPLE.com", "example.com", "simple"],
[".EXAMPLE.com", "example.com", "leading dot stripped"],
["EXAMPLE.com.", "example.com.", "trailing dot"],
[".EXAMPLE.com.", "example.com.", "leading and trailing dot"],
[".EXAMPLE...com.", "example...com.", "internal dots"],
["δοκιμή.δοκιμή", "xn--jxalpdlp.xn--jxalpdlp", "IDN: test.test in greek"]
])
})
.addBatch({
"Domain Match": matchVows(tough.domainMatch, [
// str, dom, expect
["example.com", "example.com", true],
["eXaMpLe.cOm", "ExAmPlE.CoM", true],
["example.com", "example.com", true], // identical
["eXaMpLe.cOm", "ExAmPlE.CoM", true], // both canonicalized
["no.ca", "yes.ca", false],
["wwwexample.com", "example.com", false],
["www.example.com", "example.com", true],
["example.com", "www.example.com", false],
["www.subdom.example.com", "example.com", true],
["www.subdom.example.com", "subdom.example.com", true],
["example.com", "example.com.", false], // RFC6265 S4.1.2.3
["192.168.0.1", "168.0.1", false], // S5.1.3 "The string is a host name"

// nulls and undefineds
[null, "example.com", null],
["example.com", null, null],
[null, null, null],
[undefined, undefined, null],

// suffix matching:
["www.example.com", "example.com", true], // substr AND suffix
["www.example.com.org", "example.com", false], // substr but not suffix
["example.com", "www.example.com.org", false], // neither
["example.com", "www.example.com", false], // super-str
["aaa.com", "aaaa.com", false], // str can't be suffix of domain
["aaaa.com", "aaa.com", false], // dom is suffix, but has to match on "." boundary!
["www.aaaa.com", "aaa.com", false],
["www.aaa.com", "aaa.com", true],
["www.aexample.com", "example.com", false], // has to match on "." boundary
["computer.com", "com", true], // suffix string found at start of domain
["becoming.com", "com", true], // suffix string found in middle of domain
["sitcom.com", "com", true], // suffix string found just before the '.' boundary

// S5.1.3 "The string is a host name (i.e., not an IP address)"
["192.168.0.1", "168.0.1", false], // because str is an IP (v4)
["100.192.168.0.1", "168.0.1", true], // WEIRD: because str is not a valid IPv4
["100.192.168.0.1", "192.168.0.1", true], // WEIRD: because str is not a valid IPv4
["::ffff:192.168.0.1", "168.0.1", false], // because str is an IP (v6)
["::ffff:192.168.0.1", "192.168.0.1", false], // because str is an IP (v6)
["::FFFF:192.168.0.1", "192.168.0.1", false], // because str is an IP (v6)
["::192.168.0.1", "192.168.0.1", false], // because str is an IP (yes, v6!)
[":192.168.0.1", "168.0.1", true], // WEIRD: because str is not valid IPv6
[":ffff:100.192.168.0.1", "192.168.0.1", true], // WEIRD: because str is not valid IPv6
[":ffff:192.168.0.1", "192.168.0.1", false],
[":ffff:192.168.0.1", "168.0.1", true], // WEIRD: because str is not valid IPv6
["::Fxxx:192.168.0.1", "168.0.1", true], // WEIRD: because str isnt IPv6
["192.168.0.1", "68.0.1", false],
["192.168.0.1", "2.68.0.1", false],
["192.168.0.1", "92.68.0.1", false],
["10.1.2.3", "210.1.2.3", false],
["2008::1", "::1", false],
["::1", "2008::1", false],
["::1", "::1", true], // "are identical" rule, despite IPv6
["::3xam:1e", "2008::3xam:1e", false], // malformed IPv6
["::3Xam:1e", "::3xaM:1e", true], // identical, even though malformed
["3xam::1e", "3xam::1e", true], // identical
["::3xam::1e", "3xam::1e", false],
["3xam::1e", "::3xam:1e", false],
["::f00f:10.0.0.1", "10.0.0.1", false],
["10.0.0.1", "::f00f:10.0.0.1", false],

// "IP like" hostnames:
["1.example.com", "example.com", true],
["11.example.com", "example.com", true],
["192.168.0.1.example.com", "example.com", true],

// exact length "TLD" tests:
["com", "net", false], // same len, non-match
["com", "com", true], // "are identical" rule
["NOTATLD", "notaTLD", true] // "are identical" rule (after canonicalization)
])
})

.addBatch({
"default-path": defaultPathVows([
"default-path": transformVows(tough.defaultPath, [
[null, "/"],
["/", "/"],
["/file", "/"],
["/dir/file", "/dir"],
["noslash", "/"],
["noslash", "/"]
])
})
.addBatch({
@@ -131,71 +166,88 @@ vows
["/dir/", "/dir/", true],
["/dir/file", "/dir/", true],
["/dir/file", "/dir", true],
["/directory", "/dir", false],
["/directory", "/dir", false]
])
})
.addBatch({
"permuteDomain": {
permuteDomain: {
"base case": {
topic: tough.permuteDomain.bind(null, 'example.com'),
"got the domain": function (list) {
assert.deepEqual(list, ['example.com']);
topic: tough.permuteDomain.bind(null, "example.com"),
"got the domain": function(list) {
assert.deepEqual(list, ["example.com"]);
}
},
"two levels": {
topic: tough.permuteDomain.bind(null, 'foo.bar.example.com'),
"got three things": function (list) {
assert.deepEqual(list, ['example.com', 'bar.example.com', 'foo.bar.example.com']);
topic: tough.permuteDomain.bind(null, "foo.bar.example.com"),
"got three things": function(list) {
assert.deepEqual(list, [
"example.com",
"bar.example.com",
"foo.bar.example.com"
]);
}
},
"local domain": {
topic: tough.permuteDomain.bind(null, 'foo.bar.example.localduhmain'),
"got three things": function (list) {
assert.deepEqual(list, ['example.localduhmain', 'bar.example.localduhmain', 'foo.bar.example.localduhmain']);
topic: tough.permuteDomain.bind(null, "foo.bar.example.localduhmain"),
"got three things": function(list) {
assert.deepEqual(list, [
"example.localduhmain",
"bar.example.localduhmain",
"foo.bar.example.localduhmain"
]);
}
},
"trailing dot": {
topic: tough.permuteDomain.bind(null, "foo.bar.example.com."),
"got three things": function(list) {
assert.deepEqual(list, [
"example.com",
"bar.example.com",
"foo.bar.example.com"
]);
}
}
},
"permutePath": {
permutePath: {
"base case": {
topic: tough.permutePath.bind(null, '/'),
"just slash": function (list) {
assert.deepEqual(list, ['/']);
topic: tough.permutePath.bind(null, "/"),
"just slash": function(list) {
assert.deepEqual(list, ["/"]);
}
},
"single case": {
topic: tough.permutePath.bind(null, '/foo'),
"two things": function (list) {
assert.deepEqual(list, ['/foo', '/']);
topic: tough.permutePath.bind(null, "/foo"),
"two things": function(list) {
assert.deepEqual(list, ["/foo", "/"]);
},
"path matching": function (list) {
list.forEach(function (e) {
assert.ok(tough.pathMatch('/foo', e));
"path matching": function(list) {
list.forEach(e => {
assert.ok(tough.pathMatch("/foo", e));
});
}
},
"double case": {
topic: tough.permutePath.bind(null, '/foo/bar'),
"four things": function (list) {
assert.deepEqual(list, ['/foo/bar', '/foo', '/']);
topic: tough.permutePath.bind(null, "/foo/bar"),
"four things": function(list) {
assert.deepEqual(list, ["/foo/bar", "/foo", "/"]);
},
"path matching": function (list) {
list.forEach(function (e) {
assert.ok(tough.pathMatch('/foo/bar', e));
"path matching": function(list) {
list.forEach(e => {
assert.ok(tough.pathMatch("/foo/bar", e));
});
}
},
"trailing slash": {
topic: tough.permutePath.bind(null, '/foo/bar/'),
"three things": function (list) {
assert.deepEqual(list, ['/foo/bar', '/foo', '/']);
topic: tough.permutePath.bind(null, "/foo/bar/"),
"three things": function(list) {
assert.deepEqual(list, ["/foo/bar/", "/foo/bar", "/foo", "/"]);
},
"path matching": function (list) {
list.forEach(function (e) {
assert.ok(tough.pathMatch('/foo/bar/', e));
"path matching": function(list) {
list.forEach(e => {
assert.ok(tough.pathMatch("/foo/bar/", e));
});
}
}
}
})
.export(module);

8 changes: 4 additions & 4 deletions test/ietf_data/parser.json
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@
{
"test": "0002",
"received": [
"foo=bar; Expires=Fri, 07 Aug 2019 08:04:19 GMT"
"foo=bar; Expires=Fri, 07 Aug 9999 08:04:19 GMT"
],
"sent": [
{ "name": "foo", "value": "bar" }
@@ -21,7 +21,7 @@
"test": "0003",
"received": [
"foo=bar; Expires=Fri, 07 Aug 2007 08:04:19 GMT",
"foo2=bar2; Expires=Fri, 07 Aug 2027 08:04:19 GMT"
"foo2=bar2; Expires=Fri, 07 Aug 9999 08:04:19 GMT"
],
"sent": [
{ "name": "foo2", "value": "bar2" }
@@ -707,7 +707,7 @@
{
"test": "COMMA0006",
"received": [
"foo=bar; Expires=Fri, 07 Aug 2019 08:04:19 GMT"
"foo=bar; Expires=Fri, 07 Aug 9999 08:04:19 GMT"
],
"sent": [
{ "name": "foo", "value": "bar" }
@@ -716,7 +716,7 @@
{
"test": "COMMA0007",
"received": [
"foo=bar; Expires=Fri 07 Aug 2019 08:04:19 GMT, baz=qux"
"foo=bar; Expires=Fri 07 Aug 9999 08:04:19 GMT, baz=qux"
],
"sent": [
{ "name": "foo", "value": "bar" }
79 changes: 39 additions & 40 deletions test/ietf_test.js
Original file line number Diff line number Diff line change
@@ -29,77 +29,76 @@
* POSSIBILITY OF SUCH DAMAGE.
*/

'use strict';
var vows = require('vows');
var assert = require('assert');
var fs = require('fs');
var path = require('path');
var url = require('url');
var tough = require('../lib/cookie');
var Cookie = tough.Cookie;
var CookieJar = tough.CookieJar;
"use strict";
const vows = require("vows");
const assert = require("assert");
const fs = require("fs");
const path = require("path");
const url = require("url");
const tough = require("../lib/cookie");
const CookieJar = tough.CookieJar;

function readJson(filePath) {
filePath = path.join(__dirname, filePath);
return JSON.parse(fs.readFileSync(filePath).toString());
}

function setGetCookieVows() {
var theVows = {};
var data = readJson('./ietf_data/parser.json');
const theVows = {};
const data = readJson("./ietf_data/parser.json");

data.forEach(function (testCase) {
theVows[testCase.test] = function () {
var jar = new CookieJar();
var expected = testCase['sent']
var sentFrom = 'http://home.example.org/cookie-parser?' + testCase.test;
var sentTo = testCase['sent-to'] ?
url.resolve('http://home.example.org', testCase['sent-to']) :
'http://home.example.org/cookie-parser-result?' + testCase.test;
data.forEach(testCase => {
theVows[testCase.test] = function() {
const jar = new CookieJar();
const expected = testCase["sent"];
const sentFrom = `http://home.example.org/cookie-parser?${testCase.test}`;
const sentTo = testCase["sent-to"]
? url.resolve("http://home.example.org", testCase["sent-to"])
: `http://home.example.org/cookie-parser-result?${testCase.test}`;

testCase['received'].forEach(function (cookieStr) {
jar.setCookieSync(cookieStr, sentFrom, {ignoreError: true});
testCase["received"].forEach(cookieStr => {
jar.setCookieSync(cookieStr, sentFrom, { ignoreError: true });
});

var actual = jar.getCookiesSync(sentTo,{sort:true});
const actual = jar.getCookiesSync(sentTo, { sort: true });

assert.strictEqual(actual.length, expected.length);

actual.forEach(function (actualCookie, idx) {
var expectedCookie = expected[idx];
actual.forEach((actualCookie, idx) => {
const expectedCookie = expected[idx];
assert.strictEqual(actualCookie.key, expectedCookie.name);
assert.strictEqual(actualCookie.value, expectedCookie.value);
});
};
});

return {'Set/get cookie tests': theVows};
return { "Set/get cookie tests": theVows };
}

function dateVows() {
var theVows = {};
const theVows = {};

[
'./ietf_data/dates/bsd-examples.json',
'./ietf_data/dates/examples.json'
].forEach(function (filePath) {
var data = readJson(filePath);
var fileName = path.basename(filePath);
"./ietf_data/dates/bsd-examples.json",
"./ietf_data/dates/examples.json"
].forEach(filePath => {
const data = readJson(filePath);
const fileName = path.basename(filePath);

data.forEach(function (testCase) {
theVows[fileName + ' : ' + testCase.test] = function () {
var actual = tough.parseDate(testCase.test);
actual = actual ? actual.toUTCString() : null;
assert.strictEqual(actual, testCase.expected);
};
});
data.forEach(testCase => {
theVows[`${fileName} : ${testCase.test}`] = function() {
let actual = tough.parseDate(testCase.test);
actual = actual ? actual.toUTCString() : null;
assert.strictEqual(actual, testCase.expected);
};
});
});

return {'Date': theVows};
return { Date: theVows };
}

vows
.describe('IETF http state tests')
.describe("IETF http state tests")
.addBatch(setGetCookieVows())
.addBatch(dateVows())
.export(module);
314 changes: 197 additions & 117 deletions test/jar_serialization_test.js

Large diffs are not rendered by default.

58 changes: 29 additions & 29 deletions test/lifetime_test.js
Original file line number Diff line number Diff line change
@@ -29,68 +29,68 @@
* POSSIBILITY OF SUCH DAMAGE.
*/

'use strict';
var vows = require('vows');
var assert = require('assert');
var tough = require('../lib/cookie');
var Cookie = tough.Cookie;
"use strict";
const vows = require("vows");
const assert = require("assert");
const tough = require("../lib/cookie");
const Cookie = tough.Cookie;

vows
.describe('Lifetime')
.describe("Lifetime")
.addBatch({
"TTL with max-age": function () {
var c = new Cookie();
"TTL with max-age": function() {
const c = new Cookie();
c.maxAge = 123;
assert.equal(c.TTL(), 123000);
assert.equal(c.expiryTime(new Date(9000000)), 9123000);
},
"TTL with zero max-age": function () {
var c = new Cookie();
c.key = 'a';
c.value = 'b';
"TTL with zero max-age": function() {
const c = new Cookie();
c.key = "a";
c.value = "b";
c.maxAge = 0; // should be treated as "earliest representable"
assert.equal(c.TTL(), 0);
assert.equal(c.expiryTime(new Date(9000000)), -Infinity);
assert.ok(!c.validate()); // not valid, really: non-zero-digit *DIGIT
},
"TTL with negative max-age": function () {
var c = new Cookie();
c.key = 'a';
c.value = 'b';
"TTL with negative max-age": function() {
const c = new Cookie();
c.key = "a";
c.value = "b";
c.maxAge = -1; // should be treated as "earliest representable"
assert.equal(c.TTL(), 0);
assert.equal(c.expiryTime(new Date(9000000)), -Infinity);
assert.ok(!c.validate()); // not valid, really: non-zero-digit *DIGIT
},
"TTL with max-age and expires": function () {
var c = new Cookie();
"TTL with max-age and expires": function() {
const c = new Cookie();
c.maxAge = 123;
c.expires = new Date(Date.now() + 9000);
assert.equal(c.TTL(), 123000);
assert.ok(c.isPersistent());
},
"TTL with expires": function () {
var c = new Cookie();
var now = Date.now();
"TTL with expires": function() {
const c = new Cookie();
const now = Date.now();
c.expires = new Date(now + 9000);
assert.equal(c.TTL(now), 9000);
assert.equal(c.expiryTime(), c.expires.getTime());
},
"TTL with old expires": function () {
var c = new Cookie();
c.setExpires('17 Oct 2010 00:00:00 GMT');
"TTL with old expires": function() {
const c = new Cookie();
c.setExpires("17 Oct 2010 00:00:00 GMT");
assert.ok(c.TTL() < 0);
assert.ok(c.isPersistent());
},
"default TTL": {
topic: function () {
topic: function() {
return new Cookie();
},
"is Infinite-future": function (c) {
assert.equal(c.TTL(), Infinity)
"is Infinite-future": function(c) {
assert.equal(c.TTL(), Infinity);
},
"is a 'session' cookie": function (c) {
assert.ok(!c.isPersistent())
"is a 'session' cookie": function(c) {
assert.ok(!c.isPersistent());
}
}
})
175 changes: 175 additions & 0 deletions test/node_util_fallback_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*!
* Copyright (c) 2022, 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 vows = require("vows");
const assert = require("assert");
const tough = require("../lib/cookie");
const util = require("util");
const inspectFallback = require("../lib/memstore").inspectFallback;
const { getCustomInspectSymbol, getUtilInspect } = require("../lib/utilHelper");
const Cookie = tough.Cookie;
const CookieJar = tough.CookieJar;

function resetAgeFields(str) {
return str.replace(/\d+ms/g, "0ms");
}

vows
.describe("Node util module fallback for non-node environments")
.addBatch({
getCustomInspectSymbol: {
"should not be null in a node environment": function() {
assert.equal(
getCustomInspectSymbol(),
Symbol.for("nodejs.util.inspect.custom") || util.inspect.custom
);
},
"should not be null in a node environment when custom inspect symbol cannot be retrieved (< node v10.12.0)": function() {
assert.equal(
getCustomInspectSymbol({
lookupCustomInspectSymbol: () => null
}),
Symbol.for("nodejs.util.inspect.custom") || util.inspect.custom
);
},
"should be null in a non-node environment since 'util' features cannot be relied on": function() {
assert.equal(
getCustomInspectSymbol({
lookupCustomInspectSymbol: () => null,
requireUtil: () => null
}),
null
);
}
},
getUtilInspect: {
"should use util.inspect in a node environment": function() {
const inspect = getUtilInspect(() => "fallback");
assert.equal(inspect("util.inspect"), util.inspect("util.inspect"));
},
"should use fallback inspect function in a non-node environment": function() {
const inspect = getUtilInspect(() => "fallback", {
requireUtil: () => null
});
assert.equal(inspect("util.inspect"), "fallback");
}
},
"util usage in Cookie": {
"custom inspect for Cookie still works": function() {
const cookie = Cookie.parse("a=1; Domain=example.com; Path=/");
assert.equal(cookie.inspect(), util.inspect(cookie));
}
},
"util usage in MemoryCookie": {
"when store is empty": {
topic: function() {
const cookieJar = new CookieJar();
return cookieJar.store;
},
"custom inspect for MemoryCookie still works": function(memoryStore) {
assert.equal(
resetAgeFields(util.inspect(memoryStore)),
resetAgeFields(memoryStore.inspect())
);
},
"fallback produces equivalent output to custom inspect": function(
memoryStore
) {
assert.equal(
resetAgeFields(util.inspect(memoryStore.idx)),
resetAgeFields(inspectFallback(memoryStore.idx))
);
}
},
"when store has a single cookie": {
topic: function() {
const cookieJar = new CookieJar();
cookieJar.setCookieSync(
"a=1; Domain=example.com; Path=/",
"http://example.com/index.html"
);
return cookieJar.store;
},
"custom inspect for MemoryCookie still works": function(memoryStore) {
assert.equal(
resetAgeFields(util.inspect(memoryStore)),
resetAgeFields(memoryStore.inspect())
);
},
"fallback produces equivalent output to custom inspect": function(
memoryStore
) {
assert.equal(
resetAgeFields(util.inspect(memoryStore.idx)),
resetAgeFields(inspectFallback(memoryStore.idx))
);
}
},
"when store has a multiple cookies": {
topic: function() {
const cookieJar = new CookieJar();
["a", "b", "c"].forEach((cookieName, i) => {
cookieJar.setCookieSync(
`${cookieName}=${i}; Domain=example.com; Path=/`,
"http://example.com/index.html"
);
});
["d", "e"].forEach((cookieName, i) => {
cookieJar.setCookieSync(
`${cookieName}=${i}; Domain=example.com; Path=/some-path/`,
"http://example.com/index.html"
);
});
cookieJar.setCookieSync(
`f=0; Domain=another.com; Path=/`,
"http://another.com/index.html"
);
return cookieJar.store;
},
"custom inspect for MemoryCookie still works": function(memoryStore) {
assert.equal(
resetAgeFields(util.inspect(memoryStore)),
resetAgeFields(memoryStore.inspect())
);
},
"fallback produces equivalent output to custom inspect": function(
memoryStore
) {
assert.equal(
resetAgeFields(util.inspect(memoryStore.idx)),
resetAgeFields(inspectFallback(memoryStore.idx))
);
}
}
}
})
.export(module);
Loading