Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

crumb

Package Overview
Dependencies
Maintainers
5
Versions
46
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

crumb - npm Package Compare versions

Comparing version 7.0.0 to 7.1.0

24

lib/index.js

@@ -23,4 +23,6 @@ 'use strict';

cookieOptions: Joi.object().keys(null),
headerName: Joi.string().optional(),
restful: Joi.boolean().optional(),
skip: Joi.func().optional()
skip: Joi.func().optional(),
logUnauthorized: Joi.boolean().optional()
});

@@ -37,4 +39,6 @@

},
restful: false, // Set to true for X-CSRF-Token header crumb validation. Disables payload/query validation
skip: false // Set to a function which returns true when to skip crumb generation and validation
headerName: 'X-CSRF-Token', // Specify the name of the custom CSRF header
restful: false, // Set to true for custom header crumb validation. Disables payload/query validation
skip: false, // Set to a function which returns true when to skip crumb generation and validation,
logUnauthorized: false // Set to true for crumb to write an event to the request log
};

@@ -58,2 +62,9 @@

const unauthorizedLogger = () => {
if (settings.logUnauthorized) {
request.log(['crumb', 'unauthorized'], 'validation failed');
}
};
// If skip function enabled. Call it and if returns true, do not attempt to do anything with crumb.

@@ -103,3 +114,3 @@

if (!content || content instanceof Stream) {
unauthorizedLogger();
throw Boom.forbidden();

@@ -109,2 +120,3 @@ }

if (content[request.route.settings.plugins._crumb.key] !== request.plugins.crumb) {
unauthorizedLogger();
throw Boom.forbidden();

@@ -124,5 +136,6 @@ }

const header = request.headers['x-csrf-token'];
const header = request.headers[settings.headerName.toLowerCase()];
if (!header) {
unauthorizedLogger();
throw Boom.forbidden();

@@ -132,2 +145,3 @@ }

if (header !== request.plugins.crumb) {
unauthorizedLogger();
throw Boom.forbidden();

@@ -134,0 +148,0 @@ }

