[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"repo-stars":3,"vuln-GHSA-42H9-826W-CGV3":6},{"stargazers_count":4,"fetched_at":5},8,"2026-09-19T11:35:27.363Z",{"id":7,"descriptions":8,"cisa":9,"weaknesses":10,"exploits":11,"aliases":12,"duplicate_of":9,"upstream":13,"downstream":14,"duplicates":17,"related":18,"reserved_at":9,"published_at":23,"modified_at":24,"state":9,"summary":25,"references_raw":27,"kevs":63,"epss":9,"epss_history":64,"metrics":65,"affected":70},"GHSA-42H9-826W-CGV3","Axios: Excessive recursion in formDataToJSON can cause denial of service\n\n## Summary\nAxios versions `0.28.0` and later contain uncontrolled recursion in `formDataToJSON`, the helper behind the public `axios.formToJSON()` / named `formToJSON` API and the default request transform used when FormData is sent with an `application/json` content type.\n\nApplications are affected when they pass attacker-controlled `FormData` field names into this functionality. A field name with thousands of nested bracket segments can exhaust the JavaScript call stack and throw `RangeError: Maximum call stack size exceeded`, causing request failure and, in applications that do not handle the exception or rejected promise, possible process termination.\n\n## Impact\nThe impact is denial of service against applications that process untrusted `FormData` field names through axios' FormData-to-JSON conversion.\n\nThe vulnerable path is not reached by merely installing axios, by normal multipart `FormData` pass-through, or by ordinary axios requests that do not request JSON serialisation of `FormData`. In the default axios request, the error is produced before network I/O and returned as a rejected Promise. Direct use of `formToJSON()` throws synchronously.\n\nServer-side applications are the primary risk when remote users can submit arbitrary form field names, and the application converts those fields with `formToJSON()` or sends them through axios as JSON.\n\n## Affected Functionality\nAffected APIs and paths:\n- `axios.formToJSON(formData)`\n- `import { formToJSON } from \"axios\"`\n- `lib/helpers/formDataToJSON.js`\n- axios default `transformRequest` when `data` is `FormData` and `Content-Type` contains `application/json`\n\nUnaffected or lower-risk paths:\n- Normal multipart `FormData` requests without `JSON Content-Type`\n- `toFormData()` object-to-FormData serialisation, which already has a `maxDepth` guard\n- Axios versions before 0.28.0, where this helper and public API were not present\n\n## Technical Details\n`lib/helpers/formDataToJSON.js` parses a form field name into path segments with `parsePropPath()`. For a key such as `a[x][x][x]`, each bracketed segment becomes another path element.\n\n`formDataToJSON()` then calls the nested `buildPath(path, value, target, index)` function. `buildPath()` recursively calls itself once for each path segment and does not enforce a maximum depth:\n\n`const result = buildPath(path, value, target[name], index);`\n\nA key containing thousands of bracket segments, therefore, creates thousands of recursive calls. At sufficient depth, V8 throws `RangeError: Maximum call stack size exceeded`.\n\nAxios already applies a depth guard to the inverse serializer in `lib/helpers/toFormData.js`, where `maxDepth` defaults to 100 and exceeding it throws `AxiosError` with code `ERR_FORM_DATA_DEPTH_EXCEEDED`. `formDataToJSON()` does not currently have equivalent protection.\n\n## Proof of Concept of Attack\n```js\nimport { formToJSON } from \"axios\";\n\nconst fd = new FormData();\nfd.append(\"a\" + \"[x]\".repeat(15000), \"value\");\n\ntry {\n  formToJSON(fd);\n  console.log(\"not vulnerable\");\n} catch (err) {\n  console.log(`${err.constructor.name}: ${err.message}`);\n}\n```\n\nExpected vulnerable result:\n\nRangeError: Maximum call stack size exceeded\n\nThe axios request transform path can also be reached before network I/O:\n\n```js\nimport axios from \"axios\";\n\nconst fd = new FormData();\nfd.append(\"a\" + \"[x]\".repeat(15000), \"value\");\n\nawait axios\n  .post(\"http://127.0.0.1:1/\", fd, {\n    headers: { \"Content-Type\": \"application/json\" }\n  })\n  .catch((err) => console.log(`${err.constructor.name}: ${err.message}`));\n```\n\nExpected vulnerable result:\n\nRangeError: Maximum call stack size exceeded\n\n## Workarounds\nApplications can avoid the vulnerable path by not converting attacker-controlled `FormData` to JSON with axios.\n\nIf conversion is required before a fixed axios release is available, validate `FormData` field names before calling `formToJSON()` or before sending `FormData` with `Content-Type: application/json`. Reject keys whose parsed nesting depth exceeds the application's expected schema.\n\nFor axios requests carrying untrusted `FormData`, avoid setting `Content-Type: application/json`; leaving the data as multipart FormData bypasses `formDataToJSON()`.\n\nCatching the resulting error can prevent process termination, but it does not remove the uncontrolled-recursion behaviour and should not be treated as the primary mitigation.\n\n\u003Cdetails>\n\u003Csummary>Original Report\u003C/summary>\n# Axios SSRF via Incomplete Loopback Detection\n## CWE-918 | CVSS 7.5 (HIGH) | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L\n\n---\n\n## 1. Classification\n\n| CWE | CVSS Score | Severity | Type |\n|-----|-----------|----------|------|\n| CWE-918 | 7.5 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L) | HIGH | Server-Side Request Forgery |\n\n## 2. Description\n\n### Summary\nThe `shouldBypassProxy()` function in Axios fails to recognise `0.0.0.0`, `::`, and `::ffff:0.0.0.0` as loopback addresses. When `NO_PROXY=localhost` is configured, requests to these addresses are incorrectly forwarded through the proxy instead of being sent directly, enabling an SSRF attack against internal services reachable via the proxy's loopback interface.\n\n### Root Cause\n**File:** `lib/helpers/shouldBypassProxy.js`\n\n**`isIPv4Loopback` (lines 3-8):** Only checks for `127.x.x.x` addresses by inspecting `parts[0] !== '127'`. The `0.0.0.0` address has `parts[0] === '0'`, so it falls through as non-loopback, even though on Linux `0.0.0.0` routes to the loopback interface.\n\n**`isIPv6Loopback` (lines 10-38):** Only checks `host === '::1'`. The `::` address (unspecified IPv6) also routes to the loopback, but is not recognised.\n\n**Attack Flow:**\n```\nisIPv4Loopback (line 3) — fails for 0.0.0.0\n  → isLoopback (line 44) — wraps both checks, returns false\n    → shouldBypassProxy (line 127) — PUBLIC API, exported default\n      → lib/adapters/http.js (line 190) — Node.js HTTP adapter\n```\n\n### Attack Vector\n- **Access Vector:** Network (AV:N)\n- **Access Complexity:** Low (AC:L) — attacker only needs control of a URL\n- **Privileges Required:** None (PR:N)\n- **User Interaction:** None (UI:N)\n\n## 3. Proof of Concept\n\n### Phase 1: Logic Verification\n\n```javascript\nimport shouldBypassProxy from 'axios/lib/helpers/shouldBypassProxy.js';\n\n// Normal loopback — correctly returns true (bypasses proxy)\nshouldBypassProxy('http://127.0.0.1:9999/');  // → true\n\n// Vulnerable — returns false (goes through proxy!)\nshouldBypassProxy('http://0.0.0.0:9999/');    // → false  ← SSRF\nshouldBypassProxy('http://[::]:9999/');        // → false  ← SSRF\nshouldBypassProxy('http://[::ffff:0.0.0.0]:9999/'); // → false ← SSRF\n```\n\n### Phase 2: Docker E2E Reproduction\n\nA full 3-container Docker reproduction was created and tested:\n\n- **Proxy container:** Simple HTTP forward proxy on port 8888\n- **Internal container:** Internal service on port 9999 (simulates sensitive internal resource)\n- **Attacker container:** Runs the test script with Axios source mounted\n\n**Reproduction steps:**\n```bash\ncd /tmp/deep-e2e\ndocker compose up -d\ndocker compose exec attacker node test-ssrf.js\n```\n\n**Results:**\n- Test 1: `127.0.0.1 + NO_PROXY=localhost` → BYPASS (correct) \n- Test 2: `0.0.0.0 + NO_PROXY=localhost` → VIA_PROXY (SSRF) \n- Test 3: `[::] + NO_PROXY=localhost` → VIA_PROXY (SSRF) \n- Test 4: `[::ffff:0.0.0.0] + NO_PROXY=localhost` → VIA_PROXY (SSRF) \n\n### Phase 3: Actual Axios Client\n\nThe real Axios HTTP client (v1.16.1, source tree) was tested through proxy configuration:\n- Axios with `proxy: { host: 'proxy', port: 8888 }` \n- Setting `NO_PROXY=localhost` and requesting `http://0.0.0.0:9999/`\n- Result: Axios forwarded the request through the proxy instead of bypassing it\n\n## 4. Impact\n\n### Attack Scenario\n1. Attacker has control over a URL that an Axios client will request (direct input, redirect target, open redirect chain)\n2. The Axios client is configured with a proxy (e.g., corporate proxy) and `NO_PROXY=localhost` to protect internal services\n3. Attacker supplies `http://0.0.0.0:8080/admin` as the target URL\n4. Axios sends the request through the proxy\n5. The proxy resolves `0.0.0.0` → the proxy's own loopback → reaches the internal admin service on port 8080\n\n### Potential Consequences\n- **Information disclosure (C:L):** Internal service responses become accessible\n- **Integrity impact (I:L):** Attacker can trigger actions on internal services (if proxy supports PUT/POST/DELETE)\n- **Availability impact (A:L):** Limited — depends on internal service behavior\n\n### Likelihood\n- **High** — proxy bypass is a common pattern in microservice architectures\n- **Medium** — requires attacker control of a URL (not always available)\n\n## 5. Remediation\n\n### Code Fix\n\n**File:** `lib/helpers/shouldBypassProxy.js`\n\n```javascript\nfunction isIPv4Loopback(host) {\n  if (host === '0.0.0.0') return true;  // ADD THIS LINE\n  const parts = host.split('.');\n  if (parts.length !== 4) return false;\n  if (parts[0] !== '127') return false;\n  return parts.every(p => /^\\d+$/.test(p) && Number(p) >= 0 && Number(p) \u003C= 255);\n}\n\nfunction isIPv6Loopback(host) {\n  if (host === '::1' || host === '::') return true;  // ADD '::'\n  // ... rest of implementation\n}\n```\n\n### Workarounds\n- Add `0.0.0.0` and `::` to the `NO_PROXY` environment variable explicitly\n- Use `127.0.0.1` instead of `0.0.0.0` in all internal service URLs\n- Implement URL validation to reject `0.0.0.0` and `::` before passing to Axios",null,[],[],[],[],[15],{"_key":16},"CVE-2026-67313",[],[19,21],{"_key":20},"CGA-VR57-42Q2-W973",{"_key":22},"CGA-WHCM-GCC2-6X94","2026-07-20T17:58:59Z","2026-07-22T02:59:41.082666505Z",{"cisa_kev":26,"cisa_ransomware":26,"cisa_vendor":9,"epss_severity":9,"epss_score":9,"severity":9,"severity_score":9,"severity_version":9,"severity_source":9,"severity_vector":9,"severity_status":9},false,[28,34,38,42,46,50,55,59],{"url":29,"sources":30,"tags":32},"https://github.com/axios/axios/security/advisories/GHSA-42h9-826w-cgv3",[31],"osv_npm",[33],"WEB",{"url":35,"sources":36,"tags":37},"https://github.com/axios/axios/pull/11000",[31],[33],{"url":39,"sources":40,"tags":41},"https://github.com/axios/axios/pull/11001",[31],[33],{"url":43,"sources":44,"tags":45},"https://github.com/axios/axios/commit/1417285c69344bbcc6420a021f67dee0c6fedb2d",[31],[33],{"url":47,"sources":48,"tags":49},"https://github.com/axios/axios/commit/32fc489632377d214db55bfa4e2c48486a7d7ce2",[31],[33],{"url":51,"sources":52,"tags":53},"https://github.com/axios/axios",[31],[54],"PACKAGE",{"url":56,"sources":57,"tags":58},"https://github.com/axios/axios/releases/tag/v0.33.0",[31],[33],{"url":60,"sources":61,"tags":62},"https://github.com/axios/axios/releases/tag/v1.18.0",[31],[33],[],[],[66],{"source":31,"cvss_v2_0":9,"cvss_v3_0":9,"cvss_v3_1":9,"cvss_v4_0":67},{"baseScore":68,"baseSeverity":9,"vectorString":69,"impactScore":9,"exploitabilityScore":9},6.3,"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N",[71],{"ecosystem":72,"name":73,"vendor":72,"product":73,"cpe_part":9,"purl_type":74,"purl_namespace":9,"purl_name":73,"source":9,"versions":75},"Npm","axios","npm",[76,84],{"version":77,"is_range":78,"range_type":79,"version_start":80,"version_start_type":81,"version_end":82,"version_end_type":83,"fixed_in":9},"gte0_28_0_lt0_33_0",true,"semver","0.28.0","including","0.33.0","excluding",{"version":85,"is_range":78,"range_type":79,"version_start":86,"version_start_type":81,"version_end":87,"version_end_type":83,"fixed_in":9},"gte1_0_0_lt1_18_0","1.0.0","1.18.0"]