Skip to content

Commit e9d5090

Browse files
committed
separate the tests
1 parent 32eaab5 commit e9d5090

File tree

3 files changed

+283
-107
lines changed

3 files changed

+283
-107
lines changed

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

+5-4
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,20 @@ import { isCsrfOriginAllowed } from '../../app-render/csrf-protection'
77

88
function warnOrBlockRequest(
99
res: ServerResponse | Duplex,
10-
requestPath: string,
10+
origin: string | undefined,
1111
mode: 'warn' | 'block'
1212
): boolean {
13+
const originString = origin ? `from ${origin}` : ''
1314
if (mode === 'warn') {
1415
warnOnce(
15-
`Cross origin request detected from ${requestPath}. In a future major version of Next.js, you will need to explicitly configure "allowedDevOrigins" in next.config to allow this.\nRead more: https://nextjs.org/docs/app/api-reference/config/next-config-js/allowedDevOrigins`
16+
`Cross origin request detected ${originString} to /_next/* resource. In a future major version of Next.js, you will need to explicitly configure "allowedDevOrigins" in next.config to allow this.\nRead more: https://nextjs.org/docs/app/api-reference/config/next-config-js/allowedDevOrigins`
1617
)
1718

1819
return false
1920
}
2021

2122
warnOnce(
22-
`Blocked cross-origin request from ${requestPath}. To allow this, configure "allowedDevOrigins" in next.config\nRead more: https://nextjs.org/docs/app/api-reference/config/next-config-js/allowedDevOrigins`
23+
`Blocked cross-origin request ${originString} to /_next/* resource. To allow this, configure "allowedDevOrigins" in next.config\nRead more: https://nextjs.org/docs/app/api-reference/config/next-config-js/allowedDevOrigins`
2324
)
2425

2526
if ('statusCode' in res) {
@@ -61,7 +62,7 @@ export const blockCrossSite = (
6162
req.headers['sec-fetch-mode'] === 'no-cors' &&
6263
req.headers['sec-fetch-site'] === 'cross-site'
6364
) {
64-
return warnOrBlockRequest(res, ' /_next/*', mode)
65+
return warnOrBlockRequest(res, undefined, mode)
6566
}
6667

6768
// ensure websocket requests from allowed origin
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
import http from 'http'
2+
import { join } from 'path'
3+
import webdriver from 'next-webdriver'
4+
import { createNext, FileRef } from 'e2e-utils'
5+
import { NextInstance } from 'e2e-utils'
6+
import { fetchViaHTTP, findPort, retry } from 'next-test-utils'
7+
8+
describe.each([['', '/docs']])(
9+
'allowed-dev-origins, basePath: %p',
10+
(basePath: string) => {
11+
let next: NextInstance
12+
13+
describe('warn mode', () => {
14+
beforeAll(async () => {
15+
next = await createNext({
16+
files: {
17+
pages: new FileRef(join(__dirname, 'misc/pages')),
18+
public: new FileRef(join(__dirname, 'misc/public')),
19+
},
20+
nextConfig: {
21+
basePath,
22+
},
23+
})
24+
25+
await retry(async () => {
26+
// make sure host server is running
27+
const asset = await fetchViaHTTP(
28+
next.appPort,
29+
'/_next/static/chunks/pages/_app.js'
30+
)
31+
expect(asset.status).toBe(200)
32+
})
33+
})
34+
afterAll(() => next.destroy())
35+
36+
it('should warn about WebSocket from cross-site', async () => {
37+
let server = http.createServer((req, res) => {
38+
res.end(`
39+
<html>
40+
<head>
41+
<title>testing cross-site</title>
42+
</head>
43+
<body></body>
44+
</html>
45+
`)
46+
})
47+
try {
48+
const port = await findPort()
49+
await new Promise<void>((res) => {
50+
server.listen(port, () => res())
51+
})
52+
const websocketSnippet = `(() => {
53+
const statusEl = document.createElement('p')
54+
statusEl.id = 'status'
55+
document.querySelector('body').appendChild(statusEl)
56+
57+
const ws = new WebSocket("${next.url}/_next/webpack-hmr")
58+
59+
ws.addEventListener('error', (err) => {
60+
statusEl.innerText = 'error'
61+
})
62+
ws.addEventListener('open', () => {
63+
statusEl.innerText = 'connected'
64+
})
65+
})()`
66+
67+
// ensure direct port with mismatching port is blocked
68+
const browser = await webdriver(`http://127.0.0.1:${port}`, '/about')
69+
await browser.eval(websocketSnippet)
70+
await retry(async () => {
71+
expect(await browser.elementByCss('#status').text()).toBe(
72+
'connected'
73+
)
74+
})
75+
76+
// ensure different host is blocked
77+
await browser.get(`https://example.vercel.sh/`)
78+
await browser.eval(websocketSnippet)
79+
await retry(async () => {
80+
expect(await browser.elementByCss('#status').text()).toBe(
81+
'connected'
82+
)
83+
})
84+
85+
expect(next.cliOutput).toContain('Cross origin request detected from')
86+
} finally {
87+
server.close()
88+
}
89+
})
90+
91+
it('should not allow loading scripts from cross-site', async () => {
92+
let server = http.createServer((req, res) => {
93+
res.end(`
94+
<html>
95+
<head>
96+
<title>testing cross-site</title>
97+
</head>
98+
<body></body>
99+
</html>
100+
`)
101+
})
102+
try {
103+
const port = await findPort()
104+
await new Promise<void>((res) => {
105+
server.listen(port, () => res())
106+
})
107+
const scriptSnippet = `(() => {
108+
const statusEl = document.createElement('p')
109+
statusEl.id = 'status'
110+
document.querySelector('body').appendChild(statusEl)
111+
112+
const script = document.createElement('script')
113+
script.src = "${next.url}/_next/static/chunks/pages/_app.js"
114+
115+
script.onerror = (err) => {
116+
statusEl.innerText = 'error'
117+
}
118+
script.onload = () => {
119+
statusEl.innerText = 'connected'
120+
}
121+
document.querySelector('body').appendChild(script)
122+
})()`
123+
124+
// ensure direct port with mismatching port is blocked
125+
const browser = await webdriver(`http://127.0.0.1:${port}`, '/about')
126+
await browser.eval(scriptSnippet)
127+
128+
await retry(async () => {
129+
expect(await browser.elementByCss('#status').text()).toBe(
130+
'connected'
131+
)
132+
})
133+
134+
// ensure different host is blocked
135+
await browser.get(`https://example.vercel.sh/`)
136+
await browser.eval(scriptSnippet)
137+
138+
await retry(async () => {
139+
expect(await browser.elementByCss('#status').text()).toBe(
140+
'connected'
141+
)
142+
})
143+
144+
expect(next.cliOutput).toContain('Cross origin request detected from')
145+
} finally {
146+
server.close()
147+
}
148+
})
149+
})
150+
151+
describe('block mode', () => {
152+
beforeAll(async () => {
153+
next = await createNext({
154+
files: {
155+
pages: new FileRef(join(__dirname, 'misc/pages')),
156+
public: new FileRef(join(__dirname, 'misc/public')),
157+
},
158+
nextConfig: {
159+
basePath,
160+
allowedDevOrigins: ['localhost'],
161+
},
162+
})
163+
164+
await retry(async () => {
165+
// make sure host server is running
166+
const asset = await fetchViaHTTP(
167+
next.appPort,
168+
'/_next/static/chunks/pages/_app.js'
169+
)
170+
expect(asset.status).toBe(200)
171+
})
172+
})
173+
afterAll(() => next.destroy())
174+
175+
it('should not allow dev WebSocket from cross-site', async () => {
176+
let server = http.createServer((req, res) => {
177+
res.end(`
178+
<html>
179+
<head>
180+
<title>testing cross-site</title>
181+
</head>
182+
<body></body>
183+
</html>
184+
`)
185+
})
186+
try {
187+
const port = await findPort()
188+
await new Promise<void>((res) => {
189+
server.listen(port, () => res())
190+
})
191+
const websocketSnippet = `(() => {
192+
const statusEl = document.createElement('p')
193+
statusEl.id = 'status'
194+
document.querySelector('body').appendChild(statusEl)
195+
196+
const ws = new WebSocket("${next.url}/_next/webpack-hmr")
197+
198+
ws.addEventListener('error', (err) => {
199+
statusEl.innerText = 'error'
200+
})
201+
ws.addEventListener('open', () => {
202+
statusEl.innerText = 'connected'
203+
})
204+
})()`
205+
206+
// ensure direct port with mismatching port is blocked
207+
const browser = await webdriver(`http://127.0.0.1:${port}`, '/about')
208+
await browser.eval(websocketSnippet)
209+
await retry(async () => {
210+
expect(await browser.elementByCss('#status').text()).toBe('error')
211+
})
212+
213+
// ensure different host is blocked
214+
await browser.get(`https://example.vercel.sh/`)
215+
await browser.eval(websocketSnippet)
216+
await retry(async () => {
217+
expect(await browser.elementByCss('#status').text()).toBe('error')
218+
})
219+
} finally {
220+
server.close()
221+
}
222+
})
223+
224+
it('should not allow loading scripts from cross-site', async () => {
225+
let server = http.createServer((req, res) => {
226+
res.end(`
227+
<html>
228+
<head>
229+
<title>testing cross-site</title>
230+
</head>
231+
<body></body>
232+
</html>
233+
`)
234+
})
235+
try {
236+
const port = await findPort()
237+
await new Promise<void>((res) => {
238+
server.listen(port, () => res())
239+
})
240+
const scriptSnippet = `(() => {
241+
const statusEl = document.createElement('p')
242+
statusEl.id = 'status'
243+
document.querySelector('body').appendChild(statusEl)
244+
245+
const script = document.createElement('script')
246+
script.src = "${next.url}/_next/static/chunks/pages/_app.js"
247+
248+
script.onerror = (err) => {
249+
statusEl.innerText = 'error'
250+
}
251+
script.onload = () => {
252+
statusEl.innerText = 'connected'
253+
}
254+
document.querySelector('body').appendChild(script)
255+
})()`
256+
257+
// ensure direct port with mismatching port is blocked
258+
const browser = await webdriver(`http://127.0.0.1:${port}`, '/about')
259+
await browser.eval(scriptSnippet)
260+
await retry(async () => {
261+
expect(await browser.elementByCss('#status').text()).toBe('error')
262+
})
263+
264+
// ensure different host is blocked
265+
await browser.get(`https://example.vercel.sh/`)
266+
await browser.eval(scriptSnippet)
267+
268+
await retry(async () => {
269+
expect(await browser.elementByCss('#status').text()).toBe('error')
270+
})
271+
} finally {
272+
server.close()
273+
}
274+
})
275+
})
276+
}
277+
)

0 commit comments

Comments
 (0)