'use strict'; const extend = require('extend'); const platform = require('platform'); const utils = require('../utils'); const HEADER = [ 'x-content-security-policy', 'content-security-policy', ]; const REPORT_ONLY_HEADER = [ 'x-content-security-policy-report-only', 'content-security-policy-report-only', ]; module.exports = options => { return async function csp(ctx, next) { await next(); const opts = utils.merge(options, ctx.securityOptions.csp); if (utils.checkIfIgnore(opts, ctx)) return; let finalHeader; let value; const matchedOption = extend(true, {}, opts.policy); const isIE = platform.parse(ctx.header['user-agent']).name === 'IE'; const bufArray = []; const headers = opts.reportOnly ? REPORT_ONLY_HEADER : HEADER; if (isIE && opts.supportIE) { finalHeader = headers[0]; } else { finalHeader = headers[1]; } for (const key in matchedOption) { value = matchedOption[key]; value = Array.isArray(value) ? value : [ value ]; // Other arrays are splitted into strings EXCEPT `sandbox` if (key === 'sandbox' && value[0] === true) { bufArray.push(key); } else { if (key === 'script-src') { const hasNonce = value.some(function(val) { return val.indexOf('nonce-') !== -1; }); if (!hasNonce) { value.push('\'nonce-' + ctx.nonce + '\''); } } value = value.map(function(d) { if (d.startsWith('.')) { d = '*' + d; } return d; }); bufArray.push(key + ' ' + value.join(' ')); } } const headerString = bufArray.join(';'); ctx.set(finalHeader, headerString); ctx.set('x-csp-nonce', ctx.nonce); }; };