Use nonce attribute for all scripts and preloads if provided (#4539)

When implementing a strict CSP with nonces and `strict-dynamic`, every script and preload requires a nonce.

https://csp.withgoogle.com/docs/strict-csp.html
This commit is contained in:
Thomas Hermann 2018-06-28 14:16:30 -04:00 committed by Tim Neutkens
parent 17e410a1d0
commit 1c817d2bbf
3 changed files with 26 additions and 8 deletions

View file

@ -39,6 +39,10 @@ export class Head extends Component {
_documentProps: PropTypes.any
}
static propTypes = {
nonce: PropTypes.string
}
getChunkPreloadLink (filename) {
const { __NEXT_DATA__, buildManifest } = this.context._documentProps
let { assetPrefix, buildId } = __NEXT_DATA__
@ -48,6 +52,7 @@ export class Head extends Component {
return files.map(file => {
return <link
key={filename}
nonce={this.props.nonce}
rel='preload'
href={`${assetPrefix}/_next/${file}`}
as='script'
@ -79,6 +84,7 @@ export class Head extends Component {
rel='preload'
href={`${assetPrefix}/_next/webpack/chunks/${chunk}`}
as='script'
nonce={this.props.nonce}
/>
))
}
@ -90,9 +96,9 @@ export class Head extends Component {
return <head {...this.props}>
{(head || []).map((h, i) => React.cloneElement(h, { key: h.key || i }))}
{page !== '/_error' && <link rel='preload' href={`${assetPrefix}/_next/${buildId}/page${pagePathname}`} as='script' />}
<link rel='preload' href={`${assetPrefix}/_next/${buildId}/page/_app.js`} as='script' />
<link rel='preload' href={`${assetPrefix}/_next/${buildId}/page/_error.js`} as='script' />
{page !== '/_error' && <link rel='preload' href={`${assetPrefix}/_next/${buildId}/page${pagePathname}`} as='script' nonce={this.props.nonce} />}
<link rel='preload' href={`${assetPrefix}/_next/${buildId}/page/_app.js`} as='script' nonce={this.props.nonce} />
<link rel='preload' href={`${assetPrefix}/_next/${buildId}/page/_error.js`} as='script' nonce={this.props.nonce} />
{this.getPreloadDynamicChunks()}
{this.getPreloadMainLinks()}
{styles || null}
@ -136,6 +142,7 @@ export class NextScript extends Component {
<script
key={filename}
src={`${assetPrefix}/_next/${file}`}
nonce={this.props.nonce}
{...additionalProps}
/>
))
@ -165,6 +172,7 @@ export class NextScript extends Component {
async
key={chunk}
src={`${assetPrefix}/_next/webpack/chunks/${chunk}`}
nonce={this.props.nonce}
/>
))}
</Fragment>
@ -204,9 +212,9 @@ export class NextScript extends Component {
`}
`
}} />}
{page !== '/_error' && <script async id={`__NEXT_PAGE__${pathname}`} src={`${assetPrefix}/_next/${buildId}/page${pagePathname}`} />}
<script async id={`__NEXT_PAGE__/_app`} src={`${assetPrefix}/_next/${buildId}/page/_app.js`} />
<script async id={`__NEXT_PAGE__/_error`} src={`${assetPrefix}/_next/${buildId}/page/_error.js`} />
{page !== '/_error' && <script async id={`__NEXT_PAGE__${pathname}`} src={`${assetPrefix}/_next/${buildId}/page${pagePathname}`} nonce={this.props.nonce} />}
<script async id={`__NEXT_PAGE__/_app`} src={`${assetPrefix}/_next/${buildId}/page/_app.js`} nonce={this.props.nonce} />
<script async id={`__NEXT_PAGE__/_error`} src={`${assetPrefix}/_next/${buildId}/page/_error.js`} nonce={this.props.nonce} />
{staticMarkup ? null : this.getDynamicChunks()}
{staticMarkup ? null : this.getScripts()}
</Fragment>

View file

@ -9,14 +9,14 @@ export default class MyDocument extends Document {
render () {
return (
<html>
<Head>
<Head nonce='test-nonce'>
<style>{`body { margin: 0 } /* custom! */`}</style>
</Head>
<body className='custom_class'>
<p id='custom-property'>{this.props.customProperty}</p>
<p id='document-hmr'>Hello Document HMR</p>
<Main />
<NextScript />
<NextScript nonce='test-nonce' />
</body>
</html>
)

View file

@ -24,6 +24,16 @@ export default function ({ app }, suiteName, render, fetch) {
const $ = await get$('/')
expect($('#custom-property').text() === 'Hello Document')
})
test('It adds nonces to all scripts and preload links', async () => {
const $ = await get$('/')
const nonce = 'test-nonce'
let noncesAdded = true
$('script, link[rel=preload]').each((index, element) => {
if ($(element).attr('nonce') !== nonce) noncesAdded = false
})
expect(noncesAdded).toBe(true)
})
})
describe('_app', () => {