This is a burglar using an unlocked door inside the building, not kicking in the front gate
CVE-2026-44495 is an Axios *prototype-pollution gadget*, not a standalone entry point. In affected versions >=1.0.0,<1.15.2 and >=0.19.0,<0.31.1, Axios reads configuration through normal property access during mergeConfig(), so a pre-polluted Object.prototype.transformResponse can be treated as request config and later executed during response handling. In the full exploitation path, that function can read request URL, headers, and auth credentials and can alter the response seen by application code.
The vendor's HIGH 7.0 score is technically defensible in a vacuum but too generous for enterprise patch triage. The decisive friction is that Axios does not create the pollution itself; the attacker first needs a separate prototype-pollution bug or equivalent ability to control Object.prototype in the same runtime, and many real-world PP primitives only write JSON-like values rather than executable functions. That turns this from a broad unauthenticated remote event into a chained, context-dependent post-bug amplifier.
4 steps from start to impact.
Land a helper bug that pollutes Object.prototype
qs, lodash, or similar gadget feeder named by the advisory. Without that upstream foothold, Axios is inert here. This is the single biggest severity reducer.- A reachable prototype-pollution vulnerability elsewhere in the app or dependency tree
- Attacker input reaches that vulnerable component before Axios builds a request
- Axios runs in the same JS runtime as the polluted object graph
- Requires a second vulnerability or equivalent code execution/control path first
- Many PP bugs only allow JSON-like values, not function objects
- Modern SAST/SCA often flags common PP feeder libraries before this chain is viable
Seed the Axios gadget with transformResponse
transformResponse value where Axios will inherit it during config merge. In fully affected versions, Axios can replace its default response transform with the polluted function and execute it with the request config bound as this. That exposes headers, URL, and any auth object passed to Axios.- Affected Axios version
- Polluted key is relevant to Axios config, especially
transformResponse - Application does not override that key with a safe own property
- If the PP primitive cannot inject a function, full credential theft usually collapses into request failure/DoS
- Some app patterns always set explicit transforms, narrowing reachable cases
- Null-prototype wrappers or defensive cloning in app code can break inheritance
Trigger an Axios request and harvest secrets
transformData() executes the polluted transform, it can read credentials from this.auth, inspect headers and response data, and return tampered content to the caller. The GHSA PoC shows both credential capture and response hijack behavior.- The app actually uses Axios in the compromised process
- Target requests carry useful secrets or trusted downstream data
- The request path reaches response transformation
- Apps that avoid inline basic auth and keep secrets out of Axios config reduce impact
- Short-lived tokens and service-mesh identity shrink credential value
- Some requests may carry no sensitive headers or auth at all
Abuse trust in the modified response
- Application logic trusts Axios response content without secondary validation
- Attacker-controlled transform returns a value compatible with call-site expectations
- Tampering is constrained by app behavior and type expectations
- This does not directly yield OS-level RCE by itself
- Blast radius stays inside the already-compromised app/runtime
The supporting signals.
| In-the-wild status | No public evidence of active exploitation found in the reviewed sources, and not listed in CISA KEV at review time. |
|---|---|
| Proof-of-concept availability | Public PoC is embedded in the GitHub advisory and mirrored in OSV; weaponization effort is low once the helper prototype-pollution foothold already exists. |
| EPSS | No EPSS value was surfaced in the GitHub advisory at review time; treat exploit-probability telemetry as not yet mature / not surfaced for this newly published CVE. |
| KEV status | No; no KEV entry found for CVE-2026-44495 as of 2026-06-03. |
| CVSS vector and what it means | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:L/A:L correctly captures the chain complexity better than the advisory's internal 9.4 thought experiment. The important bit is AC:H: exploitation depends on a separate upstream condition. |
| Affected versions | axios >=1.0.0,<1.15.2 and >=0.19.0,<0.31.1 per GHSA. |
| Fixed versions | Upgrade to 1.15.2+ on the v1 line or 0.31.1+ on the v0 line. The release notes say the fix hardens resolveConfig/mergeConfig/validator paths to read only own properties and use null-prototype config objects. |
| Exposure / scanning reality | This is a library issue, not an internet-facing daemon, so Shodan/Censys/FOFA/GreyNoise are poor prioritization inputs here. Your real exposure comes from SBOMs, lockfiles, container layers, and node_modules across CI artifacts and app images. |
| Disclosure date | 2026-05-29 in the GitHub Advisory Database. |
| Reporter / source | Published by jasonsaayman to axios/axios; the advisory timeline says discovery occurred during a source code audit on 2026-04-15. |
noisgate verdict.
This lands in MEDIUM because the exploit chain starts with a prerequisite that already implies compromise: a separate prototype-pollution bug or equivalent attacker control over Object.prototype in the same JavaScript runtime. Axios is a powerful amplifier once that happens, but it is not a clean front-door remote exploit and its reachable population narrows further when the helper bug cannot inject function values.
Why this verdict
- Downward pressure: requires a prior bug — the attacker needs a separate prototype-pollution foothold or equivalent runtime control before Axios is even in play.
- Downward pressure: same-process requirement — this is not internet-reachable by itself; the helper bug and Axios must coexist in the same application runtime.
- Downward pressure: many PP primitives are data-only — if the feeder bug can only set JSON-like values, impact often degrades to request breakage or DoS instead of credential theft.
- Upward pressure: wide deployment — Axios is everywhere, so once the chain is viable the reachable population inside your software estate can be large.
- Upward pressure: high-value data exposure — when exploited successfully, request URLs, headers, and
authcredentials can be exposed across normal application traffic.
Why not higher?
Because this is fundamentally a chained issue. It assumes either another exploitable vulnerability or equivalent attacker control exists first, and that control must exist in the same JavaScript runtime before Axios merges config. That sharply narrows exploitability compared with true unauthenticated remote RCE/SSRF cases.
Why not lower?
Because once the chain is satisfied, the impact is not cosmetic. The advisory PoC shows access to request credentials and response data, and Axios is common enough that a single vulnerable package can sit inside many critical services. For exposed Node services with rich service-to-service credentials, the blast radius can be meaningful even if the entry condition is constrained.
What to do — in priority order.
- Kill upstream prototype-pollution feeders — Prioritize remediation of any reachable PP vulnerabilities in parsers, merge utilities, or request-processing libraries that run in the same process as Axios. This is the most effective compensating control because it removes the prerequisite the chain depends on; for a MEDIUM verdict there is no mitigation SLA, so use this as practical risk reduction while you work toward patching within the 365-day remediation window.
- Stop putting durable secrets in Axios config — Move away from static
authobjects and long-lived API credentials in Axios request config where feasible; prefer short-lived tokens from workload identity or service mesh auth. That does not fix the bug, but it strips value from the data the polluted transform can read; for MEDIUM, there is no mitigation SLA and this is best treated as architecture hardening while patching proceeds. - Inventory lockfiles and images — Use SBOMs,
package-lock.json,pnpm-lock.yaml,yarn.lock, and container image scans to find transitive Axios usage across services rather than relying on asset scanners. This matters because library exposure is invisible on the network; for MEDIUM, there is no mitigation SLA — go straight to remediation planning within 365 days. - Harden runtime against prototype abuse — Where app compatibility allows, freeze sensitive prototypes, use null-prototype objects for config-like structures, and add tests that fail on inherited config keys. This is defensive engineering rather than a substitute for the vendor fix, and under a MEDIUM verdict it should be scheduled as part of the normal remediation window.
- A WAF does not solve this by itself; the exploit depends on an in-process JavaScript object state, not just a recognisable HTTP signature.
- Network vuln scanning does not meaningfully detect this; Axios is a package inside apps and containers, not a listening service with a banner.
- MFA is irrelevant here; there is no login boundary in the Axios gadget path.
- Blocking only outbound traffic is incomplete; it may stop exfiltration to the internet, but it does not stop response tampering or theft of internal service credentials used in-process.
Crowdsourced verification payload.
Run this on a build worker, source checkout, or application host that has the project files available. Invoke it with python3 check_axios_cve_2026_44495.py /path/to/app and no elevated privileges are required; it inspects package-lock.json, npm-shrinkwrap.json, and node_modules/axios/package.json and prints VULNERABLE, PATCHED, or UNKNOWN.
#!/usr/bin/env python3
# check_axios_cve_2026_44495.py
# Detect vulnerable axios versions for CVE-2026-44495.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN, 3=usage/error
import json
import os
import sys
from typing import List, Optional, Tuple
AFFECTED_RANGES = [
((1, 0, 0), (1, 15, 2)), # >=1.0.0, <1.15.2
((0, 19, 0), (0, 31, 1)), # >=0.19.0, <0.31.1
]
def parse_ver(v: str) -> Optional[Tuple[int, int, int]]:
if not v:
return None
v = v.strip()
if v.startswith('v'):
v = v[1:]
core = v.split('-')[0].split('+')[0]
parts = core.split('.')
nums = []
for p in parts:
digits = ''
for ch in p:
if ch.isdigit():
digits += ch
else:
break
if digits == '':
nums.append(0)
else:
nums.append(int(digits))
while len(nums) < 3:
nums.append(0)
return tuple(nums[:3])
def is_affected(v: str) -> Optional[bool]:
pv = parse_ver(v)
if pv is None:
return None
for low, high in AFFECTED_RANGES:
if pv >= low and pv < high:
return True
return False
def walk_deps(dep_obj, found: List[Tuple[str, str, str]], source: str, path: str = ''):
if not isinstance(dep_obj, dict):
return
for name, meta in dep_obj.items():
if not isinstance(meta, dict):
continue
ver = meta.get('version')
current_path = f"{path}>{name}" if path else name
if name == 'axios' and isinstance(ver, str):
found.append((source, current_path, ver))
nested = meta.get('dependencies')
if isinstance(nested, dict):
walk_deps(nested, found, source, current_path)
def inspect_package_lock(lock_path: str, found: List[Tuple[str, str, str]]):
try:
with open(lock_path, 'r', encoding='utf-8') as f:
data = json.load(f)
except Exception:
return
# npm lockfile v2/v3
packages = data.get('packages')
if isinstance(packages, dict):
for pkg_path, meta in packages.items():
if not isinstance(meta, dict):
continue
if pkg_path.endswith('node_modules/axios'):
ver = meta.get('version')
if isinstance(ver, str):
found.append((lock_path, pkg_path, ver))
# npm lockfile v1 style
deps = data.get('dependencies')
if isinstance(deps, dict):
walk_deps(deps, found, lock_path)
def inspect_node_modules(base: str, found: List[Tuple[str, str, str]]):
pkg_json = os.path.join(base, 'node_modules', 'axios', 'package.json')
if not os.path.isfile(pkg_json):
return
try:
with open(pkg_json, 'r', encoding='utf-8') as f:
data = json.load(f)
ver = data.get('version')
if isinstance(ver, str):
found.append((pkg_json, 'node_modules/axios', ver))
except Exception:
return
def main():
if len(sys.argv) != 2:
print('UNKNOWN - usage: python3 check_axios_cve_2026_44495.py /path/to/app')
sys.exit(3)
target = os.path.abspath(sys.argv[1])
if not os.path.exists(target):
print(f'UNKNOWN - path does not exist: {target}')
sys.exit(3)
found: List[Tuple[str, str, str]] = []
if os.path.isfile(target):
name = os.path.basename(target)
if name in ('package-lock.json', 'npm-shrinkwrap.json'):
inspect_package_lock(target, found)
else:
print('UNKNOWN - provide an app directory or package-lock.json/npm-shrinkwrap.json')
sys.exit(2)
else:
for root, dirs, files in os.walk(target):
# keep traversal bounded-ish
if '.git' in dirs:
dirs.remove('.git')
if name := next((x for x in ('package-lock.json', 'npm-shrinkwrap.json') if x in files), None):
inspect_package_lock(os.path.join(root, name), found)
inspect_node_modules(root, found)
# de-duplicate
uniq = []
seen = set()
for item in found:
if item not in seen:
uniq.append(item)
seen.add(item)
if not uniq:
print('UNKNOWN - no axios installation or supported npm lockfile evidence found')
sys.exit(2)
vulnerable = []
patched = []
unknown = []
for source, dep_path, ver in uniq:
affected = is_affected(ver)
rec = f'{ver} [{dep_path}] ({source})'
if affected is True:
vulnerable.append(rec)
elif affected is False:
patched.append(rec)
else:
unknown.append(rec)
if vulnerable:
print('VULNERABLE - CVE-2026-44495 affected axios versions detected:')
for r in vulnerable:
print(f' - {r}')
if patched:
print('Also found patched versions:')
for r in patched:
print(f' - {r}')
sys.exit(1)
if patched and not unknown:
print('PATCHED - only fixed axios versions detected:')
for r in patched:
print(f' - {r}')
sys.exit(0)
print('UNKNOWN - no vulnerable versions detected, but some evidence was inconclusive:')
for r in patched:
print(f' - patched: {r}')
for r in unknown:
print(f' - unknown: {r}')
sys.exit(2)
if __name__ == '__main__':
main()
If you remember one thing.
1.15.2+ or 0.31.1+) deployed within 365 days through normal dependency update waves, with earlier pull-forward for internet-facing Node services or apps already carrying PP-prone parser libraries.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.