Socket
Socket
Sign inDemoInstall

@fastify/cookie

Package Overview
Dependencies
Maintainers
19
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@fastify/cookie - npm Package Compare versions

Comparing version 7.3.1 to 7.4.0

benchmark/signer-multi.js

6

package.json
{
"name": "@fastify/cookie",
"version": "7.3.1",
"version": "7.4.0",
"description": "Plugin for fastify to add support for cookies",

@@ -42,5 +42,6 @@ "main": "plugin.js",

"devDependencies": {
"@fastify/pre-commit": "^2.0.2",
"@types/node": "^18.0.0",
"benchmark": "^2.1.4",
"fastify": "^4.0.0-rc.2",
"pre-commit": "^1.2.2",
"sinon": "^14.0.0",

@@ -55,3 +56,2 @@ "snazzy": "^9.0.0",

"cookie": "^0.5.0",
"cookie-signature": "^1.1.0",
"fastify-plugin": "^4.0.0"

@@ -58,0 +58,0 @@ },

@@ -11,7 +11,3 @@ /// <reference types='node' />

*/
unsignCookie(value: string): {
valid: boolean;
renew: boolean;
value: string | null;
};
unsignCookie(value: string): fastifyCookie.UnsignResult;
/**

@@ -43,7 +39,3 @@ * Manual cookie parsing method

*/
unsignCookie(value: string): {
valid: boolean;
renew: boolean;
value: string | null;
};
unsignCookie(value: string): fastifyCookie.UnsignResult;
}

@@ -94,7 +86,3 @@

*/
unsignCookie(value: string): {
valid: boolean;
renew: boolean;
value: string | null;
};
unsignCookie(value: string): fastifyCookie.UnsignResult;
}

@@ -108,11 +96,13 @@ }

