Skip to content

Commit 03d1214

Browse files
fix: respect the allowedHosts option for cross-origin header check (#5510)
1 parent 5ba862e commit 03d1214

File tree

3 files changed

+123
-7
lines changed

3 files changed

+123
-7
lines changed

lib/Server.js

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1986,6 +1986,13 @@ class Server {
19861986
const headers =
19871987
/** @type {{ [key: string]: string | undefined }} */
19881988
(req.headers);
1989+
const headerName = headers[":authority"] ? ":authority" : "host";
1990+
1991+
if (this.isValidHost(headers, headerName, false)) {
1992+
next();
1993+
return;
1994+
}
1995+
19891996
if (
19901997
headers["sec-fetch-mode"] === "no-cors" &&
19911998
headers["sec-fetch-site"] === "cross-site"
@@ -3166,9 +3173,10 @@ class Server {
31663173
* @private
31673174
* @param {{ [key: string]: string | undefined }} headers
31683175
* @param {string} headerToCheck
3176+
* @param {boolean} validateHost
31693177
* @returns {boolean}
31703178
*/
3171-
isValidHost(headers, headerToCheck) {
3179+
isValidHost(headers, headerToCheck, validateHost = true) {
31723180
if (this.options.allowedHosts === "all") {
31733181
return true;
31743182
}
@@ -3210,12 +3218,13 @@ class Server {
32103218
// For convenience, always allow localhost (hostname === 'localhost')
32113219
// and its subdomains (hostname.endsWith(".localhost")).
32123220
// allow hostname of listening address (hostname === this.options.host)
3213-
const isValidHostname =
3214-
ipaddr.IPv4.isValid(hostname) ||
3215-
ipaddr.IPv6.isValid(hostname) ||
3216-
hostname === "localhost" ||
3217-
hostname.endsWith(".localhost") ||
3218-
hostname === this.options.host;
3221+
const isValidHostname = validateHost
3222+
? ipaddr.IPv4.isValid(hostname) ||
3223+
ipaddr.IPv6.isValid(hostname) ||
3224+
hostname === "localhost" ||
3225+
hostname.endsWith(".localhost") ||
3226+
hostname === this.options.host
3227+
: false;
32193228

32203229
return isValidHostname;
32213230
}

test/e2e/cross-origin-request.test.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ describe("cross-origin requests", () => {
3737
htmlServer.listen(htmlServerPort, htmlServerHost);
3838

3939
const { page, browser } = await runBrowser();
40+
4041
try {
4142
const pageErrors = [];
4243

@@ -91,6 +92,111 @@ describe("cross-origin requests", () => {
9192
htmlServer.listen(htmlServerPort, htmlServerHost);
9293

9394
const { page, browser } = await runBrowser();
95+
96+
try {
97+
const pageErrors = [];
98+
99+
page.on("pageerror", (error) => {
100+
pageErrors.push(error);
101+
});
102+
103+
const scriptTagRequest = page.waitForResponse(
104+
`http://localhost:${devServerPort}/main.js`,
105+
);
106+
107+
await page.goto(`http://${htmlServerHost}:${htmlServerPort}`);
108+
109+
const response = await scriptTagRequest;
110+
111+
expect(response.status()).toBe(200);
112+
} catch (error) {
113+
throw error;
114+
} finally {
115+
await browser.close();
116+
await server.stop();
117+
htmlServer.close();
118+
}
119+
});
120+
121+
it("should return 200 for cross-origin no-cors non-module script tag requests with the 'allowedHost' option and 'all' value", async () => {
122+
const compiler = webpack(config);
123+
const devServerOptions = {
124+
port: devServerPort,
125+
allowedHosts: "all",
126+
};
127+
const server = new Server(devServerOptions, compiler);
128+
129+
await server.start();
130+
131+
// Start a separate server for serving the HTML file
132+
const http = require("http");
133+
const htmlServer = http.createServer((req, res) => {
134+
res.writeHead(200, { "Content-Type": "text/html" });
135+
res.end(`
136+
<html>
137+
<head>
138+
<script src="http://localhost:${devServerPort}/main.js"></script>
139+
</head>
140+
<body></body>
141+
</html>
142+
`);
143+
});
144+
htmlServer.listen(htmlServerPort, htmlServerHost);
145+
146+
const { page, browser } = await runBrowser();
147+
148+
try {
149+
const pageErrors = [];
150+
151+
page.on("pageerror", (error) => {
152+
pageErrors.push(error);
153+
});
154+
155+
const scriptTagRequest = page.waitForResponse(
156+
`http://localhost:${devServerPort}/main.js`,
157+
);
158+
159+
await page.goto(`http://${htmlServerHost}:${htmlServerPort}`);
160+
161+
const response = await scriptTagRequest;
162+
163+
expect(response.status()).toBe(200);
164+
} catch (error) {
165+
throw error;
166+
} finally {
167+
await browser.close();
168+
await server.stop();
169+
htmlServer.close();
170+
}
171+
});
172+
173+
it("should return 200 for cross-origin no-cors non-module script tag requests with the `allowedHost` option and the `localhost` value", async () => {
174+
const compiler = webpack(config);
175+
const devServerOptions = {
176+
port: devServerPort,
177+
allowedHosts: ["localhost"],
178+
};
179+
const server = new Server(devServerOptions, compiler);
180+
181+
await server.start();
182+
183+
// Start a separate server for serving the HTML file
184+
const http = require("http");
185+
const htmlServer = http.createServer((req, res) => {
186+
res.writeHead(200, { "Content-Type": "text/html" });
187+
res.end(`
188+
<html>
189+
<head>
190+
<script src="http://localhost:${devServerPort}/main.js"></script>
191+
</head>
192+
<body></body>
193+
</html>
194+
`);
195+
});
196+
htmlServer.listen(htmlServerPort, htmlServerHost);
197+
198+
const { page, browser } = await runBrowser();
199+
94200
try {
95201
const pageErrors = [];
96202

types/lib/Server.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,6 +1357,7 @@ declare class Server<
13571357
* @private
13581358
* @param {{ [key: string]: string | undefined }} headers
13591359
* @param {string} headerToCheck
1360+
* @param {boolean} validateHost
13601361
* @returns {boolean}
13611362
*/
13621363
private isValidHost;

0 commit comments

Comments
 (0)