CVE-2026-49857: Verified Repro With Script Download
CVE-2026-49857: auth-fetch-mcp SSRF via IPv4-mapped IPv6 loopback bypass
CVE-2026-49857 is verified against ymw0407/auth-fetch-mcp · npm. Affected versions: <= 3.0.1. Fixed in 3.0.2. Vulnerability class: SSRF. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00205.
What Is CVE-2026-49857?
CVE-2026-49857 is a high-severity SSRF (CWE-918) in the auth-fetch-mcp MCP server's URL security guard, allowing IPv4-mapped IPv6 loopback addresses to bypass its private-IP protection. Pruva reproduced it (reproduction REPRO-2026-00205).
CVE-2026-49857 Severity
CVE-2026-49857 is rated high severity.
High — serious impact or readily exploitable. Prioritize remediation.
Affected ymw0407/auth-fetch-mcp Versions
ymw0407/auth-fetch-mcp · npm versions <= 3.0.1 are affected.
How to Reproduce CVE-2026-49857
pruva-verify REPRO-2026-00205 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00205/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-49857
- reached the target end-to-end
- full exploit chain demonstrated
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
URL http://[::ffff:127.0.0.1]:18080/ passed to download_media tool via MCP JSON-RPC tools/call
- MCP tools/call download_media
- assertSafeUrl()
- isPrivateV6() hex-normalization bypass
- ctx.request.get() fetches loopback 127.0.0.1:18080
reproduction_steps.sh Redirect-following SSRF bypass of the CVE-2026-49857 fix in auth-fetch-mcp. assertSafeUrl() is applied only to the initial request URL; Playwright ctx.request.get() (download_media, src/tools.ts:234) and page.goto() (auth_fetch via src/browser.ts:66) follow HTTP 3xx redirects to private/loopback IPs WITHOUT re-validat…
How the agent worked
Root Cause and Exploit Chain for CVE-2026-49857
The auth-fetch-mcp MCP server (npm package auth-fetch-mcp, versions ≤3.0.1) contains a Server-Side Request Forgery (SSRF) vulnerability in its URL security guard. The assertSafeUrl() function in src/security.ts is designed to block requests to private/loopback IP addresses, but its isPrivateV6() helper fails to detect IPv4-mapped IPv6 addresses after the Node.js WHATWG URL parser hex-normalizes them. When a user supplies a URL like http://[::ffff:127.0.0.1]:PORT/, the URL parser normalizes the hostname to ::ffff:7f00:1 (hex form). The guard checks net.isIPv4("7f00:1"), which returns false because the suffix is in hex notation, not dotted-decimal. As a result, the loopback address is classified as non-private and the security guard is bypassed, allowing the MCP server to fetch arbitrary internal/loopback URLs via the download_media and auth_fetch tools.
- Package/component affected:
auth-fetch-mcpnpm package, specificallysrc/security.ts(assertSafeUrl→isPrivateV6→isPrivateV4guard chain) - Affected versions: ≤3.0.1 (fixed in 3.0.2)
- Risk level: High
- Consequences: An attacker (via a malicious MCP client or prompt injection) can cause the MCP server to fetch arbitrary internal/loopback URLs, bypassing the SSRF protection. The
download_mediatool downloads the fetched content to disk and returns the file path to the caller, enabling information disclosure from internal services (e.g., cloud metadata endpoints at169.254.169.254, internal APIs on127.0.0.1/10.x/192.168.x, etc.). Theauth_fetchtool renders fetched internal pages in a browser and returns extracted content.
Impact Parity
- Disclosed/claimed maximum impact: SSRF — bypass of the private-IP guard to fetch internal/loopback URLs via the MCP server's
auth_fetchordownload_mediatools. - Reproduced impact from this run: Full SSRF confirmed. The MCP server (v3.0.1) processed a
download_mediatool call with URLhttp://[::ffff:127.0.0.1]:18080/, bypassedassertSafeUrl(), fetched content from a loopback HTTP server via Playwright'sctx.request.get(), saved the response to disk, and returned the file path. The downloaded file contained the secret marker from the internal server, proving the server-side request reached the loopback target. - Parity:
full - Not demonstrated: The reproduction targeted a loopback HTTP server (
127.0.0.1:18080). Cloud metadata endpoint (169.254.169.254) and other private ranges were not exercised at runtime, but the same bypass mechanism applies to all private IPv4 ranges via their IPv4-mapped IPv6 hex forms.
Root Cause
The vulnerability is in the isPrivateV6() function in src/security.ts:
function isPrivateV6(ip: string): boolean {
const lower = ip.toLowerCase();
if (lower === "::" || lower === "::1") return true;
if (lower.startsWith("fe80:") || lower.startsWith("fe80::")) return true;
if (lower.startsWith("fc") || lower.startsWith("fd")) return true;
if (lower.startsWith("ff")) return true;
if (lower.startsWith("::ffff:")) {
const v4 = lower.slice(7);
if (net.isIPv4(v4)) return isPrivateV4(v4); // ← BUG: only handles dotted-decimal
}
return false;
}
The bug: When the WHATWG URL parser processes http://[::ffff:127.0.0.1]:PORT/, it hex-normalizes the IPv4-mapped IPv6 hostname to ::ffff:7f00:1 (where 7f = 127, 00 = 0, 01 = 1). The isPrivateV6() function extracts the suffix 7f00:1 and checks net.isIPv4("7f00:1"), which returns false because the suffix is in hex notation, not dotted-decimal form. The function then falls through to return false, classifying the loopback address as non-private.
The call chain:
- MCP client sends
tools/callwithdownload_mediaand URLhttp://[::ffff:127.0.0.1]:PORT/ download_mediahandler callsassertSafeUrl(url)insrc/tools.ts:233assertSafeUrl()callsnew URL(rawUrl)— the WHATWG parser normalizes::ffff:127.0.0.1→::ffff:7f00:1assertSafeUrl()extractshostname=::ffff:7f00:1, callsisPrivateOrLinkLocal("::ffff:7f00:1")isPrivateOrLinkLocal()callsisPrivateV6("::ffff:7f00:1")(sincenet.isIPv6()returnstrue)isPrivateV6()checks::ffff:prefix, extracts7f00:1,net.isIPv4("7f00:1")=false→ returnsfalse(bypass!)assertSafeUrl()returns the parsed URL — security check passedctx.request.get(safeUrl.toString())fetcheshttp://[::ffff:7f00:1]:PORT/→ OS maps to127.0.0.1:PORT→ SSRF succeeds
Fix commit: 177ec5f8ee9c2d5749035777e562f699971b0da9 — adds hex group parsing to reconstruct the IPv4 address from the two trailing hex groups and run it through isPrivateV4():
const groups = v4.split(":");
if (groups.length === 2 && groups.every((g) => /^[0-9a-f]{1,4}$/.test(g))) {
const hi = parseInt(groups[0], 16);
const lo = parseInt(groups[1], 16);
const mapped = `${(hi >> 8) & 0xff}.${hi & 0xff}.${(lo >> 8) & 0xff}.${lo & 0xff}`;
return isPrivateV4(mapped);
}
Reproduction Steps
- Reference:
bundle/repro/reproduction_steps.sh(self-contained, usesbundle/repro/mcp_client.js) - What the script does:
- Reads
bundle/project_cache_context.jsonto locate the prepared project cache and Playwright browser cache - Clones/reuses the
auth-fetch-mcprepo at the project cache path - Installs Chrome Headless Shell for Playwright (manual download from Google's Chrome for Testing CDN, since
npx playwright installdoesn't support Ubuntu 26.04) - Installs system libraries required by Chrome
- Builds vulnerable version v3.0.1 (commit
98f381d) - Starts a local "victim" HTTP server on
127.0.0.1:18080that returns a unique secret marker - Spawns the real MCP server (
node dist/index.js) as a child process - Sends JSON-RPC
initialize+tools/call download_mediawith URLhttp://[::ffff:127.0.0.1]:18080/over stdio - Checks if the downloaded file contains the secret marker (SSRF confirmed) or if the response contains "Refusing to fetch" (blocked)
- Repeats for fixed version v3.0.2 (commit
d4dedaf, fix177ec5f) - Writes
runtime_manifest.jsonwith evidence
- Reads
- Expected evidence:
- Vulnerable v3.0.1: victim server receives HTTP request from
127.0.0.1, downloaded file contains the secret marker, tool returnsdownloaded: 1 - Fixed v3.0.2: victim server receives no request, tool returns
error: "Refusing to fetch [::ffff:7f00:1]...",downloaded: 0
- Vulnerable v3.0.1: victim server receives HTTP request from
Evidence
Log files (under
bundle/logs/):reproduction_steps.log— full script outputvulnerable_test.log— vulnerable version test outputvulnerable_result.json— structured result:ssrfConfirmed: true,downloadedContent: "SSRF_SECRET_MARKER_..."vulnerable_victim_server.log— showsRequest from 127.0.0.1 path=/(SSRF request received)vulnerable_mcp_stdout.log— MCP server JSON-RPC responsesvulnerable_mcp_requests.log— JSON-RPC requests sentfixed_test.log— fixed version test outputfixed_result.json— structured result:blocked: true, error:"Refusing to fetch [::ffff:7f00:1]..."fixed_victim_server.log— shows NO request received (only "Listening" line)fixed_mcp_stdout.log— MCP server JSON-RPC responses showing the block
Key excerpts:
- Vulnerable victim server log:
[VICTIM:18080] Request from 127.0.0.1 path=/ - Vulnerable downloaded file:
SSRF_SECRET_MARKER_1783015602673_hgnjlkyh(matches marker from internal server) - Vulnerable tool result:
{"status":"ok","downloaded":1,"total":1,"files":[{"url":"http://[::ffff:127.0.0.1]:18080/","localPath":".../file-1.bin","size":41}]} - Fixed tool result:
{"status":"ok","downloaded":0,"total":1,"files":[{"url":"http://[::ffff:127.0.0.1]:18080/","error":"Refusing to fetch [::ffff:7f00:1] (resolves to private/loopback/link-local address ::ffff:7f00:1)..."}]}
- Vulnerable victim server log:
Environment:
- Node.js v24.18.0, npm 11.16.0
- Ubuntu 26.04 LTS (Resolute Raccoon)
- Playwright 1.58.2 with Chrome Headless Shell 145.0.7632.6 (manually installed)
- MCP SDK
@modelcontextprotocol/sdk^1.27.1 (from package-lock.json)
Recommendations / Next Steps
- Upgrade: Update to
auth-fetch-mcp@3.0.2or later, which includes the fix (commit177ec5f). - Suggested fix approach (already implemented in 3.0.2): Parse the two trailing hex groups of
::ffff:prefixed IPv6 addresses back into dotted-decimal IPv4 form and run throughisPrivateV4(). Additionally, consider using a well-maintained SSRF protection library (e.g.,ssrf-checkor equivalent) rather than custom IP range checks. - Testing recommendations: Add unit tests for
assertSafeUrl()covering:- IPv4-mapped IPv6 in both dotted-decimal (
::ffff:127.0.0.1) and hex (::ffff:7f00:1) forms - All private ranges:
127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16(link-local/metadata) - Public IPv4-mapped addresses should still pass (e.g.,
::ffff:8.8.8.8) - Integration tests that verify the MCP tool rejects loopback URLs end-to-end
- IPv4-mapped IPv6 in both dotted-decimal (
Additional Notes
- Idempotency: The script was run twice consecutively with identical results (both runs: vulnerable=SSRF confirmed, fixed=blocked). The script cleans up previous test state (removes old HOME directories, overwrites result files) and uses unique markers per run.
- Chrome installation workaround: Playwright 1.58.2 does not support
npx playwright installon Ubuntu 26.04. The script manually downloads Chrome Headless Shell and Chrome for Testing from Google's CDN (storage.googleapis.com/chrome-for-testing-public/) and places them in the Playwright browser cache directory. ThePLAYWRIGHT_BROWSERS_PATHenvironment variable is set so Playwright finds the manually installed browser regardless ofHOME. - MCP transport: The auth-fetch-mcp server uses stdio JSON-RPC (StdioServerTransport), not HTTP. The reproduction interacts with it via its real JSON-RPC API by spawning the server process and sending protocol messages over stdin/stdout. This is the actual API surface of the product — the tool-calling endpoint that processes attacker-supplied URLs.
- Port choice: The victim server uses port 18080 to avoid conflicts with common services.
Variant Analysis & Alternative Triggers for CVE-2026-49857
A distinct, working Server-Side Request Forgery (SSRF) bypass of the CVE-2026-49857 fix exists in auth-fetch-mcp and reproduces end-to-end on the fixed v3.0.2 server and on the latest default branch (origin/main, a4b9245). The fix (commit 177ec5f) only hardened isPrivateV6() against the IPv4-mapped IPv6 hex-normalization vector. It did not change the fact that assertSafeUrl() is applied only to the initial request URL. Playwright's ctx.request.get() (used by download_media at src/tools.ts:234) and page.goto() (used by auth_fetch via src/browser.ts:66) follow HTTP 3xx redirects to private/loopback IPs without re-running assertSafeUrl() on the redirect target. An attacker supplies a public URL that 302-redirects to a private URL (e.g. http://httpbin.org/redirect-to?url=http://127.0.0.1:PORT/); the guard passes the public host, the redirect is followed to the loopback target with no re-validation, and the internal response is returned to the caller. This is a true bypass: the same SSRF guard, same tools, same sink, same trust boundary — a gap the fix does not cover.
Fix Coverage / Assumptions
- Invariant the fix relies on: the URL that passes
assertSafeUrl()is the URL that gets fetched. The fix only improves the IP classification insideisPrivateV6(); it never touches the fetch layer or redirect handling. - Code paths explicitly covered:
src/security.tsisPrivateV6()for::ffff:-prefixed IPv4-mapped addresses that the WHATWG parser normalizes to exactly two trailing hex groups (::ffff:7f00:1). - What the fix does NOT cover:
- Redirect targets —
assertSafeUrl()is not invoked onLocationtargets of 3xx responses. (confirmed bypass) - Non-mapped IPv4-in-IPv6 forms (
::ffff:0:a.b.c.d,::a.b.c.d,64:ff9b::a.b.c.d) bypass the guard's string check, but the OS does not route them to IPv4 loopback in this environment, so no working SSRF (guard incompleteness only). - DNS rebinding / TOCTOU between the guard's
dns.lookupand the fetch's resolution (separate root cause).
- Redirect targets —
Variant / Alternate Trigger
Entry point: the
download_mediaMCP tool (and equivalentlyauth_fetch) over the real stdio JSON-RPC API — the product's actual tool-calling surface. A malicious MCP client or prompt-injected instruction callstools/call download_mediawith:urls: [ "http://httpbin.org/redirect-to?url=http%3A%2F%2F127.0.0.1%3A18080%2F&status_code=302" ]Why it bypasses the fix:
httpbin.orgresolves to public IPs only, soassertSafeUrl()(src/security.ts:87) passes it. The fixedisPrivateV6()is never even reached for a private address at guard time.httpbin.orgresponds302 Location: http://127.0.0.1:18080/.ctx.request.get(safeUrl.toString())atsrc/tools.ts:234follows the redirect to the loopback target and downloads its body.assertSafeUrl()is not called again. The downloaded file is written to disk and its path/content returned to the caller.Code path:
tools.ts:233 assertSafeUrl(url)→ passes (public) →tools.ts:234 ctx.request.get(safeUrl)→ 302 →ctx.request.getfollows → reaches127.0.0.1:18080(loopback victim) →tools.tswritesfile-1.bin→ returns{downloaded:1, files:[{localPath, size}]}. The sibling control URLhttp://127.0.0.1:18080/direct-controlin the same call is correctly rejected with"Refusing to fetch 127.0.0.1 ...", proving the guard works for direct private URLs but not for redirect targets.Package/component:
auth-fetch-mcpnpm package; SSRF guard insrc/security.ts+ fetch layer insrc/tools.ts(download_media) andsrc/browser.ts(auth_fetch/navigateTo).Affected versions (as tested): v3.0.1 (
98f381d, vulnerable), v3.0.2 (d4dedaf, fixed), andorigin/main(a4b9245, latest) — bypass reproduces on all three.Risk level: High. An attacker can cause the MCP server to fetch arbitrary internal/loopback URLs (e.g. cloud metadata
169.254.169.254, internal APIs on127.0.0.1/10.x/192.168.x) by pointing a public URL at them via redirect, then exfiltrate the response.download_mediareturns the fetched bytes to the caller (file path + content);auth_fetchrenders and returns extracted page content.
Impact Parity
- Disclosed/claimed maximum impact (parent CVE): SSRF — bypass of the private-IP guard to fetch internal/loopback URLs via
auth_fetch/download_media. - Reproduced impact from this variant run: Full SSRF confirmed on the fixed server. The v3.0.2 MCP server processed
download_mediawith the public 302-redirect URL, followed the redirect tohttp://127.0.0.1:18080/, the internal victim server received the request (Request from 127.0.0.1 path=/ host=127.0.0.1:18080), the response body (a unique secret marker) was downloaded to disk and returned to the caller (downloaded: 1,variant_redirect.ssrf: true,downloadedContent: "VARIANT_SSRF_MARKER_..."). - Parity:
full— same impact class (SSRF to loopback via the same guard/tools/sink) as the parent, achieved on the patched code. - Not demonstrated: Cloud metadata endpoint (
169.254.169.254) and other private ranges were not exercised at runtime (no such endpoint in the sandbox), but the identical mechanism applies — any private IP reachable from the server can be the redirect target.
Root Cause
assertSafeUrl() (src/security.ts:87) is the product's SSRF gate, but it is invoked once on the caller-supplied URL (tools.ts:233, browser.ts:58). The downstream fetch (ctx.request.get / page.goto) follows redirects without re-invoking the gate. The CVE-2026-49857 fix (177ec5f) narrowed its change to the IP-classification helper isPrivateV6() and did not address redirect-following. Consequently, on the fixed code, any public URL that 3xx-redirects to a private/loopback URL produces SSRF: the guard sees only the public host, and the redirect target is fetched unvalidated. This is a gap in the same SSRF protection the fix belongs to (same guard, same tools, same sink, same trust boundary), reached via a different mechanism (redirect-following) than the original (IPv4-mapped IPv6 hex normalization). Fix commit: 177ec5f8ee9c2d5749035777e562f699971b0da9.
Reproduction Steps
- Reference:
bundle/vuln_variant/reproduction_steps.sh(self-contained; usesbundle/vuln_variant/variant_mcp_client.js). - What the script does:
- Locates the prepared project cache (
bundle/project_cache_context.json) for the repo + Playwright browser cache. - Creates
git worktrees forv3.0.1(vulnerable),v3.0.2(fixed), andorigin/main(latest) underbundle/artifacts/wt-*, reusing the cache'snode_modulesvia symlink, and builds each (npm run build). - For each version, starts a loopback "victim" HTTP server on
127.0.0.1:18080returning a unique secret marker, spawns the real MCP server (node dist/index.js), and sends JSON-RPCinitialize+tools/call download_mediawith two URLs: a controlhttp://127.0.0.1:18080/direct-control(must be blocked) and the varianthttp://httpbin.org/redirect-to?url=http://127.0.0.1:18080/&status_code=302(public → 302 → loopback). - Verifies per version: control blocked by the guard, variant reached the loopback victim and downloaded the marker.
- Writes
bundle/vuln_variant/runtime_manifest.jsonand per-version*_variant_result.jsonunderbundle/logs/vuln_variant/.
- Locates the prepared project cache (
- Expected evidence:
- Fixed v3.0.2:
control_direct_loopback.blocked = trueANDvariant_redirect.ssrf = true(marker present in downloaded file) AND victim log showsRequest from 127.0.0.1 path=/. - Latest main: same. Vulnerable v3.0.1: same.
- Script exits 0 (bypass reproduced on the fixed version).
- Fixed v3.0.2:
Evidence
- Logs under
bundle/logs/vuln_variant/:reproduction_steps.log— full script output (both idempotent runs).fixed-variant_variant_result.json— fixed v3.0.2:ssrfConfirmed:true,control_direct_loopback.blocked:true,variant_redirect.ssrf:true,downloadedContent:"VARIANT_SSRF_MARKER_...",victim_hits:[{remote:"127.0.0.1",path:"/",host:"127.0.0.1:18080"}].fixed-variant_victim_server.log—[VICTIM:18080] Request from 127.0.0.1 path=/ host=127.0.0.1:18080.fixed-variant_variant_test.log— MCP JSON-RPC trace; control file error"Refusing to fetch 127.0.0.1 ...", variant filelocalPath/size:42(downloaded marker).main-variant_variant_result.json/vuln-variant_variant_result.json— same outcome on latest main and on vulnerable v3.0.1.redirect_tier1.log/redirect_tier1.json— mechanism proof with Playwrightrequest.newContext().get()(the same HTTP client asctx.request.get):assertSafeUrl(initial)PASS,ctx.request.getfollowed 302 tohttp://127.0.0.1:18080/,REDIRECT_FOLLOWED_TO_LOOPBACK: true,MARKER_IN_BODY: true.probe_guard.log— guard-logic matrix across candidate addresses (shows::ffff:0:127.0.0.1etc. bypass the fixed guard's string check).routing_test.log— empirical TCP routing test (shows non-mapped IPv4-in-IPv6 forms areENETUNREACH, i.e. not a working SSRF).
- Key excerpts (fixed v3.0.2):
- Tool result:
{"status":"ok","downloaded":1,"total":2,"files":[{"url":"http://127.0.0.1:18080/direct-control","error":"Refusing to fetch 127.0.0.1 (resolves to private/loopback/link-local address 127.0.0.1)..."},{"url":"http://httpbin.org/redirect-to?...#v","localPath":".../file-1.bin","size":42}]} - Victim log:
[VICTIM:18080] Request from 127.0.0.1 path=/ host=127.0.0.1:18080
- Tool result:
- Environment: Node v24.18.0; Playwright chromium headless shell-1208; Ubuntu container (eth0 172.19.0.7); outbound internet enabled (httpbin.org resolves to public IPs and returns 302).
Recommendations / Next Steps
To close the gap and ship a complete SSRF fix:
- Re-validate every fetched URL, including redirect targets. Preferred options:
- Disable auto-redirect at the guard boundary and resolve redirects manually, calling
assertSafeUrl()on eachLocationbefore following it; or - Wrap
ctx.request.get()/page.goto()so that on each redirect the target URL is passed back throughassertSafeUrl(); or - Pin the connection to the resolved IP from the guard's
dns.lookupand reject any redirect whose target hostname resolves to a private/loopback/link-local address.
- Disable auto-redirect at the guard boundary and resolve redirects manually, calling
- Broaden
isPrivateV6()IPv4-in-IPv6 handling (defense in depth): reject all IPv4-embedding forms — mapped (::ffff:x:y), translated (::ffff:0:x:y), compatible (::x:y), and NAT64 (64:ff9b::/96,64:ff9b:1::/96) — by parsing with a real IP library rather than thegroups.length === 2heuristic. Reconstruct and runisPrivateV4()on the embedded IPv4 for each recognized prefix. - Fix the link-local prefix check: use
fe80::/10(matchfe80:–fef0:) instead of onlyfe80:. - Address DNS rebinding: consider resolving once, pinning the address, and connecting to the IP directly with the original
Hostheader (or re-resolving and re-checking at fetch time). - Add tests: end-to-end SSRF tests covering (a) public-URL-redirects-to-private for both
download_mediaandauth_fetch, (b) all IPv4-in-IPv6 forms, (c) all private ranges.
Additional Notes
- Idempotency:
reproduction_steps.shwas run twice consecutively; both runs completed with identical results (vuln=true,fixed=true,main=true, exit 0). Worktrees and dist are reused across runs; per-run state (HOME dirs, markers, result files) is overwritten each run. - Bypass vs alternate trigger: this is a bypass — it reproduces on the patched/fixed code path (v3.0.2 and latest main), not only on the vulnerable version.
- Trust boundary: the malicious URL crosses the MCP client→server trust boundary (tool argument over the JSON-RPC API); the server then fetches an internal/loopback resource it is documented to refuse. No
SECURITY.mdexcludes this attack class; the product's ownassertSafeUrlguard establishes that blocking private IPs is intended behavior. - Scope honesty: the same-root-cause class (IPv4-in-IPv6 hex normalization) variants
::ffff:0:127.0.0.1,::127.0.0.1, and64:ff9b::127.0.0.1were tested and do bypass the fixed guard's string check, but they are not a working SSRF in this environment (ENETUNREACH— the kernel does not route non-mapped IPv4-in-IPv6 to IPv4). They are reported as a guard-incompleteness/defense-in-depth gap, not as a confirmed bypass. The confirmed bypass is the redirect-following path. auth_fetchparity: the same redirect gap affectsauth_fetchviabrowser.ts:58→page.goto()(page.gotofollows redirects/navigations). Runtime proof focused ondownload_mediabecause it does not require the interactive capture-button click; the code-path analysis establishesauth_fetchis equally affected.
CVE-2026-49857 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
ls -la /data/pruva/project-cache/c1700ca0-202a-4b72-bf90-7542807cb1eb/ 2>/dev/null && echo "---REPO---" && ls -la /data/pruva/project-cache/c1700ca0-202a-4b72-bf90-7542807cb1eb/repo 2>/dev/null && echo "---MIRRORS---" && ls -la /data/pruva/project-cache/c1700ca0-202a-4b72-bf90-7542807cb1eb/repo-mirrors 2>/dev/nulltotal 0 drwxr-xr-x 1 vscode vscode 24 Jul 2 17:59 . drwxr-xr-x 1 vscode vscode 1750 Jul 2 17:59 .. drwxr-xr-x 1 vscode vscode 0 Jul 2 17:59 repo-mirrors ---REPO---
node --version 2>/dev/null; npm --version 2>/dev/null; which node; which npm; echo "---"; git --versionv24.18.0 11.16.0 /usr/bin/node /usr/bin/npm --- git version 2.55.0
Artifacts and Evidence for CVE-2026-49857
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-49857
Upgrade ymw0407/auth-fetch-mcp · npm to 3.0.2 or later.
FAQ: CVE-2026-49857
How does the CVE-2026-49857 SSRF bypass work in practice?
Which auth-fetch-mcp versions are affected by CVE-2026-49857, and where is it fixed?
How severe is CVE-2026-49857?
How can I reproduce CVE-2026-49857?
References for CVE-2026-49857
Authoritative sources for CVE-2026-49857 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.