From df073c53a8cefb54210b43813fa6ee60364a554e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Mon, 20 Apr 2026 14:27:14 +0200 Subject: [PATCH 1/3] Merge commit from fork * test: add regression tests for CORP header on cross-origin responses Ref: https://github.com/webpack/webpack-dev-server/security/advisories/GHSA-79cf-xcqc-c78w * fix: set Cross-Origin-Resource-Policy header to prevent source code theft over HTTP Ref: https://github.com/webpack/webpack-dev-server/security/advisories/GHSA-79cf-xcqc-c78w * fix: update CORS handling to differentiate between wildcard and specific-origin headers --------- Co-authored-by: Sebastian Beltran --- lib/Server.js | 55 +++++++++ test/e2e/cross-origin-request.test.js | 154 ++++++++++++++++++++++++++ types/lib/Server.d.ts | 6 + 3 files changed, 215 insertions(+) diff --git a/lib/Server.js b/lib/Server.js index 87c4edb347..9f78cb2042 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -2019,6 +2019,14 @@ class Server { return; } + // Block cross-origin resource loading when Sec-Fetch-* headers are absent (HTTP origins) + if ( + this.options.allowedHosts !== "all" && + !this.isUserCORSWildcardEnabled() + ) { + res.setHeader("Cross-Origin-Resource-Policy", "same-origin"); + } + next(); }, }); @@ -3184,6 +3192,53 @@ class Server { return false; } + /** + * @private + * @returns {boolean} true when the user has configured a wildcard + * Access-Control-Allow-Origin header (opting into fully open cross-origin access) + */ + isUserCORSWildcardEnabled() { + const { headers } = this.options; + + if (!headers) { + return false; + } + + if (typeof headers === "function") { + return false; + } + + /** + * @param {string | string[]} value header value + * @returns {boolean} true when value is the "*" wildcard + */ + const isWildcard = (value) => { + if (typeof value === "string") { + return value.trim() === "*"; + } + + if (Array.isArray(value)) { + return value.length === 1 && isWildcard(value[0]); + } + + return false; + }; + + if (Array.isArray(headers)) { + return headers.some( + (header) => + header.key.toLowerCase() === "access-control-allow-origin" && + isWildcard(header.value), + ); + } + + return Object.entries(headers).some( + ([key, value]) => + key.toLowerCase() === "access-control-allow-origin" && + isWildcard(value), + ); + } + /** * @private * @param {{ [key: string]: string | undefined }} headers headers diff --git a/test/e2e/cross-origin-request.test.js b/test/e2e/cross-origin-request.test.js index d003024928..4c94b6db16 100644 --- a/test/e2e/cross-origin-request.test.js +++ b/test/e2e/cross-origin-request.test.js @@ -218,3 +218,157 @@ describe("cross-origin requests", () => { } }); }); + +// @see https://github.com/webpack/webpack-dev-server/security/advisories/GHSA-79cf-xcqc-c78w +describe("cross-origin resource policy header", () => { + const devServerPort = port1; + + let server; + + afterEach(async () => { + if (server) { + await server.stop(); + // Allow the port to be fully released before the next test + await new Promise((resolve) => { + setTimeout(resolve, 100); + }); + server = null; + } + }); + + function request(url, headers = {}) { + const http = require("node:http"); + + return new Promise((resolve, reject) => { + const req = http.get(url, { headers }, (res) => { + let body = ""; + res.on("data", (chunk) => { + body += chunk; + }); + res.on("end", () => { + resolve({ status: res.statusCode, headers: res.headers, body }); + }); + }); + req.on("error", reject); + }); + } + + it("should set Cross-Origin-Resource-Policy: same-origin by default", async () => { + const compiler = webpack(config); + server = new Server( + { port: devServerPort, allowedHosts: "auto" }, + compiler, + ); + + await server.start(); + + const res = await request(`http://localhost:${devServerPort}/main.js`); + + expect(res.headers["cross-origin-resource-policy"]).toBe("same-origin"); + }); + + it("should NOT set CORP header when allowedHosts is 'all'", async () => { + const compiler = webpack(config); + server = new Server({ port: devServerPort, allowedHosts: "all" }, compiler); + + await server.start(); + + const res = await request(`http://localhost:${devServerPort}/main.js`); + + expect(res.headers["cross-origin-resource-policy"]).toBeUndefined(); + }); + + it("should NOT set CORP header when user configures wildcard CORS", async () => { + const compiler = webpack(config); + server = new Server( + { + port: devServerPort, + allowedHosts: "auto", + headers: { "Access-Control-Allow-Origin": "*" }, + }, + compiler, + ); + + await server.start(); + + const res = await request(`http://localhost:${devServerPort}/main.js`); + + expect(res.headers["cross-origin-resource-policy"]).toBeUndefined(); + }); + + it("should set CORP header when user configures a specific-origin Access-Control-Allow-Origin (no-cors embedding is not governed by CORS)", async () => { + const compiler = webpack(config); + server = new Server( + { + port: devServerPort, + allowedHosts: "auto", + headers: { + "Access-Control-Allow-Origin": "http://foo.example.com", + }, + }, + compiler, + ); + + await server.start(); + + const res = await request(`http://localhost:${devServerPort}/main.js`); + + expect(res.headers["cross-origin-resource-policy"]).toBe("same-origin"); + }); + + it("should set CORP header when user configures Access-Control-Allow-Origin via headers array with a specific origin", async () => { + const compiler = webpack(config); + server = new Server( + { + port: devServerPort, + allowedHosts: "auto", + headers: [ + { + key: "Access-Control-Allow-Origin", + value: "http://foo.example.com", + }, + ], + }, + compiler, + ); + + await server.start(); + + const res = await request(`http://localhost:${devServerPort}/main.js`); + + expect(res.headers["cross-origin-resource-policy"]).toBe("same-origin"); + }); + + it("should NOT set CORP header when user configures wildcard Access-Control-Allow-Origin via headers array", async () => { + const compiler = webpack(config); + server = new Server( + { + port: devServerPort, + allowedHosts: "auto", + headers: [{ key: "Access-Control-Allow-Origin", value: "*" }], + }, + compiler, + ); + + await server.start(); + + const res = await request(`http://localhost:${devServerPort}/main.js`); + + expect(res.headers["cross-origin-resource-policy"]).toBeUndefined(); + }); + + it("should NOT set CORP header when host is in allowedHosts", async () => { + const compiler = webpack(config); + server = new Server( + { port: devServerPort, allowedHosts: ["localhost"] }, + compiler, + ); + + await server.start(); + + const res = await request(`http://localhost:${devServerPort}/main.js`); + + expect(res.status).toBe(200); + expect(res.headers["cross-origin-resource-policy"]).toBeUndefined(); + }); +}); diff --git a/types/lib/Server.d.ts b/types/lib/Server.d.ts index 47dfba4c62..c807755921 100644 --- a/types/lib/Server.d.ts +++ b/types/lib/Server.d.ts @@ -1356,6 +1356,12 @@ declare class Server< * @returns {boolean} true when host allowed, otherwise false */ private isHostAllowed; + /** + * @private + * @returns {boolean} true when the user has configured a wildcard + * Access-Control-Allow-Origin header (opting into fully open cross-origin access) + */ + private isUserCORSWildcardEnabled; /** * @private * @param {{ [key: string]: string | undefined }} headers headers From 5b74206f0170b1a432e1405bfe151ff8ae424b3c Mon Sep 17 00:00:00 2001 From: ThierryRakotomanana Date: Mon, 20 Apr 2026 22:38:15 +0300 Subject: [PATCH 2/3] fix: treat loopback aliases as equivalent in isSameOrigin - When host: localhost is configured, the OS decides at bind time whether localhost resolves to 127.0.0.1 (IPv4) or ::1 (IPv6). - This causes isSameOrigin() to reject valid WebSocket connections because the final string comparison localhost === ::1 fails, triggering an infinite reconnection loop in the browser console. --- lib/Server.js | 18 +- .../allowed-hosts.test.js.snap.webpack5 | 24 +++ test/e2e/allowed-hosts.test.js | 179 ++++++++++++++++++ 3 files changed, 220 insertions(+), 1 deletion(-) diff --git a/lib/Server.js b/lib/Server.js index 9f78cb2042..9cf304e8c3 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -3343,10 +3343,20 @@ class Server { return true; } + const loopbacks = new Set(["localhost", "127.0.0.1", "::1"]); + + // url.parse cannot handle bare IPv6 like "::1", need to bracket it first + const hostForParsing = + loopbacks.has(hostHeader) && hostHeader.includes(":") + ? `[${hostHeader}]` + : hostHeader; + // eslint-disable-next-line n/no-deprecated-api const host = url.parse( // if hostHeader doesn't have scheme, add // for parsing. - /^(.+:)?\/\//.test(hostHeader) ? hostHeader : `//${hostHeader}`, + /^(.+:)?\/\//.test(hostForParsing) + ? hostForParsing + : `//${hostForParsing}`, false, true, ).hostname; @@ -3359,6 +3369,12 @@ class Server { return true; } + // Treat all loopback aliases as equivalent, localhost may resolve to + // 127.0.0.1 or ::1 depending on the OS, causing a false mismatch. + if (loopbacks.has(origin) && loopbacks.has(host)) { + return true; + } + return origin === host; } diff --git a/test/e2e/__snapshots__/allowed-hosts.test.js.snap.webpack5 b/test/e2e/__snapshots__/allowed-hosts.test.js.snap.webpack5 index edd5d77571..41bada9913 100644 --- a/test/e2e/__snapshots__/allowed-hosts.test.js.snap.webpack5 +++ b/test/e2e/__snapshots__/allowed-hosts.test.js.snap.webpack5 @@ -1,5 +1,11 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +exports[`allowed hosts check host headers should NOT allow websocket connection when origin is a non-loopback address mismatching host (loopback fix must not widen trust): console messages 1`] = `[]`; + +exports[`allowed hosts check host headers should NOT allow websocket connection when origin is a non-loopback address mismatching host (loopback fix must not widen trust): page errors 1`] = `[]`; + +exports[`allowed hosts check host headers should NOT allow websocket connection when origin is a non-loopback address mismatching host (loopback fix must not widen trust): response status 1`] = `200`; + exports[`allowed hosts check host headers should allow hosts in allowedHosts: console messages 1`] = `[]`; exports[`allowed hosts check host headers should allow hosts in allowedHosts: page errors 1`] = `[]`; @@ -12,6 +18,24 @@ exports[`allowed hosts check host headers should allow hosts that pass a wildcar exports[`allowed hosts check host headers should allow hosts that pass a wildcard in allowedHosts: response status 1`] = `200`; +exports[`allowed hosts check host headers should allow websocket connection when host is 'localhost' but resolves to '::1' (loopback alias mismatch): console messages 1`] = `[]`; + +exports[`allowed hosts check host headers should allow websocket connection when host is 'localhost' but resolves to '::1' (loopback alias mismatch): page errors 1`] = `[]`; + +exports[`allowed hosts check host headers should allow websocket connection when host is 'localhost' but resolves to '::1' (loopback alias mismatch): response status 1`] = `200`; + +exports[`allowed hosts check host headers should allow websocket connection when host is 'localhost' but resolves to '127.0.0.1' (loopback alias mismatch): console messages 1`] = `[]`; + +exports[`allowed hosts check host headers should allow websocket connection when host is 'localhost' but resolves to '127.0.0.1' (loopback alias mismatch): page errors 1`] = `[]`; + +exports[`allowed hosts check host headers should allow websocket connection when host is 'localhost' but resolves to '127.0.0.1' (loopback alias mismatch): response status 1`] = `200`; + +exports[`allowed hosts check host headers should allow websocket connection when origin is '127.0.0.1' but host is 'localhost' (reverse loopback alias mismatch): console messages 1`] = `[]`; + +exports[`allowed hosts check host headers should allow websocket connection when origin is '127.0.0.1' but host is 'localhost' (reverse loopback alias mismatch): page errors 1`] = `[]`; + +exports[`allowed hosts check host headers should allow websocket connection when origin is '127.0.0.1' but host is 'localhost' (reverse loopback alias mismatch): response status 1`] = `200`; + exports[`allowed hosts check host headers should always allow \`localhost\` if options.allowedHosts is auto: console messages 1`] = `[]`; exports[`allowed hosts check host headers should always allow \`localhost\` if options.allowedHosts is auto: page errors 1`] = `[]`; diff --git a/test/e2e/allowed-hosts.test.js b/test/e2e/allowed-hosts.test.js index 163a24f265..ba1cadf726 100644 --- a/test/e2e/allowed-hosts.test.js +++ b/test/e2e/allowed-hosts.test.js @@ -1874,5 +1874,184 @@ describe("allowed hosts", () => { expect(pageErrors).toMatchSnapshot("page errors"); }); + + it("should allow websocket connection when host is 'localhost' but resolves to '127.0.0.1' (loopback alias mismatch)", async () => { + const options = { + allowedHosts: "auto", + host: "localhost", + port: port1, + }; + + server = new Server(options, compiler); + + await server.start(); + + ({ page, browser } = await runBrowser()); + + page + .on("console", (message) => { + consoleMessages.push(message); + }) + .on("pageerror", (error) => { + pageErrors.push(error); + }); + + // Simulate: browser opens from localhost, but OS resolved + // 'localhost' to '127.0.0.1' so host header is the IP + const headersLocalhostOriginIPv4Host = { + host: "127.0.0.1", + origin: "http://localhost", + }; + + if (!server.isSameOrigin(headersLocalhostOriginIPv4Host)) { + throw new Error( + "isSameOrigin should treat localhost and 127.0.0.1 as equivalent loopback addresses", + ); + } + + const response = await page.goto(`http://localhost:${port1}/main.js`, { + waitUntil: "networkidle0", + }); + + expect(response.status()).toMatchSnapshot("response status"); + expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( + "console messages", + ); + expect(pageErrors).toMatchSnapshot("page errors"); + }); + + it("should allow websocket connection when host is 'localhost' but resolves to '::1' (loopback alias mismatch)", async () => { + const options = { + allowedHosts: "auto", + host: "localhost", + port: port1, + }; + + server = new Server(options, compiler); + + await server.start(); + + ({ page, browser } = await runBrowser()); + + page + .on("console", (message) => { + consoleMessages.push(message); + }) + .on("pageerror", (error) => { + pageErrors.push(error); + }); + + // Simulate: browser opens from localhost, but OS resolved + // 'localhost' to '::1' (IPv6) so host header is the IPv6 address + const headersLocalhostOriginIPv6Host = { + host: "::1", + origin: "http://localhost", + }; + + if (!server.isSameOrigin(headersLocalhostOriginIPv6Host)) { + throw new Error( + "isSameOrigin should treat localhost and ::1 as equivalent loopback addresses", + ); + } + + const response = await page.goto(`http://localhost:${port1}/main.js`, { + waitUntil: "networkidle0", + }); + + expect(response.status()).toMatchSnapshot("response status"); + expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( + "console messages", + ); + expect(pageErrors).toMatchSnapshot("page errors"); + }); + + it("should allow websocket connection when origin is '127.0.0.1' but host is 'localhost' (reverse loopback alias mismatch)", async () => { + const options = { + allowedHosts: "auto", + host: "127.0.0.1", + port: port1, + }; + + server = new Server(options, compiler); + + await server.start(); + + ({ page, browser } = await runBrowser()); + + page + .on("console", (message) => { + consoleMessages.push(message); + }) + .on("pageerror", (error) => { + pageErrors.push(error); + }); + + // Reverse of above: server bound to 127.0.0.1, but browser + // sent origin header using 'localhost' name + const headersIPv4OriginLocalhostHost = { + host: "localhost", + origin: "http://127.0.0.1", + }; + + if (!server.isSameOrigin(headersIPv4OriginLocalhostHost)) { + throw new Error( + "isSameOrigin should treat 127.0.0.1 and localhost as equivalent loopback addresses", + ); + } + + const response = await page.goto(`http://127.0.0.1:${port1}/main.js`, { + waitUntil: "networkidle0", + }); + + expect(response.status()).toMatchSnapshot("response status"); + expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( + "console messages", + ); + expect(pageErrors).toMatchSnapshot("page errors"); + }); + + it("should NOT allow websocket connection when origin is a non-loopback address mismatching host (loopback fix must not widen trust)", async () => { + const options = { + allowedHosts: "auto", + host: "localhost", + port: port1, + }; + + server = new Server(options, compiler); + + await server.start(); + + ({ page, browser } = await runBrowser()); + + page + .on("console", (message) => { + consoleMessages.push(message); + }) + .on("pageerror", (error) => { + pageErrors.push(error); + }); + + // A real external origin must never pass as loopback equivalent. + const headersExternalOrigin = { + host: "localhost", + origin: "http://evil.example.com", + }; + + if (server.isSameOrigin(headersExternalOrigin)) { + throw new Error( + "isSameOrigin must NOT allow external origins to match loopback host", + ); + } + + const response = await page.goto(`http://localhost:${port1}/main.js`, { + waitUntil: "networkidle0", + }); + + expect(response.status()).toMatchSnapshot("response status"); + expect(consoleMessages.map((message) => message.text())).toMatchSnapshot( + "console messages", + ); + expect(pageErrors).toMatchSnapshot("page errors"); + }); }); }); From a2161442a314a06a97e25d1404d08410cb4e9c51 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Mon, 20 Apr 2026 22:28:38 -0500 Subject: [PATCH 3/3] ci: fix test (#5658) --- jest.config.js | 2 +- package-lock.json | 76 ++++++++++++++++++++--------------------------- 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/jest.config.js b/jest.config.js index 197b147833..5c1d7be9d4 100644 --- a/jest.config.js +++ b/jest.config.js @@ -28,7 +28,7 @@ module.exports = { // // FIXME: this uuid moduleNameMapper workaround can be removed after sockjs > uuid@v9 release // https://github.com/uuidjs/uuid/pull/616#issuecomment-1206283882 - // eslint-disable-next-line n/no-extraneous-require + "^uuid$": require.resolve("uuid"), }, }; diff --git a/package-lock.json b/package-lock.json index 9680dbf5e1..3f3d5bc3d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -184,7 +184,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -2424,8 +2423,7 @@ "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.18.tgz", "integrity": "sha512-EF77RqROHL+4LhMGW5NTeKqfUd/e4OOv6EDFQ/UQQiFyWuqkEKyEz0NDILxOFxWUEVdjT2GQ2cC7t12B6pESwg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@cspell/dict-dart": { "version": "2.3.1", @@ -2565,16 +2563,14 @@ "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.12.tgz", "integrity": "sha512-JFffQ1dDVEyJq6tCDWv0r/RqkdSnV43P2F/3jJ9rwLgdsOIXwQbXrz6QDlvQLVvNSnORH9KjDtenFTGDyzfCaA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@cspell/dict-html-symbol-entities": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.4.tgz", "integrity": "sha512-afea+0rGPDeOV9gdO06UW183Qg6wRhWVkgCFwiO3bDupAoyXRuvupbb5nUyqSTsLXIKL8u8uXQlJ9pkz07oVXw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@cspell/dict-java": { "version": "5.0.12", @@ -2772,8 +2768,7 @@ "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-3.2.3.tgz", "integrity": "sha512-zXh1wYsNljQZfWWdSPYwQhpwiuW0KPW1dSd8idjMRvSD0aSvWWHoWlrMsmZeRl4qM4QCEAjua8+cjflm41cQBg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@cspell/dict-vue": { "version": "3.0.5", @@ -2976,6 +2971,7 @@ "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -3070,7 +3066,6 @@ "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -3145,6 +3140,7 @@ "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" @@ -4633,6 +4629,7 @@ "integrity": "sha512-Ykums1VYonM0TgkD0VteVq9mrlO2FhF48MDJnPyv3MktIB2ydtuhlO0AfWm7xnW1kyf5bjOqA6xc7JjviuVTxg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/types": "^8.41.0", @@ -4654,6 +4651,7 @@ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -4809,7 +4807,6 @@ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -4976,7 +4973,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -5133,7 +5129,6 @@ "integrity": "sha512-XxXP5tL1txl13YFtrECECQYeZjBZad4fyd3cFV4a19LkAY/bIp9fev3US4S5fDVV2JaYFiKAZ/GRTOLer+mbyQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.48.0", @@ -5174,7 +5169,6 @@ "integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.48.0", "@typescript-eslint/types": "8.48.0", @@ -5959,7 +5953,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "devOptional": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6039,7 +6032,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -6819,13 +6811,16 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.29.tgz", - "integrity": "sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==", + "version": "2.10.20", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.20.tgz", + "integrity": "sha512-1AaXxEPfXT+GvTBJFuy4yXVHWJBXa4OdbIebGN/wX5DlsIkU0+wzGnd2lOzokSk51d5LUmqjgBLRLlypLUqInQ==", "devOptional": true, "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/basic-ftp": { @@ -6984,7 +6979,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -7057,6 +7051,7 @@ "integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=18.20" }, @@ -7260,7 +7255,8 @@ "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/char-regex": { "version": "1.0.2", @@ -7373,6 +7369,7 @@ "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "escape-string-regexp": "^1.0.5" }, @@ -7386,6 +7383,7 @@ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.8.0" } @@ -9191,7 +9189,6 @@ "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -9986,8 +9983,7 @@ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1521046.tgz", "integrity": "sha512-vhE6eymDQSKWUXwwA37NtTTVEzjtGVfDr3pRbsWEQ5onH/Snp2c+2xZHWJJawG/0hCCJLRGt4xVtEVUVILol4w==", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/dezalgo": { "version": "1.0.4", @@ -10592,7 +10588,6 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -10682,7 +10677,6 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -10841,7 +10835,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -10886,7 +10879,6 @@ "integrity": "sha512-0WLIezrIxitUGbjMIGwznVzSIp0uFJV0PZ2fiSvpyVcxe+QMXKUt7MRhUpzdbctnnLwiOTOFkACplgB0wAglFw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/utils": "^8.0.0" }, @@ -10913,7 +10905,6 @@ "integrity": "sha512-y4CA9OkachG8v5nAtrwvcvjIbdcKgSyS6U//IfQr4FZFFyeBFwZFf/tfSsMr46mWDJgidZjBTqoCRlXywfFBMg==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@es-joy/jsdoccomment": "~0.52.0", "are-docs-informative": "^0.0.2", @@ -10952,7 +10943,6 @@ "integrity": "sha512-68PealUpYoHOBh332JLLD9Sj7OQUDkFpmcfqt8R9sySfFSeuGJjMTJQvCRRB96zO3A/PELRLkPrzsHmzEFQQ5A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.5.0", "enhanced-resolve": "^5.17.1", @@ -11038,6 +11028,7 @@ "integrity": "sha512-zLihukvneYT7f74GNbVJXfWIiNQmkc/a9vYBTE4qPkQZswolWNdu+Wsp9sIXno1JOzdn6OUwLPd19ekXVkahRA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "@eslint-community/eslint-utils": "^4.7.0", @@ -11074,6 +11065,7 @@ "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -11673,7 +11665,8 @@ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/fast-equals": { "version": "5.2.2", @@ -11947,6 +11940,7 @@ "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -13229,7 +13223,6 @@ "integrity": "sha512-icXIITfw/07Q88nLSkB9aiUrd8rYzSweK681Kjo/TSggaGbOX4RRyxxm71v+3PC8C/j+4rlxGeoTRxQDkaJkUw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=16.9.0" } @@ -13691,6 +13684,7 @@ "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -13876,6 +13870,7 @@ "integrity": "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "builtin-modules": "^5.0.0" }, @@ -14542,7 +14537,6 @@ "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.2.0", "@jest/types": "30.2.0", @@ -16590,7 +16584,6 @@ "integrity": "sha512-j1n1IuTX1VQjIy3tT7cyGbX7nvQOsFLoIqobZv4ttI5axP923gA44zUj6miiA6R5Aoms4sEGVIIcucXUbRI14g==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "copy-anything": "^2.0.1", "parse-node-version": "^1.0.1", @@ -19467,6 +19460,7 @@ "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=4" } @@ -19501,7 +19495,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -19611,7 +19604,6 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -19628,6 +19620,7 @@ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-diff": "^1.1.2" }, @@ -20277,6 +20270,7 @@ "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", "dev": true, "license": "MIT", + "peer": true, "bin": { "regexp-tree": "bin/regexp-tree" } @@ -22125,6 +22119,7 @@ "integrity": "sha512-OA95x+JPmL7kc7zCu+e+TeYxEiaIyndRx0OrBcK2QPPH09oAndr2ALvymxWA+Lx1PYYvFUm4O63pRkdJAaW96w==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -22725,8 +22720,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "peer": true + "license": "0BSD" }, "node_modules/tsyringe": { "version": "4.10.0", @@ -22893,7 +22887,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -22908,7 +22901,6 @@ "integrity": "sha512-fcKOvQD9GUn3Xw63EgiDqhvWJ5jsyZUaekl3KVpGsDJnN46WJTe3jWxtQP9lMZm1LJNkFLlTaWAxK2vUQR+cqw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/eslint-plugin": "8.48.0", "@typescript-eslint/parser": "8.48.0", @@ -23355,7 +23347,6 @@ "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -23405,7 +23396,6 @@ "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.6.1", "@webpack-cli/configtest": "^3.0.1",