Skip to content

Commit 25f810b

Browse files
ztannerijjk
authored andcommitted
exclude images and static media from dev origin check (#77417)
Excludes `/_next/image` and `/_next/static/media` as they don't contain sensitive information and prevents complications loading them in cases where they are inlined in CSS, as they'll be requested with `sec-fetch-mode: no-cors`. x-ref: #77344
1 parent d9bcb83 commit 25f810b

File tree

3 files changed

+51
-2
lines changed

3 files changed

+51
-2
lines changed

packages/next/src/server/lib/router-utils/block-cross-site.ts

+20-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,25 @@ function warnOrBlockRequest(
3131
return true
3232
}
3333

34+
function isInternalDevEndpoint(req: IncomingMessage): boolean {
35+
if (!req.url) return false
36+
37+
try {
38+
// TODO: We should standardize on a single prefix for this
39+
const isMiddlewareRequest = req.url.includes('/__nextjs')
40+
const isInternalAsset = req.url.includes('/_next')
41+
// Static media requests are excluded, as they might be loaded via CSS and would fail
42+
// CORS checks.
43+
const isIgnoredRequest =
44+
req.url.includes('/_next/image') ||
45+
req.url.includes('/_next/static/media')
46+
47+
return !isIgnoredRequest && (isInternalAsset || isMiddlewareRequest)
48+
} catch (err) {
49+
return false
50+
}
51+
}
52+
3453
export const blockCrossSite = (
3554
req: IncomingMessage,
3655
res: ServerResponse | Duplex,
@@ -51,8 +70,7 @@ export const blockCrossSite = (
5170
}
5271

5372
// only process internal URLs/middleware
54-
// TODO: We should standardize on a single prefix for this
55-
if (!req.url?.includes('/_next') && !req.url?.includes('/__nextjs')) {
73+
if (!isInternalDevEndpoint(req)) {
5674
return false
5775
}
5876
// block non-cors request from cross-site e.g. script tag on

test/development/basic/allowed-dev-origins.test.ts

+31
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,37 @@ describe.each([['', '/docs']])(
318318
server.close()
319319
}
320320
})
321+
322+
it('should load images regardless of allowed origins', async () => {
323+
const { server, port } = await createHostServer()
324+
try {
325+
const browser = await webdriver(`http://127.0.0.1:${port}`, '/about')
326+
327+
const imageSnippet = `(() => {
328+
const statusEl = document.createElement('p')
329+
statusEl.id = 'status'
330+
document.querySelector('body').appendChild(statusEl)
331+
332+
const image = document.createElement('img')
333+
image.src = "${next.url}/_next/image?url=%2Fimage.png&w=256&q=75"
334+
document.querySelector('body').appendChild(image)
335+
image.onload = () => {
336+
statusEl.innerText = 'OK'
337+
}
338+
image.onerror = () => {
339+
statusEl.innerText = 'Unauthorized'
340+
}
341+
})()`
342+
343+
await browser.eval(imageSnippet)
344+
345+
await retry(async () => {
346+
expect(await browser.elementByCss('#status').text()).toBe('OK')
347+
})
348+
} finally {
349+
server.close()
350+
}
351+
})
321352
})
322353
}
323354
)
1.62 KB
Loading

0 commit comments

Comments
 (0)