overleaf/services/web/app/src/infrastructure/CSP.js

54 lines
1.4 KiB
JavaScript
Raw Normal View History

const crypto = require('crypto')
module.exports = function({
reportUri,
reportOnly = false,
exclude = [],
percentage
}) {
return function(req, res, next) {
const originalRender = res.render
res.render = (...args) => {
// use the view path after removing any prefix up to a "views" folder
const view = args[0].split('/views/').pop()
// 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()
}
}