Skip to content

Commit

Permalink
Update to helmet v4 (#84)
Browse files Browse the repository at this point in the history
* Bump to [email protected]

* Drop middie dependency

* Add back fastify-plugin

* update types to use helmet

* fix framegaurd test

* fix tests

* missing package.json edit

* Updated to helmet v4

Co-authored-by: Ethan Arrowood <[email protected]>
  • Loading branch information
mcollina and Ethan-Arrowood authored Aug 3, 2020
1 parent be73194 commit 3eb99a2
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 598 deletions.
29 changes: 5 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/) ![CI
workflow](https://github.com/fastify/fastify-helmet/workflows/CI%20workflow/badge.svg)

Important security headers for Fastify. It is a port from express of
[helmet](http://npm.im/helmet)
Important security headers for Fastify. It is a tiny wrapper around
[helmet](http://npm.im/helmet).

## Install
```
npm i fastify-helmet --save
npm i fastify-helmet
```

## Usage
Expand All @@ -27,32 +27,13 @@ fastify.register(

fastify.listen(3000, err => {
if (err) throw err
console.log('Server listenting on localhost:', fastify.server.address().port)
})
```

## How it works

`fastify-helmet` is a collection of 14 smaller middleware functions that set HTTP headers. Running `fastify.register(helmet)` will not include all of these middleware functions by default.

| Module | Default? |
|---|---|
| [contentSecurityPolicy](https://helmetjs.github.io/docs/csp/) for setting Content Security Policy | |
| [crossdomain](https://helmetjs.github.io/docs/crossdomain/) for handling Adobe products’ crossdomain requests | |
| [expectCt](https://helmetjs.github.io/docs/expect-ct/) for handling Certificate Transparency | |
| [dnsPrefetchControl](https://helmetjs.github.io/docs/dns-prefetch-control) controls browser DNS prefetching ||
| [featurePolicy](https://helmetjs.github.io/docs/feature-policy/) to limit your site’s features | |
| [frameguard](https://helmetjs.github.io/docs/frameguard/) to prevent clickjacking ||
| [hidePoweredBy](https://helmetjs.github.io/docs/hide-powered-by) to remove the X-Powered-By header ||
| [hpkp](https://helmetjs.github.io/docs/hpkp/) for HTTP Public Key Pinning | |
| [hsts](https://helmetjs.github.io/docs/hsts/) for HTTP Strict Transport Security ||
| [ieNoOpen](https://helmetjs.github.io/docs/ienoopen) sets X-Download-Options for IE8+ ||
| [noCache](https://helmetjs.github.io/docs/nocache/) to disable client-side caching | |
| [noSniff](https://helmetjs.github.io/docs/dont-sniff-mimetype) to keep clients from sniffing the MIME type ||
| [referrerPolicy](https://helmetjs.github.io/docs/referrer-policy) to hide the Referer header | |
| [xssFilter](https://helmetjs.github.io/docs/xss-filter) adds some small XSS protections ||

`fastify-helmet` accept the same options of Helmet, and you can see more in [the helmet documentation](https://helmetjs.github.io/docs/).
`fastify-helmet` is just a tiny wrapper around helmet that adds an `'onRequest'` hook.
It accepts the same options of Helmet, and you can see more in [the helmet documentation](https://helmetjs.github.io/docs/).

## License

Expand Down
11 changes: 0 additions & 11 deletions config.json

This file was deleted.

193 changes: 4 additions & 189 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,192 +1,7 @@
/// <reference types="node" />
import { FastifyPlugin } from 'fastify';
import { HelmetOptions } from 'helmet';

import {
FastifyRequest,
FastifyReply,
FastifyPlugin,
RawRequestDefaultExpression,
RawReplyDefaultExpression,
RawServerBase,
} from 'fastify';

export interface IHelmetPermittedCrossDomainPoliciesConfiguration {
permittedPolicies?: string;
}

export interface IHelmetContentSecurityPolicyDirectiveFunction {
(req: RawRequestDefaultExpression<RawServerBase>, res: RawReplyDefaultExpression<RawServerBase>): string;
}

export type HelmetCspDirectiveValue = string | IHelmetContentSecurityPolicyDirectiveFunction;

export type HelmetCspSandboxDirective =
| string
| 'allow-forms'
| 'allow-modals'
| 'allow-orientation-lock'
| 'allow-pointer-lock'
| 'allow-popups-to-escape-sandbox'
| 'allow-popups'
| 'allow-presentation'
| 'allow-same-origin'
| 'allow-scripts'
| 'allow-top-navigation';

export type HelmetCspRequireSriForValue = string | 'script' | 'style';

export interface IHelmetContentSecurityPolicyDirectives {
baseUri?: HelmetCspDirectiveValue[];
blockAllMixedContent?: boolean;
childSrc?: HelmetCspDirectiveValue[];
connectSrc?: HelmetCspDirectiveValue[];
defaultSrc?: HelmetCspDirectiveValue[];
fontSrc?: HelmetCspDirectiveValue[];
formAction?: HelmetCspDirectiveValue[];
frameAncestors?: HelmetCspDirectiveValue[];
frameSrc?: HelmetCspDirectiveValue[];
imgSrc?: HelmetCspDirectiveValue[];
manifestSrc?: HelmetCspDirectiveValue[];
mediaSrc?: HelmetCspDirectiveValue[];
objectSrc?: HelmetCspDirectiveValue[];
pluginTypes?: HelmetCspDirectiveValue[];
prefetchSrc?: HelmetCspDirectiveValue[];
reportTo?: HelmetCspDirectiveValue;
reportUri?: HelmetCspDirectiveValue;
requireSriFor?: HelmetCspRequireSriForValue[];
sandbox?: HelmetCspSandboxDirective[] | true;
scriptSrc?: HelmetCspDirectiveValue[];
styleSrc?: HelmetCspDirectiveValue[];
upgradeInsecureRequests?: boolean;
workerSrc?: HelmetCspDirectiveValue[];
}

export interface IHelmetContentSecurityPolicyDirectives {
'base-uri'?: HelmetCspDirectiveValue[];
'block-all-mixed-content'?: boolean;
'child-src'?: HelmetCspDirectiveValue[];
'connect-src'?: HelmetCspDirectiveValue[];
'default-src'?: HelmetCspDirectiveValue[];
'font-src'?: HelmetCspDirectiveValue[];
'form-action'?: HelmetCspDirectiveValue[];
'frame-ancestors'?: HelmetCspDirectiveValue[];
'frame-src'?: HelmetCspDirectiveValue[];
'img-src'?: HelmetCspDirectiveValue[];
'manifest-src'?: HelmetCspDirectiveValue[];
'media-src'?: HelmetCspDirectiveValue[];
'object-src'?: HelmetCspDirectiveValue[];
'plugin-types'?: HelmetCspDirectiveValue[];
'prefetch-src'?: HelmetCspDirectiveValue[];
'report-to'?: HelmetCspDirectiveValue;
'report-uri'?: HelmetCspDirectiveValue;
'require-sri-for'?: HelmetCspRequireSriForValue[];
sandbox?: HelmetCspSandboxDirective[] | true;
'script-src'?: HelmetCspDirectiveValue;
'style-src'?: HelmetCspDirectiveValue;
'upgrade-insecure-requests'?: boolean;
'worker-src'?: HelmetCspDirectiveValue;
}

export interface IHelmetContentSecurityPolicyConfiguration {
reportOnly?: boolean | ((req: FastifyRequest, res: FastifyReply) => boolean);
setAllHeaders?: boolean;
disableAndroid?: boolean;
browserSniff?: boolean;
directives?: IHelmetContentSecurityPolicyDirectives;
loose?: boolean;
}

export interface IHelmetDnsPrefetchControlConfiguration {
allow?: boolean;
}

export interface IHelmetFeaturePolicyConfiguration {
features: {
[key: string]: string[];
}
}

export interface IHelmetFeaturePolicyConfigurationStrict {
features: {
geolocation?: string[];
midi?: string[];
notifications?: string[];
push?: string[];
syncXhr?: string[];
microphone?: string[];
camera?: string[];
magnetometer?: string[];
gyroscope?: string[];
speaker?: string[];
vibrate?: string[];
fullscreen?: string[];
payment?: string[];
accelerometer?: string[];
usb?: string[];
vr?: string[];
autoplay?: string[];
}
}

export interface IHelmetFrameguardConfiguration {
action?: string;
domain?: string;
}

export interface IHelmetHidePoweredByConfiguration {
setTo?: string;
}

export interface IHelmetSetIfFunction {
(req: FastifyRequest, res: FastifyReply): boolean;
}

export interface IHelmetHpkpConfiguration {
maxAge: number;
sha256s: string[];
includeSubdomains?: boolean;
reportUri?: string;
reportOnly?: boolean;
setIf?: IHelmetSetIfFunction;
}

export interface IHelmetHstsConfiguration {
maxAge?: number;
includeSubdomains?: boolean;
preload?: boolean;
setIf?: IHelmetSetIfFunction;
force?: boolean;
}

export interface IHelmetReferrerPolicyConfiguration {
policy?: string;
}

export interface IHelmetXssFilterConfiguration {
setOnOldIE?: boolean;
}

export interface IHelmetExpectCtConfiguration {
enforce?: boolean;
maxAge?: number;
reportUri?: string;
}

export interface FastifyHelmetOptions {
contentSecurityPolicy?: boolean | IHelmetContentSecurityPolicyConfiguration;
dnsPrefetchControl?: boolean | IHelmetDnsPrefetchControlConfiguration;
expectCt?: boolean | IHelmetExpectCtConfiguration;
featurePolicy?: IHelmetFeaturePolicyConfigurationStrict | IHelmetFeaturePolicyConfiguration;
frameguard?: boolean | IHelmetFrameguardConfiguration;
hidePoweredBy?: boolean | IHelmetHidePoweredByConfiguration;
hpkp?: boolean | IHelmetHpkpConfiguration;
hsts?: boolean | IHelmetHstsConfiguration;
ieNoOpen?: boolean;
noCache?: boolean;
noSniff?: boolean;
permittedCrossDomainPolicies?: boolean | IHelmetPermittedCrossDomainPoliciesConfiguration;
referrerPolicy?: boolean | IHelmetReferrerPolicyConfiguration;
xssFilter?: boolean | IHelmetXssFilterConfiguration;
}
interface FastifyHelmetOptions extends HelmetOptions {}

declare const fastifyHelmet: FastifyPlugin<FastifyHelmetOptions>;
export default fastifyHelmet;
export = fastifyHelmet;
54 changes: 6 additions & 48 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,14 @@
'use strict'

const fp = require('fastify-plugin')
const config = require('./config')
const helmet = {
contentSecurityPolicy: require('helmet-csp'),
dnsPrefetchControl: require('dns-prefetch-control'),
expectCt: require('expect-ct'),
featurePolicy: require('feature-policy'),
frameguard: require('frameguard'),
hidePoweredBy: require('hide-powered-by'),
hpkp: require('hpkp'),
hsts: require('hsts'),
ieNoOpen: require('ienoopen'),
noCache: require('nocache'),
noSniff: require('dont-sniff-mimetype'),
permittedCrossDomainPolicies: require('helmet-crossdomain'),
referrerPolicy: require('referrer-policy'),
xssFilter: require('x-xss-protection')
}
const helmet = require('helmet')

const middlewares = Object.keys(helmet)
module.exports = fp(function (app, options, next) {
const middleware = helmet(options)

module.exports = fp(function (fastify, opts, next) {
function useMiddlewares () {
for (const middlewareName of middlewares) {
const middleware = helmet[middlewareName]
const option = opts[middlewareName]
const isDefault = config.defaultMiddleware.indexOf(middlewareName) !== -1

if (option === false) { continue }

if (option != null) {
if (option === true) {
fastify.use(middleware({}))
} else {
fastify.use(middleware(option))
}
} else if (isDefault) {
fastify.use(middleware({}))
}
}
}

// TODO: Once Middie uses Decorator API we can detect presence using that: https://www.fastify.io/docs/latest/Decorators/#hasdecoratorname
// Until then all we can do is check if `fastify.use` throws
try {
useMiddlewares()
} catch (error) {
fastify
.register(require('middie'))
.after(useMiddlewares)
}
app.addHook('onRequest', function (req, reply, next) {
middleware(req.raw, reply.raw, next)
})

next()
}, {
Expand Down
Loading

0 comments on commit 3eb99a2

Please sign in to comment.