@fastify/cookie 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +13 -0
- package/.github/dependabot.yml +13 -0
- package/.github/stale.yml +21 -0
- package/.github/workflows/ci.yml +49 -0
- package/.taprc +3 -0
- package/LICENSE +21 -0
- package/README.md +206 -0
- package/cookie.js +225 -0
- package/package.json +62 -0
- package/plugin.d.ts +116 -0
- package/plugin.js +110 -0
- package/signer.js +32 -0
- package/test/cookie-module.test.js +205 -0
- package/test/cookie.test.js +703 -0
- package/test/plugin.test-d.ts +191 -0
- package/test/signer.test.js +79 -0
package/plugin.d.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/// <reference types='node' />
|
|
2
|
+
|
|
3
|
+
import { FastifyPluginCallback } from 'fastify';
|
|
4
|
+
|
|
5
|
+
declare module 'fastify' {
|
|
6
|
+
interface FastifyInstance {
|
|
7
|
+
/**
|
|
8
|
+
* Unsigns the specified cookie using the secret provided.
|
|
9
|
+
* @param value Cookie value
|
|
10
|
+
*/
|
|
11
|
+
unsignCookie(value: string): {
|
|
12
|
+
valid: boolean;
|
|
13
|
+
renew: boolean;
|
|
14
|
+
value: string | null;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Manual cookie parsing method
|
|
18
|
+
* @docs https://github.com/fastify/fastify-cookie#manual-cookie-parsing
|
|
19
|
+
* @param cookieHeader Raw cookie header value
|
|
20
|
+
*/
|
|
21
|
+
parseCookie(cookieHeader: string): {
|
|
22
|
+
[key: string]: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface FastifyRequest {
|
|
27
|
+
/**
|
|
28
|
+
* Request cookies
|
|
29
|
+
*/
|
|
30
|
+
cookies: { [cookieName: string]: string };
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Unsigns the specified cookie using the secret provided.
|
|
34
|
+
* @param value Cookie value
|
|
35
|
+
*/
|
|
36
|
+
unsignCookie(value: string): {
|
|
37
|
+
valid: boolean;
|
|
38
|
+
renew: boolean;
|
|
39
|
+
value: string | null;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type setCookieWrapper = (
|
|
44
|
+
name: string,
|
|
45
|
+
value: string,
|
|
46
|
+
options?: CookieSerializeOptions
|
|
47
|
+
) => FastifyReply;
|
|
48
|
+
|
|
49
|
+
interface FastifyReply {
|
|
50
|
+
/**
|
|
51
|
+
* Set response cookie
|
|
52
|
+
* @name setCookie
|
|
53
|
+
* @param name Cookie name
|
|
54
|
+
* @param value Cookie value
|
|
55
|
+
* @param options Serialize options
|
|
56
|
+
*/
|
|
57
|
+
setCookie(
|
|
58
|
+
name: string,
|
|
59
|
+
value: string,
|
|
60
|
+
options?: CookieSerializeOptions
|
|
61
|
+
): this;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @alias setCookie
|
|
65
|
+
*/
|
|
66
|
+
cookie(name: string, value: string, options?: CookieSerializeOptions): this;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* clear response cookie
|
|
70
|
+
* @param name Cookie name
|
|
71
|
+
* @param options Serialize options
|
|
72
|
+
*/
|
|
73
|
+
clearCookie(name: string, options?: CookieSerializeOptions): this;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Unsigns the specified cookie using the secret provided.
|
|
77
|
+
* @param value Cookie value
|
|
78
|
+
*/
|
|
79
|
+
unsignCookie(value: string): {
|
|
80
|
+
valid: boolean;
|
|
81
|
+
renew: boolean;
|
|
82
|
+
value: string | null;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface CookieSerializeOptions {
|
|
88
|
+
domain?: string;
|
|
89
|
+
encode?(val: string): string;
|
|
90
|
+
expires?: Date;
|
|
91
|
+
httpOnly?: boolean;
|
|
92
|
+
maxAge?: number;
|
|
93
|
+
path?: string;
|
|
94
|
+
sameSite?: boolean | 'lax' | 'strict' | 'none';
|
|
95
|
+
secure?: boolean;
|
|
96
|
+
signed?: boolean;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
interface Signer {
|
|
100
|
+
sign: (input: string) => string;
|
|
101
|
+
unsign: (input: string) => {
|
|
102
|
+
valid: boolean;
|
|
103
|
+
renew: boolean;
|
|
104
|
+
value: string | null;
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface FastifyCookieOptions {
|
|
109
|
+
secret?: string | string[] | Signer;
|
|
110
|
+
parseOptions?: CookieSerializeOptions;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
declare const fastifyCookie: FastifyPluginCallback<NonNullable<FastifyCookieOptions>>;
|
|
114
|
+
|
|
115
|
+
export default fastifyCookie;
|
|
116
|
+
export { fastifyCookie };
|
package/plugin.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fp = require('fastify-plugin')
|
|
4
|
+
const cookie = require('./cookie')
|
|
5
|
+
|
|
6
|
+
const signerFactory = require('./signer')
|
|
7
|
+
|
|
8
|
+
function fastifyCookieSetCookie (reply, name, value, options, signer) {
|
|
9
|
+
const opts = Object.assign({}, options)
|
|
10
|
+
if (opts.expires && Number.isInteger(opts.expires)) {
|
|
11
|
+
opts.expires = new Date(opts.expires)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (opts.signed) {
|
|
15
|
+
value = signer.sign(value)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const serialized = cookie.serialize(name, value, opts)
|
|
19
|
+
let setCookie = reply.getHeader('Set-Cookie')
|
|
20
|
+
if (!setCookie) {
|
|
21
|
+
reply.header('Set-Cookie', serialized)
|
|
22
|
+
return reply
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (typeof setCookie === 'string') {
|
|
26
|
+
setCookie = [setCookie]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
setCookie.push(serialized)
|
|
30
|
+
reply.removeHeader('Set-Cookie')
|
|
31
|
+
reply.header('Set-Cookie', setCookie)
|
|
32
|
+
return reply
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function fastifyCookieClearCookie (reply, name, options) {
|
|
36
|
+
const opts = Object.assign({ path: '/' }, options || { }, {
|
|
37
|
+
expires: new Date(1),
|
|
38
|
+
signed: undefined,
|
|
39
|
+
maxAge: undefined
|
|
40
|
+
})
|
|
41
|
+
return fastifyCookieSetCookie(reply, name, '', opts)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function onReqHandlerWrapper (fastify) {
|
|
45
|
+
return function fastifyCookieOnReqHandler (fastifyReq, fastifyRes, done) {
|
|
46
|
+
fastifyReq.cookies = {} // New container per request. Issue #53
|
|
47
|
+
const cookieHeader = fastifyReq.raw.headers.cookie
|
|
48
|
+
if (cookieHeader) {
|
|
49
|
+
fastifyReq.cookies = fastify.parseCookie(cookieHeader)
|
|
50
|
+
}
|
|
51
|
+
done()
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function plugin (fastify, options, next) {
|
|
56
|
+
const secret = options.secret || ''
|
|
57
|
+
const enableRotation = Array.isArray(secret)
|
|
58
|
+
const signer = typeof secret === 'string' || enableRotation ? signerFactory(secret) : secret
|
|
59
|
+
|
|
60
|
+
fastify.decorate('parseCookie', parseCookie)
|
|
61
|
+
fastify.decorate('unsignCookie', unsignCookie)
|
|
62
|
+
|
|
63
|
+
fastify.decorateRequest('cookies', null)
|
|
64
|
+
fastify.decorateRequest('unsignCookie', unsignCookie)
|
|
65
|
+
fastify.decorateReply('setCookie', setCookie)
|
|
66
|
+
fastify.decorateReply('cookie', setCookie)
|
|
67
|
+
fastify.decorateReply('clearCookie', clearCookie)
|
|
68
|
+
fastify.decorateReply('unsignCookie', unsignCookie)
|
|
69
|
+
|
|
70
|
+
fastify.addHook('onRequest', onReqHandlerWrapper(fastify))
|
|
71
|
+
|
|
72
|
+
next()
|
|
73
|
+
|
|
74
|
+
// ***************************
|
|
75
|
+
function parseCookie (cookieHeader) {
|
|
76
|
+
return cookie.parse(cookieHeader, options.parseOptions)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function unsignCookie (value) {
|
|
80
|
+
return signer.unsign(value)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function setCookie (name, value, cookieOptions) {
|
|
84
|
+
const opts = Object.assign({}, options.parseOptions, cookieOptions)
|
|
85
|
+
return fastifyCookieSetCookie(this, name, value, opts, signer)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function clearCookie (name, options) {
|
|
89
|
+
return fastifyCookieClearCookie(this, name, options)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const fastifyCookie = fp(plugin, {
|
|
94
|
+
fastify: '>=3',
|
|
95
|
+
name: 'fastify-cookie'
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* These export configurations enable JS and TS developers
|
|
100
|
+
* to consume fastify-cookie in whatever way best suits their needs.
|
|
101
|
+
* Some examples of supported import syntax includes:
|
|
102
|
+
* - `const fastifyCookie = require('fastify-cookie')`
|
|
103
|
+
* - `const { fastifyCookie } = require('fastify-cookie')`
|
|
104
|
+
* - `import * as fastifyCookie from 'fastify-cookie'`
|
|
105
|
+
* - `import { fastifyCookie } from 'fastify-cookie'`
|
|
106
|
+
* - `import fastifyCookie from 'fastify-cookie'`
|
|
107
|
+
*/
|
|
108
|
+
fastifyCookie.fastifyCookie = fastifyCookie
|
|
109
|
+
fastifyCookie.default = fastifyCookie
|
|
110
|
+
module.exports = fastifyCookie
|
package/signer.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const cookieSignature = require('cookie-signature')
|
|
4
|
+
|
|
5
|
+
module.exports = function (secret) {
|
|
6
|
+
const secrets = Array.isArray(secret) ? secret : [secret]
|
|
7
|
+
const [signingKey] = secrets
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
sign (value) {
|
|
11
|
+
return cookieSignature.sign(value, signingKey)
|
|
12
|
+
},
|
|
13
|
+
unsign (signedValue) {
|
|
14
|
+
let valid = false
|
|
15
|
+
let renew = false
|
|
16
|
+
let value = null
|
|
17
|
+
|
|
18
|
+
for (const key of secrets) {
|
|
19
|
+
const result = cookieSignature.unsign(signedValue, key)
|
|
20
|
+
|
|
21
|
+
if (result !== false) {
|
|
22
|
+
valid = true
|
|
23
|
+
renew = key !== signingKey
|
|
24
|
+
value = result
|
|
25
|
+
break
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return { valid, renew, value }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const tap = require('tap')
|
|
4
|
+
const test = tap.test
|
|
5
|
+
|
|
6
|
+
const cookie = require('../cookie')
|
|
7
|
+
|
|
8
|
+
test('parse: argument validation', (t) => {
|
|
9
|
+
t.plan(2)
|
|
10
|
+
t.throws(cookie.parse.bind(), /argument str must be a string/)
|
|
11
|
+
t.throws(cookie.parse.bind(null, 42), /argument str must be a string/)
|
|
12
|
+
t.end()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('parse: basic', (t) => {
|
|
16
|
+
t.plan(2)
|
|
17
|
+
t.same(cookie.parse('foo=bar'), { foo: 'bar' })
|
|
18
|
+
t.same(cookie.parse('foo=123'), { foo: '123' })
|
|
19
|
+
t.end()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('parse: ignore spaces', (t) => {
|
|
23
|
+
t.plan(1)
|
|
24
|
+
t.same(cookie.parse('FOO = bar; baz = raz'), { FOO: 'bar', baz: 'raz' })
|
|
25
|
+
t.end()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('parse: escaping', (t) => {
|
|
29
|
+
t.plan(2)
|
|
30
|
+
t.same(cookie.parse('foo="bar=123456789&name=Magic+Mouse"'), { foo: 'bar=123456789&name=Magic+Mouse' })
|
|
31
|
+
t.same(cookie.parse('email=%20%22%2c%3b%2f'), { email: ' ",;/' })
|
|
32
|
+
t.end()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('parse: ignore escaping error and return original value', (t) => {
|
|
36
|
+
t.plan(1)
|
|
37
|
+
t.same(cookie.parse('foo=%1;bar=bar'), { foo: '%1', bar: 'bar' })
|
|
38
|
+
t.end()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('parse: ignore non values', (t) => {
|
|
42
|
+
t.plan(1)
|
|
43
|
+
t.same(cookie.parse('foo=%1;bar=bar;HttpOnly;Secure'),
|
|
44
|
+
{ foo: '%1', bar: 'bar' })
|
|
45
|
+
t.end()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('parse: unencoded', (t) => {
|
|
49
|
+
t.plan(2)
|
|
50
|
+
t.same(cookie.parse('foo="bar=123456789&name=Magic+Mouse"', {
|
|
51
|
+
decode: function (v) { return v }
|
|
52
|
+
}), { foo: 'bar=123456789&name=Magic+Mouse' })
|
|
53
|
+
|
|
54
|
+
t.same(cookie.parse('email=%20%22%2c%3b%2f', {
|
|
55
|
+
decode: function (v) { return v }
|
|
56
|
+
}), { email: '%20%22%2c%3b%2f' })
|
|
57
|
+
t.end()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('parse: dates', (t) => {
|
|
61
|
+
t.plan(1)
|
|
62
|
+
t.same(cookie.parse('priority=true; expires=Wed, 29 Jan 2014 17:43:25 GMT; Path=/', {
|
|
63
|
+
decode: function (v) { return v }
|
|
64
|
+
}), { priority: 'true', Path: '/', expires: 'Wed, 29 Jan 2014 17:43:25 GMT' })
|
|
65
|
+
t.end()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('parse: missing value', (t) => {
|
|
69
|
+
t.plan(1)
|
|
70
|
+
t.same(cookie.parse('foo; bar=1; fizz= ; buzz=2', {
|
|
71
|
+
decode: function (v) { return v }
|
|
72
|
+
}), { bar: '1', fizz: '', buzz: '2' })
|
|
73
|
+
t.end()
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('parse: assign only once', (t) => {
|
|
77
|
+
t.plan(3)
|
|
78
|
+
t.same(cookie.parse('foo=%1;bar=bar;foo=boo'), { foo: '%1', bar: 'bar' })
|
|
79
|
+
t.same(cookie.parse('foo=false;bar=bar;foo=true'), { foo: 'false', bar: 'bar' })
|
|
80
|
+
t.same(cookie.parse('foo=;bar=bar;foo=boo'), { foo: '', bar: 'bar' })
|
|
81
|
+
t.end()
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test('serializer: basic', (t) => {
|
|
85
|
+
t.plan(6)
|
|
86
|
+
t.same(cookie.serialize('foo', 'bar'), 'foo=bar')
|
|
87
|
+
t.same(cookie.serialize('foo', 'bar baz'), 'foo=bar%20baz')
|
|
88
|
+
t.same(cookie.serialize('foo', ''), 'foo=')
|
|
89
|
+
t.throws(cookie.serialize.bind(cookie, 'foo\n', 'bar'), /argument name is invalid/)
|
|
90
|
+
t.throws(cookie.serialize.bind(cookie, 'foo\u280a', 'bar'), /argument name is invalid/)
|
|
91
|
+
t.throws(cookie.serialize.bind(cookie, 'foo', 'bar', { encode: 42 }), /option encode is invalid/)
|
|
92
|
+
t.end()
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
test('serializer: path', (t) => {
|
|
96
|
+
t.plan(2)
|
|
97
|
+
t.same(cookie.serialize('foo', 'bar', { path: '/' }), 'foo=bar; Path=/')
|
|
98
|
+
t.throws(cookie.serialize.bind(cookie, 'foo', 'bar', {
|
|
99
|
+
path: '/\n'
|
|
100
|
+
}), /option path is invalid/)
|
|
101
|
+
t.end()
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test('serializer: secure', (t) => {
|
|
105
|
+
t.plan(2)
|
|
106
|
+
t.same(cookie.serialize('foo', 'bar', { secure: true }), 'foo=bar; Secure')
|
|
107
|
+
t.same(cookie.serialize('foo', 'bar', { secure: false }), 'foo=bar')
|
|
108
|
+
t.end()
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test('serializer: domain', (t) => {
|
|
112
|
+
t.plan(2)
|
|
113
|
+
t.same(cookie.serialize('foo', 'bar', { domain: 'example.com' }), 'foo=bar; Domain=example.com')
|
|
114
|
+
t.throws(cookie.serialize.bind(cookie, 'foo', 'bar', {
|
|
115
|
+
domain: 'example.com\n'
|
|
116
|
+
}), /option domain is invalid/)
|
|
117
|
+
t.end()
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
test('serializer: httpOnly', (t) => {
|
|
121
|
+
t.plan(1)
|
|
122
|
+
t.same(cookie.serialize('foo', 'bar', { httpOnly: true }), 'foo=bar; HttpOnly')
|
|
123
|
+
t.end()
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
test('serializer: maxAge', (t) => {
|
|
127
|
+
t.plan(9)
|
|
128
|
+
t.throws(function () {
|
|
129
|
+
cookie.serialize('foo', 'bar', {
|
|
130
|
+
maxAge: 'buzz'
|
|
131
|
+
})
|
|
132
|
+
}, /option maxAge is invalid/)
|
|
133
|
+
|
|
134
|
+
t.throws(function () {
|
|
135
|
+
cookie.serialize('foo', 'bar', {
|
|
136
|
+
maxAge: Infinity
|
|
137
|
+
})
|
|
138
|
+
}, /option maxAge is invalid/)
|
|
139
|
+
|
|
140
|
+
t.same(cookie.serialize('foo', 'bar', { maxAge: 1000 }), 'foo=bar; Max-Age=1000')
|
|
141
|
+
t.same(cookie.serialize('foo', 'bar', { maxAge: '1000' }), 'foo=bar; Max-Age=1000')
|
|
142
|
+
t.same(cookie.serialize('foo', 'bar', { maxAge: 0 }), 'foo=bar; Max-Age=0')
|
|
143
|
+
t.same(cookie.serialize('foo', 'bar', { maxAge: '0' }), 'foo=bar; Max-Age=0')
|
|
144
|
+
t.same(cookie.serialize('foo', 'bar', { maxAge: null }), 'foo=bar')
|
|
145
|
+
t.same(cookie.serialize('foo', 'bar', { maxAge: undefined }), 'foo=bar')
|
|
146
|
+
t.same(cookie.serialize('foo', 'bar', { maxAge: 3.14 }), 'foo=bar; Max-Age=3')
|
|
147
|
+
t.end()
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test('serializer: expires', (t) => {
|
|
151
|
+
t.plan(2)
|
|
152
|
+
t.same(cookie.serialize('foo', 'bar', {
|
|
153
|
+
expires: new Date(Date.UTC(2000, 11, 24, 10, 30, 59, 900))
|
|
154
|
+
}), 'foo=bar; Expires=Sun, 24 Dec 2000 10:30:59 GMT')
|
|
155
|
+
|
|
156
|
+
t.throws(cookie.serialize.bind(cookie, 'foo', 'bar', {
|
|
157
|
+
expires: Date.now()
|
|
158
|
+
}), /option expires is invalid/)
|
|
159
|
+
t.end()
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
test('sameSite', (t) => {
|
|
163
|
+
t.plan(9)
|
|
164
|
+
t.same(cookie.serialize('foo', 'bar', { sameSite: true }), 'foo=bar; SameSite=Strict')
|
|
165
|
+
t.same(cookie.serialize('foo', 'bar', { sameSite: 'Strict' }), 'foo=bar; SameSite=Strict')
|
|
166
|
+
t.same(cookie.serialize('foo', 'bar', { sameSite: 'strict' }), 'foo=bar; SameSite=Strict')
|
|
167
|
+
t.same(cookie.serialize('foo', 'bar', { sameSite: 'Lax' }), 'foo=bar; SameSite=Lax')
|
|
168
|
+
t.same(cookie.serialize('foo', 'bar', { sameSite: 'lax' }), 'foo=bar; SameSite=Lax')
|
|
169
|
+
t.same(cookie.serialize('foo', 'bar', { sameSite: 'None' }), 'foo=bar; SameSite=None')
|
|
170
|
+
t.same(cookie.serialize('foo', 'bar', { sameSite: 'none' }), 'foo=bar; SameSite=None')
|
|
171
|
+
t.same(cookie.serialize('foo', 'bar', { sameSite: false }), 'foo=bar')
|
|
172
|
+
|
|
173
|
+
t.throws(cookie.serialize.bind(cookie, 'foo', 'bar', {
|
|
174
|
+
sameSite: 'foo'
|
|
175
|
+
}), /option sameSite is invalid/)
|
|
176
|
+
t.end()
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
test('escaping', (t) => {
|
|
180
|
+
t.plan(1)
|
|
181
|
+
t.same(cookie.serialize('cat', '+ '), 'cat=%2B%20')
|
|
182
|
+
t.end()
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
test('parse->serialize', (t) => {
|
|
186
|
+
t.plan(2)
|
|
187
|
+
t.same(cookie.parse(cookie.serialize('cat', 'foo=123&name=baz five')),
|
|
188
|
+
{ cat: 'foo=123&name=baz five' })
|
|
189
|
+
|
|
190
|
+
t.same(cookie.parse(cookie.serialize('cat', ' ";/')),
|
|
191
|
+
{ cat: ' ";/' })
|
|
192
|
+
t.end()
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
test('unencoded', (t) => {
|
|
196
|
+
t.plan(2)
|
|
197
|
+
t.same(cookie.serialize('cat', '+ ', {
|
|
198
|
+
encode: function (value) { return value }
|
|
199
|
+
}), 'cat=+ ')
|
|
200
|
+
|
|
201
|
+
t.throws(cookie.serialize.bind(cookie, 'cat', '+ \n', {
|
|
202
|
+
encode: function (value) { return value }
|
|
203
|
+
}), /argument val is invalid/)
|
|
204
|
+
t.end()
|
|
205
|
+
})
|