Import Upstream version 4.1.3+~4.0.2
This commit is contained in:
parent
15563698d6
commit
7b0ded04ce
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"extends": ["plugin:prettier/recommended"],
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 6
|
"ecmaVersion": 6
|
||||||
},
|
},
|
||||||
|
@ -7,7 +8,14 @@
|
||||||
"no-var": "error",
|
"no-var": "error",
|
||||||
"prefer-arrow-callback": "error",
|
"prefer-arrow-callback": "error",
|
||||||
"prefer-const": "error",
|
"prefer-const": "error",
|
||||||
"prefer-template": "error"
|
"prefer-template": "error",
|
||||||
},
|
"no-restricted-modules": ["error",
|
||||||
"extends": ["plugin:prettier/recommended"]
|
// 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"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -6,3 +6,6 @@ docker-compose.override.yml
|
||||||
.nyc_output
|
.nyc_output
|
||||||
coverage
|
coverage
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
.npm
|
||||||
|
.config
|
||||||
|
.bash_history
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Comment line immediately above ownership line is reserved for related information. Please be careful while editing.
|
||||||
|
#ECCN:Open Source
|
|
@ -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/
|
|
@ -1,4 +1,4 @@
|
||||||
FROM node:12
|
FROM node:16
|
||||||
MAINTAINER awaterman@salesforce.com
|
MAINTAINER awaterman@salesforce.com
|
||||||
LABEL Description="Vendor=\"Salesforce.com\" Version=\"1.0\""
|
LABEL Description="Vendor=\"Salesforce.com\" Version=\"1.0\""
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
|
|
490
README.md
490
README.md
|
@ -1,191 +1,204 @@
|
||||||
[RFC6265](https://tools.ietf.org/html/rfc6265) Cookies and CookieJar for Node.js
|
# tough-cookie
|
||||||
|
|
||||||
|
[RFC 6265](https://tools.ietf.org/html/rfc6265) Cookies and CookieJar for Node.js
|
||||||
|
|
||||||
[![npm package](https://nodei.co/npm/tough-cookie.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/tough-cookie/)
|
[![npm package](https://nodei.co/npm/tough-cookie.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/tough-cookie/)
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/salesforce/tough-cookie.svg?branch=master)](https://travis-ci.org/salesforce/tough-cookie)
|
[![Build Status](https://travis-ci.org/salesforce/tough-cookie.svg?branch=master)](https://travis-ci.org/salesforce/tough-cookie)
|
||||||
|
|
||||||
# Synopsis
|
## Synopsis
|
||||||
|
|
||||||
``` javascript
|
```javascript
|
||||||
var tough = require('tough-cookie');
|
var tough = require("tough-cookie");
|
||||||
var Cookie = tough.Cookie;
|
var Cookie = tough.Cookie;
|
||||||
var cookie = Cookie.parse(header);
|
var cookie = Cookie.parse(header);
|
||||||
cookie.value = 'somethingdifferent';
|
cookie.value = "somethingdifferent";
|
||||||
header = cookie.toString();
|
header = cookie.toString();
|
||||||
|
|
||||||
var cookiejar = new tough.CookieJar();
|
var cookiejar = new tough.CookieJar();
|
||||||
cookiejar.setCookie(cookie, 'http://currentdomain.example.com/path', cb);
|
|
||||||
// ...
|
// Asynchronous!
|
||||||
cookiejar.getCookies('http://example.com/otherpath',function(err,cookies) {
|
var cookie = await cookiejar.setCookie(
|
||||||
res.headers['cookie'] = cookies.join('; ');
|
cookie,
|
||||||
|
"https://currentdomain.example.com/path"
|
||||||
|
);
|
||||||
|
var cookies = await cookiejar.getCookies("https://example.com/otherpath");
|
||||||
|
|
||||||
|
// Or with callbacks!
|
||||||
|
cookiejar.setCookie(
|
||||||
|
cookie,
|
||||||
|
"https://currentdomain.example.com/path",
|
||||||
|
function (err, cookie) {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
);
|
||||||
|
cookiejar.getCookies("http://example.com/otherpath", function (err, cookies) {
|
||||||
|
/* ... */
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
# Installation
|
|
||||||
|
|
||||||
It's _so_ easy!
|
|
||||||
|
|
||||||
`npm install tough-cookie`
|
|
||||||
|
|
||||||
Why the name? NPM modules `cookie`, `cookies` and `cookiejar` were already taken.
|
Why the name? NPM modules `cookie`, `cookies` and `cookiejar` were already taken.
|
||||||
|
|
||||||
## Version Support
|
## Installation
|
||||||
|
|
||||||
Support for versions of node.js will follow that of the [request](https://www.npmjs.com/package/request) module.
|
It's _so_ easy! Install with `npm` or your preferred package manager.
|
||||||
|
|
||||||
# API
|
```sh
|
||||||
|
npm install tough-cookie
|
||||||
|
```
|
||||||
|
|
||||||
## tough
|
## Node.js Version Support
|
||||||
|
|
||||||
Functions on the module you get from `require('tough-cookie')`. All can be used as pure functions and don't need to be "bound".
|
We follow the [node.js release schedule](https://github.com/nodejs/Release#release-schedule) and support all versions that are in Active LTS or Maintenance. We will always do a major release when dropping support for older versions of node, and we will do so in consultation with our community.
|
||||||
|
|
||||||
**Note**: prior to 1.0.x, several of these functions took a `strict` parameter. This has since been removed from the API as it was no longer necessary.
|
## API
|
||||||
|
|
||||||
### `parseDate(string)`
|
### tough
|
||||||
|
|
||||||
Parse a cookie date string into a `Date`. Parses according to RFC6265 Section 5.1.1, not `Date.parse()`.
|
The top-level exports from `require('tough-cookie')` can all be used as pure functions and don't need to be bound.
|
||||||
|
|
||||||
### `formatDate(date)`
|
#### `parseDate(string)`
|
||||||
|
|
||||||
Format a Date into a RFC1123 string (the RFC6265-recommended format).
|
Parse a cookie date string into a `Date`. Parses according to [RFC 6265 Section 5.1.1](https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.1), not `Date.parse()`.
|
||||||
|
|
||||||
### `canonicalDomain(str)`
|
#### `formatDate(date)`
|
||||||
|
|
||||||
Transforms a domain-name into a canonical domain-name. The canonical domain-name is a trimmed, lowercased, stripped-of-leading-dot and optionally punycode-encoded domain-name (Section 5.1.2 of RFC6265). For the most part, this function is idempotent (can be run again on its output without ill effects).
|
Format a `Date` into an [RFC 822](https://datatracker.ietf.org/doc/html/rfc822#section-5) string (the RFC 6265 recommended format).
|
||||||
|
|
||||||
### `domainMatch(str,domStr[,canonicalize=true])`
|
#### `canonicalDomain(str)`
|
||||||
|
|
||||||
Answers "does this real domain match the domain in a cookie?". The `str` is the "current" domain-name and the `domStr` is the "cookie" domain-name. Matches according to RFC6265 Section 5.1.3, but it helps to think of it as a "suffix match".
|
Transforms a domain name into a canonical domain name. The canonical domain name is a domain name that has been trimmed, lowercased, stripped of leading dot, and optionally punycode-encoded ([Section 5.1.2 of RFC 6265](https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.2)). For the most part, this function is idempotent (calling the function with the output from a previous call returns the same output).
|
||||||
|
|
||||||
The `canonicalize` parameter will run the other two parameters through `canonicalDomain` or not.
|
#### `domainMatch(str, domStr[, canonicalize=true])`
|
||||||
|
|
||||||
### `defaultPath(path)`
|
Answers "does this real domain match the domain in a cookie?". The `str` is the "current" domain name and the `domStr` is the "cookie" domain name. Matches according to [RFC 6265 Section 5.1.3](https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.3), but it helps to think of it as a "suffix match".
|
||||||
|
|
||||||
Given a current request/response path, gives the Path apropriate for storing in a cookie. This is basically the "directory" of a "file" in the path, but is specified by Section 5.1.4 of the RFC.
|
The `canonicalize` parameter toggles whether the domain parameters get normalized with `canonicalDomain` or not.
|
||||||
|
|
||||||
The `path` parameter MUST be _only_ the pathname part of a URI (i.e. excludes the hostname, query, fragment, etc.). This is the `.pathname` property of node's `uri.parse()` output.
|
#### `defaultPath(path)`
|
||||||
|
|
||||||
### `pathMatch(reqPath,cookiePath)`
|
Given a current request/response path, gives the path appropriate for storing in a cookie. This is basically the "directory" of a "file" in the path, but is specified by [Section 5.1.4 of the RFC](https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.4).
|
||||||
|
|
||||||
Answers "does the request-path path-match a given cookie-path?" as per RFC6265 Section 5.1.4. Returns a boolean.
|
The `path` parameter MUST be _only_ the pathname part of a URI (excluding the hostname, query, fragment, and so on). This is the `.pathname` property of node's `uri.parse()` output.
|
||||||
|
|
||||||
|
#### `pathMatch(reqPath, cookiePath)`
|
||||||
|
|
||||||
|
Answers "does the request-path path-match a given cookie-path?" as per [RFC 6265 Section 5.1.4](https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.4). Returns a boolean.
|
||||||
|
|
||||||
This is essentially a prefix-match where `cookiePath` is a prefix of `reqPath`.
|
This is essentially a prefix-match where `cookiePath` is a prefix of `reqPath`.
|
||||||
|
|
||||||
### `parse(cookieString[, options])`
|
#### `parse(cookieString[, options])`
|
||||||
|
|
||||||
alias for `Cookie.parse(cookieString[, options])`
|
Alias for [`Cookie.parse(cookieString[, options])`](#cookieparsecookiestring-options).
|
||||||
|
|
||||||
### `fromJSON(string)`
|
#### `fromJSON(string)`
|
||||||
|
|
||||||
alias for `Cookie.fromJSON(string)`
|
Alias for [`Cookie.fromJSON(string)`](#cookiefromjsonstrorobj).
|
||||||
|
|
||||||
### `getPublicSuffix(hostname)`
|
#### `getPublicSuffix(hostname)`
|
||||||
|
|
||||||
Returns the public suffix of this hostname. The public suffix is the shortest domain-name upon which a cookie can be set. Returns `null` if the hostname cannot have cookies set for it.
|
Returns the public suffix of this hostname. The public suffix is the shortest domain name upon which a cookie can be set. Returns `null` if the hostname cannot have cookies set for it.
|
||||||
|
|
||||||
For example: `www.example.com` and `www.subdomain.example.com` both have public suffix `example.com`.
|
For example: `www.example.com` and `www.subdomain.example.com` both have public suffix `example.com`.
|
||||||
|
|
||||||
For further information, see http://publicsuffix.org/. This module derives its list from that site. This call is currently a wrapper around [`psl`](https://www.npmjs.com/package/psl)'s [get() method](https://www.npmjs.com/package/psl#pslgetdomain).
|
For further information, see the [Public Suffix List](http://publicsuffix.org/). This module derives its list from that site. This call is a wrapper around [`psl`](https://www.npmjs.com/package/psl)'s [`get` method](https://www.npmjs.com/package/psl##pslgetdomain).
|
||||||
|
|
||||||
### `cookieCompare(a,b)`
|
#### `cookieCompare(a, b)`
|
||||||
|
|
||||||
For use with `.sort()`, sorts a list of cookies into the recommended order given in the RFC (Section 5.4 step 2). The sort algorithm is, in order of precedence:
|
For use with `.sort()`, sorts a list of cookies into the recommended order given in step 2 of ([RFC 6265 Section 5.4](https://datatracker.ietf.org/doc/html/rfc6265#section-5.4)). The sort algorithm is, in order of precedence:
|
||||||
|
|
||||||
* Longest `.path`
|
- Longest `.path`
|
||||||
* oldest `.creation` (which has a 1ms precision, same as `Date`)
|
- oldest `.creation` (which has a 1-ms precision, same as `Date`)
|
||||||
* lowest `.creationIndex` (to get beyond the 1ms precision)
|
- lowest `.creationIndex` (to get beyond the 1-ms precision)
|
||||||
|
|
||||||
``` javascript
|
```javascript
|
||||||
var cookies = [ /* unsorted array of Cookie objects */ ];
|
var cookies = [
|
||||||
|
/* unsorted array of Cookie objects */
|
||||||
|
];
|
||||||
cookies = cookies.sort(cookieCompare);
|
cookies = cookies.sort(cookieCompare);
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note**: Since JavaScript's `Date` is limited to a 1ms precision, cookies within the same milisecond are entirely possible. This is especially true when using the `now` option to `.setCookie()`. The `.creationIndex` property is a per-process global counter, assigned during construction with `new Cookie()`. This preserves the spirit of the RFC sorting: older cookies go first. This works great for `MemoryCookieStore`, since `Set-Cookie` headers are parsed in order, but may not be so great for distributed systems. Sophisticated `Store`s may wish to set this to some other _logical clock_ such that if cookies A and B are created in the same millisecond, but cookie A is created before cookie B, then `A.creationIndex < B.creationIndex`. If you want to alter the global counter, which you probably _shouldn't_ do, it's stored in `Cookie.cookiesCreated`.
|
> **Note**: Since the JavaScript `Date` is limited to a 1-ms precision, cookies within the same millisecond are entirely possible. This is especially true when using the `now` option to `.setCookie()`. The `.creationIndex` property is a per-process global counter, assigned during construction with `new Cookie()`, which preserves the spirit of the RFC sorting: older cookies go first. This works great for `MemoryCookieStore` since `Set-Cookie` headers are parsed in order, but is not so great for distributed systems. Sophisticated `Store`s may wish to set this to some other _logical clock_ so that if cookies A and B are created in the same millisecond, but cookie A is created before cookie B, then `A.creationIndex < B.creationIndex`. If you want to alter the global counter, which you probably _shouldn't_ do, it's stored in `Cookie.cookiesCreated`.
|
||||||
|
|
||||||
### `permuteDomain(domain)`
|
#### `permuteDomain(domain)`
|
||||||
|
|
||||||
Generates a list of all possible domains that `domainMatch()` the parameter. May be handy for implementing cookie stores.
|
Generates a list of all possible domains that `domainMatch()` the parameter. Can be handy for implementing cookie stores.
|
||||||
|
|
||||||
### `permutePath(path)`
|
#### `permutePath(path)`
|
||||||
|
|
||||||
Generates a list of all possible paths that `pathMatch()` the parameter. May be handy for implementing cookie stores.
|
Generates a list of all possible paths that `pathMatch()` the parameter. Can be handy for implementing cookie stores.
|
||||||
|
|
||||||
|
### Cookie
|
||||||
## Cookie
|
|
||||||
|
|
||||||
Exported via `tough.Cookie`.
|
Exported via `tough.Cookie`.
|
||||||
|
|
||||||
### `Cookie.parse(cookieString[, options])`
|
#### `Cookie.parse(cookieString[, options])`
|
||||||
|
|
||||||
Parses a single Cookie or Set-Cookie HTTP header into a `Cookie` object. Returns `undefined` if the string can't be parsed.
|
Parses a single Cookie or Set-Cookie HTTP header into a `Cookie` object. Returns `undefined` if the string can't be parsed.
|
||||||
|
|
||||||
The options parameter is not required and currently has only one property:
|
The options parameter is not required and currently has only one property:
|
||||||
|
|
||||||
* _loose_ - boolean - if `true` enable parsing of key-less cookies like `=abc` and `=`, which are not RFC-compliant.
|
- _loose_ - boolean - if `true` enable parsing of keyless cookies like `=abc` and `=`, which are not RFC-compliant.
|
||||||
|
|
||||||
If options is not an object, it is ignored, which means you can use `Array#map` with it.
|
If options is not an object it is ignored, which means it can be used with [`Array#map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map).
|
||||||
|
|
||||||
Here's how to process the Set-Cookie header(s) on a node HTTP/HTTPS response:
|
To process the Set-Cookie header(s) on a node HTTP/HTTPS response:
|
||||||
|
|
||||||
``` javascript
|
```javascript
|
||||||
if (res.headers['set-cookie'] instanceof Array)
|
if (Array.isArray(res.headers["set-cookie"]))
|
||||||
cookies = res.headers['set-cookie'].map(Cookie.parse);
|
cookies = res.headers["set-cookie"].map(Cookie.parse);
|
||||||
else
|
else cookies = [Cookie.parse(res.headers["set-cookie"])];
|
||||||
cookies = [Cookie.parse(res.headers['set-cookie'])];
|
|
||||||
```
|
```
|
||||||
|
|
||||||
_Note:_ in version 2.3.3, tough-cookie limited the number of spaces before the `=` to 256 characters. This limitation has since been removed.
|
_Note:_ In version 2.3.3, tough-cookie limited the number of spaces before the `=` to 256 characters. This limitation was removed in version 2.3.4.
|
||||||
See [Issue 92](https://github.com/salesforce/tough-cookie/issues/92)
|
For more details, see [issue #92](https://github.com/salesforce/tough-cookie/issues/92).
|
||||||
|
|
||||||
### Properties
|
#### Properties
|
||||||
|
|
||||||
Cookie object properties:
|
Cookie object properties:
|
||||||
|
|
||||||
* _key_ - string - the name or key of the cookie (default "")
|
- _key_ - string - the name or key of the cookie (default `""`)
|
||||||
* _value_ - string - the value of the cookie (default "")
|
- _value_ - string - the value of the cookie (default `""`)
|
||||||
* _expires_ - `Date` - if set, the `Expires=` attribute of the cookie (defaults to the string `"Infinity"`). See `setExpires()`
|
- _expires_ - `Date` - if set, the `Expires=` attribute of the cookie (defaults to the string `"Infinity"`). See `setExpires()`
|
||||||
* _maxAge_ - seconds - if set, the `Max-Age=` attribute _in seconds_ of the cookie. May also be set to strings `"Infinity"` and `"-Infinity"` for non-expiry and immediate-expiry, respectively. See `setMaxAge()`
|
- _maxAge_ - seconds - if set, the `Max-Age=` attribute _in seconds_ of the cookie. Can also be set to strings `"Infinity"` and `"-Infinity"` for non-expiry and immediate-expiry, respectively. See `setMaxAge()`
|
||||||
* _domain_ - string - the `Domain=` attribute of the cookie
|
- _domain_ - string - the `Domain=` attribute of the cookie
|
||||||
* _path_ - string - the `Path=` of the cookie
|
- _path_ - string - the `Path=` of the cookie
|
||||||
* _secure_ - boolean - the `Secure` cookie flag
|
- _secure_ - boolean - the `Secure` cookie flag
|
||||||
* _httpOnly_ - boolean - the `HttpOnly` cookie flag
|
- _httpOnly_ - boolean - the `HttpOnly` cookie flag
|
||||||
* _sameSite_ - string - the `SameSite` cookie attribute (from [RFC6265bis]); must be one of `none`, `lax`, or `strict`
|
- _sameSite_ - string - the `SameSite` cookie attribute (from [RFC 6265bis](#rfc-6265bis)); must be one of `none`, `lax`, or `strict`
|
||||||
* _extensions_ - `Array` - any unrecognized cookie attributes as strings (even if equal-signs inside)
|
- _extensions_ - `Array` - any unrecognized cookie attributes as strings (even if equal-signs inside)
|
||||||
* _creation_ - `Date` - when this cookie was constructed
|
- _creation_ - `Date` - when this cookie was constructed
|
||||||
* _creationIndex_ - number - set at construction, used to provide greater sort precision (please see `cookieCompare(a,b)` for a full explanation)
|
- _creationIndex_ - number - set at construction, used to provide greater sort precision (see `cookieCompare(a,b)` for a full explanation)
|
||||||
|
|
||||||
After a cookie has been passed through `CookieJar.setCookie()` it will have the following additional attributes:
|
After a cookie has been passed through `CookieJar.setCookie()` it has the following additional attributes:
|
||||||
|
|
||||||
* _hostOnly_ - boolean - is this a host-only cookie (i.e. no Domain field was set, but was instead implied)
|
- _hostOnly_ - boolean - is this a host-only cookie (that is, no Domain field was set, but was instead implied).
|
||||||
* _pathIsDefault_ - boolean - if true, there was no Path field on the cookie and `defaultPath()` was used to derive one.
|
- _pathIsDefault_ - boolean - if true, there was no Path field on the cookie and `defaultPath()` was used to derive one.
|
||||||
* _creation_ - `Date` - **modified** from construction to when the cookie was added to the jar
|
- _creation_ - `Date` - **modified** from construction to when the cookie was added to the jar.
|
||||||
* _lastAccessed_ - `Date` - last time the cookie got accessed. Will affect cookie cleaning once implemented. Using `cookiejar.getCookies(...)` will update this attribute.
|
- _lastAccessed_ - `Date` - last time the cookie got accessed. Affects cookie cleaning after it is implemented. Using `cookiejar.getCookies(...)` updates this attribute.
|
||||||
|
|
||||||
### `Cookie([{properties}])`
|
#### `new Cookie([properties])`
|
||||||
|
|
||||||
Receives an options object that can contain any of the above Cookie properties, uses the default for unspecified properties.
|
Receives an options object that can contain any of the above Cookie properties. Uses the default for unspecified properties.
|
||||||
|
|
||||||
### `.toString()`
|
#### `.toString()`
|
||||||
|
|
||||||
encode to a Set-Cookie header value. The Expires cookie field is set using `formatDate()`, but is omitted entirely if `.expires` is `Infinity`.
|
Encodes to a Set-Cookie header value. The Expires cookie field is set using `formatDate()`, but is omitted entirely if `.expires` is `Infinity`.
|
||||||
|
|
||||||
### `.cookieString()`
|
#### `.cookieString()`
|
||||||
|
|
||||||
encode to a Cookie header value (i.e. the `.key` and `.value` properties joined with '=').
|
Encodes to a Cookie header value (specifically, the `.key` and `.value` properties joined with `"="`).
|
||||||
|
|
||||||
### `.setExpires(String)`
|
#### `.setExpires(string)`
|
||||||
|
|
||||||
sets the expiry based on a date-string passed through `parseDate()`. If parseDate returns `null` (i.e. can't parse this date string), `.expires` is set to `"Infinity"` (a string) is set.
|
Sets the expiry based on a date-string passed through `parseDate()`. If parseDate returns `null` (that is, can't parse this date string), `.expires` is set to `"Infinity"` (a string).
|
||||||
|
|
||||||
### `.setMaxAge(number)`
|
#### `.setMaxAge(number)`
|
||||||
|
|
||||||
sets the maxAge in seconds. Coerces `-Infinity` to `"-Infinity"` and `Infinity` to `"Infinity"` so it JSON serializes correctly.
|
Sets the maxAge in seconds. Coerces `-Infinity` to `"-Infinity"` and `Infinity` to `"Infinity"` so it correctly serializes to JSON.
|
||||||
|
|
||||||
### `.expiryTime([now=Date.now()])`
|
#### `.expiryDate([now=Date.now()])`
|
||||||
|
|
||||||
### `.expiryDate([now=Date.now()])`
|
`expiryTime()` computes the absolute unix-epoch milliseconds that this cookie expires. `expiryDate()` works similarly, except it returns a `Date` object. Note that in both cases the `now` parameter should be milliseconds.
|
||||||
|
|
||||||
expiryTime() Computes the absolute unix-epoch milliseconds that this cookie expires. expiryDate() works similarly, except it returns a `Date` object. Note that in both cases the `now` parameter should be milliseconds.
|
|
||||||
|
|
||||||
Max-Age takes precedence over Expires (as per the RFC). The `.creation` attribute -- or, by default, the `now` parameter -- is used to offset the `.maxAge` attribute.
|
Max-Age takes precedence over Expires (as per the RFC). The `.creation` attribute -- or, by default, the `now` parameter -- is used to offset the `.maxAge` attribute.
|
||||||
|
|
||||||
|
@ -193,45 +206,45 @@ If Expires (`.expires`) is set, that's returned.
|
||||||
|
|
||||||
Otherwise, `expiryTime()` returns `Infinity` and `expiryDate()` returns a `Date` object for "Tue, 19 Jan 2038 03:14:07 GMT" (latest date that can be expressed by a 32-bit `time_t`; the common limit for most user-agents).
|
Otherwise, `expiryTime()` returns `Infinity` and `expiryDate()` returns a `Date` object for "Tue, 19 Jan 2038 03:14:07 GMT" (latest date that can be expressed by a 32-bit `time_t`; the common limit for most user-agents).
|
||||||
|
|
||||||
### `.TTL([now=Date.now()])`
|
#### `.TTL([now=Date.now()])`
|
||||||
|
|
||||||
compute the TTL relative to `now` (milliseconds). The same precedence rules as for `expiryTime`/`expiryDate` apply.
|
Computes the TTL relative to `now` (milliseconds). The same precedence rules as for `expiryTime`/`expiryDate` apply.
|
||||||
|
|
||||||
The "number" `Infinity` is returned for cookies without an explicit expiry and `0` is returned if the cookie is expired. Otherwise a time-to-live in milliseconds is returned.
|
`Infinity` is returned for cookies without an explicit expiry and `0` is returned if the cookie is expired. Otherwise a time-to-live in milliseconds is returned.
|
||||||
|
|
||||||
### `.canonicalizedDomain()`
|
#### `.canonicalizedDomain()`
|
||||||
|
|
||||||
### `.cdomain()`
|
#### `.cdomain()`
|
||||||
|
|
||||||
return the canonicalized `.domain` field. This is lower-cased and punycode (RFC3490) encoded if the domain has any non-ASCII characters.
|
Returns the canonicalized `.domain` field. This is lower-cased and punycode ([RFC 3490](https://datatracker.ietf.org/doc/html/rfc3490)) encoded if the domain has any non-ASCII characters.
|
||||||
|
|
||||||
### `.toJSON()`
|
#### `.toJSON()`
|
||||||
|
|
||||||
For convenience in using `JSON.serialize(cookie)`. Returns a plain-old `Object` that can be JSON-serialized.
|
For convenience in using `JSON.serialize(cookie)`. Returns a plain-old `Object` that can be JSON-serialized.
|
||||||
|
|
||||||
Any `Date` properties (i.e., `.expires`, `.creation`, and `.lastAccessed`) are exported in ISO format (`.toISOString()`).
|
Any `Date` properties (such as `.expires`, `.creation`, and `.lastAccessed`) are exported in ISO format (`.toISOString()`).
|
||||||
|
|
||||||
**NOTE**: Custom `Cookie` properties will be discarded. In tough-cookie 1.x, since there was no `.toJSON` method explicitly defined, all enumerable properties were captured. If you want a property to be serialized, add the property name to the `Cookie.serializableProperties` Array.
|
> **NOTE**: Custom `Cookie` properties are discarded. In tough-cookie 1.x, since there was no `.toJSON` method explicitly defined, all enumerable properties were captured. If you want a property to be serialized, add the property name to the `Cookie.serializableProperties` Array.
|
||||||
|
|
||||||
### `Cookie.fromJSON(strOrObj)`
|
#### `Cookie.fromJSON(strOrObj)`
|
||||||
|
|
||||||
Does the reverse of `cookie.toJSON()`. If passed a string, will `JSON.parse()` that first.
|
Does the reverse of `cookie.toJSON()`. If passed a string, will `JSON.parse()` that first.
|
||||||
|
|
||||||
Any `Date` properties (i.e., `.expires`, `.creation`, and `.lastAccessed`) are parsed via `Date.parse()`, not the tough-cookie `parseDate`, since it's JavaScript/JSON-y timestamps being handled at this layer.
|
Any `Date` properties (such as `.expires`, `.creation`, and `.lastAccessed`) are parsed via [`Date.parse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse), not tough-cookie's `parseDate`, since ISO timestamps are being handled at this layer.
|
||||||
|
|
||||||
Returns `null` upon JSON parsing error.
|
Returns `null` upon a JSON parsing error.
|
||||||
|
|
||||||
### `.clone()`
|
#### `.clone()`
|
||||||
|
|
||||||
Does a deep clone of this cookie, exactly implemented as `Cookie.fromJSON(cookie.toJSON())`.
|
Does a deep clone of this cookie, implemented exactly as `Cookie.fromJSON(cookie.toJSON())`.
|
||||||
|
|
||||||
### `.validate()`
|
#### `.validate()`
|
||||||
|
|
||||||
Status: *IN PROGRESS*. Works for a few things, but is by no means comprehensive.
|
Status: _IN PROGRESS_. Works for a few things, but is by no means comprehensive.
|
||||||
|
|
||||||
validates cookie attributes for semantic correctness. Useful for "lint" checking any Set-Cookie headers you generate. For now, it returns a boolean, but eventually could return a reason string -- you can future-proof with this construct:
|
Validates cookie attributes for semantic correctness. Useful for "lint" checking any Set-Cookie headers you generate. For now, it returns a boolean, but eventually could return a reason string. Future-proof with this construct:
|
||||||
|
|
||||||
``` javascript
|
```javascript
|
||||||
if (cookie.validate() === true) {
|
if (cookie.validate() === true) {
|
||||||
// it's tasty
|
// it's tasty
|
||||||
} else {
|
} else {
|
||||||
|
@ -239,225 +252,225 @@ if (cookie.validate() === true) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### CookieJar
|
||||||
## CookieJar
|
|
||||||
|
|
||||||
Exported via `tough.CookieJar`.
|
Exported via `tough.CookieJar`.
|
||||||
|
|
||||||
### `CookieJar([store],[options])`
|
#### `CookieJar([store][, options])`
|
||||||
|
|
||||||
Simply use `new CookieJar()`. If you'd like to use a custom store, pass that to the constructor otherwise a `MemoryCookieStore` will be created and used.
|
Simply use `new CookieJar()`. If a custom store is not passed to the constructor, a [`MemoryCookieStore`](#memorycookiestore) is created and used.
|
||||||
|
|
||||||
The `options` object can be omitted and can have the following properties:
|
The `options` object can be omitted and can have the following properties:
|
||||||
|
|
||||||
* _rejectPublicSuffixes_ - boolean - default `true` - reject cookies with domains like "com" and "co.uk"
|
- _rejectPublicSuffixes_ - boolean - default `true` - reject cookies with domains like "com" and "co.uk"
|
||||||
* _looseMode_ - boolean - default `false` - accept malformed cookies like `bar` and `=bar`, which have an implied empty name.
|
- _looseMode_ - boolean - default `false` - accept malformed cookies like `bar` and `=bar`, which have an implied empty name.
|
||||||
* _prefixSecurity_ - string - default `silent` - set to `'unsafe-disabled'`, `'silent'`, or `'strict'`. See [Cookie Prefixes] below.
|
- _prefixSecurity_ - string - default `silent` - set to `'unsafe-disabled'`, `'silent'`, or `'strict'`. See [Cookie Prefixes](#cookie-prefixes) below.
|
||||||
* _allowSpecialUseDomain_ - boolean - default `false` - accepts special-use domain suffixes, such as `local`. Useful for testing purposes.
|
- _allowSpecialUseDomain_ - boolean - default `true` - accepts special-use domain suffixes, such as `local`. Useful for testing purposes.
|
||||||
This is not in the standard, but is used sometimes on the web and is accepted by (most) browsers.
|
This is not in the standard, but is used sometimes on the web and is accepted by most browsers.
|
||||||
|
|
||||||
Since eventually this module would like to support database/remote/etc. CookieJars, continuation passing style is used for CookieJar methods.
|
#### `.setCookie(cookieOrString, currentUrl[, options][, callback(err, cookie)])`
|
||||||
|
|
||||||
### `.setCookie(cookieOrString, currentUrl, [{options},] cb(err,cookie))`
|
Attempt to set the cookie in the cookie jar. The cookie has updated `.creation`, `.lastAccessed` and `.hostOnly` properties. And returns a promise if a callback is not provided.
|
||||||
|
|
||||||
Attempt to set the cookie in the cookie jar. If the operation fails, an error will be given to the callback `cb`, otherwise the cookie is passed through. The cookie will have updated `.creation`, `.lastAccessed` and `.hostOnly` properties.
|
|
||||||
|
|
||||||
The `options` object can be omitted and can have the following properties:
|
The `options` object can be omitted and can have the following properties:
|
||||||
|
|
||||||
* _http_ - boolean - default `true` - indicates if this is an HTTP or non-HTTP API. Affects HttpOnly cookies.
|
- _http_ - boolean - default `true` - indicates if this is an HTTP or non-HTTP API. Affects `HttpOnly` cookies.
|
||||||
* _secure_ - boolean - autodetect from url - indicates if this is a "Secure" API. If the currentUrl starts with `https:` or `wss:` then this is defaulted to `true`, otherwise `false`.
|
- _secure_ - boolean - autodetect from URL - indicates if this is a "Secure" API. If the currentUrl starts with `https:` or `wss:` this defaults to `true`, otherwise `false`.
|
||||||
* _now_ - Date - default `new Date()` - what to use for the creation/access time of cookies
|
- _now_ - Date - default `new Date()` - what to use for the creation or access time of cookies.
|
||||||
* _ignoreError_ - boolean - default `false` - silently ignore things like parse errors and invalid domains. `Store` errors aren't ignored by this option.
|
- _ignoreError_ - boolean - default `false` - silently ignore things like parse errors and invalid domains. `Store` errors aren't ignored by this option.
|
||||||
* _sameSiteContext_ - string - default unset - set to `'none'`, `'lax'`, or `'strict'` See [SameSite Cookies] below.
|
- _sameSiteContext_ - string - default unset - set to `'none'`, `'lax'`, or `'strict'` See [SameSite Cookies](#samesite-cookies) below.
|
||||||
|
|
||||||
As per the RFC, the `.hostOnly` property is set if there was no "Domain=" parameter in the cookie string (or `.domain` was null on the Cookie object). The `.domain` property is set to the fully-qualified hostname of `currentUrl` in this case. Matching this cookie requires an exact hostname match (not a `domainMatch` as per usual).
|
As per the RFC, the `.hostOnly` property is set if there was no "Domain=" parameter in the cookie string (or `.domain` was null on the Cookie object). The `.domain` property is set to the fully-qualified hostname of `currentUrl` in this case. Matching this cookie requires an exact hostname match (not a `domainMatch` as per usual).
|
||||||
|
|
||||||
### `.setCookieSync(cookieOrString, currentUrl, [{options}])`
|
#### `.setCookieSync(cookieOrString, currentUrl[, options])`
|
||||||
|
|
||||||
Synchronous version of `setCookie`; only works with synchronous stores (e.g. the default `MemoryCookieStore`).
|
Synchronous version of [`setCookie`](#setcookiecookieorstring-currenturl-options-callbackerr-cookie); only works with synchronous stores (that is, the default `MemoryCookieStore`).
|
||||||
|
|
||||||
### `.getCookies(currentUrl, [{options},] cb(err,cookies))`
|
#### `.getCookies(currentUrl[, options][, callback(err, cookies)])`
|
||||||
|
|
||||||
Retrieve the list of cookies that can be sent in a Cookie header for the current url.
|
Retrieve the list of cookies that can be sent in a Cookie header for the current URL. Returns a promise if a callback is not provided.
|
||||||
|
|
||||||
If an error is encountered, that's passed as `err` to the callback, otherwise an `Array` of `Cookie` objects is passed. The array is sorted with `cookieCompare()` unless the `{sort:false}` option is given.
|
Returns an array of `Cookie` objects, sorted by default using [`cookieCompare`](#cookiecomparea-b).
|
||||||
|
|
||||||
|
If an error is encountered it's passed as `err` to the callback, otherwise an array of `Cookie` objects is passed. The array is sorted with `cookieCompare()` unless the `{sort:false}` option is given.
|
||||||
|
|
||||||
The `options` object can be omitted and can have the following properties:
|
The `options` object can be omitted and can have the following properties:
|
||||||
|
|
||||||
* _http_ - boolean - default `true` - indicates if this is an HTTP or non-HTTP API. Affects HttpOnly cookies.
|
- _http_ - boolean - default `true` - indicates if this is an HTTP or non-HTTP API. Affects `HttpOnly` cookies.
|
||||||
* _secure_ - boolean - autodetect from url - indicates if this is a "Secure" API. If the currentUrl starts with `https:` or `wss:` then this is defaulted to `true`, otherwise `false`.
|
- _secure_ - boolean - autodetect from URL - indicates if this is a "Secure" API. If the currentUrl starts with `https:` or `wss:` then this is defaulted to `true`, otherwise `false`.
|
||||||
* _now_ - Date - default `new Date()` - what to use for the creation/access time of cookies
|
- _now_ - Date - default `new Date()` - what to use for the creation or access time of cookies
|
||||||
* _expire_ - boolean - default `true` - perform expiry-time checking of cookies and asynchronously remove expired cookies from the store. Using `false` will return expired cookies and **not** remove them from the store (which is useful for replaying Set-Cookie headers, potentially).
|
- _expire_ - boolean - default `true` - perform expiry-time checking of cookies and asynchronously remove expired cookies from the store. Using `false` returns expired cookies and does **not** remove them from the store (which is potentially useful for replaying Set-Cookie headers).
|
||||||
* _allPaths_ - boolean - default `false` - if `true`, do not scope cookies by path. The default uses RFC-compliant path scoping. **Note**: may not be supported by the underlying store (the default `MemoryCookieStore` supports it).
|
- _allPaths_ - boolean - default `false` - if `true`, do not scope cookies by path. The default uses RFC-compliant path scoping. **Note**: may not be supported by the underlying store (the default `MemoryCookieStore` supports it).
|
||||||
* _sameSiteContext_ - string - default unset - Set this to `'none'`, `'lax'` or `'strict'` to enforce SameSite cookies upon retrival. See [SameSite Cookies] below.
|
- _sameSiteContext_ - string - default unset - Set this to `'none'`, `'lax'`, or `'strict'` to enforce SameSite cookies upon retrieval. See [SameSite Cookies](#samesite-cookies) below.
|
||||||
|
- _sort_ - boolean - whether to sort the list of cookies.
|
||||||
|
|
||||||
The `.lastAccessed` property of the returned cookies will have been updated.
|
The `.lastAccessed` property of the returned cookies will have been updated.
|
||||||
|
|
||||||
### `.getCookiesSync(currentUrl, [{options}])`
|
#### `.getCookiesSync(currentUrl, [{options}])`
|
||||||
|
|
||||||
Synchronous version of `getCookies`; only works with synchronous stores (e.g. the default `MemoryCookieStore`).
|
Synchronous version of [`getCookies`](#getcookiescurrenturl-options-callbackerr-cookies); only works with synchronous stores (for example, the default `MemoryCookieStore`).
|
||||||
|
|
||||||
### `.getCookieString(...)`
|
#### `.getCookieString(...)`
|
||||||
|
|
||||||
Accepts the same options as `.getCookies()` but passes a string suitable for a Cookie header rather than an array to the callback. Simply maps the `Cookie` array via `.cookieString()`.
|
Accepts the same options as [`.getCookies()`](#getcookiescurrenturl-options-callbackerr-cookies) but returns a string suitable for a Cookie header rather than an Array.
|
||||||
|
|
||||||
### `.getCookieStringSync(...)`
|
#### `.getCookieStringSync(...)`
|
||||||
|
|
||||||
Synchronous version of `getCookieString`; only works with synchronous stores (e.g. the default `MemoryCookieStore`).
|
Synchronous version of [`getCookieString`](#getcookiestring); only works with synchronous stores (for example, the default `MemoryCookieStore`).
|
||||||
|
|
||||||
### `.getSetCookieStrings(...)`
|
#### `.getSetCookieStrings(...)`
|
||||||
|
|
||||||
Returns an array of strings suitable for **Set-Cookie** headers. Accepts the same options as `.getCookies()`. Simply maps the cookie array via `.toString()`.
|
Returns an array of strings suitable for **Set-Cookie** headers. Accepts the same options as [`.getCookies()`](#getcookiescurrenturl-options-callbackerr-cookies). Simply maps the cookie array via `.toString()`.
|
||||||
|
|
||||||
### `.getSetCookieStringsSync(...)`
|
#### `.getSetCookieStringsSync(...)`
|
||||||
|
|
||||||
Synchronous version of `getSetCookieStrings`; only works with synchronous stores (e.g. the default `MemoryCookieStore`).
|
Synchronous version of [`getSetCookieStrings`](#getsetcookiestrings); only works with synchronous stores (for example, the default `MemoryCookieStore`).
|
||||||
|
|
||||||
### `.serialize(cb(err,serializedObject))`
|
#### `.serialize([callback(err, serializedObject)])`
|
||||||
|
|
||||||
|
Returns a promise if a callback is not provided.
|
||||||
|
|
||||||
Serialize the Jar if the underlying store supports `.getAllCookies`.
|
Serialize the Jar if the underlying store supports `.getAllCookies`.
|
||||||
|
|
||||||
**NOTE**: Custom `Cookie` properties will be discarded. If you want a property to be serialized, add the property name to the `Cookie.serializableProperties` Array.
|
> **NOTE**: Custom `Cookie` properties are discarded. If you want a property to be serialized, add the property name to the `Cookie.serializableProperties` Array.
|
||||||
|
|
||||||
See [Serialization Format].
|
See [Serialization Format](#serialization-format).
|
||||||
|
|
||||||
### `.serializeSync()`
|
#### `.serializeSync()`
|
||||||
|
|
||||||
Sync version of .serialize
|
Synchronous version of [`serialize`](#serializecallbackerr-serializedobject); only works with synchronous stores (for example, the default `MemoryCookieStore`).
|
||||||
|
|
||||||
### `.toJSON()`
|
#### `.toJSON()`
|
||||||
|
|
||||||
Alias of .serializeSync() for the convenience of `JSON.stringify(cookiejar)`.
|
Alias of [`.serializeSync()`](#serializesync) for the convenience of `JSON.stringify(cookiejar)`.
|
||||||
|
|
||||||
### `CookieJar.deserialize(serialized, [store], cb(err,object))`
|
#### `CookieJar.deserialize(serialized[, store][, callback(err, object)])`
|
||||||
|
|
||||||
A new Jar is created and the serialized Cookies are added to the underlying store. Each `Cookie` is added via `store.putCookie` in the order in which they appear in the serialization.
|
A new Jar is created and the serialized Cookies are added to the underlying store. Each `Cookie` is added via `store.putCookie` in the order in which they appear in the serialization. A promise is returned if a callback is not provided.
|
||||||
|
|
||||||
The `store` argument is optional, but should be an instance of `Store`. By default, a new instance of `MemoryCookieStore` is created.
|
The `store` argument is optional, but should be an instance of `Store`. By default, a new instance of `MemoryCookieStore` is created.
|
||||||
|
|
||||||
As a convenience, if `serialized` is a string, it is passed through `JSON.parse` first. If that throws an error, this is passed to the callback.
|
As a convenience, if `serialized` is a string, it is passed through `JSON.parse` first.
|
||||||
|
|
||||||
### `CookieJar.deserializeSync(serialized, [store])`
|
#### `CookieJar.deserializeSync(serialized[, store])`
|
||||||
|
|
||||||
Sync version of `.deserialize`. _Note_ that the `store` must be synchronous for this to work.
|
Sync version of [`.deserialize`](#cookiejardeserializeserialized-store-callbackerr-object); only works with synchronous stores (for example, the default `MemoryCookieStore`).
|
||||||
|
|
||||||
### `CookieJar.fromJSON(string)`
|
#### `CookieJar.fromJSON(string)`
|
||||||
|
|
||||||
Alias of `.deserializeSync` to provide consistency with `Cookie.fromJSON()`.
|
Alias of [`.deserializeSync`](#cookiejardeserializesyncserialized-store) to provide consistency with [`Cookie.fromJSON()`](#cookiefromjsonstrorobj).
|
||||||
|
|
||||||
### `.clone([store,]cb(err,newJar))`
|
#### `.clone([store][, callback(err, cloned))`
|
||||||
|
|
||||||
Produces a deep clone of this jar. Modifications to the original won't affect the clone, and vice versa.
|
Produces a deep clone of this jar. Modifications to the original do not affect the clone, and vice versa. Returns a promise if a callback is not provided.
|
||||||
|
|
||||||
The `store` argument is optional, but should be an instance of `Store`. By default, a new instance of `MemoryCookieStore` is created. Transferring between store types is supported so long as the source implements `.getAllCookies()` and the destination implements `.putCookie()`.
|
The `store` argument is optional, but should be an instance of `Store`. By default, a new instance of `MemoryCookieStore` is created. Transferring between store types is supported so long as the source implements `.getAllCookies()` and the destination implements `.putCookie()`.
|
||||||
|
|
||||||
### `.cloneSync([store])`
|
#### `.cloneSync([store])`
|
||||||
|
|
||||||
Synchronous version of `.clone`, returning a new `CookieJar` instance.
|
Synchronous version of [`.clone`](#clonestore-callbackerr-cloned), returning a new `CookieJar` instance.
|
||||||
|
|
||||||
The `store` argument is optional, but must be a _synchronous_ `Store` instance if specified. If not passed, a new instance of `MemoryCookieStore` is used.
|
The `store` argument is optional, but must be a _synchronous_ `Store` instance if specified. If not passed, a new instance of `MemoryCookieStore` is used.
|
||||||
|
|
||||||
The _source_ and _destination_ must both be synchronous `Store`s. If one or both stores are asynchronous, use `.clone` instead. Recall that `MemoryCookieStore` supports both synchronous and asynchronous API calls.
|
The _source_ and _destination_ must both be synchronous `Store`s. If one or both stores are asynchronous, use `.clone` instead. Recall that `MemoryCookieStore` supports both synchronous and asynchronous API calls.
|
||||||
|
|
||||||
### `.removeAllCookies(cb(err))`
|
#### `.removeAllCookies([callback(err)])`
|
||||||
|
|
||||||
Removes all cookies from the jar.
|
Removes all cookies from the jar. Returns a promise if a callback is not provided.
|
||||||
|
|
||||||
This is a new backwards-compatible feature of `tough-cookie` version 2.5, so not all Stores will implement it efficiently. For Stores that do not implement `removeAllCookies`, the fallback is to call `removeCookie` after `getAllCookies`. If `getAllCookies` fails or isn't implemented in the Store, that error is returned. If one or more of the `removeCookie` calls fail, only the first error is returned.
|
This is a new backwards-compatible feature of `tough-cookie` version 2.5, so not all Stores will implement it efficiently. For Stores that do not implement `removeAllCookies`, the fallback is to call `removeCookie` after `getAllCookies`. If `getAllCookies` fails or isn't implemented in the Store, that error is returned. If one or more of the `removeCookie` calls fail, only the first error is returned.
|
||||||
|
|
||||||
### `.removeAllCookiesSync()`
|
#### `.removeAllCookiesSync()`
|
||||||
|
|
||||||
Sync version of `.removeAllCookies()`
|
Sync version of [`.removeAllCookies()`](#removeallcookiescallbackerr); only works with synchronous stores (for example, the default `MemoryCookieStore`).
|
||||||
|
|
||||||
## Store
|
### Store
|
||||||
|
|
||||||
Base class for CookieJar stores. Available as `tough.Store`.
|
Base class for CookieJar stores. Available as `tough.Store`.
|
||||||
|
|
||||||
## Store API
|
### Store API
|
||||||
|
|
||||||
The storage model for each `CookieJar` instance can be replaced with a custom implementation. The default is `MemoryCookieStore` which can be found in the `lib/memstore.js` file. The API uses continuation-passing-style to allow for asynchronous stores.
|
The storage model for each `CookieJar` instance can be replaced with a custom implementation. The default is `MemoryCookieStore` which can be found in [`lib/memstore.js`](https://github.com/salesforce/tough-cookie/blob/master/lib/memstore.js). The API uses continuation-passing-style to allow for asynchronous stores.
|
||||||
|
|
||||||
Stores should inherit from the base `Store` class, which is available as `require('tough-cookie').Store`.
|
Stores should inherit from the base `Store` class, which is available as a top-level export.
|
||||||
|
|
||||||
Stores are asynchronous by default, but if `store.synchronous` is set to `true`, then the `*Sync` methods on the of the containing `CookieJar` can be used (however, the continuation-passing style
|
Stores are asynchronous by default, but if `store.synchronous` is set to `true`, then the `*Sync` methods of the containing `CookieJar` can be used.
|
||||||
|
|
||||||
All `domain` parameters will have been normalized before calling.
|
All `domain` parameters are normalized before calling.
|
||||||
|
|
||||||
The Cookie store must have all of the following methods.
|
The Cookie store must have all of the following methods. Note that asynchronous implementations **must** support callback parameters.
|
||||||
|
|
||||||
### `store.findCookie(domain, path, key, cb(err,cookie))`
|
#### `store.findCookie(domain, path, key, callback(err, cookie))`
|
||||||
|
|
||||||
Retrieve a cookie with the given domain, path and key (a.k.a. name). The RFC maintains that exactly one of these cookies should exist in a store. If the store is using versioning, this means that the latest/newest such cookie should be returned.
|
Retrieve a cookie with the given domain, path, and key (name). The RFC maintains that exactly one of these cookies should exist in a store. If the store is using versioning, this means that the latest or newest such cookie should be returned.
|
||||||
|
|
||||||
Callback takes an error and the resulting `Cookie` object. If no cookie is found then `null` MUST be passed instead (i.e. not an error).
|
Callback takes an error and the resulting `Cookie` object. If no cookie is found then `null` MUST be passed instead (that is, not an error).
|
||||||
|
|
||||||
### `store.findCookies(domain, path, cb(err,cookies))`
|
#### `store.findCookies(domain, path, allowSpecialUseDomain, callback(err, cookies))`
|
||||||
|
|
||||||
Locates cookies matching the given domain and path. This is most often called in the context of `cookiejar.getCookies()` above.
|
Locates cookies matching the given domain and path. This is most often called in the context of [`cookiejar.getCookies()`](#getcookiescurrenturl-options-callbackerr-cookies).
|
||||||
|
|
||||||
If no cookies are found, the callback MUST be passed an empty array.
|
If no cookies are found, the callback MUST be passed an empty array.
|
||||||
|
|
||||||
The resulting list will be checked for applicability to the current request according to the RFC (domain-match, path-match, http-only-flag, secure-flag, expiry, etc.), so it's OK to use an optimistic search algorithm when implementing this method. However, the search algorithm used SHOULD try to find cookies that `domainMatch()` the domain and `pathMatch()` the path in order to limit the amount of checking that needs to be done.
|
The resulting list is checked for applicability to the current request according to the RFC (domain-match, path-match, http-only-flag, secure-flag, expiry, and so on), so it's OK to use an optimistic search algorithm when implementing this method. However, the search algorithm used SHOULD try to find cookies that `domainMatch()` the domain and `pathMatch()` the path in order to limit the amount of checking that needs to be done.
|
||||||
|
|
||||||
As of version 0.9.12, the `allPaths` option to `cookiejar.getCookies()` above will cause the path here to be `null`. If the path is `null`, path-matching MUST NOT be performed (i.e. domain-matching only).
|
As of version 0.9.12, the `allPaths` option to `cookiejar.getCookies()` above causes the path here to be `null`. If the path is `null`, path-matching MUST NOT be performed (that is, domain-matching only).
|
||||||
|
|
||||||
### `store.putCookie(cookie, cb(err))`
|
#### `store.putCookie(cookie, callback(err))`
|
||||||
|
|
||||||
Adds a new cookie to the store. The implementation SHOULD replace any existing cookie with the same `.domain`, `.path`, and `.key` properties -- depending on the nature of the implementation, it's possible that between the call to `fetchCookie` and `putCookie` that a duplicate `putCookie` can occur.
|
Adds a new cookie to the store. The implementation SHOULD replace any existing cookie with the same `.domain`, `.path`, and `.key` properties. Depending on the nature of the implementation, it's possible that between the call to `fetchCookie` and `putCookie` that a duplicate `putCookie` can occur.
|
||||||
|
|
||||||
The `cookie` object MUST NOT be modified; the caller will have already updated the `.creation` and `.lastAccessed` properties.
|
The `cookie` object MUST NOT be modified; as the caller has already updated the `.creation` and `.lastAccessed` properties.
|
||||||
|
|
||||||
Pass an error if the cookie cannot be stored.
|
Pass an error if the cookie cannot be stored.
|
||||||
|
|
||||||
### `store.updateCookie(oldCookie, newCookie, cb(err))`
|
#### `store.updateCookie(oldCookie, newCookie, callback(err))`
|
||||||
|
|
||||||
Update an existing cookie. The implementation MUST update the `.value` for a cookie with the same `domain`, `.path` and `.key`. The implementation SHOULD check that the old value in the store is equivalent to `oldCookie` - how the conflict is resolved is up to the store.
|
Update an existing cookie. The implementation MUST update the `.value` for a cookie with the same `domain`, `.path`, and `.key`. The implementation SHOULD check that the old value in the store is equivalent to `oldCookie` - how the conflict is resolved is up to the store.
|
||||||
|
|
||||||
The `.lastAccessed` property will always be different between the two objects (to the precision possible via JavaScript's clock). Both `.creation` and `.creationIndex` are guaranteed to be the same. Stores MAY ignore or defer the `.lastAccessed` change at the cost of affecting how cookies are selected for automatic deletion (e.g., least-recently-used, which is up to the store to implement).
|
The `.lastAccessed` property is always different between the two objects (to the precision possible via JavaScript's clock). Both `.creation` and `.creationIndex` are guaranteed to be the same. Stores MAY ignore or defer the `.lastAccessed` change at the cost of affecting how cookies are selected for automatic deletion (for example, least-recently-used, which is up to the store to implement).
|
||||||
|
|
||||||
Stores may wish to optimize changing the `.value` of the cookie in the store versus storing a new cookie. If the implementation doesn't define this method a stub that calls `putCookie(newCookie,cb)` will be added to the store object.
|
Stores may wish to optimize changing the `.value` of the cookie in the store versus storing a new cookie. If the implementation doesn't define this method, a stub that calls [`putCookie`](#storeputcookiecookie-callbackerr) is added to the store object.
|
||||||
|
|
||||||
The `newCookie` and `oldCookie` objects MUST NOT be modified.
|
The `newCookie` and `oldCookie` objects MUST NOT be modified.
|
||||||
|
|
||||||
Pass an error if the newCookie cannot be stored.
|
Pass an error if the newCookie cannot be stored.
|
||||||
|
|
||||||
### `store.removeCookie(domain, path, key, cb(err))`
|
#### `store.removeCookie(domain, path, key, callback(err))`
|
||||||
|
|
||||||
Remove a cookie from the store (see notes on `findCookie` about the uniqueness constraint).
|
Remove a cookie from the store (see notes on [`findCookie`](#storefindcookiedomain-path-key-callbackerr-cookie) about the uniqueness constraint).
|
||||||
|
|
||||||
The implementation MUST NOT pass an error if the cookie doesn't exist; only pass an error due to the failure to remove an existing cookie.
|
The implementation MUST NOT pass an error if the cookie doesn't exist, and only pass an error due to the failure to remove an existing cookie.
|
||||||
|
|
||||||
### `store.removeCookies(domain, path, cb(err))`
|
#### `store.removeCookies(domain, path, callback(err))`
|
||||||
|
|
||||||
Removes matching cookies from the store. The `path` parameter is optional, and if missing means all paths in a domain should be removed.
|
Removes matching cookies from the store. The `path` parameter is optional and if missing, means all paths in a domain should be removed.
|
||||||
|
|
||||||
Pass an error ONLY if removing any existing cookies failed.
|
Pass an error ONLY if removing any existing cookies failed.
|
||||||
|
|
||||||
### `store.removeAllCookies(cb(err))`
|
#### `store.removeAllCookies(callback(err))`
|
||||||
|
|
||||||
_Optional_. Removes all cookies from the store.
|
_Optional_. Removes all cookies from the store.
|
||||||
|
|
||||||
Pass an error if one or more cookies can't be removed.
|
Pass an error if one or more cookies can't be removed.
|
||||||
|
|
||||||
**Note**: New method as of `tough-cookie` version 2.5, so not all Stores will implement this, plus some stores may choose not to implement this.
|
#### `store.getAllCookies(callback(err, cookies))`
|
||||||
|
|
||||||
### `store.getAllCookies(cb(err, cookies))`
|
_Optional_. Produces an `Array` of all cookies during [`jar.serialize()`](#serializecallbackerr-serializedobject). The items in the array can be true `Cookie` objects or generic `Object`s with the [Serialization Format](#serialization-format) data structure.
|
||||||
|
|
||||||
_Optional_. Produces an `Array` of all cookies during `jar.serialize()`. The items in the array can be true `Cookie` objects or generic `Object`s with the [Serialization Format] data structure.
|
Cookies SHOULD be returned in creation order to preserve sorting via [`compareCookie()`](#cookiecomparea-b). For reference, `MemoryCookieStore` sorts by `.creationIndex` since it uses true `Cookie` objects internally. If you don't return the cookies in creation order, they'll still be sorted by creation time, but this only has a precision of 1-ms. See `cookieCompare` for more detail.
|
||||||
|
|
||||||
Cookies SHOULD be returned in creation order to preserve sorting via `compareCookies()`. For reference, `MemoryCookieStore` will sort by `.creationIndex` since it uses true `Cookie` objects internally. If you don't return the cookies in creation order, they'll still be sorted by creation time, but this only has a precision of 1ms. See `compareCookies` for more detail.
|
|
||||||
|
|
||||||
Pass an error if retrieval fails.
|
Pass an error if retrieval fails.
|
||||||
|
|
||||||
**Note**: not all Stores can implement this due to technical limitations, so it is optional.
|
**Note**: Not all Stores can implement this due to technical limitations, so it is optional.
|
||||||
|
|
||||||
## MemoryCookieStore
|
### MemoryCookieStore
|
||||||
|
|
||||||
Inherits from `Store`.
|
Inherits from `Store`.
|
||||||
|
|
||||||
A just-in-memory CookieJar synchronous store implementation, used by default. Despite being a synchronous implementation, it's usable with both the synchronous and asynchronous forms of the `CookieJar` API. Supports serialization, `getAllCookies`, and `removeAllCookies`.
|
A just-in-memory CookieJar synchronous store implementation, used by default. Despite being a synchronous implementation, it's usable with both the synchronous and asynchronous forms of the `CookieJar` API. Supports serialization, `getAllCookies`, and `removeAllCookies`.
|
||||||
|
|
||||||
## Community Cookie Stores
|
### Community Cookie Stores
|
||||||
|
|
||||||
These are some Store implementations authored and maintained by the community. They aren't official and we don't vouch for them but you may be interested to have a look:
|
These are some Store implementations authored and maintained by the community. They aren't official and we don't vouch for them but you may be interested to have a look:
|
||||||
|
|
||||||
|
@ -467,10 +480,9 @@ These are some Store implementations authored and maintained by the community. T
|
||||||
- [`tough-cookie-filestore`](https://github.com/mitsuru/tough-cookie-filestore): JSON on disk
|
- [`tough-cookie-filestore`](https://github.com/mitsuru/tough-cookie-filestore): JSON on disk
|
||||||
- [`tough-cookie-web-storage-store`](https://github.com/exponentjs/tough-cookie-web-storage-store): DOM localStorage and sessionStorage
|
- [`tough-cookie-web-storage-store`](https://github.com/exponentjs/tough-cookie-web-storage-store): DOM localStorage and sessionStorage
|
||||||
|
|
||||||
|
## Serialization Format
|
||||||
|
|
||||||
# Serialization Format
|
**NOTE**: If you want to have custom `Cookie` properties serialized, add the property name to `Cookie.serializableProperties`.
|
||||||
|
|
||||||
**NOTE**: if you want to have custom `Cookie` properties serialized, add the property name to `Cookie.serializableProperties`.
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
{
|
{
|
||||||
|
@ -496,57 +508,59 @@ These are some Store implementations authored and maintained by the community. T
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
# RFC6265bis
|
## RFC 6265bis
|
||||||
|
|
||||||
Support for RFC6265bis revision 02 is being developed. Since this is a bit of an omnibus revision to the RFC6252, support is broken up into the functional areas.
|
Support for RFC 6265bis revision 02 is being developed. Since this is a bit of an omnibus revision to the RFC 6252, support is broken up into the functional areas.
|
||||||
|
|
||||||
## Leave Secure Cookies Alone
|
### Leave Secure Cookies Alone
|
||||||
|
|
||||||
Not yet supported.
|
Not yet supported.
|
||||||
|
|
||||||
This change makes it so that if a cookie is sent from the server to the client with a `Secure` attribute, the channel must also be secure or the cookie is ignored.
|
This change makes it so that if a cookie is sent from the server to the client with a `Secure` attribute, the channel must also be secure or the cookie is ignored.
|
||||||
|
|
||||||
## SameSite Cookies
|
### SameSite Cookies
|
||||||
|
|
||||||
Supported.
|
Supported.
|
||||||
|
|
||||||
This change makes it possible for servers, and supporting clients, to mitigate certain types of CSRF attacks by disallowing `SameSite` cookies from being sent cross-origin.
|
This change makes it possible for servers, and supporting clients, to mitigate certain types of CSRF attacks by disallowing `SameSite` cookies from being sent cross-origin.
|
||||||
|
|
||||||
On the Cookie object itself, you can get/set the `.sameSite` attribute, which will be serialized into the `SameSite=` cookie attribute. When unset or `undefined`, no `SameSite=` attribute will be serialized. The valid values of this attribute are `'none'`, `'lax'`, or `'strict'`. Other values will be serialized as-is.
|
On the Cookie object itself, you can get or set the `.sameSite` attribute, which is serialized into the `SameSite=` cookie attribute. When unset or `undefined`, no `SameSite=` attribute is serialized. The valid values of this attribute are `'none'`, `'lax'`, or `'strict'`. Other values are serialized as-is.
|
||||||
|
|
||||||
When parsing cookies with a `SameSite` cookie attribute, values other than `'lax'` or `'strict'` are parsed as `'none'`. For example, `SomeCookie=SomeValue; SameSite=garbage` will parse so that `cookie.sameSite === 'none'`.
|
When parsing cookies with a `SameSite` cookie attribute, values other than `'lax'` or `'strict'` are parsed as `'none'`. For example, `SomeCookie=SomeValue; SameSite=garbage` parses so that `cookie.sameSite === 'none'`.
|
||||||
|
|
||||||
In order to support SameSite cookies, you must provide a `sameSiteContext` option to _both_ `setCookie` and `getCookies`. Valid values for this option are just like for the Cookie object, but have particular meanings:
|
In order to support SameSite cookies, you must provide a `sameSiteContext` option to _both_ `setCookie` and `getCookies`. Valid values for this option are just like for the Cookie object, but have particular meanings:
|
||||||
1. `'strict'` mode - If the request is on the same "site for cookies" (see the RFC draft for what this means), pass this option to add a layer of defense against CSRF.
|
|
||||||
2. `'lax'` mode - If the request is from another site, _but_ is directly because of navigation by the user, e.g., `<link type=prefetch>` or `<a href="...">`, pass `sameSiteContext: 'lax'`.
|
1. `'strict'` mode - If the request is on the same "site for cookies" (see the RFC draft for more information), pass this option to add a layer of defense against CSRF.
|
||||||
|
2. `'lax'` mode - If the request is from another site, _but_ is directly because of navigation by the user, such as, `<link type=prefetch>` or `<a href="...">`, pass `sameSiteContext: 'lax'`.
|
||||||
3. `'none'` - Otherwise, pass `sameSiteContext: 'none'` (this indicates a cross-origin request).
|
3. `'none'` - Otherwise, pass `sameSiteContext: 'none'` (this indicates a cross-origin request).
|
||||||
4. unset/`undefined` - SameSite **will not** be enforced! This can be a valid use-case for when CSRF isn't in the threat model of the system being built.
|
4. unset/`undefined` - SameSite **is not** be enforced! This can be a valid use-case for when CSRF isn't in the threat model of the system being built.
|
||||||
|
|
||||||
It is highly recommended that you read RFC 6265bis for fine details on SameSite cookies. In particular [Section 8.8](https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-02#section-8.8) discusses security considerations and defense in depth.
|
It is highly recommended that you read RFC 6265bis for fine details on SameSite cookies. In particular [Section 8.8](https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-02##section-8.8) discusses security considerations and defense in depth.
|
||||||
|
|
||||||
## Cookie Prefixes
|
### Cookie Prefixes
|
||||||
|
|
||||||
Supported.
|
Supported.
|
||||||
|
|
||||||
Cookie prefixes are a way to indicate that a given cookie was set with a set of attributes simply by inspecting the first few characters of the cookie's name.
|
Cookie prefixes are a way to indicate that a given cookie was set with a set of attributes simply by inspecting the first few characters of the cookie's name.
|
||||||
|
|
||||||
Cookie prefixes are defined in [Section 4.1.3 of 6265bis](https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.3). Two prefixes are defined:
|
Cookie prefixes are defined in [Section 4.1.3 of 6265bis](https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03##section-4.1.3).
|
||||||
|
|
||||||
1. `"__Secure-" Prefix`: If a cookie's name begins with a case-sensitive match for the string "__Secure-", then the cookie will have been set with a "Secure" attribute.
|
Two prefixes are defined:
|
||||||
2. `"__Host-" Prefix`: If a cookie's name begins with a case-sensitive match for the string "__Host-", then the cookie will have been set with a "Secure" attribute, a "Path" attribute with a value of "/", and no "Domain" attribute.
|
|
||||||
|
|
||||||
If `prefixSecurity` is enabled for `CookieJar`, then cookies that match the prefixes defined above but do not obey the attribute restrictions will not be added.
|
1. `"__Secure-" Prefix`: If a cookie's name begins with a case-sensitive match for the string "\_\_Secure-", then the cookie was set with a "Secure" attribute.
|
||||||
|
2. `"__Host-" Prefix`: If a cookie's name begins with a case-sensitive match for the string "\_\_Host-", then the cookie was set with a "Secure" attribute, a "Path" attribute with a value of "/", and no "Domain" attribute.
|
||||||
|
|
||||||
You can define this functionality by passing in `prefixSecurity` option to `CookieJar`. It can be one of 3 values:
|
If `prefixSecurity` is enabled for `CookieJar`, then cookies that match the prefixes defined above but do not obey the attribute restrictions are not added.
|
||||||
|
|
||||||
1. `silent`: Enable cookie prefix checking but silently fail to add the cookie if conditions not met. Default.
|
You can define this functionality by passing in the `prefixSecurity` option to `CookieJar`. It can be one of 3 values:
|
||||||
2. `strict`: Enable cookie prefix checking and error out if conditions not met.
|
|
||||||
|
1. `silent`: Enable cookie prefix checking but silently fail to add the cookie if conditions are not met. Default.
|
||||||
|
2. `strict`: Enable cookie prefix checking and error out if conditions are not met.
|
||||||
3. `unsafe-disabled`: Disable cookie prefix checking.
|
3. `unsafe-disabled`: Disable cookie prefix checking.
|
||||||
|
|
||||||
Note that if `ignoreError` is passed in as `true` then the error will be silent regardless of `prefixSecurity` option (assuming it's enabled).
|
Note that if `ignoreError` is passed in as `true` then the error is silent regardless of the `prefixSecurity` option (assuming it's enabled).
|
||||||
|
|
||||||
|
## Copyright and License
|
||||||
# Copyright and License
|
|
||||||
|
|
||||||
BSD-3-Clause:
|
BSD-3-Clause:
|
||||||
|
|
||||||
|
|
|
@ -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,5 +0,0 @@
|
||||||
node-tough-cookie (4.0.0-ok1) yangtze; urgency=medium
|
|
||||||
|
|
||||||
* Build for openKylin.
|
|
||||||
|
|
||||||
-- zhouganqing <zhouganqing@kylinos.cn> Tue, 21 Feb 2023 10:42:59 +0800
|
|
|
@ -1,33 +0,0 @@
|
||||||
Source: node-tough-cookie
|
|
||||||
Maintainer: OpenKylin Developers <packaging@lists.openkylin.top>
|
|
||||||
Section: javascript
|
|
||||||
Testsuite: autopkgtest-pkg-nodejs
|
|
||||||
Priority: optional
|
|
||||||
Build-Depends:
|
|
||||||
debhelper-compat (= 13)
|
|
||||||
, nodejs
|
|
||||||
, node-async <!nocheck>
|
|
||||||
, node-psl <!nocheck>
|
|
||||||
, node-punycode <!nocheck>
|
|
||||||
, node-universalify <!nocheck>
|
|
||||||
, node-vows <!nocheck>
|
|
||||||
, dh-sequence-nodejs
|
|
||||||
Standards-Version: 4.5.1
|
|
||||||
Vcs-Browser: https://gitee.com/openkylin/node-tough-cookie
|
|
||||||
Vcs-Git: https://gitee.com/openkylin/node-tough-cookie.git
|
|
||||||
Homepage: https://github.com/salesforce/tough-cookie
|
|
||||||
Rules-Requires-Root: no
|
|
||||||
|
|
||||||
Package: node-tough-cookie
|
|
||||||
Architecture: all
|
|
||||||
Depends:
|
|
||||||
${misc:Depends}
|
|
||||||
, node-psl
|
|
||||||
, node-punycode
|
|
||||||
, node-universalify
|
|
||||||
Provides: ${nodejs:Provides}
|
|
||||||
Description: RFC6265 Cookies and Cookie Jar for node.js
|
|
||||||
This library just provides a way to read and write RFC6265 HTTP cookie
|
|
||||||
headers.
|
|
||||||
.
|
|
||||||
Node.js is an event-based server-side JavaScript engine.
|
|
|
@ -1,69 +0,0 @@
|
||||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
|
||||||
Upstream-Name: tough-cookie
|
|
||||||
Upstream-Contact: https://github.com/salesforce/tough-cookie/issues
|
|
||||||
Source: https://github.com/salesforce/tough-cookie
|
|
||||||
https://registry.npmjs.org/@types/tough-cookie
|
|
||||||
Files-Excluded: lib/pubsuffix.js
|
|
||||||
|
|
||||||
Files: *
|
|
||||||
Copyright: 2015, Salesforce.com, Inc.
|
|
||||||
Jeremy Stashewsky <jstashewsky@salesforce.com>
|
|
||||||
License: BSD-3-Clause
|
|
||||||
|
|
||||||
Files: typestough-cookie/*
|
|
||||||
Copyright: Microsoft Corporation
|
|
||||||
License: Expat
|
|
||||||
|
|
||||||
Files: debian/*
|
|
||||||
Copyright: 2017 Pirate Praveen <praveen@debian.org>
|
|
||||||
2019, Xavier Guimard <yadd@debian.org>
|
|
||||||
License: BSD-3-Clause
|
|
||||||
|
|
||||||
Files: debian/tests/test_modules/string.prototype.repeat/*
|
|
||||||
Copyright: Mathias Bynens <http://mathiasbynens.be/>
|
|
||||||
License: Expat
|
|
||||||
|
|
||||||
License: BSD-3-Clause
|
|
||||||
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 the University 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 HOLDERS 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.
|
|
||||||
|
|
||||||
License: Expat
|
|
||||||
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.
|
|
|
@ -1 +0,0 @@
|
||||||
README.md
|
|
|
@ -1 +0,0 @@
|
||||||
*
|
|
|
@ -1,8 +0,0 @@
|
||||||
#!/usr/bin/make -f
|
|
||||||
# -*- makefile -*-
|
|
||||||
|
|
||||||
# Uncomment this to turn on verbose mode.
|
|
||||||
#export DH_VERBOSE=1
|
|
||||||
|
|
||||||
%:
|
|
||||||
dh $@
|
|
|
@ -1 +0,0 @@
|
||||||
3.0 (native)
|
|
|
@ -1,4 +0,0 @@
|
||||||
# Data
|
|
||||||
source-is-missing lib/cookie.js line length is 1188 characters (>512)
|
|
||||||
source-contains-prebuilt-javascript-object lib/cookie.js line length is 1188 characters (>512)
|
|
||||||
very-long-line-length-in-source-file lib/cookie.js line length is 1188 characters (>512)
|
|
|
@ -1,2 +0,0 @@
|
||||||
debian/tests/test_modules
|
|
||||||
test
|
|
|
@ -1 +0,0 @@
|
||||||
NODE_PATH=debian/tests/test_modules vows test/*_test.js
|
|
|
@ -1,67 +0,0 @@
|
||||||
{
|
|
||||||
"_from": "string.prototype.repeat@^0.2.0",
|
|
||||||
"_id": "string.prototype.repeat@0.2.0",
|
|
||||||
"_inBundle": false,
|
|
||||||
"_integrity": "sha1-q6Nt4I3O5qWjN9SbLqHaGyj8Ds8=",
|
|
||||||
"_location": "/string.prototype.repeat",
|
|
||||||
"_phantomChildren": {},
|
|
||||||
"_requested": {
|
|
||||||
"type": "range",
|
|
||||||
"registry": true,
|
|
||||||
"raw": "string.prototype.repeat@^0.2.0",
|
|
||||||
"name": "string.prototype.repeat",
|
|
||||||
"escapedName": "string.prototype.repeat",
|
|
||||||
"rawSpec": "^0.2.0",
|
|
||||||
"saveSpec": null,
|
|
||||||
"fetchSpec": "^0.2.0"
|
|
||||||
},
|
|
||||||
"_requiredBy": [
|
|
||||||
"#DEV:/"
|
|
||||||
],
|
|
||||||
"_resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-0.2.0.tgz",
|
|
||||||
"_shasum": "aba36de08dcee6a5a337d49b2ea1da1b28fc0ecf",
|
|
||||||
"_spec": "string.prototype.repeat@^0.2.0",
|
|
||||||
"_where": "/home/xavier/dev/debian/src/pkg-js/packages/node-tough-cookie",
|
|
||||||
"author": {
|
|
||||||
"name": "Mathias Bynens",
|
|
||||||
"url": "http://mathiasbynens.be/"
|
|
||||||
},
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/mathiasbynens/String.prototype.repeat/issues"
|
|
||||||
},
|
|
||||||
"bundleDependencies": false,
|
|
||||||
"deprecated": false,
|
|
||||||
"description": "A robust & optimized `String.prototype.repeat` polyfill, based on the ECMAScript 6 specification.",
|
|
||||||
"directories": {
|
|
||||||
"test": "tests"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"LICENSE-MIT.txt",
|
|
||||||
"repeat.js"
|
|
||||||
],
|
|
||||||
"homepage": "http://mths.be/repeat",
|
|
||||||
"keywords": [
|
|
||||||
"string",
|
|
||||||
"repeat",
|
|
||||||
"es6",
|
|
||||||
"ecmascript",
|
|
||||||
"polyfill"
|
|
||||||
],
|
|
||||||
"licenses": [
|
|
||||||
{
|
|
||||||
"type": "MIT",
|
|
||||||
"url": "http://mths.be/mit"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"main": "repeat.js",
|
|
||||||
"name": "string.prototype.repeat",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/mathiasbynens/String.prototype.repeat.git"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"cover": "istanbul cover --report html --verbose --dir coverage tests/tests.js",
|
|
||||||
"test": "node tests/tests.js"
|
|
||||||
},
|
|
||||||
"version": "0.2.0"
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*! http://mths.be/repeat v0.2.0 by @mathias */
|
|
||||||
if (!String.prototype.repeat) {
|
|
||||||
(function() {
|
|
||||||
'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
|
|
||||||
var defineProperty = (function() {
|
|
||||||
// IE 8 only supports `Object.defineProperty` on DOM elements
|
|
||||||
try {
|
|
||||||
var object = {};
|
|
||||||
var $defineProperty = Object.defineProperty;
|
|
||||||
var result = $defineProperty(object, object, object) && $defineProperty;
|
|
||||||
} catch(error) {}
|
|
||||||
return result;
|
|
||||||
}());
|
|
||||||
var repeat = function(count) {
|
|
||||||
if (this == null) {
|
|
||||||
throw TypeError();
|
|
||||||
}
|
|
||||||
var string = String(this);
|
|
||||||
// `ToInteger`
|
|
||||||
var n = count ? Number(count) : 0;
|
|
||||||
if (n != n) { // better `isNaN`
|
|
||||||
n = 0;
|
|
||||||
}
|
|
||||||
// Account for out-of-bounds indices
|
|
||||||
if (n < 0 || n == Infinity) {
|
|
||||||
throw RangeError();
|
|
||||||
}
|
|
||||||
var result = '';
|
|
||||||
while (n) {
|
|
||||||
if (n % 2 == 1) {
|
|
||||||
result += string;
|
|
||||||
}
|
|
||||||
if (n > 1) {
|
|
||||||
string += string;
|
|
||||||
}
|
|
||||||
n >>= 1;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
if (defineProperty) {
|
|
||||||
defineProperty(String.prototype, 'repeat', {
|
|
||||||
'value': repeat,
|
|
||||||
'configurable': true,
|
|
||||||
'writable': true
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
String.prototype.repeat = repeat;
|
|
||||||
}
|
|
||||||
}());
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
version=4
|
|
||||||
opts=\
|
|
||||||
repacksuffix=+dfsg,\
|
|
||||||
repack,compression=xz,\
|
|
||||||
dversionmangle=auto,\
|
|
||||||
filenamemangle=s/.*\/v?([\d\.-]+)\.tar\.gz/node-tough-cookie-$1.tar.gz/ \
|
|
||||||
https://github.com/salesforce/tough-cookie/tags .*/archive/v?([\d\.]+).tar.gz
|
|
||||||
|
|
||||||
# It is not recommended use npmregistry. Please investigate more.
|
|
||||||
# Take a look at https://wiki.debian.org/debian/watch/
|
|
||||||
opts="searchmode=plain,component=typestough-cookie,pgpmode=none" \
|
|
||||||
https://registry.npmjs.org/@types/tough-cookie https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-([\d\.]+)@ARCHIVE_EXT@ ignore
|
|
133
lib/cookie.js
133
lib/cookie.js
|
@ -1,5 +1,5 @@
|
||||||
/*!
|
/*!
|
||||||
* Copyright (c) 2015, Salesforce.com, Inc.
|
* Copyright (c) 2015-2020, Salesforce.com, Inc.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -29,15 +29,16 @@
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
"use strict";
|
"use strict";
|
||||||
const punycode = require("punycode");
|
const punycode = require("punycode/");
|
||||||
const urlParse = require("url").parse;
|
const urlParse = require("url-parse");
|
||||||
const util = require("util");
|
|
||||||
const pubsuffix = require("./pubsuffix-psl");
|
const pubsuffix = require("./pubsuffix-psl");
|
||||||
const Store = require("./store").Store;
|
const Store = require("./store").Store;
|
||||||
const MemoryCookieStore = require("./memstore").MemoryCookieStore;
|
const MemoryCookieStore = require("./memstore").MemoryCookieStore;
|
||||||
const pathMatch = require("./pathMatch").pathMatch;
|
const pathMatch = require("./pathMatch").pathMatch;
|
||||||
|
const validators = require("./validators.js");
|
||||||
const VERSION = require("./version");
|
const VERSION = require("./version");
|
||||||
const { fromCallback } = require("universalify");
|
const { fromCallback } = require("universalify");
|
||||||
|
const { getCustomInspectSymbol } = require("./utilHelper");
|
||||||
|
|
||||||
// From RFC6265 S4.1.1
|
// From RFC6265 S4.1.1
|
||||||
// note that it excludes \x3B ";"
|
// note that it excludes \x3B ";"
|
||||||
|
@ -79,6 +80,7 @@ const SAME_SITE_CONTEXT_VAL_ERR =
|
||||||
'Invalid sameSiteContext option for getCookies(); expected one of "strict", "lax", or "none"';
|
'Invalid sameSiteContext option for getCookies(); expected one of "strict", "lax", or "none"';
|
||||||
|
|
||||||
function checkSameSiteContext(value) {
|
function checkSameSiteContext(value) {
|
||||||
|
validators.validate(validators.isNonEmptyString(value), value);
|
||||||
const context = String(value).toLowerCase();
|
const context = String(value).toLowerCase();
|
||||||
if (context === "none" || context === "lax" || context === "strict") {
|
if (context === "none" || context === "lax" || context === "strict") {
|
||||||
return context;
|
return context;
|
||||||
|
@ -97,7 +99,23 @@ const PrefixSecurityEnum = Object.freeze({
|
||||||
// * all capturing groups converted to non-capturing -- "(?:)"
|
// * all capturing groups converted to non-capturing -- "(?:)"
|
||||||
// * support for IPv6 Scoped Literal ("%eth1") removed
|
// * support for IPv6 Scoped Literal ("%eth1") removed
|
||||||
// * lowercase hexadecimal only
|
// * lowercase hexadecimal only
|
||||||
var IP_REGEX_LOWERCASE =/(?:^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$)|(?:^(?:(?:[a-f\d]{1,4}:){7}(?:[a-f\d]{1,4}|:)|(?:[a-f\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|:[a-f\d]{1,4}|:)|(?:[a-f\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,2}|:)|(?:[a-f\d]{1,4}:){4}(?:(?::[a-f\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,3}|:)|(?:[a-f\d]{1,4}:){3}(?:(?::[a-f\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,4}|:)|(?:[a-f\d]{1,4}:){2}(?:(?::[a-f\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,5}|:)|(?:[a-f\d]{1,4}:){1}(?:(?::[a-f\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,6}|:)|(?::(?:(?::[a-f\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,7}|:)))$)/;
|
const IP_REGEX_LOWERCASE = /(?:^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$)|(?:^(?:(?:[a-f\d]{1,4}:){7}(?:[a-f\d]{1,4}|:)|(?:[a-f\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|:[a-f\d]{1,4}|:)|(?:[a-f\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,2}|:)|(?:[a-f\d]{1,4}:){4}(?:(?::[a-f\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,3}|:)|(?:[a-f\d]{1,4}:){3}(?:(?::[a-f\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,4}|:)|(?:[a-f\d]{1,4}:){2}(?:(?::[a-f\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,5}|:)|(?:[a-f\d]{1,4}:){1}(?:(?::[a-f\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,6}|:)|(?::(?:(?::[a-f\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,7}|:)))$)/;
|
||||||
|
const IP_V6_REGEX = `
|
||||||
|
\\[?(?:
|
||||||
|
(?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|
|
||||||
|
(?:[a-fA-F\\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|:[a-fA-F\\d]{1,4}|:)|
|
||||||
|
(?:[a-fA-F\\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,2}|:)|
|
||||||
|
(?:[a-fA-F\\d]{1,4}:){4}(?:(?::[a-fA-F\\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,3}|:)|
|
||||||
|
(?:[a-fA-F\\d]{1,4}:){3}(?:(?::[a-fA-F\\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,4}|:)|
|
||||||
|
(?:[a-fA-F\\d]{1,4}:){2}(?:(?::[a-fA-F\\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,5}|:)|
|
||||||
|
(?:[a-fA-F\\d]{1,4}:){1}(?:(?::[a-fA-F\\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,6}|:)|
|
||||||
|
(?::(?:(?::[a-fA-F\\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,7}|:))
|
||||||
|
)(?:%[0-9a-zA-Z]{1,})?\\]?
|
||||||
|
`
|
||||||
|
.replace(/\s*\/\/.*$/gm, "")
|
||||||
|
.replace(/\n/g, "")
|
||||||
|
.trim();
|
||||||
|
const IP_V6_REGEX_OBJECT = new RegExp(`^${IP_V6_REGEX}$`);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parses a Natural number (i.e., non-negative integer) with either the
|
* Parses a Natural number (i.e., non-negative integer) with either the
|
||||||
|
@ -301,6 +319,7 @@ function parseDate(str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDate(date) {
|
function formatDate(date) {
|
||||||
|
validators.validate(validators.isDate(date), date);
|
||||||
return date.toUTCString();
|
return date.toUTCString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,6 +330,10 @@ function canonicalDomain(str) {
|
||||||
}
|
}
|
||||||
str = str.trim().replace(/^\./, ""); // S4.1.2.3 & S5.2.3: ignore leading .
|
str = str.trim().replace(/^\./, ""); // S4.1.2.3 & S5.2.3: ignore leading .
|
||||||
|
|
||||||
|
if (IP_V6_REGEX_OBJECT.test(str)) {
|
||||||
|
str = str.replace("[", "").replace("]", "");
|
||||||
|
}
|
||||||
|
|
||||||
// convert to IDN if any non-ASCII characters
|
// convert to IDN if any non-ASCII characters
|
||||||
if (punycode && /[^\u0001-\u007f]/.test(str)) {
|
if (punycode && /[^\u0001-\u007f]/.test(str)) {
|
||||||
str = punycode.toASCII(str);
|
str = punycode.toASCII(str);
|
||||||
|
@ -345,7 +368,7 @@ function domainMatch(str, domStr, canonicalize) {
|
||||||
/* " o All of the following [three] conditions hold:" */
|
/* " o All of the following [three] conditions hold:" */
|
||||||
|
|
||||||
/* "* The domain string is a suffix of the string" */
|
/* "* The domain string is a suffix of the string" */
|
||||||
const idx = str.indexOf(domStr);
|
const idx = str.lastIndexOf(domStr);
|
||||||
if (idx <= 0) {
|
if (idx <= 0) {
|
||||||
return false; // it's a non-match (-1) or prefix (0)
|
return false; // it's a non-match (-1) or prefix (0)
|
||||||
}
|
}
|
||||||
|
@ -359,7 +382,7 @@ function domainMatch(str, domStr, canonicalize) {
|
||||||
|
|
||||||
/* " * The last character of the string that is not included in the
|
/* " * The last character of the string that is not included in the
|
||||||
* domain string is a %x2E (".") character." */
|
* domain string is a %x2E (".") character." */
|
||||||
if (str.substr(idx-1,1) !== '.') {
|
if (str.substr(idx - 1, 1) !== ".") {
|
||||||
return false; // doesn't align on "."
|
return false; // doesn't align on "."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,6 +426,7 @@ function defaultPath(path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function trimTerminator(str) {
|
function trimTerminator(str) {
|
||||||
|
if (validators.isEmptyString(str)) return str;
|
||||||
for (let t = 0; t < TERMINATORS.length; t++) {
|
for (let t = 0; t < TERMINATORS.length; t++) {
|
||||||
const terminatorIdx = str.indexOf(TERMINATORS[t]);
|
const terminatorIdx = str.indexOf(TERMINATORS[t]);
|
||||||
if (terminatorIdx !== -1) {
|
if (terminatorIdx !== -1) {
|
||||||
|
@ -415,6 +439,7 @@ function trimTerminator(str) {
|
||||||
|
|
||||||
function parseCookiePair(cookiePair, looseMode) {
|
function parseCookiePair(cookiePair, looseMode) {
|
||||||
cookiePair = trimTerminator(cookiePair);
|
cookiePair = trimTerminator(cookiePair);
|
||||||
|
validators.validate(validators.isString(cookiePair), cookiePair);
|
||||||
|
|
||||||
let firstEq = cookiePair.indexOf("=");
|
let firstEq = cookiePair.indexOf("=");
|
||||||
if (looseMode) {
|
if (looseMode) {
|
||||||
|
@ -454,6 +479,11 @@ function parse(str, options) {
|
||||||
if (!options || typeof options !== "object") {
|
if (!options || typeof options !== "object") {
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (validators.isEmptyString(str) || !validators.isString(str)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
str = str.trim();
|
str = str.trim();
|
||||||
|
|
||||||
// We use a regex to parse the "name-value-pair" part of S5.2
|
// We use a regex to parse the "name-value-pair" part of S5.2
|
||||||
|
@ -589,11 +619,11 @@ function parse(str, options) {
|
||||||
case "lax":
|
case "lax":
|
||||||
c.sameSite = "lax";
|
c.sameSite = "lax";
|
||||||
break;
|
break;
|
||||||
|
case "none":
|
||||||
|
c.sameSite = "none";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// RFC6265bis-02 S5.3.7 step 1:
|
c.sameSite = undefined;
|
||||||
// "If cookie-av's attribute-value is not a case-insensitive match
|
|
||||||
// for "Strict" or "Lax", ignore the "cookie-av"."
|
|
||||||
// This effectively sets it to 'none' from the prototype.
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -616,6 +646,7 @@ function parse(str, options) {
|
||||||
* @returns boolean
|
* @returns boolean
|
||||||
*/
|
*/
|
||||||
function isSecurePrefixConditionMet(cookie) {
|
function isSecurePrefixConditionMet(cookie) {
|
||||||
|
validators.validate(validators.isObject(cookie), cookie);
|
||||||
return !cookie.key.startsWith("__Secure-") || cookie.secure;
|
return !cookie.key.startsWith("__Secure-") || cookie.secure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -631,6 +662,7 @@ function isSecurePrefixConditionMet(cookie) {
|
||||||
* @returns boolean
|
* @returns boolean
|
||||||
*/
|
*/
|
||||||
function isHostPrefixConditionMet(cookie) {
|
function isHostPrefixConditionMet(cookie) {
|
||||||
|
validators.validate(validators.isObject(cookie));
|
||||||
return (
|
return (
|
||||||
!cookie.key.startsWith("__Host-") ||
|
!cookie.key.startsWith("__Host-") ||
|
||||||
(cookie.secure &&
|
(cookie.secure &&
|
||||||
|
@ -652,7 +684,7 @@ function jsonParse(str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromJSON(str) {
|
function fromJSON(str) {
|
||||||
if (!str) {
|
if (!str || validators.isEmptyString(str)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -698,6 +730,8 @@ function fromJSON(str) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function cookieCompare(a, b) {
|
function cookieCompare(a, b) {
|
||||||
|
validators.validate(validators.isObject(a), a);
|
||||||
|
validators.validate(validators.isObject(b), b);
|
||||||
let cmp = 0;
|
let cmp = 0;
|
||||||
|
|
||||||
// descending for length: b CMP a
|
// descending for length: b CMP a
|
||||||
|
@ -725,6 +759,7 @@ function cookieCompare(a, b) {
|
||||||
// Gives the permutation of all possible pathMatch()es of a given path. The
|
// Gives the permutation of all possible pathMatch()es of a given path. The
|
||||||
// array is in longest-to-shortest order. Handy for indexing.
|
// array is in longest-to-shortest order. Handy for indexing.
|
||||||
function permutePath(path) {
|
function permutePath(path) {
|
||||||
|
validators.validate(validators.isString(path));
|
||||||
if (path === "/") {
|
if (path === "/") {
|
||||||
return ["/"];
|
return ["/"];
|
||||||
}
|
}
|
||||||
|
@ -772,13 +807,14 @@ const cookieDefaults = {
|
||||||
pathIsDefault: null,
|
pathIsDefault: null,
|
||||||
creation: null,
|
creation: null,
|
||||||
lastAccessed: null,
|
lastAccessed: null,
|
||||||
sameSite: "none"
|
sameSite: undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
class Cookie {
|
class Cookie {
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
if (util.inspect.custom) {
|
const customInspectSymbol = getCustomInspectSymbol();
|
||||||
this[util.inspect.custom] = this.inspect;
|
if (customInspectSymbol) {
|
||||||
|
this[customInspectSymbol] = this.inspect;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.assign(this, cookieDefaults, options);
|
Object.assign(this, cookieDefaults, options);
|
||||||
|
@ -1060,9 +1096,13 @@ class CookieJar {
|
||||||
if (typeof options === "boolean") {
|
if (typeof options === "boolean") {
|
||||||
options = { rejectPublicSuffixes: options };
|
options = { rejectPublicSuffixes: options };
|
||||||
}
|
}
|
||||||
|
validators.validate(validators.isObject(options), options);
|
||||||
this.rejectPublicSuffixes = options.rejectPublicSuffixes;
|
this.rejectPublicSuffixes = options.rejectPublicSuffixes;
|
||||||
this.enableLooseMode = !!options.looseMode;
|
this.enableLooseMode = !!options.looseMode;
|
||||||
this.allowSpecialUseDomain = !!options.allowSpecialUseDomain;
|
this.allowSpecialUseDomain =
|
||||||
|
typeof options.allowSpecialUseDomain === "boolean"
|
||||||
|
? options.allowSpecialUseDomain
|
||||||
|
: true;
|
||||||
this.store = store || new MemoryCookieStore();
|
this.store = store || new MemoryCookieStore();
|
||||||
this.prefixSecurity = getNormalizedPrefixSecurity(options.prefixSecurity);
|
this.prefixSecurity = getNormalizedPrefixSecurity(options.prefixSecurity);
|
||||||
this._cloneSync = syncWrap("clone");
|
this._cloneSync = syncWrap("clone");
|
||||||
|
@ -1076,13 +1116,31 @@ class CookieJar {
|
||||||
}
|
}
|
||||||
|
|
||||||
setCookie(cookie, url, options, cb) {
|
setCookie(cookie, url, options, cb) {
|
||||||
|
validators.validate(validators.isNonEmptyString(url), cb, options);
|
||||||
let err;
|
let err;
|
||||||
|
|
||||||
|
if (validators.isFunction(url)) {
|
||||||
|
cb = url;
|
||||||
|
return cb(new Error("No URL was specified"));
|
||||||
|
}
|
||||||
|
|
||||||
const context = getCookieContext(url);
|
const context = getCookieContext(url);
|
||||||
if (typeof options === "function") {
|
if (validators.isFunction(options)) {
|
||||||
cb = options;
|
cb = options;
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validators.validate(validators.isFunction(cb), cb);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!validators.isNonEmptyString(cookie) &&
|
||||||
|
!validators.isObject(cookie) &&
|
||||||
|
cookie instanceof String &&
|
||||||
|
cookie.length == 0
|
||||||
|
) {
|
||||||
|
return cb(null);
|
||||||
|
}
|
||||||
|
|
||||||
const host = canonicalDomain(context.hostname);
|
const host = canonicalDomain(context.hostname);
|
||||||
const loose = options.loose || this.enableLooseMode;
|
const loose = options.loose || this.enableLooseMode;
|
||||||
|
|
||||||
|
@ -1119,8 +1177,11 @@ class CookieJar {
|
||||||
|
|
||||||
// S5.3 step 5: public suffixes
|
// S5.3 step 5: public suffixes
|
||||||
if (this.rejectPublicSuffixes && cookie.domain) {
|
if (this.rejectPublicSuffixes && cookie.domain) {
|
||||||
const suffix = pubsuffix.getPublicSuffix(cookie.cdomain());
|
const suffix = pubsuffix.getPublicSuffix(cookie.cdomain(), {
|
||||||
if (suffix == null) {
|
allowSpecialUseDomain: this.allowSpecialUseDomain,
|
||||||
|
ignoreError: options.ignoreError
|
||||||
|
});
|
||||||
|
if (suffix == null && !IP_V6_REGEX_OBJECT.test(cookie.domain)) {
|
||||||
// e.g. "com"
|
// e.g. "com"
|
||||||
err = new Error("Cookie has domain set to a public suffix");
|
err = new Error("Cookie has domain set to a public suffix");
|
||||||
return cb(options.ignoreError ? null : err);
|
return cb(options.ignoreError ? null : err);
|
||||||
|
@ -1163,7 +1224,11 @@ class CookieJar {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6252bis-02 S5.4 Step 13 & 14:
|
// 6252bis-02 S5.4 Step 13 & 14:
|
||||||
if (cookie.sameSite !== "none" && sameSiteContext) {
|
if (
|
||||||
|
cookie.sameSite !== "none" &&
|
||||||
|
cookie.sameSite !== undefined &&
|
||||||
|
sameSiteContext
|
||||||
|
) {
|
||||||
// "If the cookie's "same-site-flag" is not "None", and the cookie
|
// "If the cookie's "same-site-flag" is not "None", and the cookie
|
||||||
// is being set from a context whose "site for cookies" is not an
|
// is being set from a context whose "site for cookies" is not an
|
||||||
// exact match for request-uri's host's registered domain, then
|
// exact match for request-uri's host's registered domain, then
|
||||||
|
@ -1249,11 +1314,14 @@ class CookieJar {
|
||||||
|
|
||||||
// RFC6365 S5.4
|
// RFC6365 S5.4
|
||||||
getCookies(url, options, cb) {
|
getCookies(url, options, cb) {
|
||||||
|
validators.validate(validators.isNonEmptyString(url), cb, url);
|
||||||
const context = getCookieContext(url);
|
const context = getCookieContext(url);
|
||||||
if (typeof options === "function") {
|
if (validators.isFunction(options)) {
|
||||||
cb = options;
|
cb = options;
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
|
validators.validate(validators.isObject(options), cb, options);
|
||||||
|
validators.validate(validators.isFunction(cb), cb);
|
||||||
|
|
||||||
const host = canonicalDomain(context.hostname);
|
const host = canonicalDomain(context.hostname);
|
||||||
const path = context.pathname || "/";
|
const path = context.pathname || "/";
|
||||||
|
@ -1369,6 +1437,7 @@ class CookieJar {
|
||||||
|
|
||||||
getCookieString(...args) {
|
getCookieString(...args) {
|
||||||
const cb = args.pop();
|
const cb = args.pop();
|
||||||
|
validators.validate(validators.isFunction(cb), cb);
|
||||||
const next = function(err, cookies) {
|
const next = function(err, cookies) {
|
||||||
if (err) {
|
if (err) {
|
||||||
cb(err);
|
cb(err);
|
||||||
|
@ -1388,6 +1457,7 @@ class CookieJar {
|
||||||
|
|
||||||
getSetCookieStrings(...args) {
|
getSetCookieStrings(...args) {
|
||||||
const cb = args.pop();
|
const cb = args.pop();
|
||||||
|
validators.validate(validators.isFunction(cb), cb);
|
||||||
const next = function(err, cookies) {
|
const next = function(err, cookies) {
|
||||||
if (err) {
|
if (err) {
|
||||||
cb(err);
|
cb(err);
|
||||||
|
@ -1405,8 +1475,9 @@ class CookieJar {
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(cb) {
|
serialize(cb) {
|
||||||
|
validators.validate(validators.isFunction(cb), cb);
|
||||||
let type = this.store.constructor.name;
|
let type = this.store.constructor.name;
|
||||||
if (type === "Object") {
|
if (validators.isObject(type)) {
|
||||||
type = null;
|
type = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1422,6 +1493,9 @@ class CookieJar {
|
||||||
|
|
||||||
// CookieJar configuration:
|
// CookieJar configuration:
|
||||||
rejectPublicSuffixes: !!this.rejectPublicSuffixes,
|
rejectPublicSuffixes: !!this.rejectPublicSuffixes,
|
||||||
|
enableLooseMode: !!this.enableLooseMode,
|
||||||
|
allowSpecialUseDomain: !!this.allowSpecialUseDomain,
|
||||||
|
prefixSecurity: getNormalizedPrefixSecurity(this.prefixSecurity),
|
||||||
|
|
||||||
// this gets filled from getAllCookies:
|
// this gets filled from getAllCookies:
|
||||||
cookies: []
|
cookies: []
|
||||||
|
@ -1524,6 +1598,7 @@ class CookieJar {
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAllCookies(cb) {
|
removeAllCookies(cb) {
|
||||||
|
validators.validate(validators.isFunction(cb), cb);
|
||||||
const store = this.store;
|
const store = this.store;
|
||||||
|
|
||||||
// Check that the store implements its own removeAllCookies(). The default
|
// Check that the store implements its own removeAllCookies(). The default
|
||||||
|
@ -1577,6 +1652,7 @@ class CookieJar {
|
||||||
cb = store;
|
cb = store;
|
||||||
store = null;
|
store = null;
|
||||||
}
|
}
|
||||||
|
validators.validate(validators.isFunction(cb), cb);
|
||||||
|
|
||||||
let serialized;
|
let serialized;
|
||||||
if (typeof strOrObj === "string") {
|
if (typeof strOrObj === "string") {
|
||||||
|
@ -1588,7 +1664,12 @@ class CookieJar {
|
||||||
serialized = strOrObj;
|
serialized = strOrObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
const jar = new CookieJar(store, serialized.rejectPublicSuffixes);
|
const jar = new CookieJar(store, {
|
||||||
|
rejectPublicSuffixes: serialized.rejectPublicSuffixes,
|
||||||
|
looseMode: serialized.enableLooseMode,
|
||||||
|
allowSpecialUseDomain: serialized.allowSpecialUseDomain,
|
||||||
|
prefixSecurity: serialized.prefixSecurity
|
||||||
|
});
|
||||||
jar._importCookies(serialized, err => {
|
jar._importCookies(serialized, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return cb(err);
|
return cb(err);
|
||||||
|
@ -1600,7 +1681,10 @@ class CookieJar {
|
||||||
static deserializeSync(strOrObj, store) {
|
static deserializeSync(strOrObj, store) {
|
||||||
const serialized =
|
const serialized =
|
||||||
typeof strOrObj === "string" ? JSON.parse(strOrObj) : strOrObj;
|
typeof strOrObj === "string" ? JSON.parse(strOrObj) : strOrObj;
|
||||||
const jar = new CookieJar(store, serialized.rejectPublicSuffixes);
|
const jar = new CookieJar(store, {
|
||||||
|
rejectPublicSuffixes: serialized.rejectPublicSuffixes,
|
||||||
|
looseMode: serialized.enableLooseMode
|
||||||
|
});
|
||||||
|
|
||||||
// catch this mistake early:
|
// catch this mistake early:
|
||||||
if (!jar.store.synchronous) {
|
if (!jar.store.synchronous) {
|
||||||
|
@ -1669,3 +1753,4 @@ exports.permuteDomain = require("./permuteDomain").permuteDomain;
|
||||||
exports.permutePath = permutePath;
|
exports.permutePath = permutePath;
|
||||||
exports.canonicalDomain = canonicalDomain;
|
exports.canonicalDomain = canonicalDomain;
|
||||||
exports.PrefixSecurityEnum = PrefixSecurityEnum;
|
exports.PrefixSecurityEnum = PrefixSecurityEnum;
|
||||||
|
exports.ParameterError = validators.ParameterError;
|
||||||
|
|
|
@ -33,19 +33,21 @@ const { fromCallback } = require("universalify");
|
||||||
const Store = require("./store").Store;
|
const Store = require("./store").Store;
|
||||||
const permuteDomain = require("./permuteDomain").permuteDomain;
|
const permuteDomain = require("./permuteDomain").permuteDomain;
|
||||||
const pathMatch = require("./pathMatch").pathMatch;
|
const pathMatch = require("./pathMatch").pathMatch;
|
||||||
const util = require("util");
|
const { getCustomInspectSymbol, getUtilInspect } = require("./utilHelper");
|
||||||
|
|
||||||
class MemoryCookieStore extends Store {
|
class MemoryCookieStore extends Store {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.synchronous = true;
|
this.synchronous = true;
|
||||||
this.idx = {};
|
this.idx = Object.create(null);
|
||||||
if (util.inspect.custom) {
|
const customInspectSymbol = getCustomInspectSymbol();
|
||||||
this[util.inspect.custom] = this.inspect;
|
if (customInspectSymbol) {
|
||||||
|
this[customInspectSymbol] = this.inspect;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inspect() {
|
inspect() {
|
||||||
|
const util = { inspect: getUtilInspect(inspectFallback) };
|
||||||
return `{ idx: ${util.inspect(this.idx, false, 2)} }`;
|
return `{ idx: ${util.inspect(this.idx, false, 2)} }`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +64,7 @@ class MemoryCookieStore extends Store {
|
||||||
const results = [];
|
const results = [];
|
||||||
if (typeof allowSpecialUseDomain === "function") {
|
if (typeof allowSpecialUseDomain === "function") {
|
||||||
cb = allowSpecialUseDomain;
|
cb = allowSpecialUseDomain;
|
||||||
allowSpecialUseDomain = false;
|
allowSpecialUseDomain = true;
|
||||||
}
|
}
|
||||||
if (!domain) {
|
if (!domain) {
|
||||||
return cb(null, []);
|
return cb(null, []);
|
||||||
|
@ -109,10 +111,10 @@ class MemoryCookieStore extends Store {
|
||||||
|
|
||||||
putCookie(cookie, cb) {
|
putCookie(cookie, cb) {
|
||||||
if (!this.idx[cookie.domain]) {
|
if (!this.idx[cookie.domain]) {
|
||||||
this.idx[cookie.domain] = {};
|
this.idx[cookie.domain] = Object.create(null);
|
||||||
}
|
}
|
||||||
if (!this.idx[cookie.domain][cookie.path]) {
|
if (!this.idx[cookie.domain][cookie.path]) {
|
||||||
this.idx[cookie.domain][cookie.path] = {};
|
this.idx[cookie.domain][cookie.path] = Object.create(null);
|
||||||
}
|
}
|
||||||
this.idx[cookie.domain][cookie.path][cookie.key] = cookie;
|
this.idx[cookie.domain][cookie.path][cookie.key] = cookie;
|
||||||
cb(null);
|
cb(null);
|
||||||
|
@ -144,7 +146,7 @@ class MemoryCookieStore extends Store {
|
||||||
return cb(null);
|
return cb(null);
|
||||||
}
|
}
|
||||||
removeAllCookies(cb) {
|
removeAllCookies(cb) {
|
||||||
this.idx = {};
|
this.idx = Object.create(null);
|
||||||
return cb(null);
|
return cb(null);
|
||||||
}
|
}
|
||||||
getAllCookies(cb) {
|
getAllCookies(cb) {
|
||||||
|
@ -184,7 +186,57 @@ class MemoryCookieStore extends Store {
|
||||||
"removeAllCookies",
|
"removeAllCookies",
|
||||||
"getAllCookies"
|
"getAllCookies"
|
||||||
].forEach(name => {
|
].forEach(name => {
|
||||||
MemoryCookieStore[name] = fromCallback(MemoryCookieStore.prototype[name]);
|
MemoryCookieStore.prototype[name] = fromCallback(
|
||||||
|
MemoryCookieStore.prototype[name]
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.MemoryCookieStore = MemoryCookieStore;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.inspectFallback = inspectFallback;
|
||||||
|
|
|
@ -33,21 +33,11 @@ const pubsuffix = require("./pubsuffix-psl");
|
||||||
|
|
||||||
// Gives the permutation of all possible domainMatch()es of a given domain. The
|
// Gives the permutation of all possible domainMatch()es of a given domain. The
|
||||||
// array is in shortest-to-longest order. Handy for indexing.
|
// array is in shortest-to-longest order. Handy for indexing.
|
||||||
const SPECIAL_USE_DOMAINS = ["local"]; // RFC 6761
|
|
||||||
function permuteDomain(domain, allowSpecialUseDomain) {
|
function permuteDomain(domain, allowSpecialUseDomain) {
|
||||||
let pubSuf = null;
|
const pubSuf = pubsuffix.getPublicSuffix(domain, {
|
||||||
if (allowSpecialUseDomain) {
|
allowSpecialUseDomain: allowSpecialUseDomain
|
||||||
const domainParts = domain.split(".");
|
});
|
||||||
if (SPECIAL_USE_DOMAINS.includes(domainParts[domainParts.length - 1])) {
|
|
||||||
pubSuf = `${domainParts[domainParts.length - 2]}.${
|
|
||||||
domainParts[domainParts.length - 1]
|
|
||||||
}`;
|
|
||||||
} else {
|
|
||||||
pubSuf = pubsuffix.getPublicSuffix(domain);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pubSuf = pubsuffix.getPublicSuffix(domain);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pubSuf) {
|
if (!pubSuf) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -56,6 +46,11 @@ function permuteDomain(domain, allowSpecialUseDomain) {
|
||||||
return [domain];
|
return [domain];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nuke trailing dot
|
||||||
|
if (domain.slice(-1) == ".") {
|
||||||
|
domain = domain.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
const prefix = domain.slice(0, -(pubSuf.length + 1)); // ".example.com"
|
const prefix = domain.slice(0, -(pubSuf.length + 1)); // ".example.com"
|
||||||
const parts = prefix.split(".").reverse();
|
const parts = prefix.split(".").reverse();
|
||||||
let cur = pubSuf;
|
let cur = pubSuf;
|
||||||
|
|
|
@ -31,7 +31,42 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
const psl = require("psl");
|
const psl = require("psl");
|
||||||
|
|
||||||
function getPublicSuffix(domain) {
|
// 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}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return psl.get(domain);
|
return psl.get(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
function requireUtil() {
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line no-restricted-modules
|
||||||
|
return require("util");
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for v10.12.0+
|
||||||
|
function lookupCustomInspectSymbol() {
|
||||||
|
return Symbol.for("nodejs.util.inspect.custom");
|
||||||
|
}
|
||||||
|
|
||||||
|
// for older node environments
|
||||||
|
function tryReadingCustomSymbolFromUtilInspect(options) {
|
||||||
|
const _requireUtil = options.requireUtil || requireUtil;
|
||||||
|
const util = _requireUtil();
|
||||||
|
return util ? util.inspect.custom : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getUtilInspect = function getUtilInspect(fallback, options = {}) {
|
||||||
|
const _requireUtil = options.requireUtil || requireUtil;
|
||||||
|
const util = _requireUtil();
|
||||||
|
return function inspect(value, showHidden, depth) {
|
||||||
|
return util ? util.inspect(value, showHidden, depth) : fallback(value);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getCustomInspectSymbol = function getCustomInspectSymbol(options = {}) {
|
||||||
|
const _lookupCustomInspectSymbol =
|
||||||
|
options.lookupCustomInspectSymbol || lookupCustomInspectSymbol;
|
||||||
|
|
||||||
|
// get custom inspect symbol for node environments
|
||||||
|
return (
|
||||||
|
_lookupCustomInspectSymbol() ||
|
||||||
|
tryReadingCustomSymbolFromUtilInspect(options)
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,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;
|
|
@ -1,2 +1,2 @@
|
||||||
// generated by genversion
|
// generated by genversion
|
||||||
module.exports = '4.0.0'
|
module.exports = '4.1.3'
|
||||||
|
|
|
@ -67,7 +67,7 @@
|
||||||
"RFC6265",
|
"RFC6265",
|
||||||
"RFC2965"
|
"RFC2965"
|
||||||
],
|
],
|
||||||
"version": "4.0.0",
|
"version": "4.1.3",
|
||||||
"homepage": "https://github.com/salesforce/tough-cookie",
|
"homepage": "https://github.com/salesforce/tough-cookie",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -82,7 +82,7 @@
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"version": "genversion lib/version.js && git add lib/version.js",
|
"version": "genversion lib/version.js && git add lib/version.js",
|
||||||
"test": "vows test/*_test.js",
|
"test": "vows test/*_test.js && npm run eslint",
|
||||||
"cover": "nyc --reporter=lcov --reporter=html vows test/*_test.js",
|
"cover": "nyc --reporter=lcov --reporter=html vows test/*_test.js",
|
||||||
"eslint": "eslint --env node --ext .js .",
|
"eslint": "eslint --env node --ext .js .",
|
||||||
"prettier": "prettier '**/*.{json,ts,yaml,md}'",
|
"prettier": "prettier '**/*.{json,ts,yaml,md}'",
|
||||||
|
@ -104,6 +104,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"psl": "^1.1.33",
|
"psl": "^1.1.33",
|
||||||
"punycode": "^2.1.1",
|
"punycode": "^2.1.1",
|
||||||
"universalify": "^0.1.2"
|
"universalify": "^0.2.0",
|
||||||
|
"url-parse": "^1.5.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"rules": {
|
||||||
|
"no-restricted-modules": "off"
|
||||||
|
}
|
||||||
|
}
|
166
test/api_test.js
166
test/api_test.js
|
@ -579,4 +579,170 @@ vows
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.addBatch(allowSpecialUseOptionVows())
|
||||||
.export(module);
|
.export(module);
|
||||||
|
|
||||||
|
function allowSpecialUseOptionVows() {
|
||||||
|
const specialUseDomains = [
|
||||||
|
"local",
|
||||||
|
"example",
|
||||||
|
"invalid",
|
||||||
|
"localhost",
|
||||||
|
"test"
|
||||||
|
];
|
||||||
|
|
||||||
|
const specialTreatmentDomains = ["localhost", "invalid"];
|
||||||
|
|
||||||
|
return specialUseDomains.reduce((vows, specialUseDomain) => {
|
||||||
|
if (specialTreatmentDomains.includes(specialUseDomain)) {
|
||||||
|
vows[
|
||||||
|
`cookie jar with allowSpecialUseDomain set to the default value and domain is "${specialUseDomain}"`
|
||||||
|
] = {
|
||||||
|
topic: function() {
|
||||||
|
const cb = this.callback;
|
||||||
|
const cj = new CookieJar();
|
||||||
|
cj.setCookie(
|
||||||
|
`settingThisShouldPass=true; Domain=${specialUseDomain}; Path=/;`,
|
||||||
|
`http://${specialUseDomain}`,
|
||||||
|
at(-1),
|
||||||
|
(err, cookie) => {
|
||||||
|
cb(err, { cj: cj, cookie: cookie });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
"set the cookie": function(t) {
|
||||||
|
assert.ok(t.cookie, "didn't set?!");
|
||||||
|
assert.equal(t.cookie.key, "settingThisShouldPass");
|
||||||
|
},
|
||||||
|
"then, retrieving": {
|
||||||
|
topic: function(t) {
|
||||||
|
const cb = this.callback;
|
||||||
|
setTimeout(() => {
|
||||||
|
t.cj.getCookies(
|
||||||
|
`http://${specialUseDomain}`,
|
||||||
|
{ http: true },
|
||||||
|
(err, cookies) => {
|
||||||
|
t.cookies = cookies;
|
||||||
|
cb(err, t);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, 2000);
|
||||||
|
},
|
||||||
|
"got the cookie": function(t) {
|
||||||
|
assert.lengthOf(t.cookies, 1);
|
||||||
|
assert.equal(t.cookies[0].key, "settingThisShouldPass");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
vows[
|
||||||
|
`cookie jar with allowSpecialUseDomain set to the default value and domain is "dev.${specialUseDomain}"`
|
||||||
|
] = {
|
||||||
|
topic: function() {
|
||||||
|
const cb = this.callback;
|
||||||
|
const cj = new CookieJar();
|
||||||
|
cj.setCookie(
|
||||||
|
`settingThisShouldPass=true; Domain=dev.${specialUseDomain}; Path=/;`,
|
||||||
|
`http://dev.${specialUseDomain}`,
|
||||||
|
at(-1),
|
||||||
|
(err, cookie) => {
|
||||||
|
cb(err, { cj: cj, cookie: cookie });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
"set the cookie": function(t) {
|
||||||
|
assert.ok(t.cookie, "didn't set?!");
|
||||||
|
assert.equal(t.cookie.key, "settingThisShouldPass");
|
||||||
|
},
|
||||||
|
"then, retrieving": {
|
||||||
|
topic: function(t) {
|
||||||
|
const cb = this.callback;
|
||||||
|
setTimeout(() => {
|
||||||
|
t.cj.getCookies(
|
||||||
|
`http://dev.${specialUseDomain}`,
|
||||||
|
{ http: true },
|
||||||
|
(err, cookies) => {
|
||||||
|
t.cookies = cookies;
|
||||||
|
cb(err, t);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, 2000);
|
||||||
|
},
|
||||||
|
"got the cookie": function(t) {
|
||||||
|
assert.lengthOf(t.cookies, 1);
|
||||||
|
assert.equal(t.cookies[0].key, "settingThisShouldPass");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
vows[
|
||||||
|
`cookie jar with allowSpecialUseDomain enabled and domain is "dev.${specialUseDomain}"`
|
||||||
|
] = {
|
||||||
|
topic: function() {
|
||||||
|
const cb = this.callback;
|
||||||
|
const cj = new CookieJar(new tough.MemoryCookieStore(), {
|
||||||
|
rejectPublicSuffixes: true,
|
||||||
|
allowSpecialUseDomain: true
|
||||||
|
});
|
||||||
|
cj.setCookie(
|
||||||
|
`settingThisShouldPass=true; Domain=dev.${specialUseDomain}; Path=/;`,
|
||||||
|
`http://dev.${specialUseDomain}`,
|
||||||
|
at(-1),
|
||||||
|
(err, cookie) => {
|
||||||
|
cb(err, { cj: cj, cookie: cookie });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
"set the cookie": function(t) {
|
||||||
|
assert.ok(t.cookie, "didn't set?!");
|
||||||
|
assert.equal(t.cookie.key, "settingThisShouldPass");
|
||||||
|
},
|
||||||
|
"then, retrieving": {
|
||||||
|
topic: function(t) {
|
||||||
|
const cb = this.callback;
|
||||||
|
setTimeout(() => {
|
||||||
|
t.cj.getCookies(
|
||||||
|
`http://dev.${specialUseDomain}`,
|
||||||
|
{ http: true },
|
||||||
|
(err, cookies) => {
|
||||||
|
t.cookies = cookies;
|
||||||
|
cb(err, t);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, 2000);
|
||||||
|
},
|
||||||
|
"got the cookie": function(t) {
|
||||||
|
assert.lengthOf(t.cookies, 1);
|
||||||
|
assert.equal(t.cookies[0].key, "settingThisShouldPass");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
vows[
|
||||||
|
`cookie jar with allowSpecialUseDomain disabled and domain is "dev.${specialUseDomain}"`
|
||||||
|
] = {
|
||||||
|
topic: function() {
|
||||||
|
const cj = new CookieJar(new tough.MemoryCookieStore(), {
|
||||||
|
allowSpecialUseDomain: false,
|
||||||
|
rejectPublicSuffixes: true
|
||||||
|
});
|
||||||
|
cj.setCookie(
|
||||||
|
`settingThisShouldFail=true; Domain=dev.${specialUseDomain}; Path=/;`,
|
||||||
|
`http://dev.${specialUseDomain}`,
|
||||||
|
this.callback
|
||||||
|
);
|
||||||
|
},
|
||||||
|
errors: function(err, cookie) {
|
||||||
|
assert.ok(err);
|
||||||
|
assert.ok(!cookie);
|
||||||
|
assert.equal(
|
||||||
|
err.message,
|
||||||
|
`Cookie has domain set to the public suffix "${specialUseDomain}" which is a special use domain. To allow this, configure your CookieJar with {allowSpecialUseDomain:true, rejectPublicSuffixes: false}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return vows;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
|
@ -193,6 +193,94 @@ vows
|
||||||
assert.match(err.message, /HttpOnly/i);
|
assert.match(err.message, /HttpOnly/i);
|
||||||
assert.ok(!c);
|
assert.ok(!c);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Setting a basic IPv6 cookie": {
|
||||||
|
topic: function() {
|
||||||
|
const cj = new CookieJar();
|
||||||
|
const c = Cookie.parse("a=b; Domain=[::1]; Path=/");
|
||||||
|
assert.strictEqual(c.hostOnly, null);
|
||||||
|
assert.instanceOf(c.creation, Date);
|
||||||
|
assert.strictEqual(c.lastAccessed, null);
|
||||||
|
c.creation = new Date(Date.now() - 10000);
|
||||||
|
cj.setCookie(c, "http://[::1]/", this.callback);
|
||||||
|
},
|
||||||
|
works: function(c) {
|
||||||
|
assert.instanceOf(c, Cookie);
|
||||||
|
}, // C is for Cookie, good enough for me
|
||||||
|
"gets timestamped": function(c) {
|
||||||
|
assert.ok(c.creation);
|
||||||
|
assert.ok(Date.now() - c.creation.getTime() < 5000); // recently stamped
|
||||||
|
assert.ok(c.lastAccessed);
|
||||||
|
assert.equal(c.creation, c.lastAccessed);
|
||||||
|
assert.equal(c.TTL(), Infinity);
|
||||||
|
assert.ok(!c.isPersistent());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Setting a prefix IPv6 cookie": {
|
||||||
|
topic: function() {
|
||||||
|
const cj = new CookieJar();
|
||||||
|
const c = Cookie.parse("a=b; Domain=[::ffff:127.0.0.1]; Path=/");
|
||||||
|
assert.strictEqual(c.hostOnly, null);
|
||||||
|
assert.instanceOf(c.creation, Date);
|
||||||
|
assert.strictEqual(c.lastAccessed, null);
|
||||||
|
c.creation = new Date(Date.now() - 10000);
|
||||||
|
cj.setCookie(c, "http://[::ffff:127.0.0.1]/", this.callback);
|
||||||
|
},
|
||||||
|
works: function(c) {
|
||||||
|
assert.instanceOf(c, Cookie);
|
||||||
|
}, // C is for Cookie, good enough for me
|
||||||
|
"gets timestamped": function(c) {
|
||||||
|
assert.ok(c.creation);
|
||||||
|
assert.ok(Date.now() - c.creation.getTime() < 5000); // recently stamped
|
||||||
|
assert.ok(c.lastAccessed);
|
||||||
|
assert.equal(c.creation, c.lastAccessed);
|
||||||
|
assert.equal(c.TTL(), Infinity);
|
||||||
|
assert.ok(!c.isPersistent());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Setting a classic IPv6 cookie": {
|
||||||
|
topic: function() {
|
||||||
|
const cj = new CookieJar();
|
||||||
|
const c = Cookie.parse("a=b; Domain=[2001:4860:4860::8888]; Path=/");
|
||||||
|
assert.strictEqual(c.hostOnly, null);
|
||||||
|
assert.instanceOf(c.creation, Date);
|
||||||
|
assert.strictEqual(c.lastAccessed, null);
|
||||||
|
c.creation = new Date(Date.now() - 10000);
|
||||||
|
cj.setCookie(c, "http://[2001:4860:4860::8888]/", this.callback);
|
||||||
|
},
|
||||||
|
works: function(c) {
|
||||||
|
assert.instanceOf(c, Cookie);
|
||||||
|
}, // C is for Cookie, good enough for me
|
||||||
|
"gets timestamped": function(c) {
|
||||||
|
assert.ok(c.creation);
|
||||||
|
assert.ok(Date.now() - c.creation.getTime() < 5000); // recently stamped
|
||||||
|
assert.ok(c.lastAccessed);
|
||||||
|
assert.equal(c.creation, c.lastAccessed);
|
||||||
|
assert.equal(c.TTL(), Infinity);
|
||||||
|
assert.ok(!c.isPersistent());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Setting a short IPv6 cookie": {
|
||||||
|
topic: function() {
|
||||||
|
const cj = new CookieJar();
|
||||||
|
const c = Cookie.parse("a=b; Domain=[2600::]; Path=/");
|
||||||
|
assert.strictEqual(c.hostOnly, null);
|
||||||
|
assert.instanceOf(c.creation, Date);
|
||||||
|
assert.strictEqual(c.lastAccessed, null);
|
||||||
|
c.creation = new Date(Date.now() - 10000);
|
||||||
|
cj.setCookie(c, "http://[2600::]/", this.callback);
|
||||||
|
},
|
||||||
|
works: function(c) {
|
||||||
|
assert.instanceOf(c, Cookie);
|
||||||
|
}, // C is for Cookie, good enough for me
|
||||||
|
"gets timestamped": function(c) {
|
||||||
|
assert.ok(c.creation);
|
||||||
|
assert.ok(Date.now() - c.creation.getTime() < 5000); // recently stamped
|
||||||
|
assert.ok(c.lastAccessed);
|
||||||
|
assert.equal(c.creation, c.lastAccessed);
|
||||||
|
assert.equal(c.TTL(), Infinity);
|
||||||
|
assert.ok(!c.isPersistent());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.addBatch({
|
.addBatch({
|
||||||
|
@ -543,6 +631,19 @@ vows
|
||||||
assert.strictEqual(cookies[0].key, "");
|
assert.strictEqual(cookies[0].key, "");
|
||||||
assert.strictEqual(cookies[0].value, "FooBar");
|
assert.strictEqual(cookies[0].value, "FooBar");
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Loose Mode Cloned": {
|
||||||
|
topic: function() {
|
||||||
|
const cj = new CookieJar(null, { looseMode: true });
|
||||||
|
return CookieJar.fromJSON(cj.toJSON());
|
||||||
|
},
|
||||||
|
"parses loose cookies from serialized cookie jar": function(cj) {
|
||||||
|
cj.setCookieSync("FooBar", "http://www.foonet.net", {});
|
||||||
|
const cookies = cj.getCookiesSync("http://www.foonet.net");
|
||||||
|
assert.strictEqual(cookies.length, 1);
|
||||||
|
assert.strictEqual(cookies[0].key, "");
|
||||||
|
assert.strictEqual(cookies[0].value, "FooBar");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.addBatch({
|
.addBatch({
|
||||||
|
@ -658,9 +759,12 @@ vows
|
||||||
GMT; path=/`;
|
GMT; path=/`;
|
||||||
|
|
||||||
jar.setCookieSync(cookieString, "https://google.com");
|
jar.setCookieSync(cookieString, "https://google.com");
|
||||||
jar.getCookies("https://google.com", this.callback)
|
jar.getCookies("https://google.com", this.callback);
|
||||||
},
|
},
|
||||||
"results in a 1-length array with a valid cookie": function(err, cookies) {
|
"results in a 1-length array with a valid cookie": function(
|
||||||
|
err,
|
||||||
|
cookies
|
||||||
|
) {
|
||||||
assert(!err, err);
|
assert(!err, err);
|
||||||
assert(cookies.length == 1);
|
assert(cookies.length == 1);
|
||||||
assert.instanceOf(cookies[0], Cookie);
|
assert.instanceOf(cookies[0], Cookie);
|
||||||
|
@ -669,4 +773,65 @@ vows
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.addBatch({
|
||||||
|
"Issue #145 - Missing parameter validation on setCookie function causes TypeError": {
|
||||||
|
"with missing parameters": {
|
||||||
|
topic: function() {
|
||||||
|
const jar = new tough.CookieJar();
|
||||||
|
jar.setCookie(
|
||||||
|
new String("x=y; Domain=example.com; Path=/"),
|
||||||
|
this.callback
|
||||||
|
);
|
||||||
|
},
|
||||||
|
"results in a error being returned because of missing parameters": function(
|
||||||
|
err,
|
||||||
|
cookies
|
||||||
|
) {
|
||||||
|
assert(err != null);
|
||||||
|
assert(err instanceof tough.ParameterError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.addBatch({
|
||||||
|
"Issue #197 - CookieJar().setCookie crashes when empty cookie is passed": {
|
||||||
|
"with missing parameters": {
|
||||||
|
topic: function() {
|
||||||
|
const jar = new tough.CookieJar();
|
||||||
|
jar.setCookie("", "https://google.com", this.callback);
|
||||||
|
},
|
||||||
|
"results in a error being returned because of missing parameters": function(
|
||||||
|
err,
|
||||||
|
cookies
|
||||||
|
) {
|
||||||
|
assert(cookies == undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.addBatch({
|
||||||
|
"Issue #282 - Prototype pollution": {
|
||||||
|
"when setting a cookie with the domain __proto__": {
|
||||||
|
topic: function() {
|
||||||
|
const jar = new tough.CookieJar(undefined, {
|
||||||
|
rejectPublicSuffixes: false
|
||||||
|
});
|
||||||
|
// try to pollute the prototype
|
||||||
|
jar.setCookieSync(
|
||||||
|
"Slonser=polluted; Domain=__proto__; Path=/notauth",
|
||||||
|
"https://__proto__/admin"
|
||||||
|
);
|
||||||
|
jar.setCookieSync(
|
||||||
|
"Auth=Lol; Domain=google.com; Path=/notauth",
|
||||||
|
"https://google.com/"
|
||||||
|
);
|
||||||
|
this.callback();
|
||||||
|
},
|
||||||
|
"results in a cookie that is not affected by the attempted prototype pollution": function() {
|
||||||
|
const pollutedObject = {};
|
||||||
|
assert(pollutedObject["/notauth"] === undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
.export(module);
|
.export(module);
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
const vows = require("vows");
|
const vows = require("vows");
|
||||||
const assert = require("assert");
|
const assert = require("assert");
|
||||||
const tough = require("../lib/cookie");
|
const tough = require("../lib/cookie");
|
||||||
const Cookie = tough.Cookie;
|
|
||||||
|
|
||||||
function matchVows(func, table) {
|
function matchVows(func, table) {
|
||||||
const theVows = {};
|
const theVows = {};
|
||||||
|
@ -50,15 +49,15 @@ function matchVows(func, table) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function transformVows(fn, table) {
|
function transformVows(fn, table) {
|
||||||
var theVows = {};
|
const theVows = {};
|
||||||
table.forEach(function (item) {
|
table.forEach(item => {
|
||||||
var str = item[0];
|
const str = item[0];
|
||||||
var expect = item[1];
|
const expect = item[1];
|
||||||
var label = str + " gives " + expect;
|
let label = `${str} gives ${expect}`;
|
||||||
if (item.length >= 3) {
|
if (item.length >= 3) {
|
||||||
label += " (" + item[2] + ")";
|
label += ` (${item[2]})`;
|
||||||
}
|
}
|
||||||
theVows[label] = function () {
|
theVows[label] = function() {
|
||||||
assert.equal(fn(str), expect);
|
assert.equal(fn(str), expect);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -75,7 +74,7 @@ vows
|
||||||
["EXAMPLE.com.", "example.com.", "trailing dot"],
|
["EXAMPLE.com.", "example.com.", "trailing dot"],
|
||||||
[".EXAMPLE.com.", "example.com.", "leading and trailing dot"],
|
[".EXAMPLE.com.", "example.com.", "leading and trailing dot"],
|
||||||
[".EXAMPLE...com.", "example...com.", "internal dots"],
|
[".EXAMPLE...com.", "example...com.", "internal dots"],
|
||||||
["δοκιμή.δοκιμή","xn--jxalpdlp.xn--jxalpdlp", "IDN: test.test in greek"],
|
["δοκιμή.δοκιμή", "xn--jxalpdlp.xn--jxalpdlp", "IDN: test.test in greek"]
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
.addBatch({
|
.addBatch({
|
||||||
|
@ -105,6 +104,9 @@ vows
|
||||||
["www.aaaa.com", "aaa.com", false],
|
["www.aaaa.com", "aaa.com", false],
|
||||||
["www.aaa.com", "aaa.com", true],
|
["www.aaa.com", "aaa.com", true],
|
||||||
["www.aexample.com", "example.com", false], // has to match on "." boundary
|
["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)"
|
// 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)
|
["192.168.0.1", "168.0.1", false], // because str is an IP (v4)
|
||||||
|
@ -142,12 +144,12 @@ vows
|
||||||
// exact length "TLD" tests:
|
// exact length "TLD" tests:
|
||||||
["com", "net", false], // same len, non-match
|
["com", "net", false], // same len, non-match
|
||||||
["com", "com", true], // "are identical" rule
|
["com", "com", true], // "are identical" rule
|
||||||
["NOTATLD", "notaTLD", true], // "are identical" rule (after canonicalization)
|
["NOTATLD", "notaTLD", true] // "are identical" rule (after canonicalization)
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
.addBatch({
|
.addBatch({
|
||||||
"default-path": transformVows(tough.defaultPath,[
|
"default-path": transformVows(tough.defaultPath, [
|
||||||
[null, "/"],
|
[null, "/"],
|
||||||
["/", "/"],
|
["/", "/"],
|
||||||
["/file", "/"],
|
["/file", "/"],
|
||||||
|
@ -194,6 +196,16 @@ vows
|
||||||
"foo.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: {
|
||||||
|
|
|
@ -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);
|
|
@ -158,8 +158,8 @@ vows
|
||||||
"has max-age": function(c) {
|
"has max-age": function(c) {
|
||||||
assert.equal(c.maxAge, 1234);
|
assert.equal(c.maxAge, 1234);
|
||||||
},
|
},
|
||||||
"has same-site 'none'": function(c) {
|
"has same-site 'undefined'": function(c) {
|
||||||
assert.equal(c.sameSite, "none");
|
assert.equal(c.sameSite, undefined);
|
||||||
},
|
},
|
||||||
"has extensions": function(c) {
|
"has extensions": function(c) {
|
||||||
assert.ok(c.extensions);
|
assert.ok(c.extensions);
|
||||||
|
@ -677,20 +677,80 @@ vows
|
||||||
assert.equal(c.extensions, null);
|
assert.equal(c.extensions, null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
absent: {
|
none: {
|
||||||
|
topic: function() {
|
||||||
|
return Cookie.parse("abc=xyz; SameSite=NoNe") || null;
|
||||||
|
},
|
||||||
|
parsed: function(c) {
|
||||||
|
assert.ok(c);
|
||||||
|
},
|
||||||
|
"is none (lowercased)": function(c) {
|
||||||
|
assert.equal(c.sameSite, "none");
|
||||||
|
},
|
||||||
|
"no extensions": function(c) {
|
||||||
|
assert.equal(c.extensions, null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bad: {
|
||||||
topic: function() {
|
topic: function() {
|
||||||
return Cookie.parse("abc=xyzzy; SameSite=example.com") || null;
|
return Cookie.parse("abc=xyzzy; SameSite=example.com") || null;
|
||||||
},
|
},
|
||||||
parsed: function(c) {
|
parsed: function(c) {
|
||||||
assert.ok(c);
|
assert.ok(c);
|
||||||
},
|
},
|
||||||
"is set to 'none' (by prototype)": function(c) {
|
"is set to 'undefined'": function(c) {
|
||||||
assert.equal(c.sameSite, "none");
|
assert.equal(c.sameSite, undefined);
|
||||||
|
},
|
||||||
|
"no extensions": function(c) {
|
||||||
|
assert.equal(c.extensions, null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
absent: {
|
||||||
|
topic: function() {
|
||||||
|
return Cookie.parse("abc=xyzzy;") || null;
|
||||||
|
},
|
||||||
|
parsed: function(c) {
|
||||||
|
assert.ok(c);
|
||||||
|
},
|
||||||
|
"is set to 'undefined'": function(c) {
|
||||||
|
assert.equal(c.sameSite, undefined);
|
||||||
},
|
},
|
||||||
"no extensions": function(c) {
|
"no extensions": function(c) {
|
||||||
assert.equal(c.extensions, null);
|
assert.equal(c.extensions, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"empty string": {
|
||||||
|
topic: function() {
|
||||||
|
return Cookie.parse("");
|
||||||
|
},
|
||||||
|
"is empty": function(c) {
|
||||||
|
assert.isNull(c);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"missing string": {
|
||||||
|
topic: function() {
|
||||||
|
return Cookie.parse();
|
||||||
|
},
|
||||||
|
"is empty": function(c) {
|
||||||
|
assert.isNull(c);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"some string object": {
|
||||||
|
topic: function() {
|
||||||
|
return Cookie.parse(new String(""));
|
||||||
|
},
|
||||||
|
"is empty": function(c) {
|
||||||
|
assert.isNull(c, null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"some empty string object": {
|
||||||
|
topic: function() {
|
||||||
|
return Cookie.parse(new String());
|
||||||
|
},
|
||||||
|
"is empty": function(c) {
|
||||||
|
assert.isNull(c, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.export(module);
|
.export(module);
|
||||||
|
|
|
@ -36,6 +36,7 @@ const async = require("async");
|
||||||
const tough = require("../lib/cookie");
|
const tough = require("../lib/cookie");
|
||||||
const Cookie = tough.Cookie;
|
const Cookie = tough.Cookie;
|
||||||
const CookieJar = tough.CookieJar;
|
const CookieJar = tough.CookieJar;
|
||||||
|
const MemoryCookieStore = tough.MemoryCookieStore;
|
||||||
|
|
||||||
const atNow = Date.now();
|
const atNow = Date.now();
|
||||||
|
|
||||||
|
@ -188,4 +189,96 @@ vows
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.addBatch(
|
||||||
|
{
|
||||||
|
"setCookie with localhost (GH-215)": {
|
||||||
|
topic: function() {
|
||||||
|
const cookieJar = new CookieJar();
|
||||||
|
return cookieJar.setCookieSync(
|
||||||
|
"a=b; Domain=localhost",
|
||||||
|
"http://localhost"
|
||||||
|
); // Users are free to use localhost names as they would any other domain names. [RFC 6761, Sec. 6.3.1]
|
||||||
|
},
|
||||||
|
works: function(err, c) {
|
||||||
|
assert.instanceOf(c, Cookie);
|
||||||
|
assert.match(c, /Domain=localhost/);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setCookie with localhost (localhost. domain) (GH-215)": {
|
||||||
|
topic: function() {
|
||||||
|
const cookieJar = new CookieJar();
|
||||||
|
return cookieJar.setCookieSync(
|
||||||
|
"a=b; Domain=localhost.",
|
||||||
|
"http://localhost."
|
||||||
|
); // Users are free to use localhost names as they would any other domain names. [RFC 6761, Sec. 6.3.1]
|
||||||
|
},
|
||||||
|
works: function(err, c) {
|
||||||
|
assert.instanceOf(c, Cookie);
|
||||||
|
assert.match(c, /Domain=localhost/);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setCookie with localhost (GH-215) (null domain)": {
|
||||||
|
topic: function() {
|
||||||
|
const cookieJar = new CookieJar();
|
||||||
|
return cookieJar.setCookieSync("a=b; Domain=", "http://localhost");
|
||||||
|
},
|
||||||
|
works: function(c) {
|
||||||
|
assert.instanceOf(c, Cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setCookie with localhost (localhost.local domain) (GH-215)": {
|
||||||
|
topic: function() {
|
||||||
|
const cookieJar = new CookieJar();
|
||||||
|
return cookieJar.setCookieSync(
|
||||||
|
"a=b; Domain=localhost.local",
|
||||||
|
"http://localhost"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
works: function(c) {
|
||||||
|
assert.instanceOf(c, Cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setCookie with localhost (.localhost domain), (GH-215)": {
|
||||||
|
topic: function() {
|
||||||
|
const cookieJar = new CookieJar();
|
||||||
|
return cookieJar.setCookieSync(
|
||||||
|
"a=b; Domain=.localhost",
|
||||||
|
"http://localhost"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
works: function(c) {
|
||||||
|
assert.instanceOf(c, Cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addBatch({
|
||||||
|
MemoryCookieStore: {
|
||||||
|
topic: new MemoryCookieStore(),
|
||||||
|
"has no static methods": function() {
|
||||||
|
assert.deepEqual(Object.keys(MemoryCookieStore), []);
|
||||||
|
},
|
||||||
|
"has instance methods that return promises": function(store) {
|
||||||
|
assert.instanceOf(store.findCookie("example.com", "/", "key"), Promise);
|
||||||
|
assert.instanceOf(store.findCookies("example.com", "/"), Promise);
|
||||||
|
assert.instanceOf(store.putCookie({}), Promise);
|
||||||
|
assert.instanceOf(store.updateCookie({}, {}), Promise);
|
||||||
|
assert.instanceOf(
|
||||||
|
store.removeCookie("example.com", "/", "key"),
|
||||||
|
Promise
|
||||||
|
);
|
||||||
|
assert.instanceOf(store.removeCookies("example.com", "/"), Promise);
|
||||||
|
assert.instanceOf(store.removeAllCookies(), Promise);
|
||||||
|
assert.instanceOf(store.getAllCookies(), Promise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
.export(module);
|
.export(module);
|
||||||
|
|
|
@ -124,9 +124,9 @@ vows
|
||||||
topic: function(options) {
|
topic: function(options) {
|
||||||
this.callSetCookie("garbage", options, this.callback);
|
this.callSetCookie("garbage", options, this.callback);
|
||||||
},
|
},
|
||||||
"treated as 'none'": function(err, cookie) {
|
"treated as 'undefined'": function(err, cookie) {
|
||||||
assert.isNull(err);
|
assert.isNull(err);
|
||||||
assert.equal(cookie.sameSite, "none");
|
assert.equal(cookie.sameSite, undefined);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"for strict cookie": {
|
"for strict cookie": {
|
||||||
|
@ -151,9 +151,9 @@ vows
|
||||||
topic: function(options) {
|
topic: function(options) {
|
||||||
this.callSetCookie("normal", options, this.callback);
|
this.callSetCookie("normal", options, this.callback);
|
||||||
},
|
},
|
||||||
"treated as 'none'": function(err, cookie) {
|
"treated as 'undefined'": function(err, cookie) {
|
||||||
assert.isNull(err);
|
assert.isNull(err);
|
||||||
assert.equal(cookie.sameSite, "none");
|
assert.equal(cookie.sameSite, undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
Copyright (c) Microsoft Corporation.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -8,7 +8,7 @@ This package contains type definitions for tough-cookie (https://github.com/sale
|
||||||
Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/tough-cookie.
|
Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/tough-cookie.
|
||||||
|
|
||||||
### Additional Details
|
### Additional Details
|
||||||
* Last updated: Tue, 31 Mar 2020 23:35:07 GMT
|
* Last updated: Thu, 14 Apr 2022 07:01:24 GMT
|
||||||
* Dependencies: none
|
* Dependencies: none
|
||||||
* Global values: none
|
* Global values: none
|
||||||
|
|
||||||
|
|
|
@ -132,26 +132,26 @@ export class Cookie {
|
||||||
|
|
||||||
export namespace Cookie {
|
export namespace Cookie {
|
||||||
interface ParseOptions {
|
interface ParseOptions {
|
||||||
loose?: boolean;
|
loose?: boolean | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Properties {
|
interface Properties {
|
||||||
key?: string;
|
key?: string | undefined;
|
||||||
value?: string;
|
value?: string | undefined;
|
||||||
expires?: Date;
|
expires?: Date | 'Infinity' | undefined;
|
||||||
maxAge?: number | 'Infinity' | '-Infinity';
|
maxAge?: number | 'Infinity' | '-Infinity' | undefined;
|
||||||
domain?: string;
|
domain?: string | undefined;
|
||||||
path?: string;
|
path?: string | undefined;
|
||||||
secure?: boolean;
|
secure?: boolean | undefined;
|
||||||
httpOnly?: boolean;
|
httpOnly?: boolean | undefined;
|
||||||
extensions?: string[];
|
extensions?: string[] | undefined;
|
||||||
creation?: Date;
|
creation?: Date | undefined;
|
||||||
creationIndex?: number;
|
creationIndex?: number | undefined;
|
||||||
|
|
||||||
hostOnly?: boolean;
|
hostOnly?: boolean | undefined;
|
||||||
pathIsDefault?: boolean;
|
pathIsDefault?: boolean | undefined;
|
||||||
lastAccessed?: Date;
|
lastAccessed?: Date | undefined;
|
||||||
sameSite?: string;
|
sameSite?: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Serialized {
|
interface Serialized {
|
||||||
|
@ -215,25 +215,25 @@ export class CookieJar {
|
||||||
|
|
||||||
export namespace CookieJar {
|
export namespace CookieJar {
|
||||||
interface Options {
|
interface Options {
|
||||||
allowSpecialUseDomain?: boolean;
|
allowSpecialUseDomain?: boolean | undefined;
|
||||||
looseMode?: boolean;
|
looseMode?: boolean | undefined;
|
||||||
rejectPublicSuffixes?: boolean;
|
rejectPublicSuffixes?: boolean | undefined;
|
||||||
prefixSecurity?: string;
|
prefixSecurity?: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SetCookieOptions {
|
interface SetCookieOptions {
|
||||||
http?: boolean;
|
http?: boolean | undefined;
|
||||||
secure?: boolean;
|
secure?: boolean | undefined;
|
||||||
now?: Date;
|
now?: Date | undefined;
|
||||||
ignoreError?: boolean;
|
ignoreError?: boolean | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GetCookiesOptions {
|
interface GetCookiesOptions {
|
||||||
http?: boolean;
|
http?: boolean | undefined;
|
||||||
secure?: boolean;
|
secure?: boolean | undefined;
|
||||||
now?: Date;
|
now?: Date | undefined;
|
||||||
expire?: boolean;
|
expire?: boolean | undefined;
|
||||||
allPaths?: boolean;
|
allPaths?: boolean | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Serialized {
|
interface Serialized {
|
||||||
|
@ -262,4 +262,26 @@ export abstract class Store {
|
||||||
getAllCookies(cb: (err: Error | null, cookie: Cookie[]) => void): void;
|
getAllCookies(cb: (err: Error | null, cookie: Cookie[]) => void): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MemoryCookieStore extends Store { }
|
export class MemoryCookieStore extends Store {
|
||||||
|
findCookie(domain: string, path: string, key: string, cb: (err: Error | null, cookie: Cookie | null) => void): void;
|
||||||
|
findCookie(domain: string, path: string, key: string): Promise<Cookie | null>;
|
||||||
|
|
||||||
|
findCookies(domain: string, path: string, allowSpecialUseDomain: boolean, cb: (err: Error | null, cookie: Cookie[]) => void): void;
|
||||||
|
findCookies(domain: string, path: string, cb: (err: Error | null, cookie: Cookie[]) => void): void;
|
||||||
|
findCookies(domain: string, path: string, allowSpecialUseDomain?: boolean): Promise<Cookie[]>;
|
||||||
|
|
||||||
|
putCookie(cookie: Cookie, cb: (err: Error | null) => void): void;
|
||||||
|
putCookie(cookie: Cookie): Promise<void>;
|
||||||
|
|
||||||
|
updateCookie(oldCookie: Cookie, newCookie: Cookie, cb: (err: Error | null) => void): void;
|
||||||
|
updateCookie(oldCookie: Cookie, newCookie: Cookie): Promise<void>;
|
||||||
|
|
||||||
|
removeCookie(domain: string, path: string, key: string, cb: (err: Error | null) => void): void;
|
||||||
|
removeCookie(domain: string, path: string, key: string): Promise<void>;
|
||||||
|
|
||||||
|
removeCookies(domain: string, path: string, cb: (err: Error | null) => void): void;
|
||||||
|
removeCookies(domain: string, path: string): Promise<void>;
|
||||||
|
|
||||||
|
getAllCookies(cb: (err: Error | null, cookie: Cookie[]) => void): void;
|
||||||
|
getAllCookies(): Promise<Cookie[]>;
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
{
|
{
|
||||||
"name": "@types/tough-cookie",
|
"name": "@types/tough-cookie",
|
||||||
"version": "4.0.0",
|
"version": "4.0.2",
|
||||||
"description": "TypeScript definitions for tough-cookie",
|
"description": "TypeScript definitions for tough-cookie",
|
||||||
|
"homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/tough-cookie",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
{
|
{
|
||||||
|
@ -29,6 +30,6 @@
|
||||||
},
|
},
|
||||||
"scripts": {},
|
"scripts": {},
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"typesPublisherContentHash": "ca0d31f2e9fc2a883ed38f569a7c21cb47c3ad66089c59faa840a59b14c0daf5",
|
"typesPublisherContentHash": "b7a67b8b87baf3d5d1b8d6f2b4493dad84d9ec7e93abf68824da9da25ac176d6",
|
||||||
"typeScriptVersion": "2.8"
|
"typeScriptVersion": "3.9"
|
||||||
}
|
}
|
Loading…
Reference in New Issue