mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-23 14:15:13 +00:00
Adding support for Content-Security-Policy script-src safe inline, for SSR state transfer
This commit is contained in:
parent
c316be20ef
commit
6e9be715b2
@ -34,6 +34,9 @@ Options.from = function (_options) {
|
||||
if (typeof options.extensions === 'string') {
|
||||
options.extensions = [ options.extensions ]
|
||||
}
|
||||
if (options.render.csp === true) {
|
||||
options.render.csp = { hashAlgorithm: 'sha256' }
|
||||
}
|
||||
|
||||
const hasValue = v => typeof v === 'string' && v
|
||||
options.rootDir = hasValue(options.rootDir) ? options.rootDir : process.cwd()
|
||||
@ -278,7 +281,8 @@ Options.defaults = {
|
||||
},
|
||||
etag: {
|
||||
weak: false
|
||||
}
|
||||
},
|
||||
csp: undefined
|
||||
},
|
||||
watchers: {
|
||||
webpack: {
|
||||
|
@ -11,7 +11,7 @@ module.exports = async function nuxtMiddleware(req, res, next) {
|
||||
try {
|
||||
const result = await this.renderRoute(req.url, context)
|
||||
await this.nuxt.callHook('render:route', req.url, result)
|
||||
const { html, error, redirected, getPreloadFiles } = result
|
||||
const { html, cspScriptSrcHashes, error, redirected, getPreloadFiles } = result
|
||||
|
||||
if (redirected) {
|
||||
return html
|
||||
@ -61,6 +61,10 @@ module.exports = async function nuxtMiddleware(req, res, next) {
|
||||
res.setHeader('Link', pushAssets.join(','))
|
||||
}
|
||||
|
||||
if (this.options.render.csp) {
|
||||
res.setHeader('Content-Security-Policy', `script-src 'self' ${(cspScriptSrcHashes || []).join(' ')}`)
|
||||
}
|
||||
|
||||
// Send response
|
||||
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
||||
res.setHeader('Content-Length', Buffer.byteLength(html))
|
||||
|
@ -9,6 +9,7 @@ const { createBundleRenderer } = require('vue-server-renderer')
|
||||
const Debug = require('debug')
|
||||
const connect = require('connect')
|
||||
const launchMiddleware = require('launch-editor-middleware')
|
||||
const crypto = require('crypto')
|
||||
|
||||
const { setAnsiColors, isUrl, waitFor } = require('../common/utils')
|
||||
const { Options } = require('../common')
|
||||
@ -315,7 +316,15 @@ module.exports = class Renderer {
|
||||
HEAD += context.renderResourceHints()
|
||||
}
|
||||
|
||||
APP += `<script type="text/javascript">window.__NUXT__=${serialize(context.nuxt, { isJSON: true })};</script>`
|
||||
let serializedSession = `window.__NUXT__=${serialize(context.nuxt, { isJSON: true })};`
|
||||
let cspScriptSrcHashes = []
|
||||
if (this.options.render.csp) {
|
||||
let hash = crypto.createHash(this.options.render.csp.hashAlgorithm)
|
||||
hash.update(serializedSession)
|
||||
cspScriptSrcHashes.push(`'${this.options.render.csp.hashAlgorithm}-${hash.digest('base64')}'`)
|
||||
}
|
||||
|
||||
APP += `<script type="text/javascript">${serializedSession}</script>`
|
||||
APP += context.renderScripts()
|
||||
APP += m.script.text({ body: true })
|
||||
|
||||
@ -331,6 +340,7 @@ module.exports = class Renderer {
|
||||
|
||||
return {
|
||||
html,
|
||||
cspScriptSrcHashes,
|
||||
getPreloadFiles: context.getPreloadFiles,
|
||||
error: context.nuxt.error,
|
||||
redirected: context.redirected
|
||||
|
@ -22,6 +22,9 @@ test.serial('Init Nuxt.js', async t => {
|
||||
},
|
||||
build: {
|
||||
stats: false
|
||||
},
|
||||
render: {
|
||||
csp: true
|
||||
}
|
||||
}
|
||||
|
||||
@ -228,6 +231,12 @@ test('ETag Header', async t => {
|
||||
t.is(error.statusCode, 304)
|
||||
})
|
||||
|
||||
test('Content-Security-Policy Header', async t => {
|
||||
const { headers } = await rp(url('/stateless'), { resolveWithFullResponse: true })
|
||||
// Verify functionality
|
||||
t.is(headers['content-security-policy'], "script-src 'self' 'sha256-BBvfKxDOoRM/gnFwke9u60HBZX3HUss/0lSI1sBRvOU='")
|
||||
})
|
||||
|
||||
test('/_nuxt/server-bundle.json should return 404', async t => {
|
||||
const err = await t.throws(rp(url('/_nuxt/server-bundle.json'), { resolveWithFullResponse: true }))
|
||||
t.is(err.statusCode, 404)
|
||||
|
Loading…
Reference in New Issue
Block a user