{
"name": "crumb",
"description": "CSRF crumb generation and validation plugin",
"version": "7.0.0",
"version": "7.1.0",
"repository": "git://github.com/hapijs/crumb",

@@ -6,0 +6,0 @@ "bugs": {

@@ -71,5 +71,7 @@ ![crumb Logo](https://raw.github.com/hapijs/crumb/master/images/crumb.png)

* `cookieOptions` - storage options for the cookie containing the crumb, see the [server.state](http://hapijs.com/api#serverstatename-options) documentation of hapi for more information. Default to `cookieOptions.path=/`
* `headerName` - specify the name of the custom CSRF header. Defaults to `X-CSRF-Token`.
* `restful` - RESTful mode that validates crumb tokens from *"X-CSRF-Token"* request header for **POST**, **PUT**, **PATCH** and **DELETE** server routes. Disables payload/query crumb validation. Defaults to `false`.
* `skip` - a function with the signature of `function (request, h) {}`, which when provided, is called for every request. If the provided function returns true, validation and generation of crumb is skipped. Defaults to `false`.
* `logUnauthorized` - whether to add to the request log with tag 'crumb' and data 'validation failed' (defaults to false)
### Routes configuration

@@ -76,0 +78,0 @@

@@ -23,3 +23,3 @@ 'use strict';

const str = '' + i;
const buf = new Buffer(str, 'ascii');
const buf = Buffer.from(str, 'ascii');
this.push(buf);

@@ -26,0 +26,0 @@ }

@@ -315,2 +315,101 @@ 'use strict';

it('Adds to the request log if plugin option logUnauthorized is set to true', async () => {
const server = new Hapi.Server();
let logFound;
const preResponse = function (request, h) {
const logs = request.logs;
logFound = logs.find((log) => {
return log.tags[0] === 'crumb' && log.data === 'validation failed';
});
return h.continue;
};
server.ext('onPreResponse', preResponse);
server.route({
method: 'POST',
path: '/1',
config: {
log: {
collect: true
}
},
handler: (request, h) => 'test'
});
await server.register([
{
plugin: Crumb,
options: {
logUnauthorized: true
}
}
]);
const headers = {};
headers['X-API-Token'] = 'test';
await server.inject({
method: 'POST',
url: '/1',
headers
});
expect(logFound).to.exist();
});
it('Does not add to the request log if plugin option logUnauthorized is set to false', async () => {
const server = new Hapi.Server();
let logFound;
const preResponse = function (request, h) {
const logs = request.logs;
logFound = logs.find((log) => {
return log.tags[0] === 'crumb' && log.data === 'validation failed';
});
return h.continue;
};
server.ext('onPreResponse', preResponse);
server.route({
method: 'POST',
path: '/1',
config: {
log: {
collect: true
}
},
handler: (request, h) => 'test'
});
await server.register([
{
plugin: Crumb,
options: {
logUnauthorized: false
}
}
]);
const headers = {};
headers['X-API-Token'] = 'test';
await server.inject({
method: 'POST',
url: '/1',
headers
});
expect(logFound).to.not.exist();
});
it('should fail to register with bad options', async () => {

@@ -832,2 +931,234 @@

});
it('validates crumb with a custom header name', async () => {
const server = new Hapi.Server();
server.route([
{
method: 'GET',
path: '/1',
handler: (request, h) => {
expect(request.plugins.crumb).to.exist();
expect(request.server.plugins.crumb.generate).to.exist();
return h.view('index', {
title: 'test',
message: 'hi'
});
}
},
{
method: 'POST',
path: '/2',
handler: (request, h) => {
expect(request.payload).to.equal({ key: 'value' });
return 'valid';
}
},
{
method: 'POST',
path: '/3',
options: { payload: { output: 'stream' } },
handler: (request, h) => 'never'
},
{
method: 'PUT',
path: '/4',
handler: (request, h) => {
expect(request.payload).to.equal({ key: 'value' });
return 'valid';
}
},
{
method: 'PATCH',
path: '/5',
handler: (request, h) => {
expect(request.payload).to.equal({ key: 'value' });
return 'valid';
}
},
{
method: 'DELETE',
path: '/6',
handler: (request, h) => 'valid'
},
{
method: 'POST',
path: '/7',
options: {
plugins: {
crumb: false
}
},
handler: (request, h) => {
expect(request.payload).to.equal({ key: 'value' });
return 'valid';
}
},
{
method: 'POST',
path: '/8',
options: {
plugins: {
crumb: {
restful: false,
source: 'payload'
}
}
},
handler: (request, h) => {
expect(request.payload).to.equal({ key: 'value' });
return 'valid';
}
}
]);
await server.register([
Vision,
{
plugin: Crumb,
options: {
restful: true,
cookieOptions: {
isSecure: true
},
headerName: 'X-CUSTOM-TOKEN'
}
}
]);
server.views(internals.viewOptions);
const res = await server.inject({
method: 'GET',
url: '/1'
});
const header = res.headers['set-cookie'];
expect(header.length).to.equal(1);
expect(header[0]).to.contain('Secure');
const cookie = header[0].match(/crumb=([^\x00-\x20\"\,\;\\\x7F]*)/);
const validHeader = {
cookie: 'crumb=' + cookie[1],
'x-custom-token': cookie[1]
};
const invalidHeader = {
cookie: 'crumb=' + cookie[1],
'x-custom-token': 'x' + cookie[1]
};
expect(res.result).to.equal(Views.viewWithCrumb(cookie[1]));
const res2 = await server.inject({
method: 'POST',
url: '/2',
payload: '{ "key": "value" }',
headers: validHeader
});
expect(res2.result).to.equal('valid');
const res3 = await server.inject({
method: 'POST',
url: '/2',
payload: '{ "key": "value" }',
headers: invalidHeader
});
expect(res3.statusCode).to.equal(403);
const res4 = await server.inject({
method: 'POST',
url: '/3',
headers: {
cookie: 'crumb=' + cookie[1]
}
});
expect(res4.statusCode).to.equal(403);
const res5 = await server.inject({
method: 'PUT',
url: '/4',
payload: '{ "key": "value" }',
headers: validHeader
});
expect(res5.result).to.equal('valid');
const res6 = await server.inject({
method: 'PUT',
url: '/4',
payload: '{ "key": "value" }',
headers: invalidHeader
});
expect(res6.statusCode).to.equal(403);
const res7 = await server.inject({
method: 'PATCH',
url: '/5',
payload: '{ "key": "value" }',
headers: validHeader
});
expect(res7.result).to.equal('valid');
const res8 = await server.inject({
method: 'PATCH',
url: '/5',
payload: '{ "key": "value" }',
headers: invalidHeader
});
expect(res8.statusCode).to.equal(403);
const res9 = await server.inject({
method: 'DELETE',
url: '/6',
headers: validHeader
});
expect(res9.result).to.equal('valid');
const res10 = await server.inject({
method: 'DELETE',
url: '/6',
headers: invalidHeader
});
expect(res10.statusCode).to.equal(403);
const res11 = await server.inject({
method: 'POST',
url: '/7',
payload: '{ "key": "value" }'
});
expect(res11.result).to.equal('valid');
const payload = { key: 'value', crumb: cookie[1] };
delete validHeader['x-custom-token'];
const res12 = await server.inject({
method: 'POST',
url: '/8',
payload: JSON.stringify(payload),
headers: validHeader
});
expect(res12.statusCode).to.equal(200);
});
});
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