Skip to content

Commit

Permalink
Ensure nonce is applied on all script and link tags
Browse files Browse the repository at this point in the history
  • Loading branch information
timneutkens committed May 9, 2024
1 parent cef1ccc commit 765b1d8
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 21 deletions.
23 changes: 14 additions & 9 deletions packages/next/src/server/app-render/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export type AppRenderContext = AppRenderBaseContext & {
flightDataRendererErrorHandler: ErrorHandler
serverComponentsErrorHandler: ErrorHandler
isNotFoundPath: boolean
nonce: string | undefined
res: BaseNextResponse
}

Expand Down Expand Up @@ -362,6 +363,7 @@ async function generateFlight(
ctx.clientReferenceManifest.clientModules,
{
onError: ctx.flightDataRendererErrorHandler,
nonce: ctx.nonce,
}
)

Expand Down Expand Up @@ -841,6 +843,15 @@ async function renderToHTMLOrFlightImpl(
parsedFlightRouterState
)

// Get the nonce from the incoming request if it has one.
const csp =
req.headers['content-security-policy'] ||
req.headers['content-security-policy-report-only']
let nonce: string | undefined
if (csp && typeof csp === 'string') {
nonce = getScriptNonceFromHeader(csp)
}

const ctx: AppRenderContext = {
...baseCtx,
getDynamicParamFromSegment,
Expand All @@ -859,6 +870,7 @@ async function renderToHTMLOrFlightImpl(
flightDataRendererErrorHandler,
serverComponentsErrorHandler,
isNotFoundPath,
nonce,
res,
}

Expand All @@ -875,15 +887,6 @@ async function renderToHTMLOrFlightImpl(
? createFlightDataResolver(ctx)
: null

// Get the nonce from the incoming request if it has one.
const csp =
req.headers['content-security-policy'] ||
req.headers['content-security-policy-report-only']
let nonce: string | undefined
if (csp && typeof csp === 'string') {
nonce = getScriptNonceFromHeader(csp)
}

const validateRootLayout = dev

const { HeadManagerContext } =
Expand Down Expand Up @@ -943,6 +946,7 @@ async function renderToHTMLOrFlightImpl(
clientReferenceManifest.clientModules,
{
onError: serverComponentsErrorHandler,
nonce,
}
)

Expand Down Expand Up @@ -1279,6 +1283,7 @@ async function renderToHTMLOrFlightImpl(
clientReferenceManifest.clientModules,
{
onError: serverComponentsErrorHandler,
nonce,
}
)

Expand Down
27 changes: 22 additions & 5 deletions packages/next/src/server/app-render/get-layer-assets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,21 @@ export function getLayerAssets({
const ext = /\.(woff|woff2|eot|ttf|otf)$/.exec(fontFilename)![1]
const type = `font/${ext}`
const href = `${ctx.assetPrefix}/_next/${encodeURIPath(fontFilename)}`
ctx.componentMod.preloadFont(href, type, ctx.renderOpts.crossOrigin)
ctx.componentMod.preloadFont(
href,
type,
ctx.renderOpts.crossOrigin,
ctx.nonce
)
}
} else {
try {
let url = new URL(ctx.assetPrefix)
ctx.componentMod.preconnect(url.origin, 'anonymous')
ctx.componentMod.preconnect(url.origin, 'anonymous', ctx.nonce)
} catch (error) {
// assetPrefix must not be a fully qualified domain name. We assume
// we should preconnect to same origin instead
ctx.componentMod.preconnect('/', 'anonymous')
ctx.componentMod.preconnect('/', 'anonymous', ctx.nonce)
}
}
}
Expand All @@ -78,7 +83,11 @@ export function getLayerAssets({
const precedence =
process.env.NODE_ENV === 'development' ? 'next_' + href : 'next'

ctx.componentMod.preloadStyle(fullHref, ctx.renderOpts.crossOrigin)
ctx.componentMod.preloadStyle(
fullHref,
ctx.renderOpts.crossOrigin,
ctx.nonce
)

return (
<link
Expand All @@ -88,6 +97,7 @@ export function getLayerAssets({
precedence={precedence}
crossOrigin={ctx.renderOpts.crossOrigin}
key={index}
nonce={ctx.nonce}
/>
)
})
Expand All @@ -99,7 +109,14 @@ export function getLayerAssets({
href
)}${getAssetQueryString(ctx, true)}`

return <script src={fullSrc} async={true} key={`script-${index}`} />
return (
<script
src={fullSrc}
async={true}
key={`script-${index}`}
nonce={ctx.nonce}
/>
)
})
: []

Expand Down
33 changes: 26 additions & 7 deletions packages/next/src/server/app-render/rsc/preloads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,48 @@ Files in the rsc directory are meant to be packaged as part of the RSC graph usi

import ReactDOM from 'react-dom'

export function preloadStyle(href: string, crossOrigin?: string | undefined) {
export function preloadStyle(
href: string,
crossOrigin: string | undefined,
nonce: string | undefined
) {
const opts: any = { as: 'style' }
if (typeof crossOrigin === 'string') {
opts.crossOrigin = crossOrigin
}
if (typeof nonce === 'string') {
opts.nonce = nonce
}
ReactDOM.preload(href, opts)
}

export function preloadFont(
href: string,
type: string,
crossOrigin?: string | undefined
crossOrigin: string | undefined,
nonce: string | undefined
) {
const opts: any = { as: 'font', type }
if (typeof crossOrigin === 'string') {
opts.crossOrigin = crossOrigin
}
if (typeof nonce === 'string') {
opts.nonce = nonce
}
ReactDOM.preload(href, opts)
}

export function preconnect(href: string, crossOrigin?: string | undefined) {
;(ReactDOM as any).preconnect(
href,
typeof crossOrigin === 'string' ? { crossOrigin } : undefined
)
export function preconnect(
href: string,
crossOrigin: string | undefined,
nonce: string | undefined
) {
const opts: any = {}
if (typeof crossOrigin === 'string') {
opts.crossOrigin = crossOrigin
}
if (typeof nonce === 'string') {
opts.nonce = nonce
}
;(ReactDOM as any).preconnect(href, opts)
}

0 comments on commit 765b1d8

Please sign in to comment.