GitHub Advisoryadvisory · npm
GHSA-pmwg-cvhr-8vh7 Axios: Incomplete Fix for CVE-2025-62718 — NO_PROXY Protection Bypassed via RFC 1122 Loopback Subnet (127.0.0.0/8) in Axios 1.15.0
HIGH
Official page ↗
- Aliases
- CVE-2026-42043
- Ecosystem
- npm
- Packages
- axios
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
- Weaknesses
- CWE-183, CWE-441, CWE-918
- Reviewed
- 2026-05-05
Affected packages
• npm axios: >= 1.0.0, < 1.15.1 → fixed in 1.15.1
• npm axios: < 0.31.1 → fixed in 0.31.1
Description
1. Executive Summary
This report documents an incomplete security patch for the previously disclosed vulnerability GHSA-3p68-rc4w-qgx5 (CVE-2025-62718), which affects the `NO_PROXY` hostname resolution logic in the Axios HTTP library.
Background — The Original Vulnerability
The original vulnerability (GHSA-3p68-rc4w-qgx5) disclosed that Axios did not normalize hostnames before comparing them against `NO_PROXY` rules. Specifically, a request to `http://localhost./` (with a trailing dot) or `http://[::1]/` (with IPv6 bracket notation) would bypass NO_PROXY matching entirely and be forwarded to the configured HTTP proxy — even when `NO_PROXY=localhost,127.0.0.1,::1` was explicitly set by the developer to protect loopback services.
The Axios maintainers addressed this in version 1.15.0 by introducing a `normalizeNoProxyHost()` function in `lib/helpers/shouldBypassProxy.js`, which strips trailing dots from hostnames and removes brackets from IPv6 literals before performing the NO_PROXY comparison.
The Incomplete Patch — This Finding
While the patch correctly addresses the specific cases reported (trailing dot normalization and IPv6 bracket removal), the fix is architecturally incomplete.
The patch introduced a hardcoded set of recognized loopback addresses:
```
// lib/helpers/shouldBypassProxy.js — Line 1
const LOOPBACK_ADDRESSES = new Set(['localhost', '127.0.0.1', '::1']);
```
However, RFC 1122 §3.2.1.3 explicitly defines the entire 127.0.0.0/8 subnet as the IPv4 loopback address block not just the single address `127.0.0.1`. On all major operating systems (Linux, macOS, Windows with WSL), any IP address in the range `127.0.0.2` through `127.255.255.254` is a valid, functional loopback address that routes to the local machine.
As a result, an attacker who can influence the target URL of an Axios request can substitute 127.0.0.1 with any other address in the `127.0.0.0/8` range (e.g., `127.0.0.2`, `127.0.0.100`, `127.1.2.3`) to completely bypass the `NO_PROXY` protection even in the fully patched Axios 1.15.0 release.
Verification
This bypass has been independently verified on:
• Axios version: 1.15.0 (latest patched release)
• Node.js version: v22.16.0
• OS: Kali Linux (rolling)
The Proof-of-Concept demonstrates that while `localhost`, `localhost`., and `[::1]` are correctly blocked by the patched version, requests to `127.0.0.2`, `127.0.0.100`, and `127.1.2.3` are transparently forwarded to the attacker-controlled proxy server, confirming that the patch does not cover the full RFC-defined loopback address space.
2. Deep-Dive: Technical Root Cause Analysis
2.1 Vulnerable File & Location
| Field | Detail |
| ------------- | ------------- |
| File | lib/helpers/shouldBypassProxy.js|
| Primary Flaw| isLoopback() — Line 1–3 |
| Supporting Function | shouldBypassProxy() — Line 59–110 |
| Axios Version | 1.15.0 (Latest Patched Release) |
2.2 How Axios Routes HTTP Requests The Call Chain
When Axios dispatches any HTTP request, `lib/adapters/http.js` calls `setProxy()`, which invokes `shouldBypassProxy()` to decide whether to honour a configured proxy:
```
// lib/adapters/http.js — Lines 191–199
function setProxy(options, configProxy, location) {
let proxy = configProxy;
if (!proxy && proxy !== false) {
const proxyUrl = getProxyForUrl(location); // Step 1: Read proxy env var
if (proxyUrl) {
if (!shouldBypassProxy(location)) { // Step 2: Check NO_PROXY
proxy = new URL(proxyUrl); // Step 3: Assign proxy
}
}
}
}
```
`shouldBypassProxy()` is the single gatekeeper for NO_PROXY enforcement. A bypass here means all proxy protection fails silently.
2.3 The Original Vulnerability (GHSA-3p68-rc4w-qgx5)
Before Axios 1.15.0, hostnames were compared against `NO_PROXY` using a raw literal string match with no normalization:
```
Request URL → http://localhost./secret
NO_PROXY → "localhost,127.0.0.1,::1"
Comparison:
"localhost." === "localhost" → FALSE → Proxy used ← BYPASS
"[::1]" === "::1" → FALSE → Proxy used ← BYPASS
```
Both `localhost.` (FQDN trailing dot, RFC 1034 §3.1) and `[::1]` (bracketed IPv6 literal, RFC 3986 §3.2.2) are canonical representations of loopback addresses, but Axios treated them as unknown hosts.
2.4 What the Patch Fixed (Axios 1.15.0)
The patch introduced three changes inside `lib/helpers/shouldBypassProxy.js`:
Fix A `normalizeNoProxyHost()` (Lines 47–57)
Strips alternate representations before comparison:
```
const normalizeNoProxyHost = (hostname) => {
if (!hostname) return hostname;
// Remove IPv6 brackets: "[::1]" → "::1"
if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {
hostname = hostname.slice(1, -1);
}
// Strip trailing FQDN dot: "localhost." → "localhost"
return hostname.replace(/\.+$/, '');
};
```
Fix B Cross-Loopback Equivalence (Lines 1–3 & 108)
Allows `127.0.0.1` and `localhost` to match each other interchangeably:
```
const LOOPBACK_ADDRESSES = new Set(['localhost', '127.0.0.1', '::1']);
const isLoopback = (host) => LOOPBACK_ADDRESSES.has(host);
// Line 108 — Final match condition:
return hostname === entryHost
|| (isLoopback(hostname) && isLoopback(entryHost));
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// If both sides are "loopback" → treat as match
```
Fix C Normalization Applied on Both Sides (Lines 81 & 90)
```
// Request hostname normalized:
const hostname = normalizeNoProxyHost(parsed.hostname.toLowerCase());
// Each NO_PROXY entry normalized:
entryHost = normalizeNoProxyHost(entryHost);
```
2.5 The Incomplete Patch Exact Root Cause
The fundamental flaw resides in Line 1:
```
// lib/helpers/shouldBypassProxy.js — Line 1 ← ROOT CAUSE
const LOOPBACK_ADDRESSES = new Set(['localhost', '127.0.0.1', '::1']);
// ^^^^^^^^^^^
// Only ONE IPv4 loopback address is recognized.
// The entire 127.0.0.0/8 subnet is unaccounted for.
// Line 3 — Lookup against this incomplete set:
const isLoopback = (host) => LOOPBACK_ADDRESSES.has(host);
// ^^^^^^^^^
// Returns FALSE for any 127.x.x.x ≠ 127.0.0.1
```
*RFC 1122 §3.2.1.3 is unambiguous:
> "The address 127.0.0.0/8 is assigned for loopback. A datagram sent by a higher-level protocol to a loopback address MUST NOT appear on any network."
This means all addresses from `127.0.0.1` through `127.255.255.254` are valid loopback addresses on any RFC-compliant operating system. On Linux, the entire `/8` block is routed to the `lo` interface by default. The patch recognises only `127.0.0.1`, leaving `16,777,213` valid loopback addresses unprotected.
2.6 Step-by-Step Bypass Execution Trace
Environment:
```
NO_PROXY = "localhost,127.0.0.1,::1"
HTTP_PROXY = "http://attacker-proxy:5300"
Target URL = "http://127.0.0.2:9191/internal-api"
```
Annotated execution of shouldBypassProxy("http://127.0.0.2:9191/internal-api"):
```
// Step 1 — Parse the request URL
parsed = new URL("http://127.0.0.2:9191/internal-api")
hostname = "127.0.0.2" // parsed.hostname
// Step 2 — Read NO_PROXY environment variable
noProxy = "localhost,127.0.0.1,::1" // lowercased
// Step 3 — Normalize the request hostname
hostname = normalizeNoProxyHost("127.0.0.2")
// No bra…
Related
Alias · CVE (1)
- CVE-2026-42043 Axios is a promise based HTTP client for the browser and Node.js. vulnerability