GHSA-XHJH-PMCV-23JW
Vulnerability from github – Published: 2026-05-05 00:18 – Updated: 2026-05-05 00:18Vulnerability Disclosure: Null Byte Injection via Reverse-Encoding in AxiosURLSearchParams
Summary
The encode() function in lib/helpers/AxiosURLSearchParams.js contains a character mapping (charMap) at line 21 that reverses the safe percent-encoding of null bytes. After encodeURIComponent('\x00') correctly produces the safe sequence %00, the charMap entry '%00': '\x00' converts it back to a raw null byte.
This is a clear encoding defect: every other charMap entry encodes in the safe direction (literal → percent-encoded), while this single entry decodes in the opposite (dangerous) direction.
Severity: Low (CVSS 3.7)
Affected Versions: All versions containing this charMap entry
Vulnerable Component: lib/helpers/AxiosURLSearchParams.js:21
CWE
- CWE-626: Null Byte Interaction Error (Poison Null Byte)
- CWE-116: Improper Encoding or Escaping of Output
CVSS 3.1
Score: 3.7 (Low)
Vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N
| Metric | Value | Justification |
|---|---|---|
| Attack Vector | Network | Attacker controls input parameters remotely |
| Attack Complexity | High | Standard axios request flow (buildURL) uses its own encode function which does NOT have this bug. Only triggered via direct AxiosURLSearchParams.toString() without an encoder, or via custom paramsSerializer delegation |
| Privileges Required | None | No authentication needed |
| User Interaction | None | No user interaction required |
| Scope | Unchanged | Impact limited to HTTP request URL |
| Confidentiality | None | No confidentiality impact |
| Integrity | Low | Null byte in URL can cause truncation in C-based backends, but requires a vulnerable downstream parser |
| Availability | None | No availability impact |
Vulnerable Code
File: lib/helpers/AxiosURLSearchParams.js, lines 13-26
function encode(str) {
const charMap = {
'!': '%21', // literal → encoded (SAFE direction)
"'": '%27', // literal → encoded (SAFE direction)
'(': '%28', // literal → encoded (SAFE direction)
')': '%29', // literal → encoded (SAFE direction)
'~': '%7E', // literal → encoded (SAFE direction)
'%20': '+', // standard transformation (SAFE)
'%00': '\x00', // LINE 21: encoded → raw null byte (UNSAFE direction!)
};
return encodeURIComponent(str).replace(/[!'()~]|%20|%00/g, function replacer(match) {
return charMap[match];
});
}
Why the Standard Flow Is NOT Affected
// buildURL.js:36 — uses its OWN encode function (lines 14-20), not AxiosURLSearchParams's
const _encode = (options && options.encode) || encode; // buildURL's encode
// buildURL.js:53 — passes buildURL's encode to AxiosURLSearchParams
new AxiosURLSearchParams(params, _options).toString(_encode); // external encoder used
// AxiosURLSearchParams.js:48 — when encoder is provided, internal encode is NOT used
const _encode = encoder ? function(value) { return encoder.call(this, value, encode); } : encode;
// ^^^^^^
// internal encode passed as 2nd arg but only used if
// the external encoder explicitly delegates to it
Proof of Concept
import AxiosURLSearchParams from './lib/helpers/AxiosURLSearchParams.js';
import buildURL from './lib/helpers/buildURL.js';
// Test 1: Direct AxiosURLSearchParams (VULNERABLE path)
const params = new AxiosURLSearchParams({ file: 'test\x00.txt' });
const result = params.toString(); // NO encoder → uses internal encode with charMap
console.log('Direct toString():', JSON.stringify(result));
// Output: "file=test\u0000.txt" (contains raw null byte)
console.log('Hex:', Buffer.from(result).toString('hex'));
// Output: 66696c653d74657374002e747874 (00 = null byte)
// Test 2: Via buildURL (NOT vulnerable — standard axios flow)
const url = buildURL('http://example.com/api', { file: 'test\x00.txt' });
console.log('Via buildURL:', url);
// Output: http://example.com/api?file=test%00.txt (%00 preserved safely)
Verified PoC Output
Direct toString(): "file=test\u0000.txt"
Contains raw null byte: true
Hex: 66696c653d74657374002e747874
Via buildURL: http://example.com/api?file=test%00.txt
Contains raw null byte: false
Contains safe %00: true
Impact Analysis
Primary impact is limited because the standard axios request flow is not affected. However:
- Direct API users: Applications using
AxiosURLSearchParamsdirectly for custom serialization are affected - Custom paramsSerializer: A
paramsSerializer.encodethat delegates to the internal encoder triggers the bug - Code defect signal: The directional inconsistency in charMap is a clear coding error with no legitimate use case
If null bytes reach a downstream C-based parser, impacts include URL truncation, WAF bypass, and log injection.
Recommended Fix
Remove the %00 entry from charMap and update the regex:
function encode(str) {
const charMap = {
'!': '%21',
"'": '%27',
'(': '%28',
')': '%29',
'~': '%7E',
'%20': '+',
// REMOVED: '%00': '\x00'
};
return encodeURIComponent(str).replace(/[!'()~]|%20/g, function replacer(match) {
// ^^^^ removed |%00
return charMap[match];
});
}
Resources
- CWE-626: Null Byte Interaction Error
- CWE-116: Improper Encoding or Escaping of Output
- OWASP: Embedding Null Code
- Axios GitHub Repository
Timeline
| Date | Event |
|---|---|
| 2026-04-15 | Vulnerability discovered during source code audit |
| 2026-04-16 | Report revised: documented standard-flow limitation, corrected CVSS |
| TBD | Report submitted to vendor via GitHub Security Advisory |
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "axios"
},
"ranges": [
{
"events": [
{
"introduced": "1.0.0"
},
{
"fixed": "1.15.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.31.0"
},
"package": {
"ecosystem": "npm",
"name": "axios"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.31.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-42040"
],
"database_specific": {
"cwe_ids": [
"CWE-116",
"CWE-626"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-05T00:18:03Z",
"nvd_published_at": "2026-04-24T18:16:30Z",
"severity": "LOW"
},
"details": "# Vulnerability Disclosure: Null Byte Injection via Reverse-Encoding in AxiosURLSearchParams\n\n## Summary\n\nThe `encode()` function in `lib/helpers/AxiosURLSearchParams.js` contains a character mapping (`charMap`) at line 21 that **reverses** the safe percent-encoding of null bytes. After `encodeURIComponent(\u0027\\x00\u0027)` correctly produces the safe sequence `%00`, the charMap entry `\u0027%00\u0027: \u0027\\x00\u0027` converts it back to a raw null byte.\n\nThis is a clear encoding defect: every other charMap entry encodes in the safe direction (literal \u2192 percent-encoded), while this single entry decodes in the opposite (dangerous) direction.\n\n**Severity:** Low (CVSS 3.7)\n**Affected Versions:** All versions containing this charMap entry\n**Vulnerable Component:** `lib/helpers/AxiosURLSearchParams.js:21`\n\n## CWE\n\n- **CWE-626:** Null Byte Interaction Error (Poison Null Byte)\n- **CWE-116:** Improper Encoding or Escaping of Output\n\n## CVSS 3.1\n\n**Score: 3.7 (Low)**\n\nVector: `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N`\n\n| Metric | Value | Justification |\n|---|---|---|\n| Attack Vector | Network | Attacker controls input parameters remotely |\n| Attack Complexity | High | Standard axios request flow (`buildURL`) uses its own `encode` function which does NOT have this bug. Only triggered via direct `AxiosURLSearchParams.toString()` without an encoder, or via custom `paramsSerializer` delegation |\n| Privileges Required | None | No authentication needed |\n| User Interaction | None | No user interaction required |\n| Scope | Unchanged | Impact limited to HTTP request URL |\n| Confidentiality | None | No confidentiality impact |\n| Integrity | Low | Null byte in URL can cause truncation in C-based backends, but requires a vulnerable downstream parser |\n| Availability | None | No availability impact |\n\n## Vulnerable Code\n\n**File:** `lib/helpers/AxiosURLSearchParams.js`, lines 13-26\n\n```javascript\nfunction encode(str) {\n const charMap = {\n \u0027!\u0027: \u0027%21\u0027, // literal \u2192 encoded (SAFE direction)\n \"\u0027\": \u0027%27\u0027, // literal \u2192 encoded (SAFE direction)\n \u0027(\u0027: \u0027%28\u0027, // literal \u2192 encoded (SAFE direction)\n \u0027)\u0027: \u0027%29\u0027, // literal \u2192 encoded (SAFE direction)\n \u0027~\u0027: \u0027%7E\u0027, // literal \u2192 encoded (SAFE direction)\n \u0027%20\u0027: \u0027+\u0027, // standard transformation (SAFE)\n \u0027%00\u0027: \u0027\\x00\u0027, // LINE 21: encoded \u2192 raw null byte (UNSAFE direction!)\n };\n return encodeURIComponent(str).replace(/[!\u0027()~]|%20|%00/g, function replacer(match) {\n return charMap[match];\n });\n}\n```\n\n### Why the Standard Flow Is NOT Affected\n\n```javascript\n// buildURL.js:36 \u2014 uses its OWN encode function (lines 14-20), not AxiosURLSearchParams\u0027s\nconst _encode = (options \u0026\u0026 options.encode) || encode; // buildURL\u0027s encode\n\n// buildURL.js:53 \u2014 passes buildURL\u0027s encode to AxiosURLSearchParams\nnew AxiosURLSearchParams(params, _options).toString(_encode); // external encoder used\n\n// AxiosURLSearchParams.js:48 \u2014 when encoder is provided, internal encode is NOT used\nconst _encode = encoder ? function(value) { return encoder.call(this, value, encode); } : encode;\n// ^^^^^^\n// internal encode passed as 2nd arg but only used if\n// the external encoder explicitly delegates to it\n```\n\n## Proof of Concept\n\n```javascript\nimport AxiosURLSearchParams from \u0027./lib/helpers/AxiosURLSearchParams.js\u0027;\nimport buildURL from \u0027./lib/helpers/buildURL.js\u0027;\n\n// Test 1: Direct AxiosURLSearchParams (VULNERABLE path)\nconst params = new AxiosURLSearchParams({ file: \u0027test\\x00.txt\u0027 });\nconst result = params.toString(); // NO encoder \u2192 uses internal encode with charMap\nconsole.log(\u0027Direct toString():\u0027, JSON.stringify(result));\n// Output: \"file=test\\u0000.txt\" (contains raw null byte)\nconsole.log(\u0027Hex:\u0027, Buffer.from(result).toString(\u0027hex\u0027));\n// Output: 66696c653d74657374002e747874 (00 = null byte)\n\n// Test 2: Via buildURL (NOT vulnerable \u2014 standard axios flow)\nconst url = buildURL(\u0027http://example.com/api\u0027, { file: \u0027test\\x00.txt\u0027 });\nconsole.log(\u0027Via buildURL:\u0027, url);\n// Output: http://example.com/api?file=test%00.txt (%00 preserved safely)\n```\n\n## Verified PoC Output\n\n```\nDirect toString(): \"file=test\\u0000.txt\"\nContains raw null byte: true\nHex: 66696c653d74657374002e747874\n\nVia buildURL: http://example.com/api?file=test%00.txt\nContains raw null byte: false\nContains safe %00: true\n```\n\n## Impact Analysis\n\n**Primary impact is limited** because the standard axios request flow is not affected. However:\n\n- **Direct API users:** Applications using `AxiosURLSearchParams` directly for custom serialization are affected\n- **Custom paramsSerializer:** A `paramsSerializer.encode` that delegates to the internal encoder triggers the bug\n- **Code defect signal:** The directional inconsistency in charMap is a clear coding error with no legitimate use case\n\nIf null bytes reach a downstream C-based parser, impacts include URL truncation, WAF bypass, and log injection.\n\n## Recommended Fix\n\nRemove the `%00` entry from charMap and update the regex:\n\n```javascript\nfunction encode(str) {\n const charMap = {\n \u0027!\u0027: \u0027%21\u0027,\n \"\u0027\": \u0027%27\u0027,\n \u0027(\u0027: \u0027%28\u0027,\n \u0027)\u0027: \u0027%29\u0027,\n \u0027~\u0027: \u0027%7E\u0027,\n \u0027%20\u0027: \u0027+\u0027,\n // REMOVED: \u0027%00\u0027: \u0027\\x00\u0027\n };\n return encodeURIComponent(str).replace(/[!\u0027()~]|%20/g, function replacer(match) {\n // ^^^^ removed |%00\n return charMap[match];\n });\n}\n```\n\n## Resources\n\n- [CWE-626: Null Byte Interaction Error](https://cwe.mitre.org/data/definitions/626.html)\n- [CWE-116: Improper Encoding or Escaping of Output](https://cwe.mitre.org/data/definitions/116.html)\n- [OWASP: Embedding Null Code](https://owasp.org/www-community/attacks/Embedding_Null_Code)\n- [Axios GitHub Repository](https://github.com/axios/axios)\n\n## Timeline\n\n| Date | Event |\n|---|---|\n| 2026-04-15 | Vulnerability discovered during source code audit |\n| 2026-04-16 | Report revised: documented standard-flow limitation, corrected CVSS |\n| TBD | Report submitted to vendor via GitHub Security Advisory |",
"id": "GHSA-xhjh-pmcv-23jw",
"modified": "2026-05-05T00:18:03Z",
"published": "2026-05-05T00:18:03Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/axios/axios/security/advisories/GHSA-xhjh-pmcv-23jw"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42040"
},
{
"type": "PACKAGE",
"url": "https://github.com/axios/axios"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Axios: Null Byte Injection via Reverse-Encoding in AxiosURLSearchParams"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.