diff --git a/lib/Server.js b/lib/Server.js index ff25797b34..49906ece2a 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -1986,6 +1986,13 @@ class Server { const headers = /** @type {{ [key: string]: string | undefined }} */ (req.headers); + const headerName = headers[":authority"] ? ":authority" : "host"; + + if (this.isValidHost(headers, headerName, false)) { + next(); + return; + } + if ( headers["sec-fetch-mode"] === "no-cors" && headers["sec-fetch-site"] === "cross-site" @@ -3166,9 +3173,10 @@ class Server { * @private * @param {{ [key: string]: string | undefined }} headers * @param {string} headerToCheck + * @param {boolean} validateHost * @returns {boolean} */ - isValidHost(headers, headerToCheck) { + isValidHost(headers, headerToCheck, validateHost = true) { if (this.options.allowedHosts === "all") { return true; } @@ -3210,12 +3218,13 @@ class Server { // For convenience, always allow localhost (hostname === 'localhost') // and its subdomains (hostname.endsWith(".localhost")). // allow hostname of listening address (hostname === this.options.host) - const isValidHostname = - ipaddr.IPv4.isValid(hostname) || - ipaddr.IPv6.isValid(hostname) || - hostname === "localhost" || - hostname.endsWith(".localhost") || - hostname === this.options.host; + const isValidHostname = validateHost + ? ipaddr.IPv4.isValid(hostname) || + ipaddr.IPv6.isValid(hostname) || + hostname === "localhost" || + hostname.endsWith(".localhost") || + hostname === this.options.host + : false; return isValidHostname; } diff --git a/test/e2e/cross-origin-request.test.js b/test/e2e/cross-origin-request.test.js index 7d511c51ee..65de9cf3f2 100644 --- a/test/e2e/cross-origin-request.test.js +++ b/test/e2e/cross-origin-request.test.js @@ -37,6 +37,7 @@ describe("cross-origin requests", () => { htmlServer.listen(htmlServerPort, htmlServerHost); const { page, browser } = await runBrowser(); + try { const pageErrors = []; @@ -91,6 +92,111 @@ describe("cross-origin requests", () => { htmlServer.listen(htmlServerPort, htmlServerHost); const { page, browser } = await runBrowser(); + + try { + const pageErrors = []; + + page.on("pageerror", (error) => { + pageErrors.push(error); + }); + + const scriptTagRequest = page.waitForResponse( + `http://localhost:${devServerPort}/main.js`, + ); + + await page.goto(`http://${htmlServerHost}:${htmlServerPort}`); + + const response = await scriptTagRequest; + + expect(response.status()).toBe(200); + } catch (error) { + throw error; + } finally { + await browser.close(); + await server.stop(); + htmlServer.close(); + } + }); + + it("should return 200 for cross-origin no-cors non-module script tag requests with the 'allowedHost' option and 'all' value", async () => { + const compiler = webpack(config); + const devServerOptions = { + port: devServerPort, + allowedHosts: "all", + }; + const server = new Server(devServerOptions, compiler); + + await server.start(); + + // Start a separate server for serving the HTML file + const http = require("http"); + const htmlServer = http.createServer((req, res) => { + res.writeHead(200, { "Content-Type": "text/html" }); + res.end(` + +
+ + + + + `); + }); + htmlServer.listen(htmlServerPort, htmlServerHost); + + const { page, browser } = await runBrowser(); + + try { + const pageErrors = []; + + page.on("pageerror", (error) => { + pageErrors.push(error); + }); + + const scriptTagRequest = page.waitForResponse( + `http://localhost:${devServerPort}/main.js`, + ); + + await page.goto(`http://${htmlServerHost}:${htmlServerPort}`); + + const response = await scriptTagRequest; + + expect(response.status()).toBe(200); + } catch (error) { + throw error; + } finally { + await browser.close(); + await server.stop(); + htmlServer.close(); + } + }); + + it("should return 200 for cross-origin no-cors non-module script tag requests with the `allowedHost` option and the `localhost` value", async () => { + const compiler = webpack(config); + const devServerOptions = { + port: devServerPort, + allowedHosts: ["localhost"], + }; + const server = new Server(devServerOptions, compiler); + + await server.start(); + + // Start a separate server for serving the HTML file + const http = require("http"); + const htmlServer = http.createServer((req, res) => { + res.writeHead(200, { "Content-Type": "text/html" }); + res.end(` + + + + + + + `); + }); + htmlServer.listen(htmlServerPort, htmlServerHost); + + const { page, browser } = await runBrowser(); + try { const pageErrors = []; diff --git a/types/lib/Server.d.ts b/types/lib/Server.d.ts index a8e54e2df8..db75276fe2 100644 --- a/types/lib/Server.d.ts +++ b/types/lib/Server.d.ts @@ -1357,6 +1357,7 @@ declare class Server< * @private * @param {{ [key: string]: string | undefined }} headers * @param {string} headerToCheck + * @param {boolean} validateHost * @returns {boolean} */ private isValidHost;