declare namespace fastifyCookie {
export interface Signer {
sign: (input: string) => string;
unsign: (input: string) => {
valid: boolean;
renew: boolean;
value: string | null;
};
interface SignerBase {
sign: (value: string) => string;
unsign: (input: string) => UnsignResult;
}
export class Signer implements SignerBase {
constructor (secrets: string | Array<string>, algorithm?: string)
sign: (value: string) => string;
unsign: (input: string) => UnsignResult;
}
export interface CookieSerializeOptions {

@@ -136,6 +126,12 @@ domain?: string;

export type Sign = (value: string, secret: string) => string;
export type Unsign = (input: string, secret: string) => string | false;
export type SignerFactory = (secret: string) => Signer;
export type Sign = (value: string, secret: string, algorithm?: string) => string;
export type Unsign = (input: string, secret: string, algorithm?: string) => UnsignResult;
export type SignerFactory = (secrets: string | Array<string>, algorithm?: string) => SignerBase;
export interface UnsignResult {
valid: boolean;
renew: boolean;
value: string | null;
}
export const signerFactory: SignerFactory;

@@ -147,2 +143,3 @@ export const sign: Sign;

signerFactory: SignerFactory;
Signer: Signer;
sign: Sign;

@@ -155,3 +152,4 @@ unsign: Unsign;

export interface FastifyCookieOptions {
secret?: string | string[] | Signer;
secret?: string | string[] | SignerBase;
algorithm?: string;
parseOptions?: CookieSerializeOptions;

@@ -167,2 +165,2 @@ }

export = fastifyCookie;
export = fastifyCookie;
'use strict'
const { sign, unsign } = require('cookie-signature')
const fp = require('fastify-plugin')
const cookie = require('cookie')
const signerFactory = require('./signer')
const { Signer, sign, unsign } = require('./signer')

@@ -68,3 +67,4 @@ function fastifyCookieSetCookie (reply, name, value, options, signer) {

const enableRotation = Array.isArray(secret)
const signer = typeof secret === 'string' || enableRotation ? signerFactory(secret) : secret
const algorithm = options.algorithm || 'sha256'
const signer = typeof secret === 'string' || enableRotation ? new Signer(secret, algorithm) : secret

@@ -131,2 +131,3 @@ fastify.decorate('parseCookie', parseCookie)

*/
fastifyCookie.signerFactory = Signer
fastifyCookie.fastifyCookie = fastifyCookie

@@ -136,8 +137,10 @@ fastifyCookie.default = fastifyCookie

fastifyCookie.fastifyCookie.signerFactory = signerFactory
fastifyCookie.fastifyCookie.signerFactory = Signer
fastifyCookie.fastifyCookie.Signer = Signer
fastifyCookie.fastifyCookie.sign = sign
fastifyCookie.fastifyCookie.unsign = unsign
module.exports.signerFactory = signerFactory
module.exports.signerFactory = Signer
module.exports.Signer = Signer
module.exports.sign = sign
module.exports.unsign = unsign

@@ -239,8 +239,8 @@ # @fastify/cookie

with signerFactory
with Signer
```js
const { fastifyCookie } = require('@fastify/cookie');
const { Signer } = require('@fastify/cookie');
const signer = fastifyCookie.signerFactory('secret');
const signer = new Signer('secret');
const signedValue = signer.sign('test');

@@ -247,0 +247,0 @@ const {valid, renew, value } = signer.unsign(signedValue);

'use strict'
const cookieSignature = require('cookie-signature')
// Inspired by node-cookie-signature
// https://github.com/tj/node-cookie-signature
//
// The MIT License
// Copyright (c) 2012–2022 LearnBoost <tj@learnboost.com> and other contributors;
module.exports = function (secret) {
const secrets = Array.isArray(secret) ? secret : [secret]
const [signingKey] = secrets
const crypto = require('crypto')
return {
sign (value) {
return cookieSignature.sign(value, signingKey)
},
unsign (signedValue) {
let valid = false
let renew = false
let value = null
const base64PaddingRE = /=/g
for (const key of secrets) {
const result = cookieSignature.unsign(signedValue, key)
function Signer (secrets, algorithm = 'sha256') {
if (!(this instanceof Signer)) {
return new Signer(secrets, algorithm)
}
if (result !== false) {
valid = true
renew = key !== signingKey
value = result
break
}
}
this.secrets = Array.isArray(secrets) ? secrets : [secrets]
this.signingKey = this.secrets[0]
this.algorithm = algorithm
return { valid, renew, value }
validateSecrets(this.secrets)
validateAlgorithm(this.algorithm)
}
function validateSecrets (secrets) {
for (const secret of secrets) {
if (typeof secret !== 'string') {
throw new TypeError('Secret key must be a string.')
}
}
}
function validateAlgorithm (algorithm) {
// validate that the algorithm is supported by the node runtime
try {
crypto.createHmac(algorithm, 'dummyHmac')
} catch (e) {
throw new TypeError(`Algorithm ${algorithm} not supported.`)
}
}
function _sign (value, secret, algorithm) {
if (typeof value !== 'string') {
throw new TypeError('Cookie value must be provided as a string.')
}
return value + '.' + crypto
.createHmac(algorithm, secret)
.update(value)
.digest('base64')
// remove base64 padding (=) as it has special meaning in cookies
.replace(base64PaddingRE, '')
}
function _unsign (signedValue, secrets, algorithm) {
if (typeof signedValue !== 'string') {
throw new TypeError('Signed cookie string must be provided.')
}
const value = signedValue.slice(0, signedValue.lastIndexOf('.'))
const actual = Buffer.from(signedValue.slice(signedValue.lastIndexOf('.') + 1))
for (const secret of secrets) {
const expected = Buffer.from(crypto
.createHmac(algorithm, secret)
.update(value)
.digest('base64')
// remove base64 padding (=) as it has special meaning in cookies
.replace(base64PaddingRE, ''))
if (
expected.length === actual.length &&
crypto.timingSafeEqual(expected, actual)
) {
return {
valid: true,
renew: secret !== secrets[0],
value
}
}
}
return {
valid: false,
renew: false,
value: null
}
}
Signer.prototype.sign = function (value) {
return _sign(value, this.signingKey, this.algorithm)
}
Signer.prototype.unsign = function (signedValue) {
return _unsign(signedValue, this.secrets, this.algorithm)
}
function sign (value, secret, algorithm = 'sha256') {
const secrets = Array.isArray(secret) ? secret : [secret]
validateSecrets(secrets)
return _sign(value, secrets[0], algorithm)
}
function unsign (signedValue, secret, algorithm = 'sha256') {
const secrets = Array.isArray(secret) ? secret : [secret]
validateSecrets(secrets)
return _unsign(signedValue, secrets, algorithm)
}
module.exports = Signer
module.exports.signerFactory = Signer
module.exports.Signer = Signer
module.exports.sign = sign
module.exports.unsign = unsign

@@ -7,3 +7,3 @@ 'use strict'

const sinon = require('sinon')
const cookieSignature = require('cookie-signature')
const { sign, unsign } = require('../signer')
const plugin = require('../')

@@ -155,3 +155,3 @@

t.equal(cookies[0].name, 'foo')
t.equal(cookies[0].value, cookieSignature.sign('foo', secret))
t.equal(cookies[0].value, sign('foo', secret))
t.equal(cookies[0].maxAge, 36000)

@@ -195,3 +195,3 @@

t.equal(cookies[0].name, 'foo')
t.equal(cookies[0].value, cookieSignature.sign('foo', secret))
t.equal(cookies[0].value, sign('foo', secret))
const expires = new Date(cookies[0].expires)

@@ -384,3 +384,3 @@ t.ok(expires < new Date(Date.now() + 5000))

t.equal(cookies[0].name, 'foo')
t.equal(cookieSignature.unsign(cookies[0].value, secret), 'foo')
t.same(unsign(cookies[0].value, secret), { valid: true, renew: false, value: 'foo' })
})

@@ -413,3 +413,3 @@ })

t.equal(cookies[0].name, 'foo')
t.equal(cookieSignature.unsign(cookies[0].value, secret1), 'cookieVal') // decode using first key
t.same(unsign(cookies[0].value, secret1), { valid: true, renew: false, value: 'cookieVal' }) // decode using first key
})

@@ -435,3 +435,3 @@ })

headers: {
cookie: `foo=${cookieSignature.sign('foo', secret)}`
cookie: `foo=${sign('foo', secret)}`
}

@@ -461,3 +461,3 @@ }, (err, res) => {

headers: {
cookie: `foo=${cookieSignature.sign('foo', secret)}`
cookie: `foo=${sign('foo', secret)}`
}

@@ -487,3 +487,3 @@ }, (err, res) => {

headers: {
cookie: `foo=${cookieSignature.sign('foo', secret)}`
cookie: `foo=${sign('foo', secret)}`
}

@@ -514,3 +514,3 @@ }, (err, res) => {

headers: {
cookie: `foo=${cookieSignature.sign('foo', secret2)}`
cookie: `foo=${sign('foo', secret2)}`
}

@@ -541,3 +541,3 @@ }, (err, res) => {

headers: {
cookie: `foo=${cookieSignature.sign('foo', secret2)}`
cookie: `foo=${sign('foo', secret2)}`
}

@@ -568,3 +568,3 @@ }, (err, res) => {

headers: {
cookie: `foo=${cookieSignature.sign('foo', 'invalid-secret')}`
cookie: `foo=${sign('foo', 'invalid-secret')}`
}

@@ -595,3 +595,3 @@ }, (err, res) => {

headers: {
cookie: `foo=${cookieSignature.sign('foo', 'invalid-secret')}`
cookie: `foo=${sign('foo', 'invalid-secret')}`
}

@@ -598,0 +598,0 @@ }, (err, res) => {

@@ -112,2 +112,6 @@ import cookie from '../plugin';

});
appWithImplicitHttpSigned.register(cookie, {
secret: 'testsecret',
algorithm: 'sha512'
});
appWithImplicitHttpSigned.after(() => {

@@ -198,1 +202,7 @@ server.get('/', (request, reply) => {

})
new fastifyCookieStar.Signer('secretString')
new fastifyCookieStar.Signer(['secretStringInArray'])
const signer = new fastifyCookieStar.Signer(['secretStringInArray'], 'sha256')
signer.sign('Lorem Ipsum')
signer.unsign('Lorem Ipsum')

@@ -5,24 +5,43 @@ 'use strict'

const sinon = require('sinon')
const cookieSignature = require('cookie-signature')
const signerFactory = require('../signer')
const crypto = require('crypto')
const { Signer, sign, unsign } = require('../signer')
test('default', (t) => {
t.plan(2)
test('default', t => {
t.plan(5)
const secret = 'my-secret'
const signer = signerFactory(secret)
const signer = Signer(secret)
t.test('signer.sign', (t) => {
t.test('signer.sign should throw if there is no value provided', (t) => {
t.plan(1)
t.throws(() => signer.sign(undefined), 'Cookie value must be provided as a string.')
})
t.test('signer.sign', (t) => {
t.plan(2)
const input = 'some-value'
const result = signer.sign(input)
t.equal(result, cookieSignature.sign(input, secret))
t.equal(result, sign(input, secret))
t.throws(() => sign(undefined), 'Cookie value must be provided as a string.')
})
t.test('signer.unsign', (t) => {
t.test('sign', (t) => {
t.plan(3)
const input = cookieSignature.sign('some-value', secret)
const input = 'some-value'
const result = signer.sign(input)
t.equal(result, sign(input, secret))
t.equal(result, sign(input, [secret]))
t.throws(() => sign(undefined), 'Cookie value must be provided as a string.')
})
t.test('signer.unsign', (t) => {
t.plan(4)
const input = signer.sign('some-value', secret)
const result = signer.unsign(input)

@@ -33,3 +52,18 @@

t.equal(result.value, 'some-value')
t.throws(() => signer.unsign(undefined), 'Signed cookie string must be provided.')
})
t.test('unsign', (t) => {
t.plan(6)
const input = sign('some-value', secret)
const result = unsign(input, secret)
t.equal(result.valid, true)
t.equal(result.renew, false)
t.equal(result.value, 'some-value')
t.same(result, unsign(input, [secret]))
t.throws(() => unsign(undefined), 'Secret key must be a string.')
t.throws(() => unsign(undefined, secret), 'Signed cookie string must be provided.')
})
})

@@ -42,7 +76,7 @@

const secret3 = 'my-secret-3'
const signer = signerFactory([secret1, secret2, secret3])
const unsignSpy = sinon.spy(cookieSignature, 'unsign')
const signer = Signer([secret1, secret2, secret3])
const signSpy = sinon.spy(crypto, 'createHmac')
t.beforeEach(() => {
unsignSpy.resetHistory()
signSpy.resetHistory()
})

@@ -56,3 +90,3 @@

t.equal(result, cookieSignature.sign(input, secret1))
t.equal(result, sign(input, secret1))
})

@@ -63,3 +97,4 @@

const input = cookieSignature.sign('some-value', secret2)
const input = sign('some-value', secret2)
signSpy.resetHistory()
const result = signer.unsign(input)

@@ -70,3 +105,3 @@

t.equal(result.value, 'some-value')
t.equal(unsignSpy.callCount, 2) // should have returned early when the right key was found
t.equal(signSpy.callCount, 2) // should have returned early when the right key was found
})

@@ -77,3 +112,4 @@

const input = cookieSignature.sign('some-value', 'invalid-secret')
const input = sign('some-value', 'invalid-secret')
signSpy.resetHistory()
const result = signer.unsign(input)

@@ -84,4 +120,23 @@

t.equal(result.value, null)
t.equal(unsignSpy.callCount, 3) // should have tried all 3
t.equal(signSpy.callCount, 3) // should have tried all 3
})
})
test('Signer', t => {
t.plan(2)
t.test('Signer needs a string as secret', (t) => {
t.plan(4)
t.throws(() => Signer(1), 'Secret key must be a string.')
t.throws(() => Signer(undefined), 'Secret key must be a string.')
t.doesNotThrow(() => Signer('secret'))
t.doesNotThrow(() => Signer(['secret']))
})
t.test('Signer handles algorithm properly', (t) => {
t.plan(3)
t.throws(() => Signer('secret', 'invalid'), 'Algorithm invalid not supported.')
t.doesNotThrow(() => Signer('secret', 'sha512'))
t.doesNotThrow(() => Signer('secret', 'sha256'))
})
})

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc