This is a loaded tow hitch, not the truck that breaks through the gate
CVE-2026-44494 is a prototype-pollution gadget in axios's Node HTTP adapter, not a direct prototype-pollution source by itself. In affected versions >=1.0.0, <1.16.0, axios can inherit a polluted Object.prototype.proxy value because config.proxy is read through the prototype chain; that can route outbound axios traffic through an attacker-controlled proxy and expose credentials, request bodies, URLs, and responses.
The vendor's HIGH 8.7 score captures the *impact* once the chain is assembled, but overstates the *standalone enterprise urgency*. The decisive friction is that the attacker first needs a separate way to pollute Object.prototype inside the same Node.js runtime; without that first-stage bug, axios does nothing dangerous on its own. That makes this a chained, second-stage library risk rather than a clean unauthenticated internet-to-RCE style event.
4 steps from start to impact.
Land a prototype-pollution source in the same Node process
Object.prototype. This is the real entry point; axios is only useful after that first-stage bug succeeds. Typical weaponized sources are qs-style parser abuse or unsafe deep-merge behavior discussed in the prototype-pollution research literature.- Attacker can send crafted data into the application
- A separate prototype-pollution source exists somewhere in the same runtime
- That first-stage source is reachable in production
- No exploit exists if the app has no reachable prototype-pollution source
- Modern schema validation and safe parsers often kill the chain before pollution happens
- This prerequisite already implies application-layer exposure to attacker-controlled input
Pollute Object.prototype.proxy
proxy object on Object.prototype, pointing to an attacker-controlled host and port. This is the exact gadget state described in the advisory: axios later reads config.proxy with normal property access and inherits the polluted value if no own proxy property is set.- Prototype pollution succeeded
- The polluted process continues serving requests after the write
- Axios requests are made without an explicit own
proxyoverride
- Some apps restart workers frequently, limiting persistence
- Some code paths always set
proxyexplicitly, which blocks this inheritance path - Browser-side axios use is less relevant here; the dangerous path is the Node HTTP adapter
Trigger axios HTTP adapter proxy routing
config.proxy and passes it into setProxy(), transparently steering requests to the attacker's proxy instead of the intended destination.- The target service uses axios on Node.js
- The relevant code path makes outbound HTTP(S) requests
- The process is still polluted when the request is sent
- No impact if the service never makes outbound axios calls
- Strict egress policy or proxy allowlists can block arbitrary attacker proxy destinations
- Service-to-service traffic on private networks may be hard for an attacker to monetize without useful credentials in transit
Intercept credentials and tamper with responses
- Sensitive axios traffic exists in the process
- Proxy redirection is not blocked by egress controls
- The attacker can meaningfully use the intercepted material
- If outbound traffic is low-value or non-sensitive, impact drops sharply
- TLS, certificate validation, and proxy architecture may limit straightforward content interception in some deployments
- Short-lived credentials reduce post-capture usefulness
The supporting signals.
| In-the-wild status | No CISA KEV listing and no authoritative public evidence of active exploitation found in the reviewed sources. |
|---|---|
| Proof-of-concept availability | A public verified PoC is embedded in the GitHub advisory / OSV record, including Object.prototype.proxy pollution and request interception. |
| EPSS | No reliable FIRST EPSS score was surfaced in the reviewed primary sources for this CVE yet; treat EPSS as unavailable/immature, not as risk-reducing evidence. |
| KEV status and dates | Not KEV-listed. Public disclosure/publishing date is 2026-05-29. |
| CVSS vector meaning | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:N correctly flags high attack complexity; the hidden reality behind that AC:H is 'requires a separate prototype-pollution source in the same runtime.' |
| Affected versions | GitHub/OSV list axios npm versions >= 1.0.0, < 1.16.0 as affected. |
| Fixed versions | Upgrade to 1.16.0 or later. For this npm library, distro-style backport guidance is generally not applicable; verify your lockfile/SBOM, not an OS package feed. |
| Scanning and exposure reality | Shodan/Censys/FOFA are basically irrelevant here. Axios is a dependency, not a network appliance; exposure must be measured with SCA/SBOM/runtime inventory, not internet census data. |
| Researcher / reporter | The GitHub advisory was published in axios/axios and attributes publication to jasonsaayman on 2026-05-29. |
noisgate verdict.
The single biggest downgrading factor is that axios is not the initial foothold: exploitation requires a separate prototype-pollution vulnerability reachable in the same Node.js process. That sharply narrows the exposed population from 'everyone running axios' to 'services running vulnerable axios *and* a reachable pollution source *and* valuable outbound traffic,' which is not a HIGH-at-scale patch emergency.
Why this verdict
- Downgrade for attacker position: this is not
unauthenticated remote -> axios compromiseby itself. The attacker must first gain a remote path to polluteObject.prototypethrough another bug in the same runtime. - Downgrade for exposure population: axios is not internet-discoverable as a service. Real exposure is only the subset of Node applications that both use vulnerable axios and have a reachable first-stage prototype-pollution source.
- Downgrade for chain fragility: modern controls like input validation, safer parsers, dependency hygiene, worker restarts, and egress filtering can break the chain at multiple points before the proxy hijack ever occurs.
- Keep it at MEDIUM, not LOW: if the chain exists, impact is nasty. The attacker can transparently proxy all axios traffic in that process, steal credentials, and tamper with responses without noisy application errors.
Why not higher?
A higher rating would fit only if axios were the initial exploit primitive or if there were confirmed in-the-wild campaigns chaining this reliably at scale. Here, the vulnerability is a second-stage gadget that depends on another reachable bug and on meaningful outbound axios traffic, so the real-world target set is much smaller than the vendor score suggests.
Why not lower?
This is not harmless version-churn. Once the prerequisite pollution exists, the attacker gets a powerful credential-harvesting and response-tampering position inside the service's trust boundary, and axios is deployed widely enough that many enterprises will find some meaningful exposure in Node estates.
What to do — in priority order.
- Sweep for first-stage prototype-pollution sources — Hunt for reachable parser/merge/path-setter flaws in the same Node services that use axios, because those are the real exploit enablers. There is no mitigation SLA for this MEDIUM finding — go straight to the 365-day remediation window, but do this first on internet-facing services and credential-heavy integrations.
- Restrict outbound egress to approved destinations — Force application tiers to use approved proxies or explicit allowlists so a polluted
config.proxycannot send traffic to arbitrary attacker infrastructure. There is no mitigation SLA for this MEDIUM finding, so implement this as part of normal network-hardening work inside the remediation window, prioritizing services that handle secrets or call internal APIs. - Alert on unexpected proxying and new egress paths — Monitor for unapproved outbound proxy hosts, unusual
CONNECTbehavior, and Node services suddenly talking to unfamiliar internet destinations. There is no mitigation SLA for this MEDIUM finding; treat this as detection engineering that reduces dwell time while you clean up package exposure. - Gate CI on axios version floor — Add a policy check that rejects builds or lockfiles containing
axiosbelow1.16.0, because library vulnerabilities like this persist through dependency drift more than through host drift. There is no mitigation SLA for this MEDIUM finding, but this is a cheap control that prevents reintroduction during the remediation window.
WAF-onlythinking doesn't help much; the dangerous part is the server-side runtime state after prototype pollution, not an obvious network signature at the edge.- Cleaning only
HTTP_PROXY/HTTPS_PROXYenvironment variables is insufficient; this gadget abusesconfig.proxyinheritance, not just environment-based proxy configuration. - Perimeter internet scanning won't tell you who is exposed; axios is a dependency problem, so you need SBOM/SCA/runtime inventory, not Shodan screenshots.
Crowdsourced verification payload.
Run this on a developer workstation, CI runner, or application host against a source tree or deployed app directory that contains package-lock.json, npm-shrinkwrap.json, node_modules, or package.json. Invoke it as python3 check_axios_cve_2026_44494.py /path/to/app; no elevated privileges are required unless the target directory is unreadable to your account.
#!/usr/bin/env python3
# check_axios_cve_2026_44494.py
# Detects axios exposure to CVE-2026-44494.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN, 3=usage/error
import json
import os
import re
import sys
from typing import List, Optional, Tuple
AFFECTED_MIN = (1, 0, 0)
PATCHED = (1, 16, 0)
def parse_semver(version: str) -> Optional[Tuple[int, int, int]]:
if not version:
return None
v = version.strip()
v = re.sub(r'^[^0-9]+', '', v) # drop ^ ~ >= <= v etc.
m = re.match(r'^(\d+)\.(\d+)\.(\d+)', v)
if not m:
return None
return tuple(int(x) for x in m.groups())
def version_is_vulnerable(version: str) -> Optional[bool]:
sv = parse_semver(version)
if sv is None:
return None
return AFFECTED_MIN <= sv < PATCHED
def collect_from_package_lock(obj, results: List[Tuple[str, str]]):
if isinstance(obj, dict):
if obj.get('name') == 'axios' and 'version' in obj:
results.append(('lockfile', str(obj['version'])))
packages = obj.get('packages')
if isinstance(packages, dict):
for path, meta in packages.items():
if path.endswith('node_modules/axios') and isinstance(meta, dict) and 'version' in meta:
results.append((f'lockfile:{path}', str(meta['version'])))
deps = obj.get('dependencies')
if isinstance(deps, dict):
for name, meta in deps.items():
if name == 'axios' and isinstance(meta, dict) and 'version' in meta:
results.append(('lockfile:dependencies.axios', str(meta['version'])))
collect_from_package_lock(meta, results)
def collect_from_package_json(obj, results: List[Tuple[str, str]]):
if not isinstance(obj, dict):
return
for section in ('dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'):
deps = obj.get(section)
if isinstance(deps, dict) and 'axios' in deps:
results.append((f'package.json:{section}', str(deps['axios'])))
def read_json(path: str):
try:
with open(path, 'r', encoding='utf-8') as f:
return json.load(f)
except Exception:
return None
def find_versions(root: str) -> List[Tuple[str, str]]:
results: List[Tuple[str, str]] = []
for name in ('package-lock.json', 'npm-shrinkwrap.json'):
p = os.path.join(root, name)
if os.path.isfile(p):
data = read_json(p)
if data is not None:
collect_from_package_lock(data, results)
p = os.path.join(root, 'package.json')
if os.path.isfile(p):
data = read_json(p)
if data is not None:
collect_from_package_json(data, results)
p = os.path.join(root, 'node_modules', 'axios', 'package.json')
if os.path.isfile(p):
data = read_json(p)
if isinstance(data, dict) and 'version' in data:
results.append(('node_modules/axios/package.json', str(data['version'])))
# Deduplicate while preserving order
seen = set()
deduped = []
for item in results:
if item not in seen:
deduped.append(item)
seen.add(item)
return deduped
def main():
if len(sys.argv) != 2:
print('UNKNOWN - usage: python3 check_axios_cve_2026_44494.py /path/to/app')
sys.exit(3)
root = sys.argv[1]
if not os.path.isdir(root):
print(f'UNKNOWN - path not found or not a directory: {root}')
sys.exit(3)
findings = find_versions(root)
if not findings:
print('UNKNOWN - axios version not found in package-lock.json, npm-shrinkwrap.json, package.json, or node_modules')
sys.exit(2)
vulnerable = []
patched = []
unknown = []
for source, version in findings:
verdict = version_is_vulnerable(version)
if verdict is True:
vulnerable.append((source, version))
elif verdict is False:
patched.append((source, version))
else:
unknown.append((source, version))
if vulnerable:
details = '; '.join([f'{src}={ver}' for src, ver in vulnerable])
print(f'VULNERABLE - axios version(s) below 1.16.0 detected: {details}')
sys.exit(1)
if patched:
details = '; '.join([f'{src}={ver}' for src, ver in patched])
if unknown:
details += ' | unresolved ranges: ' + '; '.join([f'{src}={ver}' for src, ver in unknown])
print(f'PATCHED - no installed axios version below 1.16.0 found: {details}')
sys.exit(0)
details = '; '.join([f'{src}={ver}' for src, ver in unknown])
print(f'UNKNOWN - axios referenced but version range could not be resolved automatically: {details}')
sys.exit(2)
if __name__ == '__main__':
main()
If you remember one thing.
axios <1.16.0, prioritize Node services that both accept attacker-controlled input and make high-value outbound API calls, and document that there is no noisgate mitigation SLA — go straight to the 365-day remediation window for this MEDIUM finding; the noisgate remediation SLA is ≤365 days for the actual upgrade to 1.16.0+. If you discover a reachable prototype-pollution source in the same service, reclassify that application locally and pull it forward.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.