cookies
Advanced tools
Comparing version 0.8.0 to 0.9.0
@@ -0,1 +1,12 @@ | ||
0.9.0 / 2023-12-28 | ||
================== | ||
* Add `partitioned` option for CHIPS support | ||
* Add `priority` option for Priority cookie support | ||
* Fix accidental cookie name/value truncation when given invalid chars | ||
* Fix `maxAge` option to reject invalid values | ||
* Remove quotes from returned quoted cookie value | ||
* Use `req.socket` over deprecated `req.connection` | ||
* pref: small lookup regexp optimization | ||
0.8.0 / 2019-10-11 | ||
@@ -2,0 +13,0 @@ ================== |
82
index.js
@@ -13,3 +13,2 @@ /*! | ||
var http = require('http') | ||
var cache = {} | ||
@@ -27,2 +26,26 @@ /** | ||
/** | ||
* RegExp to match Priority cookie attribute value. | ||
*/ | ||
var PRIORITY_REGEXP = /^(?:low|medium|high)$/i | ||
/** | ||
* Cache for generated name regular expressions. | ||
*/ | ||
var REGEXP_CACHE = Object.create(null) | ||
/** | ||
* RegExp to match all characters to escape in a RegExp. | ||
*/ | ||
var REGEXP_ESCAPE_CHARS_REGEXP = /[\^$\\.*+?()[\]{}|]/g | ||
/** | ||
* RegExp to match basic restricted characters for loose validation. | ||
*/ | ||
var RESTRICTED_CHARS_REGEXP = /[;=]/ | ||
/** | ||
* RegExp to match Same-Site cookie attribute value. | ||
@@ -68,2 +91,3 @@ */ | ||
value = match[1] | ||
if (value[0] === '"') value = value.slice(1, -1) | ||
if (!opts || !signed) return value | ||
@@ -90,5 +114,7 @@ | ||
, headers = res.getHeader("Set-Cookie") || [] | ||
, secure = this.secure !== undefined ? !!this.secure : req.protocol === 'https' || req.connection.encrypted | ||
, cookie = new Cookie(name, value, opts) | ||
, signed = opts && opts.signed !== undefined ? opts.signed : !!this.keys | ||
var secure = this.secure === undefined | ||
? req.protocol === 'https' || isRequestEncrypted(req) | ||
: Boolean(this.secure) | ||
@@ -125,7 +151,7 @@ if (typeof headers == "string") headers = [headers] | ||
function Cookie(name, value, attrs) { | ||
if (!fieldContentRegExp.test(name)) { | ||
if (!fieldContentRegExp.test(name) || RESTRICTED_CHARS_REGEXP.test(name)) { | ||
throw new TypeError('argument name is invalid'); | ||
} | ||
if (value && !fieldContentRegExp.test(value)) { | ||
if (value && (!fieldContentRegExp.test(value) || RESTRICTED_CHARS_REGEXP.test(value))) { | ||
throw new TypeError('argument value is invalid'); | ||
@@ -154,2 +180,10 @@ } | ||
if (typeof this.maxAge === 'number' ? (isNaN(this.maxAge) || !isFinite(this.maxAge)) : this.maxAge) { | ||
throw new TypeError('option maxAge is invalid') | ||
} | ||
if (this.priority && !PRIORITY_REGEXP.test(this.priority)) { | ||
throw new TypeError('option priority is invalid') | ||
} | ||
if (this.sameSite && this.sameSite !== true && !SAME_SITE_REGEXP.test(this.sameSite)) { | ||
@@ -164,2 +198,4 @@ throw new TypeError('option sameSite is invalid') | ||
Cookie.prototype.httpOnly = true; | ||
Cookie.prototype.partitioned = false | ||
Cookie.prototype.priority = undefined | ||
Cookie.prototype.sameSite = false; | ||
@@ -181,5 +217,7 @@ Cookie.prototype.secure = false; | ||
if (this.domain ) header += "; domain=" + this.domain | ||
if (this.priority ) header += "; priority=" + this.priority.toLowerCase() | ||
if (this.sameSite ) header += "; samesite=" + (this.sameSite === true ? 'strict' : this.sameSite.toLowerCase()) | ||
if (this.secure ) header += "; secure" | ||
if (this.httpOnly ) header += "; httponly" | ||
if (this.partitioned) header += '; partitioned' | ||
@@ -198,12 +236,34 @@ return header | ||
function getPattern(name) { | ||
if (cache[name]) return cache[name] | ||
/** | ||
* Get the pattern to search for a cookie in a string. | ||
* @param {string} name | ||
* @private | ||
*/ | ||
return cache[name] = new RegExp( | ||
"(?:^|;) *" + | ||
name.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&") + | ||
"=([^;]*)" | ||
) | ||
function getPattern (name) { | ||
if (!REGEXP_CACHE[name]) { | ||
REGEXP_CACHE[name] = new RegExp( | ||
'(?:^|;) *' + | ||
name.replace(REGEXP_ESCAPE_CHARS_REGEXP, '\\$&') + | ||
'=([^;]*)' | ||
) | ||
} | ||
return REGEXP_CACHE[name] | ||
} | ||
/** | ||
* Get the encrypted status for a request. | ||
* | ||
* @param {object} req | ||
* @return {string} | ||
* @private | ||
*/ | ||
function isRequestEncrypted (req) { | ||
return req.socket | ||
? req.socket.encrypted | ||
: req.connection.encrypted | ||
} | ||
function pushCookie(headers, cookie) { | ||
@@ -210,0 +270,0 @@ if (cookie.overwrite) { |
{ | ||
"name": "cookies", | ||
"description": "Cookies, optionally signed using Keygrip.", | ||
"version": "0.8.0", | ||
"version": "0.9.0", | ||
"author": "Jed Schmidt <tr@nslator.jp> (http://jed.is)", | ||
@@ -16,8 +16,8 @@ "contributors": [ | ||
"devDependencies": { | ||
"eslint": "4.19.1", | ||
"express": "4.17.1", | ||
"mocha": "6.2.1", | ||
"nyc": "14.1.1", | ||
"restify": "8.4.0", | ||
"supertest": "4.0.2" | ||
"eslint": "8.56.0", | ||
"express": "4.18.2", | ||
"mocha": "10.2.0", | ||
"nyc": "15.1.0", | ||
"restify": "8.6.1", | ||
"supertest": "6.3.3" | ||
}, | ||
@@ -36,5 +36,5 @@ "files": [ | ||
"test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/", | ||
"test-ci": "nyc --reporter=text npm test", | ||
"test-ci": "nyc --reporter=lcov --reporter=text npm test", | ||
"test-cov": "nyc --reporter=html --reporter=text npm test" | ||
} | ||
} |
@@ -7,3 +7,3 @@ Cookies | ||
[![Node.js Version][node-version-image]][node-version-url] | ||
[![Build Status][travis-image]][travis-url] | ||
[![Build Status][ci-image]][ci-url] | ||
[![Test Coverage][coveralls-image]][coveralls-url] | ||
@@ -35,17 +35,17 @@ | ||
### cookies = new Cookies( request, response, [ options ] ) | ||
### new Cookies(request, response [, options]) | ||
This creates a cookie jar corresponding to the current _request_ and _response_, additionally passing an object _options_. | ||
Create a new cookie jar for a given `request` and `response` pair. The `request` argument is a [Node.js HTTP incoming request object](https://nodejs.org/dist/latest-v16.x/docs/api/http.html#class-httpincomingmessage) and the `response` argument is a [Node.js HTTP server response object](https://nodejs.org/dist/latest-v16.x/docs/api/http.html#class-httpserverresponse). | ||
A [Keygrip](https://www.npmjs.com/package/keygrip) object or an array of keys can optionally be passed as _options.keys_ to enable cryptographic signing based on SHA1 HMAC, using rotated credentials. | ||
A [Keygrip](https://www.npmjs.com/package/keygrip) object or an array of keys can optionally be passed as `options.keys` to enable cryptographic signing based on SHA1 HMAC, using rotated credentials. | ||
A Boolean can optionally be passed as _options.secure_ to explicitally specify if the connection is secure, rather than this module examining _request_. | ||
A Boolean can optionally be passed as `options.secure` to explicitally specify if the connection is secure, rather than this module examining `request`. | ||
Note that since this only saves parameters without any other processing, it is very lightweight. Cookies are only parsed on demand when they are accessed. | ||
### express.createServer( Cookies.express( keys ) ) | ||
### Cookies.express(keys) | ||
This adds cookie support as a Connect middleware layer for use in Express apps, allowing inbound cookies to be read using `req.cookies.get` and outbound cookies to be set using `res.cookies.set`. | ||
### cookies.get( name, [ options ] ) | ||
### cookies.get(name [, options]) | ||
@@ -62,3 +62,3 @@ This extracts the cookie with the given name from the `Cookie` header in the request. If such a cookie exists, its value is returned. Otherwise, nothing is returned. | ||
### cookies.set( name, [ value ], [ options ] ) | ||
### cookies.set(name [, values [, options]]) | ||
@@ -77,3 +77,5 @@ This sets the given cookie in the response and returns the current context to allow chaining. | ||
* `httpOnly`: a boolean indicating whether the cookie is only to be sent over HTTP(S), and not made available to client JavaScript (`true` by default). | ||
* `sameSite`: a boolean or string indicating whether the cookie is a "same site" cookie (`false` by default). This can be set to `'strict'`, `'lax'`, or `true` (which maps to `'strict'`). | ||
* `partitioned`: a boolean indicating whether to partition the cookie in Chrome for the [CHIPS Update](https://developers.google.com/privacy-sandbox/3pcd/chips) (`false` by default). If this is true, Cookies from embedded sites will be partitioned and only readable from the same top level site from which it was created. | ||
* `priority`: a string indicating the cookie priority. This can be set to `'low'`, `'medium'`, or `'high'`. | ||
* `sameSite`: a boolean or string indicating whether the cookie is a "same site" cookie (`false` by default). This can be set to `'strict'`, `'lax'`, `'none'`, or `true` (which maps to `'strict'`). | ||
* `signed`: a boolean indicating whether the cookie is to be signed (`false` by default). If this is true, another cookie of the same name with the `.sig` suffix appended will also be sent, with a 27-byte url-safe base64 SHA1 value representing the hash of _cookie-name_=_cookie-value_ against the first [Keygrip](https://www.npmjs.com/package/keygrip) key. This signature key is used to detect tampering the next time a cookie is received. | ||
@@ -140,4 +142,8 @@ * `overwrite`: a boolean indicating whether to overwrite previously set cookies of the same name (`false` by default). If this is true, all cookies set during the same request with the same name (regardless of path or domain) are filtered out of the Set-Cookie header when setting this cookie. | ||
[ci-image]: https://badgen.net/github/checks/pillarjs/cookies/master?label=ci | ||
[ci-url]: https://github.com/pillarjs/cookies/actions/workflows/ci.yml | ||
[npm-image]: https://img.shields.io/npm/v/cookies.svg | ||
[npm-url]: https://npmjs.org/package/cookies | ||
[node-version-image]: https://img.shields.io/node/v/cookies.svg | ||
[node-version-url]: http://nodejs.org/download/ | ||
[coveralls-image]: https://img.shields.io/coveralls/pillarjs/cookies/master.svg | ||
@@ -147,5 +153,1 @@ [coveralls-url]: https://coveralls.io/r/pillarjs/cookies?branch=master | ||
[downloads-url]: https://npmjs.org/package/cookies | ||
[node-version-image]: https://img.shields.io/node/v/cookies.svg | ||
[node-version-url]: https://nodejs.org/en/download/ | ||
[travis-image]: https://img.shields.io/travis/pillarjs/cookies/master.svg | ||
[travis-url]: https://travis-ci.org/pillarjs/cookies |
22119
224
148