Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Subresource Integrity for App Directory (#39729)
<!-- Thanks for opening a PR! Your contribution is much appreciated. In order to make sure your PR is handled as smoothly as possible we request that you follow the checklist sections below. Choose the right checklist for the change that you're making: --> This serves to add support for [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) hashes for scripts added from the new app directory. This also has support for utilizing nonce values passed from request headers (expected to be generated per request in middleware) in the bootstrapping scripts via the `Content-Security-Policy` header as such: ``` Content-Security-Policy: script-src 'nonce-2726c7f26c' ``` Which results in the inline scripts having a new `nonce` attribute hash added. These features combined support for setting an aggressive Content Security Policy on scripts loaded. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [x] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [x] Make sure the linting passes by running `pnpm lint` - [x] The examples guidelines are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing.md#adding-examples) Co-authored-by: Steven <steven@ceriously.com>
- Loading branch information
Showing
27 changed files
with
482 additions
and
104 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# nonce contained invalid characters | ||
|
||
#### Why This Error Occurred | ||
|
||
This happens when there is a request that contains a `Content-Security-Policy` | ||
header that contains a `script-src` directive with a nonce value that contains | ||
invalid characters (any one of `<>&` characters). For example: | ||
|
||
- `'nonce-<script />'`: not allowed | ||
- `'nonce-/>script<>'`: not allowed | ||
- `'nonce-PHNjcmlwdCAvPg=='`: allowed | ||
- `'nonce-Lz5zY3JpcHQ8Pg=='`: allowed | ||
|
||
#### Possible Ways to Fix It | ||
|
||
Replace the nonce value with a base64 encoded value. | ||
|
||
### Useful Links | ||
|
||
- [Content Security Policy Sources](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/Sources#sources) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 71 additions & 0 deletions
71
packages/next/build/webpack/plugins/subresource-integrity-plugin.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { webpack, sources } from 'next/dist/compiled/webpack/webpack' | ||
import crypto from 'crypto' | ||
import { SUBRESOURCE_INTEGRITY_MANIFEST } from '../../../shared/lib/constants' | ||
|
||
const PLUGIN_NAME = 'SubresourceIntegrityPlugin' | ||
|
||
export type SubresourceIntegrityAlgorithm = 'sha256' | 'sha384' | 'sha512' | ||
|
||
export class SubresourceIntegrityPlugin { | ||
constructor(private readonly algorithm: SubresourceIntegrityAlgorithm) {} | ||
|
||
public apply(compiler: webpack.Compiler) { | ||
compiler.hooks.make.tap(PLUGIN_NAME, (compilation) => { | ||
compilation.hooks.afterOptimizeAssets.tap( | ||
{ | ||
name: PLUGIN_NAME, | ||
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS, | ||
}, | ||
(assets) => { | ||
// Collect all the entrypoint files. | ||
let files = new Set<string>() | ||
for (const entrypoint of compilation.entrypoints.values()) { | ||
const iterator = entrypoint?.getFiles() | ||
if (!iterator) { | ||
continue | ||
} | ||
|
||
for (const file of iterator) { | ||
files.add(file) | ||
} | ||
} | ||
|
||
// For each file, deduped, calculate the file hash. | ||
const hashes: Record<string, string> = {} | ||
for (const file of files.values()) { | ||
// Get the buffer for the asset. | ||
const asset = assets[file] | ||
if (!asset) { | ||
throw new Error(`could not get asset: ${file}`) | ||
} | ||
|
||
// Get the buffer for the asset. | ||
const buffer = asset.buffer() | ||
|
||
// Create the hash for the content. | ||
const hash = crypto | ||
.createHash(this.algorithm) | ||
.update(buffer) | ||
.digest() | ||
.toString('base64') | ||
|
||
hashes[file] = `${this.algorithm}-${hash}` | ||
} | ||
|
||
const json = JSON.stringify(hashes, null, 2) | ||
const file = 'server/' + SUBRESOURCE_INTEGRITY_MANIFEST | ||
assets[file + '.js'] = new sources.RawSource( | ||
'self.__SUBRESOURCE_INTEGRITY_MANIFEST=' + json | ||
// Work around webpack 4 type of RawSource being used | ||
// TODO: use webpack 5 type by default | ||
) as unknown as webpack.sources.RawSource | ||
assets[file + '.json'] = new sources.RawSource( | ||
json | ||
// Work around webpack 4 type of RawSource being used | ||
// TODO: use webpack 5 type by default | ||
) as unknown as webpack.sources.RawSource | ||
} | ||
) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.