diff --git a/packages/config/src/options.js b/packages/config/src/options.js index e5da7d33d6..fb8d5dd85d 100644 --- a/packages/config/src/options.js +++ b/packages/config/src/options.js @@ -3,7 +3,6 @@ import fs from 'fs' import defaultsDeep from 'lodash/defaultsDeep' import defaults from 'lodash/defaults' import pick from 'lodash/pick' -import isObject from 'lodash/isObject' import uniq from 'lodash/uniq' import consola from 'consola' import { guardDir, isNonEmptyString, isPureObject, isUrl, getMainModule } from '@nuxt/utils' @@ -179,16 +178,14 @@ export function getNuxtConfig(_options) { } // Apply default hash to CSP option - const { csp } = options.render - - const cspDefaults = { - hashAlgorithm: 'sha256', - allowedSources: undefined, - policies: undefined, - reportOnly: options.debug - } - if (csp) { - options.render.csp = defaults(isObject(csp) ? csp : {}, cspDefaults) + if (options.render.csp) { + options.render.csp = defaults({}, options.render.csp, { + hashAlgorithm: 'sha256', + allowedSources: undefined, + policies: undefined, + addMeta: Boolean(options._generate), + reportOnly: options.debug + }) } // cssSourceMap diff --git a/packages/config/test/options.test.js b/packages/config/test/options.test.js index dc60fd2062..e744db32ea 100644 --- a/packages/config/test/options.test.js +++ b/packages/config/test/options.test.js @@ -81,6 +81,7 @@ describe('config: options', () => { const { render: { csp } } = getNuxtConfig({ render: { csp: { allowedSources: true, test: true } } }) expect(csp).toEqual({ hashAlgorithm: 'sha256', + addMeta: false, allowedSources: true, policies: undefined, reportOnly: false, diff --git a/packages/vue-renderer/src/renderer.js b/packages/vue-renderer/src/renderer.js index 6e41ef04ea..898c5a7f2d 100644 --- a/packages/vue-renderer/src/renderer.js +++ b/packages/vue-renderer/src/renderer.js @@ -407,21 +407,26 @@ export default class VueRenderer { APP += `` // Calculate CSP hashes + const { csp } = this.context.options.render const cspScriptSrcHashes = [] - const csp = this.context.options.render.csp - const containsUnsafeInlineScriptSrc = csp && csp.policies && csp.policies['script-src'] && csp.policies['script-src'].includes(`'unsafe-inline'`) + if (csp) { + // Only add the hash if 'unsafe-inline' rule isn't present to avoid conflicts (#5387) + const containsUnsafeInlineScriptSrc = csp.policies && csp.policies['script-src'] && csp.policies['script-src'].includes(`'unsafe-inline'`) + if (!containsUnsafeInlineScriptSrc) { + const hash = crypto.createHash(csp.hashAlgorithm) + hash.update(serializedSession) + cspScriptSrcHashes.push(`'${csp.hashAlgorithm}-${hash.digest('base64')}'`) + } - // Only add the hash if 'unsafe-inline' rule isn't present to avoid conflicts (#5387) - if (csp && !containsUnsafeInlineScriptSrc) { - const { hashAlgorithm } = this.context.options.render.csp - const hash = crypto.createHash(hashAlgorithm) - hash.update(serializedSession) - cspScriptSrcHashes.push(`'${hashAlgorithm}-${hash.digest('base64')}'`) + // Call ssr:csp hook + await this.context.nuxt.callHook('vue-renderer:ssr:csp', cspScriptSrcHashes) + + // Add csp meta tags + if (csp.addMeta) { + HEAD += `` + } } - // Call ssr:csp hook - await this.context.nuxt.callHook('vue-renderer:ssr:csp', cspScriptSrcHashes) - // Prepend scripts APP += this.renderScripts(context) APP += m.script.text({ body: true })