Axios: Null Byte Injection via Reverse-Encoding in AxiosURLSearchParams
Published 5 May 2026 · Updated 10 Sept 2026
Affected packages
- Affected
- >=1.0.0 <1.15.1
- Fixed in
- 1.15.1
npm install axios@1.15.1- Affected
- <0.31.1
- Fixed in
- 0.31.1
npm install axios@0.31.1Details
Vulnerability 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 itProof 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 |
Is GHSA-xhjh-pmcv-23jw in any of your client repos?
Checking one package by hand is fine. Checking it across forty client projects is not. NPMscan reads the lockfiles in every GitHub repo you pick, finds each project with an affected axios, and gives you the command that fixes it.
References
Advisory data from OSV.dev (opens OSV.dev in a new tab), refreshed daily.