diff --git a/examples/with-dynamic-csp/README.md b/examples/with-dynamic-csp/README.md new file mode 100644 index 0000000000000..56ad655092f73 --- /dev/null +++ b/examples/with-dynamic-csp/README.md @@ -0,0 +1,48 @@ +[![Deploy to now](https://deploy.now.sh/static/button.svg)](https://deploy.now.sh/?repo=https://github.com/zeit/next.js/tree/master/examples/with-dynamic-csp) + +# Strict CSP example + +## How to use + +### Using `create-next-app` + +Execute [`create-next-app`](https://github.com/segmentio/create-next-app) with [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) or [npx](https://github.com/zkat/npx#readme) to bootstrap the example: + +```bash +npx create-next-app --example with-dynamic-csp with-dynamic-csp-app +# or +yarn create next-app --example with-dynamic-csp with-dynamic-csp-app +``` + +### Download manually + +Download the example: + +```bash +curl https://codeload.github.com/zeit/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-dynamic-csp +cd with-dynamic-csp +``` + +Install it and run: + +```bash +npm install +npm run dev +# or +yarn +yarn dev +``` + +Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download)) + +```bash +now +``` + +## The idea behind the example + +**If** you are having difficulty setting up a *standard* Content Security Policy, then you might need to use a [dynamic policy](https://csp.withgoogle.com/docs/strict-csp.html). This is less secure than effective whitelisting, but if your project cannot employ effective whitelisting, this can be a secure alternative. For it to work, we need to generate a nonce on every request, so this is limited to server rendered projects. + +Next.js supports generating nonces for our CSP out of the box. + +Note that when using `style-src 'nonce-{style-nonce}' 'unsafe-inline';` the nonce is automatically applied to your `styled-jsx` styles when using ` + {inlineScript(`console.log('Inline script with nonce')`, scriptNonce)}
- + ) diff --git a/examples/with-dynamic-csp/pages/index.js b/examples/with-dynamic-csp/pages/index.js new file mode 100644 index 0000000000000..023b00939e083 --- /dev/null +++ b/examples/with-dynamic-csp/pages/index.js @@ -0,0 +1,8 @@ +export default () =>
+

Hello World

+ +
diff --git a/examples/with-strict-csp-hash/README.md b/examples/with-strict-csp-hash/README.md deleted file mode 100644 index 21b5e667c21f9..0000000000000 --- a/examples/with-strict-csp-hash/README.md +++ /dev/null @@ -1,48 +0,0 @@ -[![Deploy to now](https://deploy.now.sh/static/button.svg)](https://deploy.now.sh/?repo=https://github.com/zeit/next.js/tree/master/examples/with-strict-csp-hash) - -# Example app with strict CSP generating script hash - -## How to use - -### Using `create-next-app` - -Execute [`create-next-app`](https://github.com/segmentio/create-next-app) with [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) or [npx](https://github.com/zkat/npx#readme) to bootstrap the example: - -```bash -npx create-next-app --example with-strict-csp-hash with-strict-csp-hash-app -# or -yarn create next-app --example with-strict-csp-hash with-strict-csp-hash-app -``` - -### Download manually - -Download the example: - -```bash -curl https://codeload.github.com/zeit/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-strict-csp-hash -cd with-strict-csp-hash -``` - -Install it and run: - -```bash -npm install -npm run dev -# or -yarn -yarn dev -``` - -Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download)) - -```bash -now -``` - -## The idea behind the example - -This example features how you can set up a strict CSP for your pages whitelisting next's inline bootstrap script by hash. -In contrast to the example `with-strict-csp` based on nonces, this way doesn't require running a server to generate fresh nonce values on every document request. -It defines the CSP by document `meta` tag. - -Note: There are still valid cases for using a nonce in case you need to inline scripts or styles for which calculating a hash is not feasible. diff --git a/examples/with-strict-csp-hash/pages/_document.js b/examples/with-strict-csp-hash/pages/_document.js deleted file mode 100644 index a894ff12f5397..0000000000000 --- a/examples/with-strict-csp-hash/pages/_document.js +++ /dev/null @@ -1,26 +0,0 @@ -import crypto from 'crypto' -import Document, { Head, Main, NextScript } from 'next/document' - -const cspHashOf = (text) => { - const hash = crypto.createHash('sha256') - hash.update(text) - return `'sha256-${hash.digest('base64')}'` -} - -export default class extends Document { - render () { - const csp = `default-src 'self'; script-src 'self' ${cspHashOf(NextScript.getInlineScriptSource(this.props))}` - - return ( - - - - - -
- - - - ) - } -} diff --git a/examples/with-strict-csp-hash/pages/index.js b/examples/with-strict-csp-hash/pages/index.js deleted file mode 100644 index 3d446a4e89b82..0000000000000 --- a/examples/with-strict-csp-hash/pages/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export default () => ( -
Hello World
-) diff --git a/examples/with-strict-csp/README.md b/examples/with-strict-csp/README.md deleted file mode 100644 index d3ea38d8fe6bf..0000000000000 --- a/examples/with-strict-csp/README.md +++ /dev/null @@ -1,46 +0,0 @@ -[![Deploy to now](https://deploy.now.sh/static/button.svg)](https://deploy.now.sh/?repo=https://github.com/zeit/next.js/tree/master/examples/with-strict-csp) - -# Strict CSP example - -## How to use - -### Using `create-next-app` - -Execute [`create-next-app`](https://github.com/segmentio/create-next-app) with [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) or [npx](https://github.com/zkat/npx#readme) to bootstrap the example: - -```bash -npx create-next-app --example with-strict-csp with-strict-csp-app -# or -yarn create next-app --example with-strict-csp with-strict-csp-app -``` - -### Download manually - -Download the example: - -```bash -curl https://codeload.github.com/zeit/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-strict-csp -cd with-strict-csp -``` - -Install it and run: - -```bash -npm install -npm run dev -# or -yarn -yarn dev -``` - -Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download)) - -```bash -now -``` - -## The idea behind the example - -If you want to implement a CSP, the most effective way is to follow the [strict CSP](https://csp.withgoogle.com/docs/strict-csp.html) approach. For it to work, we need to generate a nonce on every request. - -This example uses [Helmet](https://github.com/helmetjs/helmet) to configure the CSP and add the appropriate headers to all server responses. The nonce is generated with [uuid](https://github.com/kelektiv/node-uuid). Then we can pass the nonce to `` and `` in the custom ``. diff --git a/examples/with-strict-csp/csp.js b/examples/with-strict-csp/csp.js deleted file mode 100644 index c775c1f664a8d..0000000000000 --- a/examples/with-strict-csp/csp.js +++ /dev/null @@ -1,31 +0,0 @@ -const helmet = require('helmet') -const uuidv4 = require('uuid/v4') - -module.exports = function csp (app) { - // Create a nonce on every request and make it available to other middleware - app.use((req, res, next) => { - res.locals.nonce = Buffer.from(uuidv4()).toString('base64') - next() - }) - - const nonce = (req, res) => `'nonce-${res.locals.nonce}'` - - const scriptSrc = [nonce, "'strict-dynamic'", "'unsafe-inline'", 'https:'] - - // In dev we allow 'unsafe-eval', so HMR doesn't trigger the CSP - if (process.env.NODE_ENV !== 'production') { - scriptSrc.push("'unsafe-eval'") - } - - app.use( - helmet({ - contentSecurityPolicy: { - directives: { - baseUri: ["'none'"], - objectSrc: ["'none'"], - scriptSrc - } - } - }) - ) -} diff --git a/examples/with-strict-csp/package.json b/examples/with-strict-csp/package.json deleted file mode 100644 index 0a6bafdb7cb05..0000000000000 --- a/examples/with-strict-csp/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "with-strict-csp", - "version": "1.0.0", - "scripts": { - "dev": "node server.js", - "build": "next build", - "start": "NODE_ENV=production node server.js" - }, - "dependencies": { - "express": "^4.14.0", - "helmet": "3.13.0", - "next": "latest", - "react": "^16.0.0", - "react-dom": "^16.0.0", - "uuid": "3.3.2" - } -} diff --git a/examples/with-strict-csp/pages/index.js b/examples/with-strict-csp/pages/index.js deleted file mode 100644 index 3d446a4e89b82..0000000000000 --- a/examples/with-strict-csp/pages/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export default () => ( -
Hello World
-) diff --git a/examples/with-strict-csp/server.js b/examples/with-strict-csp/server.js deleted file mode 100644 index 5ea055bb52e93..0000000000000 --- a/examples/with-strict-csp/server.js +++ /dev/null @@ -1,22 +0,0 @@ -const express = require('express') -const next = require('next') -const csp = require('./csp') - -const port = parseInt(process.env.PORT, 10) || 3000 -const dev = process.env.NODE_ENV !== 'production' -const app = next({ dev }) -const handle = app.getRequestHandler() - -app.prepare() - .then(() => { - const server = express() - - csp(server) - - server.use(handle) - - server.listen(port, (err) => { - if (err) throw err - console.log(`> Ready on http://localhost:${port}`) - }) - }) diff --git a/packages/next-server/server/config.js b/packages/next-server/server/config.js index b560b4754ecde..bd4cd50d1c0c3 100644 --- a/packages/next-server/server/config.js +++ b/packages/next-server/server/config.js @@ -11,6 +11,7 @@ const defaultConfig = { useFileSystemPublicRoutes: true, generateBuildId: () => null, generateEtags: true, + contentSecurityPolicy: null, pageExtensions: ['jsx', 'js'] } diff --git a/packages/next-server/server/next-server.ts b/packages/next-server/server/next-server.ts index 54887e789c14a..adb8ec52038eb 100644 --- a/packages/next-server/server/next-server.ts +++ b/packages/next-server/server/next-server.ts @@ -4,6 +4,7 @@ import { resolve, join, sep } from 'path' import { parse as parseUrl, UrlWithParsedQuery } from 'url' import { parse as parseQs, ParsedUrlQuery } from 'querystring' import fs from 'fs' +import nanoid from 'nanoid' import {renderToHTML} from './render' import {sendHTML} from './send-html' import {serveStatic} from './serve-static' @@ -31,6 +32,9 @@ export default class Server { buildId: string renderOpts: { staticMarkup: boolean, + csp?: string, + scriptNonce?: string, + styleNonce?: string, distDir: string, buildId: string, generateEtags: boolean, @@ -203,6 +207,18 @@ export default class Server { } public async render (req: IncomingMessage, res: ServerResponse, pathname: string, query: ParsedUrlQuery = {}, parsedUrl?: UrlWithParsedQuery): Promise { + if (this.nextConfig.contentSecurityPolicy) { + if (!this.renderOpts.staticMarkup) { + this.renderOpts.csp = this.nextConfig.contentSecurityPolicy + .replace(/{script-nonce}/gi, () => (this.renderOpts.scriptNonce = this.genNonce())) + .replace(/{style-nonce}/gi, () => (this.renderOpts.styleNonce = this.genNonce())) + + this.renderOpts.csp && res.setHeader('Content-Security-Policy', this.renderOpts.csp) + } else { + this.renderOpts.csp = this.nextConfig.contentSecurityPolicy + } + } + const url: any = req.url if (isInternalUrl(url)) { return this.handleRequest(req, res, parsedUrl) @@ -304,4 +320,8 @@ export default class Server { throw err } } + + genNonce () { + return Buffer.from(nanoid(32)).toString('base64') + } } diff --git a/packages/next-server/server/render.tsx b/packages/next-server/server/render.tsx index e16907a612828..6f7a4fe9f12ce 100644 --- a/packages/next-server/server/render.tsx +++ b/packages/next-server/server/render.tsx @@ -57,6 +57,9 @@ type RenderOpts = { buildId: string, runtimeConfig?: {[key: string]: any}, assetPrefix?: string, + csp?: string, + scriptNonce?: string, + styleNonce?: string, err?: Error|null, nextExport?: boolean, dev?: boolean @@ -69,6 +72,9 @@ function renderDocument(Document: React.ComponentType, { query, buildId, assetPrefix, + csp, + scriptNonce, + styleNonce, runtimeConfig, nextExport, dynamicImportsIds, @@ -106,6 +112,9 @@ function renderDocument(Document: React.ComponentType, { files={files} dynamicImports={dynamicImports} assetPrefix={assetPrefix} + csp={csp} + scriptNonce={scriptNonce} + styleNonce={styleNonce} {...docProps} /> ) @@ -116,6 +125,9 @@ export async function renderToHTML (req: IncomingMessage, res: ServerResponse, p err, buildId, distDir, + csp, + scriptNonce, + styleNonce, dev = false, staticMarkup = false } = renderOpts @@ -137,7 +149,7 @@ export async function renderToHTML (req: IncomingMessage, res: ServerResponse, p if (!Document.prototype || !Document.prototype.isReactComponent) throw new Error('_document.js is not exporting a React component') const asPath = req.url - const ctx = { err, req, res, pathname, query, asPath } + const ctx = { err, req, res, pathname, query, asPath, csp, styleNonce, scriptNonce } const router = new Router(pathname, query, asPath) const props = await loadGetInitialProps(App, {Component, router, ctx}) diff --git a/packages/next-server/server/send-html.ts b/packages/next-server/server/send-html.ts index ae1395a9c19a4..3b50fa1e4bc93 100644 --- a/packages/next-server/server/send-html.ts +++ b/packages/next-server/server/send-html.ts @@ -5,7 +5,11 @@ import { isResSent } from '../lib/utils' export function sendHTML (req: IncomingMessage, res: ServerResponse, html: string, { generateEtags }: {generateEtags: boolean}) { if (isResSent(res)) return - const etag = generateEtags ? generateETag(html) : undefined + const etag = generateEtags ? generateETag( + html + .replace(/

-It's possible to use any existing CSS-in-JS solution. The simplest one is inline styles: +It's possible to use any existing CSS-in-JS solution. + +Inline styles should only be used for javascript animations (which should be replaced with CSS animations if possible). ```jsx +// Never use inline styles for something as simple as this export default () =>

hi there

``` @@ -674,19 +678,24 @@ If you want to access the `router` object inside any component in your app, you import { withRouter } from 'next/router' const ActiveLink = ({ children, router, href }) => { - const style = { - marginRight: 10, - color: router.pathname === href ? 'red' : 'black' - } - const handleClick = (e) => { e.preventDefault() router.push(href) } return ( - + {children} + ) } @@ -1269,6 +1278,77 @@ module.exports = { } ``` +#### Content Security Policy + +Next.js supports the use of [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) out of the box. CSP allows us to whitelist the domains that our app can communicate with to help prevent XSS attacks and minimize the damage that a rouge package might do. + +This is the policy Next.js is tested against: +``` +default-src 'none'; +connect-src 'self'; +img-src 'self'; +script-src 'self'; +style-src 'nonce-{style-nonce}' 'unsafe-inline'; +``` + +##### Whitelisting Tips + +When whitelisting, it's important to whitelist to a point of control. For example, you wouldn't want to whitelist `cdn.tld` because other users might have control over the assets served over this domain. You would want to be more specific like `cdn.tld/yourZoneOfControl`. Obviosuly not all external assets can be whitelisted in this way, so you always want to put some thought into how specific you should be, for instance if you were using a script called `helper` and you could find the asset at `example.com/helper/0.1.0.js` then maybe `example.com/helper` would make sense as a whitelist. This of course depends on the entity who controls the site to serve the expected files, so you should make sure all origins you whitelist are trustworthy. + +##### Applying a CSP to Next.js + +You can add a CSP to your app by adding a config option. You will have to test your site to ensure all your assets load, if any fail you just need to tweak your policy to support it (see [directives](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)). + +```js +// next.config.js +module.exports = { + contentSecurityPolicy: "default-src 'none'; connect-src 'self' img-src 'self'; script-src 'self'; style-src 'nonce-{style-nonce}' 'unsafe-inline';" +} +``` +This policy will work on a fresh Next.js install, but if you have added any external assets, you will have to add some origins to this policy. You will also have to use [custom error handling](#custom-error-handling), but this isn't too difficult. + +If you can't do custom error handling or are using static rendering, leave off the `'nonce-{style-nonce}`. + +##### Whitelisting Styles (`style-src`) + +Styles are inserted inline with `styled-jsx`, so must use a nonce to be secure. This is done automatically when you use `style-src 'nonce-{style-nonce}' 'unsafe-inline';` although you can only use `styled-jsx` to add styles to the page (`unsafe-inline` is for backwards compatibility). + +If you pull in any other stylesheets, you can whitelist them by adding them to the policy by using: +``` +style-src https://cdn.tld/controlled 'nonce-{style-nonce}' 'unsafe-inline'; +``` + +It is posible to use nonces with other CSS-in-JS libraries like [Material-UI](https://material-ui.com/css-in-js/advanced/#content-security-policy-csp) or [JSS](https://github.com/cssinjs/jss/blob/master/docs/csp.md) (See next code snippet to see where to get the generated nonce). + +##### Whitelisting Scripts (`script-src`) + +Next.js doesn't use any inline-scripts, and neither should you. If you need to use external scripts, then you can whitelist them in your policy. +``` +script-src https://cdn.tld/controlled 'self'; +``` + +However, if you cannot whitelist the scripts that you use in your app or you must use inline scripts, you can use: + +``` +script-src 'nonce-{script-nonce}' 'unsafe-inline' 'strict-dynamic' https: http:; +``` +This should be used as a last resort and all scripts you import must have the nonce appended to it, which you can access like this: + +```js +// pages/_document.js +export default class MyDocument extends Document { + static async getInitialProps (ctx) { + const initialProps = await Document.getInitialProps(ctx) + const { scriptNonce, styleNonce } = ctx + return { ...initialProps, scriptNonce, styleNonce } + } + + render () { + const { scriptNonce, styleNonce } = this.props + } +} +``` + #### Disabling etag generation You can disable etag generation for HTML pages depending on your cache strategy. If no configuration is specified then Next will generate etags for every page. diff --git a/packages/next/build/webpack-config.js b/packages/next/build/webpack-config.js index ed68e6d91a32c..f6f7e0167d1c9 100644 --- a/packages/next/build/webpack-config.js +++ b/packages/next/build/webpack-config.js @@ -15,8 +15,7 @@ import BuildManifestPlugin from './webpack/plugins/build-manifest-plugin' import ChunkNamesPlugin from './webpack/plugins/chunk-names-plugin' import { ReactLoadablePlugin } from './webpack/plugins/react-loadable-plugin' import {SERVER_DIRECTORY, REACT_LOADABLE_MANIFEST, CLIENT_STATIC_FILES_RUNTIME_WEBPACK, CLIENT_STATIC_FILES_RUNTIME_MAIN} from 'next-server/constants' -import {NEXT_PROJECT_ROOT, NEXT_PROJECT_ROOT_NODE_MODULES, NEXT_PROJECT_ROOT_DIST_CLIENT, NEXT_PROJECT_ROOT_DIST_SERVER, DEFAULT_PAGES_DIR} from '../lib/constants' -import AutoDllPlugin from 'autodll-webpack-plugin' +import {NEXT_PROJECT_ROOT, NEXT_PROJECT_ROOT_NODE_MODULES, NEXT_PROJECT_ROOT_DIST_CLIENT, NEXT_PROJECT_ROOT_DIST_SERVER, NEXT_PROJECT_ROOT_DIST_STATIC, DEFAULT_PAGES_DIR} from '../lib/constants' import TerserPlugin from 'terser-webpack-plugin' import AssetsSizePlugin from './webpack/plugins/assets-size-plugin' @@ -278,6 +277,19 @@ export default async function getBaseWebpackConfig (dir, {dev = false, isServer include: defaultLoaders.hotSelfAccept.options.include, use: defaultLoaders.hotSelfAccept }, + // TODO: putting !isServer here causes problems? + { + include: [NEXT_PROJECT_ROOT_DIST_STATIC], + use: [ + { + loader: 'emit-file-loader', + options: { + name: `static/runtime/${dev ? '[name]-[hash].[ext]' : '[name]-[hash].[ext]'}`, + importPath: true + } + } + ] + }, { test: /\.(js|jsx)$/, include: [dir, NEXT_PROJECT_ROOT_DIST_CLIENT, DEFAULT_PAGES_DIR, /next-server[\\/]dist[\\/]lib/], @@ -293,22 +305,6 @@ export default async function getBaseWebpackConfig (dir, {dev = false, isServer ].filter(Boolean) }, plugins: [ - // Precompile react / react-dom for development, speeding up webpack - dev && !isServer && new AutoDllPlugin({ - filename: '[name]_[hash].js', - path: './static/development/dll', - context: dir, - entry: { - dll: [ - 'react', - 'react-dom' - ] - }, - config: { - mode: webpackMode, - resolve: resolveConfig - } - }), // This plugin makes sure `output.filename` is used for entry chunks new ChunkNamesPlugin(), !isServer && new ReactLoadablePlugin({ diff --git a/packages/next/build/webpack/loaders/emit-file-loader.js b/packages/next/build/webpack/loaders/emit-file-loader.js index 7ab6020f58759..2c226f1b839cb 100644 --- a/packages/next/build/webpack/loaders/emit-file-loader.js +++ b/packages/next/build/webpack/loaders/emit-file-loader.js @@ -25,7 +25,7 @@ module.exports = function (content, sourceMap) { const interpolatedName = interpolateName(loaderUtils.interpolateName(this, name, opts), {name, opts}) const emit = (code, map) => { this.emitFile(interpolatedName, code, map) - callback(null, code, map) + query.importPath ? callback(null, `module.exports = '${interpolatedName}';`) : callback(null, code, map) } if (query.transform) { diff --git a/packages/next/build/webpack/plugins/build-manifest-plugin.js b/packages/next/build/webpack/plugins/build-manifest-plugin.js index ab3f28c7fbdd7..9450ec7670578 100644 --- a/packages/next/build/webpack/plugins/build-manifest-plugin.js +++ b/packages/next/build/webpack/plugins/build-manifest-plugin.js @@ -12,13 +12,6 @@ export default class BuildManifestPlugin { const mainJsChunk = chunks.find((c) => c.name === CLIENT_STATIC_FILES_RUNTIME_MAIN) const mainJsFiles = mainJsChunk && mainJsChunk.files.length > 0 ? mainJsChunk.files.filter((file) => /\.js$/.test(file)) : [] - for (const filePath of Object.keys(compilation.assets)) { - const path = filePath.replace(/\\/g, '/') - if (/^static\/development\/dll\//.test(path)) { - assetMap.devFiles.push(path) - } - } - // compilation.entrypoints is a Map object, so iterating over it 0 is the key and 1 is the value for (const [, entrypoint] of compilation.entrypoints.entries()) { const result = ROUTE_NAME_REGEX.exec(entrypoint.name) diff --git a/packages/next/client/dev-error-overlay/hot-dev-client.js b/packages/next/client/dev-error-overlay/hot-dev-client.js index f2c5073d2eb39..ae2b2fefa1ff7 100644 --- a/packages/next/client/dev-error-overlay/hot-dev-client.js +++ b/packages/next/client/dev-error-overlay/hot-dev-client.js @@ -70,7 +70,8 @@ export default function connect (options) { ErrorOverlay.startReportingRuntimeErrors({ onError: function () { hadRuntimeError = true - } + }, + className: '__next_error__' }) if (module.hot && typeof module.hot.dispose === 'function') { diff --git a/packages/next/lib/constants.js b/packages/next/lib/constants.js index ced263c849a85..0fdb78136317e 100644 --- a/packages/next/lib/constants.js +++ b/packages/next/lib/constants.js @@ -5,3 +5,4 @@ export const NEXT_PROJECT_ROOT_NODE_MODULES = join(NEXT_PROJECT_ROOT, 'node_modu export const DEFAULT_PAGES_DIR = join(NEXT_PROJECT_ROOT_DIST, 'pages') export const NEXT_PROJECT_ROOT_DIST_CLIENT = join(NEXT_PROJECT_ROOT_DIST, 'client') export const NEXT_PROJECT_ROOT_DIST_SERVER = join(NEXT_PROJECT_ROOT_DIST, 'server') +export const NEXT_PROJECT_ROOT_DIST_STATIC = join(NEXT_PROJECT_ROOT_DIST, 'static') diff --git a/packages/next/package.json b/packages/next/package.json index 122006bd3a9d3..dbd2ce1e08936 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -50,7 +50,6 @@ "@babel/template": "7.1.2", "ansi-html": "0.0.7", "async-sema": "^2.1.4", - "autodll-webpack-plugin": "0.4.2", "babel-core": "7.0.0-bridge.0", "babel-loader": "8.0.2", "babel-plugin-react-require": "3.0.0", diff --git a/packages/next/pages/_document.js b/packages/next/pages/_document.js index 560c402fd42d5..b4e3dc5f0cd77 100644 --- a/packages/next/pages/_document.js +++ b/packages/next/pages/_document.js @@ -13,9 +13,9 @@ export default class Document extends Component { _documentProps: PropTypes.any } - static getInitialProps ({ renderPage }) { + static getInitialProps ({ renderPage, styleNonce }) { const { html, head } = renderPage() - const styles = flush() + const styles = flush({ nonce: styleNonce }) return { html, head, styles } } @@ -58,7 +58,6 @@ export class Head extends Component { return { return }) } getPreloadMainLinks () { - const { assetPrefix, files } = this.context._documentProps + const { assetPrefix, files, scriptNonce } = this.context._documentProps if(!files || files.length === 0) { return null } @@ -94,7 +93,7 @@ export class Head extends Component { return + { (staticMarkup && csp) ? : null } + { styleNonce ? : null } {children} {head} - {page !== '/_error' && } - - + {page !== '/_error' && } + + {this.getPreloadDynamicChunks()} {this.getPreloadMainLinks()} {this.getCssLinks()} {styles || null} + { process.env.NODE_ENV !== 'production' && } } } @@ -158,20 +170,20 @@ export class NextScript extends Component { } getDynamicChunks () { - const { dynamicImports, assetPrefix } = this.context._documentProps + const { dynamicImports, assetPrefix, scriptNonce } = this.context._documentProps return dynamicImports.map((bundle) => { return