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

fastify-helmet

Package Overview
Dependencies
Maintainers
17
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fastify-helmet - npm Package Compare versions

Comparing version 7.0.1 to 7.1.0

142

index.js
'use strict'
const { randomBytes } = require('crypto')
const fp = require('fastify-plugin')
const helmet = require('helmet')
const warning = require('process-warning')()
warning.create('FastifyWarning.fastify-helmet', 'FST_MODULE_DEP_fastify-helmet'.toUpperCase(), 'fastify-helmet has been deprecated. Use @fastify/helmet@8.0.0 instead.')
warning.emit('FST_MODULE_DEP_fastify-helmet'.toUpperCase())
async function helmetPlugin (fastify, options) {
// helmet will throw when any option is explicitly set to "true"
// using ECMAScript destructuring is a clean workaround as we do not need to alter options
const { enableCSPNonces, global, ...globalConfiguration } = options
const isGlobal = typeof global === 'boolean' ? global : true
// We initialize the `helmet` reply decorator only if it does not already exists
if (!fastify.hasReplyDecorator('helmet')) {
fastify.decorateReply('helmet', null)
}
// We initialize the `cspNonce` reply decorator only if it does not already exists
if (!fastify.hasReplyDecorator('cspNonce')) {
fastify.decorateReply('cspNonce', null)
}
fastify.addHook('onRoute', (routeOptions) => {
if (typeof routeOptions.helmet !== 'undefined') {
if (typeof routeOptions.helmet === 'object') {
routeOptions.config = Object.assign(routeOptions.config || Object.create(null), { helmet: routeOptions.helmet })
} else if (routeOptions.helmet === false) {
routeOptions.config = Object.assign(routeOptions.config || Object.create(null), { helmet: { skipRoute: true } })
} else {
throw new Error('Unknown value for route helmet configuration')
}
}
})
fastify.addHook('onRequest', async (request, reply) => {
const { helmet: routeOptions } = request.context.config
if (typeof routeOptions !== 'undefined') {
const { enableCSPNonces: enableRouteCSPNonces, skipRoute, ...helmetRouteConfiguration } = routeOptions
// If route helmet options are set they overwrite the global helmet configuration
const mergedHelmetConfiguration = Object.assign(Object.create(null), globalConfiguration, helmetRouteConfiguration)
// We decorate the reply with a fallback to the route scoped helmet options
return replyDecorators(request, reply, mergedHelmetConfiguration, enableRouteCSPNonces)
} else {
// We decorate the reply with a fallback to the global helmet options
return replyDecorators(request, reply, globalConfiguration, enableCSPNonces)
}
})
fastify.addHook('onRequest', (request, reply, next) => {
const { helmet: routeOptions } = request.context.config
if (typeof routeOptions !== 'undefined') {
const { enableCSPNonces: enableRouteCSPNonces, skipRoute, ...helmetRouteConfiguration } = routeOptions
if (skipRoute === true) {
// If helmet route option is set to `false` we skip the route
} else {
// If route helmet options are set they overwrite the global helmet configuration
const mergedHelmetConfiguration = Object.assign(Object.create(null), globalConfiguration, helmetRouteConfiguration)
return buildHelmetOnRoutes(request, reply, mergedHelmetConfiguration, enableRouteCSPNonces)
}
return next()
} else if (isGlobal) {
// if the plugin is set globally (meaning that all the routes will be decorated)
// As the endpoint, does not have a custom helmet configuration, use the global one.
return buildHelmetOnRoutes(request, reply, globalConfiguration, enableCSPNonces)
} else {
// if the plugin is not global we can skip the route
}
return next()
})
}
async function replyDecorators (request, reply, configuration, enableCSP) {
if (enableCSP) {
reply.cspNonce = {
script: randomBytes(16).toString('hex'),
style: randomBytes(16).toString('hex')
}
}
reply.helmet = function (opts) {
const helmetConfiguration = opts
? Object.assign(Object.create(null), configuration, opts)
: configuration
return helmet(helmetConfiguration)(request.raw, reply.raw, done)
}
}
async function buildHelmetOnRoutes (request, reply, configuration, enableCSP) {
if (enableCSP === true) {
const cspDirectives = configuration.contentSecurityPolicy
? configuration.contentSecurityPolicy.directives
: helmet.contentSecurityPolicy.getDefaultDirectives()
const cspReportOnly = configuration.contentSecurityPolicy
? configuration.contentSecurityPolicy.reportOnly
: undefined
// We get the csp nonce from the reply
const { script: scriptCSPNonce, style: styleCSPNonce } = reply.cspNonce
// We prevent object reference: https://github.com/fastify/fastify-helmet/issues/118
const directives = { ...cspDirectives }
// We push nonce to csp
// We allow both 'script-src' or 'scriptSrc' syntax
const scriptKey = Array.isArray(directives['script-src']) ? 'script-src' : 'scriptSrc'
directives[scriptKey] = Array.isArray(directives[scriptKey]) ? [...directives[scriptKey]] : []
directives[scriptKey].push(`'nonce-${scriptCSPNonce}'`)
// allow both style-src or styleSrc syntax
const styleKey = Array.isArray(directives['style-src']) ? 'style-src' : 'styleSrc'
directives[styleKey] = Array.isArray(directives[styleKey]) ? [...directives[styleKey]] : []
directives[styleKey].push(`'nonce-${styleCSPNonce}'`)
const contentSecurityPolicy = { directives, reportOnly: cspReportOnly }
const mergedHelmetConfiguration = Object.assign(Object.create(null), configuration, { contentSecurityPolicy })
helmet(mergedHelmetConfiguration)(request.raw, reply.raw, done)
} else {
helmet(configuration)(request.raw, reply.raw, done)
}
}
// Helmet forward a typeof Error object so we just need to throw it as is.
function done (error) {
if (error) throw error
}
module.exports = fp(helmetPlugin, {
fastify: '3.x',
name: 'fastify-helmet'
})
module.exports.contentSecurityPolicy = helmet.contentSecurityPolicy
module.exports = require('fastify-helmet-deprecated')
{
"name": "fastify-helmet",
"version": "7.0.1",
"description": "Important security headers for Fastify",
"version": "7.1.0",
"main": "index.js",
"types": "index.d.ts",
"scripts": {
"coverage": "npm run unit -- --coverage-report=lcovonly",
"lint": "standard | snazzy",
"lint:fix": "standard --fix | snazzy",
"test": "npm run lint && npm run unit && npm run typescript",
"test:ci": "npm run lint && npm run coverage && npm run typescript",
"unit": "tap -J \"test/*.test.js\"",
"unit:report": "npm run unit -- --coverage-report=html",
"unit:verbose": "npm run unit -- -Rspec",
"typescript": "tsd"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/fastify/fastify-helmet.git"
"url": "git://github.com/fastify/fastify-helmet.git"
},
"keywords": [
"fastify",
"helmet",
"security",
"headers",
"x-frame-options",
"csp",
"hsts",
"clickjack"
],
"author": "Matteo Collina <hello@matteocollina.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/fastify/fastify-helmet/issues"
},
"homepage": "https://github.com/fastify/fastify-helmet#readme",
"devDependencies": {
"@types/node": "^17.0.8",
"fastify": "^3.25.3",
"pre-commit": "^1.2.2",
"snazzy": "^9.0.0",
"standard": "^16.0.4",
"tap": "^15.1.6",
"tsd": "^0.19.1",
"typescript": "^4.5.4"
},
"homepage": "https://github.com/fastify/fastify-helmet",
"dependencies": {
"fastify-plugin": "^3.0.0",
"helmet": "^5.0.1"
},
"tsd": {
"directory": "test/types"
"process-warning": "^1.0.0",
"fastify-helmet-deprecated": "npm:fastify-helmet@7.0.1"
}
}
# fastify-helmet
![CI](https://github.com/fastify/fastify-helmet/workflows/CI/badge.svg)
[![NPM version](https://img.shields.io/npm/v/fastify-helmet)](https://www.npmjs.com/package/fastify-helmet)
[![Known Vulnerabilities](https://snyk.io/test/github/fastify/fastify-helmet/badge.svg)](https://snyk.io/test/github/fastify/fastify-helmet)
[![Coverage Status](https://coveralls.io/repos/github/fastify/fastify-helmet/badge.svg?branch=master)](https://coveralls.io/github/fastify/fastify-helmet?branch=master)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/)
Important security headers for Fastify. It is a tiny wrapper around
[helmet](https://npm.im/helmet).
## Install
```
npm i fastify-helmet
```
## Usage
Simply require this plugin, and the basic security headers will be set.
```js
const fastify = require('fastify')()
const helmet = require('fastify-helmet')
fastify.register(
helmet,
// Example disables the `contentSecurityPolicy` middleware but keeps the rest.
{ contentSecurityPolicy: false }
)
fastify.listen(3000, err => {
if (err) throw err
})
```
## How it works
`fastify-helmet` is a tiny wrapper around helmet that adds an `'onRequest'` hook
and a `reply.helmet` decorator.
It accepts the same options as helmet, and you can see more in [the helmet documentation](https://helmetjs.github.io/docs/).
### Apply Helmet to all your application routes
By passing `{ global: true }` into the options, `fastify-helmet` allows you to register Helmet for all your application
routes by default. If you want a more granular control on how to apply Helmet to your application you can choose to
disable it on a global scope by passing `{ global: false }` to the options. By default, this option is set to `true`.
#### Example - enable `fastify-helmet` globally
```js
fastify.register(helmet)
// or
fastify.register(helmet, { global: true })
```
#### Example - disable `fastify-helmet` globally
```js
// register the package with the `{ global: false }` option
fastify.register(helmet, { global: false })
fastify.get('/route-with-disabled-helmet', async (request, reply) => {
return { message: 'helmet is not enabled here' }
})
fastify.get('/route-with-enabled-helmet', {
// We enable and configure helmet for this route only
helmet: {
dnsPrefetchControl: {
allow: true
},
expectCt: {
maxAge: 1,
enforce: true,
reportUri: 'foo'
},
frameguard: {
action: 'foo'
},
referrerPolicy: false
}
}, async (request, reply) => {
return { message: 'helmet is enabled here' }
})
// helmet is disabled on this route but we have access to `reply.helmet` decorator
// that allows us to apply helmet conditionally
fastify.get('/here-we-use-helmet-reply-decorator', async (request, reply) => {
if (condition) {
// we apply the default options
await reply.helmet()
} else {
// we apply customized options
await reply.helmet({ frameguard: false })
}
return {
message: 'we use the helmet reply decorator to conditionally apply helmet middlewares'
}
})
```
### `helmet` route option
`fastify-helmet` allows you to enable, disable, and customize helmet for each one of your application hooks by using the
`helmet` shorthand route option when you register your application routes.
If you want to disable helmet for a specific endpoint you must pass `{ helmet: false }` to your route options.
If you want to enable or customize helmet for a specific endpoint you must pass a helmet configuration object to your
route options. E.g.: `{ helmet: { frameguard: false } }`.
#### Example - `fastify-helmet` configuration using the `helmet` shorthand route option
```js
// register the package with the `{ global: true }` option
fastify.register(helmet, { global: true })
fastify.get('/route-with-disabled-helmet', { helmet: false }, async (request, reply) => {
return { message: 'helmet is not enabled here' }
})
fastify.get('/route-with-enabled-helmet', async (request, reply) => {
return { message: 'helmet is enabled by default here' }
})
fastify.get('/route-with-custom-helmet-configuration', {
// We change the helmet configuration for this route only
helmet: {
enableCSPNonces: true,
contentSecurityPolicy: {
directives: {
'directive-1': ['foo', 'bar']
},
reportOnly: true
},
dnsPrefetchControl: {
allow: true
},
expectCt: {
maxAge: 1,
enforce: true,
reportUri: 'foo'
},
frameguard: {
action: 'foo'
},
hsts: {
maxAge: 1,
includeSubDomains: true,
preload: true
},
permittedCrossDomainPolicies: {
permittedPolicies: 'foo'
},
referrerPolicy: false
}
}, async (request, reply) => {
return { message: 'helmet is enabled with a custom configuration on this route' }
})
```
### Content-Security-Policy Nonce
`fastify-helmet` provide a simple way for `csp nonces generation`. You can enable this behavior by passing
`{ enableCSPNonces: true }` into the options. Then, you can retrieve the `nonces` through `reply.cspNonce`.
> Note: This feature is implemented inside this module. It is not a valid option or supported by helmet.
> If you need to use helmet feature only for csp nonce you can follow the example [here](#example---generate-by-helmet).
#### Example - Generate by options
```js
fastify.register(
helmet,
// enable csp nonces generation with default content-security-policy option
{ enableCSPNonces: true }
)
fastify.register(
helmet,
// customize content security policy with nonce generation
{
enableCSPNonces: true,
contentSecurityPolicy: {
directives: {
...
}
}
}
)
fastify.get('/', function(request, reply) {
// retrieve script nonce
reply.cspNonce.script
// retrieve style nonce
reply.cspNonce.style
})
```
#### Example - Generate by helmet
```js
fastify.register(
helmet,
{
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: [
function (req, res) {
// "res" here is actually "reply.raw" in fastify
res.scriptNonce = crypto.randomBytes(16).toString('hex')
}
],
styleSrc: [
function (req, res) {
// "res" here is actually "reply.raw" in fastify
res.styleNonce = crypto.randomBytes(16).toString('hex')
}
]
}
}
}
)
fastify.get('/', function(request, reply) {
// you can access the generated nonce by "reply.raw"
reply.raw.scriptNonce
reply.raw.styleNonce
})
```
## License
MIT
`fastify-helmet@7.1.0` has been deprecated. Please use
`@fastify/helmet@8.0.0` instead.
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