| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162 |
- const crypto = require('crypto')
- const path = require('path')
- module.exports = function ({
- reportUri,
- reportOnly = false,
- exclude = [],
- percentage
- }) {
- return function (req, res, next) {
- const originalRender = res.render
- res.render = (...args) => {
- const view = relativeViewPath(args[0])
- // enable the CSP header for a percentage of requests
- const belowCutoff = Math.random() * 100 <= percentage
- if (belowCutoff && !exclude.includes(view)) {
- res.locals.cspEnabled = true
- const scriptNonce = crypto.randomBytes(16).toString('base64')
- res.locals.scriptNonce = scriptNonce
- const directives = [
- `script-src 'nonce-${scriptNonce}' 'unsafe-inline' 'strict-dynamic' https: 'report-sample'`,
- `object-src 'none'`,
- `base-uri 'none'`
- ]
- if (reportUri) {
- directives.push(`report-uri ${reportUri}`)
- // NOTE: implement report-to once it's more widely supported
- }
- const policy = directives.join('; ')
- // Note: https://csp-evaluator.withgoogle.com/ is useful for checking the policy
- const header = reportOnly
- ? 'Content-Security-Policy-Report-Only'
- : 'Content-Security-Policy'
- res.set(header, policy)
- }
- originalRender.apply(res, args)
- }
- next()
- }
- }
- const webRoot = path.resolve(__dirname, '..', '..', '..')
- // build the view path relative to the web root
- function relativeViewPath(view) {
- return path.isAbsolute(view)
- ? path.relative(webRoot, view)
- : path.join('app', 'views', view)
- }
|