VYPR
High severityNVD Advisory· Published Sep 12, 2025· Updated Jan 16, 2026

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

CVE-2025-58754

Description

Axios is a promise based HTTP client for the browser and Node.js. When Axios starting in version 0.28.0 and prior to versions 0.30.2 and 1.12.0 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'. Versions 0.30.2 and 1.12.0 contain a patch for the issue.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
axiosnpm
>= 1.0.0, < 1.12.01.12.0
axiosnpm
>= 0.28.0, < 0.30.20.30.2

Affected products

1

Patches

3
a1b1d3f073a9

fix: backport `maxContentLength` vulnerability fix to v0.x (#7034)

https://github.com/axios/axiosFelix BernhardSep 16, 2025via ghsa
3 files changed · +122 0
  • lib/adapters/http.js+16 0 modified
    @@ -18,6 +18,7 @@ var CanceledError = require('../cancel/CanceledError');
     var platform = require('../platform');
     var fromDataURI = require('../helpers/fromDataURI');
     var stream = require('stream');
    +var estimateDataURLDecodedBytes = require('../helpers/estimateDataURLDecodedBytes.js');
     
     var isHttps = /https:?/;
     
    @@ -114,6 +115,21 @@ module.exports = function httpAdapter(config) {
         var protocol = parsed.protocol || supportedProtocols[0];
     
         if (protocol === 'data:') {
    +      // Apply the same semantics as HTTP: only enforce if a finite, non-negative cap is set.
    +      if (config.maxContentLength > -1) {
    +        // Use the exact string passed to fromDataURI (config.url); fall back to fullPath if needed.
    +        var dataUrl = String(config.url || fullPath || '');
    +        var estimated = estimateDataURLDecodedBytes(dataUrl);
    +
    +        if (estimated > config.maxContentLength) {
    +          return reject(new AxiosError(
    +            'maxContentLength size of ' + config.maxContentLength + ' exceeded',
    +            AxiosError.ERR_BAD_RESPONSE,
    +            config
    +          ));
    +        }
    +      }
    +
           var convertedData;
     
           if (method !== 'GET') {
    
  • lib/helpers/estimateDataURLDecodedBytes.js+76 0 added
    @@ -0,0 +1,76 @@
    +/**
    + * Estimate decoded byte length of a data:// URL *without* allocating large buffers.
    + * - For base64: compute exact decoded size using length and padding;
    + *               handle %XX at the character-count level (no string allocation).
    + * - For non-base64: use UTF-8 byteLength of the encoded body as a safe upper bound.
    + *
    + * @param {string} url
    + * @returns {number}
    + */
    +function estimateDataURLDecodedBytes(url) {
    +  if (!url || typeof url !== 'string') return 0;
    +  if (!url.startsWith('data:')) return 0;
    +
    +  var comma = url.indexOf(',');
    +  if (comma < 0) return 0;
    +
    +  var meta = url.slice(5, comma);
    +  var body = url.slice(comma + 1);
    +  var isBase64 = /;base64/i.test(meta);
    +
    +  if (isBase64) {
    +    var effectiveLen = body.length;
    +    var len = body.length; // cache length
    +
    +    for (var i = 0; i < len; i++) {
    +      if (body.charCodeAt(i) === 37 /* '%' */ && i + 2 < len) {
    +        var a = body.charCodeAt(i + 1);
    +        var b = body.charCodeAt(i + 2);
    +        var isHex =
    +          ((a >= 48 && a <= 57) || (a >= 65 && a <= 70) || (a >= 97 && a <= 102)) &&
    +          ((b >= 48 && b <= 57) || (b >= 65 && b <= 70) || (b >= 97 && b <= 102));
    +
    +        if (isHex) {
    +          effectiveLen -= 2;
    +          i += 2;
    +        }
    +      }
    +    }
    +
    +    var pad = 0;
    +    var idx = len - 1;
    +
    +    function tailIsPct3D(j) {
    +      return j >= 2 &&
    +        body.charCodeAt(j - 2) === 37 && // '%'
    +        body.charCodeAt(j - 1) === 51 && // '3'
    +        (body.charCodeAt(j) === 68 || body.charCodeAt(j) === 100); // 'D' or 'd'
    +    }
    +
    +    if (idx >= 0) {
    +      if (body.charCodeAt(idx) === 61 /* '=' */) {
    +        pad++;
    +        idx--;
    +      } else if (tailIsPct3D(idx)) {
    +        pad++;
    +        idx -= 3;
    +      }
    +    }
    +
    +    if (pad === 1 && idx >= 0) {
    +      if (body.charCodeAt(idx) === 61 /* '=' */) {
    +        pad++;
    +      } else if (tailIsPct3D(idx)) {
    +        pad++;
    +      }
    +    }
    +
    +    var groups = Math.floor(effectiveLen / 4);
    +    var bytes = groups * 3 - (pad || 0);
    +    return bytes > 0 ? bytes : 0;
    +  }
    +
    +  return Buffer.byteLength(body, 'utf8');
    +}
    +
    +module.exports = estimateDataURLDecodedBytes;
    
  • test/unit/helpers/estimateDataURLDecodedBytes.spec.js+30 0 added
    @@ -0,0 +1,30 @@
    +var assert = require('assert');
    +var estimateDataURLDecodedBytes = require('../../../lib/helpers/estimateDataURLDecodedBytes');
    +
    +describe('estimateDataURLDecodedBytes', () => {
    +  it('should return 0 for non-data URLs', () => {
    +    assert.strictEqual(estimateDataURLDecodedBytes('http://example.com'), 0);
    +  });
    +
    +  it('should calculate length for simple non-base64 data URL', () => {
    +    const url = 'data:,Hello';
    +    assert.strictEqual(estimateDataURLDecodedBytes(url), Buffer.byteLength('Hello', 'utf8'));
    +  });
    +
    +  it('should calculate decoded length for base64 data URL', () => {
    +    const str = 'Hello';
    +    const b64 = Buffer.from(str, 'utf8').toString('base64');
    +    const url = `data:text/plain;base64,${b64}`;
    +    assert.strictEqual(estimateDataURLDecodedBytes(url), str.length);
    +  });
    +
    +  it('should handle base64 with = padding', () => {
    +    const url = 'data:text/plain;base64,TQ=='; // "M"
    +    assert.strictEqual(estimateDataURLDecodedBytes(url), 1);
    +  });
    +
    +  it('should handle base64 with %3D padding', () => {
    +    const url = 'data:text/plain;base64,TQ%3D%3D'; // "M"
    +    assert.strictEqual(estimateDataURLDecodedBytes(url), 1);
    +  });
    +});
    
945435fc5146

fix(node): enforce maxContentLength for data: URLs (#7011)

https://github.com/axios/axiosAmeer AssadiSep 10, 2025via ghsa
4 files changed · +123 0
  • lib/adapters/http.js+18 0 modified
    @@ -25,6 +25,7 @@ import readBlob from "../helpers/readBlob.js";
     import ZlibHeaderTransformStream from '../helpers/ZlibHeaderTransformStream.js';
     import callbackify from "../helpers/callbackify.js";
     import {progressEventReducer, progressEventDecorator, asyncDecorator} from "../helpers/progressEventReducer.js";
    +import estimateDataURLDecodedBytes from '../helpers/estimateDataURLDecodedBytes.js';
     
     const zlibOptions = {
       flush: zlib.constants.Z_SYNC_FLUSH,
    @@ -46,6 +47,7 @@ const supportedProtocols = platform.protocols.map(protocol => {
       return protocol + ':';
     });
     
    +
     const flushOnFinish = (stream, [throttled, flush]) => {
       stream
         .on('end', flush)
    @@ -54,6 +56,7 @@ const flushOnFinish = (stream, [throttled, flush]) => {
       return throttled;
     }
     
    +
     /**
      * If the proxy or config beforeRedirects functions are defined, call them with the options
      * object.
    @@ -233,6 +236,21 @@ export default isHttpAdapterSupported && function httpAdapter(config) {
         const protocol = parsed.protocol || supportedProtocols[0];
     
         if (protocol === 'data:') {
    +      // Apply the same semantics as HTTP: only enforce if a finite, non-negative cap is set.
    +      if (config.maxContentLength > -1) {
    +        // Use the exact string passed to fromDataURI (config.url); fall back to fullPath if needed.
    +        const dataUrl = String(config.url || fullPath || '');
    +        const estimated = estimateDataURLDecodedBytes(dataUrl);
    +
    +        if (estimated > config.maxContentLength) {
    +          return reject(new AxiosError(
    +            'maxContentLength size of ' + config.maxContentLength + ' exceeded',
    +            AxiosError.ERR_BAD_RESPONSE,
    +            config
    +          ));
    +        }
    +      }
    +
           let convertedData;
     
           if (method !== 'GET') {
    
  • lib/helpers/estimateDataURLDecodedBytes.js+73 0 added
    @@ -0,0 +1,73 @@
    +/**
    + * Estimate decoded byte length of a data:// URL *without* allocating large buffers.
    + * - For base64: compute exact decoded size using length and padding;
    + *               handle %XX at the character-count level (no string allocation).
    + * - For non-base64: use UTF-8 byteLength of the encoded body as a safe upper bound.
    + *
    + * @param {string} url
    + * @returns {number}
    + */
    +export default function estimateDataURLDecodedBytes(url) {
    +  if (!url || typeof url !== 'string') return 0;
    +  if (!url.startsWith('data:')) return 0;
    +
    +  const comma = url.indexOf(',');
    +  if (comma < 0) return 0;
    +
    +  const meta = url.slice(5, comma);
    +  const body = url.slice(comma + 1);
    +  const isBase64 = /;base64/i.test(meta);
    +
    +  if (isBase64) {
    +    let effectiveLen = body.length;
    +    const len = body.length; // cache length
    +
    +    for (let i = 0; i < len; i++) {
    +      if (body.charCodeAt(i) === 37 /* '%' */ && i + 2 < len) {
    +        const a = body.charCodeAt(i + 1);
    +        const b = body.charCodeAt(i + 2);
    +        const isHex =
    +          ((a >= 48 && a <= 57) || (a >= 65 && a <= 70) || (a >= 97 && a <= 102)) &&
    +          ((b >= 48 && b <= 57) || (b >= 65 && b <= 70) || (b >= 97 && b <= 102));
    +
    +        if (isHex) {
    +          effectiveLen -= 2;
    +          i += 2;
    +        }
    +      }
    +    }
    +
    +    let pad = 0;
    +    let idx = len - 1;
    +
    +    const tailIsPct3D = (j) =>
    +      j >= 2 &&
    +      body.charCodeAt(j - 2) === 37 && // '%'
    +      body.charCodeAt(j - 1) === 51 && // '3'
    +      (body.charCodeAt(j) === 68 || body.charCodeAt(j) === 100); // 'D' or 'd'
    +
    +    if (idx >= 0) {
    +      if (body.charCodeAt(idx) === 61 /* '=' */) {
    +        pad++;
    +        idx--;
    +      } else if (tailIsPct3D(idx)) {
    +        pad++;
    +        idx -= 3;
    +      }
    +    }
    +
    +    if (pad === 1 && idx >= 0) {
    +      if (body.charCodeAt(idx) === 61 /* '=' */) {
    +        pad++;
    +      } else if (tailIsPct3D(idx)) {
    +        pad++;
    +      }
    +    }
    +
    +    const groups = Math.floor(effectiveLen / 4);
    +    const bytes = groups * 3 - (pad || 0);
    +    return bytes > 0 ? bytes : 0;
    +  }
    +
    +  return Buffer.byteLength(body, 'utf8');
    +}
    
  • lib/utils.js+2 0 modified
    @@ -635,6 +635,8 @@ const toFiniteNumber = (value, defaultValue) => {
       return value != null && Number.isFinite(value = +value) ? value : defaultValue;
     }
     
    +
    +
     /**
      * If the thing is a FormData object, return true, otherwise return false.
      *
    
  • test/unit/helpers/estimateDataURLDecodedBytes.spec.js+30 0 added
    @@ -0,0 +1,30 @@
    +import assert from 'assert';
    +import estimateDataURLDecodedBytes from '../../../lib/helpers/estimateDataURLDecodedBytes.js';
    +
    +describe('estimateDataURLDecodedBytes', () => {
    +  it('should return 0 for non-data URLs', () => {
    +    assert.strictEqual(estimateDataURLDecodedBytes('http://example.com'), 0);
    +  });
    +
    +  it('should calculate length for simple non-base64 data URL', () => {
    +    const url = 'data:,Hello';
    +    assert.strictEqual(estimateDataURLDecodedBytes(url), Buffer.byteLength('Hello', 'utf8'));
    +  });
    +
    +  it('should calculate decoded length for base64 data URL', () => {
    +    const str = 'Hello';
    +    const b64 = Buffer.from(str, 'utf8').toString('base64');
    +    const url = `data:text/plain;base64,${b64}`;
    +    assert.strictEqual(estimateDataURLDecodedBytes(url), str.length);
    +  });
    +
    +  it('should handle base64 with = padding', () => {
    +    const url = 'data:text/plain;base64,TQ=='; // "M"
    +    assert.strictEqual(estimateDataURLDecodedBytes(url), 1);
    +  });
    +
    +  it('should handle base64 with %3D padding', () => {
    +    const url = 'data:text/plain;base64,TQ%3D%3D'; // "M"
    +    assert.strictEqual(estimateDataURLDecodedBytes(url), 1);
    +  });
    +});
    
c30252f685e8

Added data URL support for node.js; (#4725)

https://github.com/axios/axiosDmitriy MozgovoyMay 20, 2022via ghsa
17 files changed · +226 48
  • .eslintrc.js+1 1 modified
    @@ -80,7 +80,7 @@ module.exports = {
         'no-new-wrappers': 2,            // http://eslint.org/docs/rules/no-new-wrappers
         'no-octal': 2,                   // http://eslint.org/docs/rules/no-octal
         'no-octal-escape': 2,            // http://eslint.org/docs/rules/no-octal-escape
    -    'no-param-reassign': 2,          // http://eslint.org/docs/rules/no-param-reassign
    +    'no-param-reassign': 0,          // http://eslint.org/docs/rules/no-param-reassign
         'no-proto': 2,                   // http://eslint.org/docs/rules/no-proto
         'no-redeclare': 2,               // http://eslint.org/docs/rules/no-redeclare
         'no-return-assign': 2,           // http://eslint.org/docs/rules/no-return-assign
    
  • index.d.ts+3 0 modified
    @@ -194,13 +194,16 @@ export class AxiosError<T = unknown, D = any> extends Error {
       isAxiosError: boolean;
       status?: number;
       toJSON: () => object;
    +  cause?: Error;
       static readonly ERR_FR_TOO_MANY_REDIRECTS = "ERR_FR_TOO_MANY_REDIRECTS";
       static readonly ERR_BAD_OPTION_VALUE = "ERR_BAD_OPTION_VALUE";
       static readonly ERR_BAD_OPTION = "ERR_BAD_OPTION";
       static readonly ERR_NETWORK = "ERR_NETWORK";
       static readonly ERR_DEPRECATED = "ERR_DEPRECATED";
       static readonly ERR_BAD_RESPONSE = "ERR_BAD_RESPONSE";
       static readonly ERR_BAD_REQUEST = "ERR_BAD_REQUEST";
    +  static readonly ERR_NOT_SUPPORT = "ERR_NOT_SUPPORT";
    +  static readonly ERR_INVALID_URL = "ERR_INVALID_URL";
       static readonly ERR_CANCELED = "ERR_CANCELED";
       static readonly ECONNABORTED = "ECONNABORTED";
       static readonly ETIMEDOUT = "ETIMEDOUT";
    
  • lib/adapters/http.js+76 28 modified
    @@ -15,10 +15,15 @@ var VERSION = require('./../env/data').version;
     var transitionalDefaults = require('../defaults/transitional');
     var AxiosError = require('../core/AxiosError');
     var CanceledError = require('../cancel/CanceledError');
    +var platform = require('../platform');
    +var fromDataURI = require('../helpers/fromDataURI');
    +var stream = require('stream');
     
     var isHttps = /https:?/;
     
    -var supportedProtocols = [ 'http:', 'https:', 'file:' ];
    +var supportedProtocols = platform.protocols.map(function(protocol) {
    +  return protocol + ':';
    +});
     
     function dispatchBeforeRedirect(options) {
       if (options.beforeRedirects.proxy) {
    @@ -99,6 +104,62 @@ module.exports = function httpAdapter(config) {
           rejectPromise(value);
         };
         var data = config.data;
    +    var responseType = config.responseType;
    +    var responseEncoding = config.responseEncoding;
    +    var method = config.method.toUpperCase();
    +
    +    // Parse url
    +    var fullPath = buildFullPath(config.baseURL, config.url);
    +    var parsed = url.parse(fullPath);
    +    var protocol = parsed.protocol || supportedProtocols[0];
    +
    +    if (protocol === 'data:') {
    +      var convertedData;
    +
    +      if (method !== 'GET') {
    +        return settle(resolve, reject, {
    +          status: 405,
    +          statusText: 'method not allowed',
    +          headers: {},
    +          config: config
    +        });
    +      }
    +
    +      try {
    +        convertedData = fromDataURI(config.url, responseType === 'blob', {
    +          Blob: config.env && config.env.Blob
    +        });
    +      } catch (err) {
    +        throw AxiosError.from(err, AxiosError.ERR_BAD_REQUEST, config);
    +      }
    +
    +      if (responseType === 'text') {
    +        convertedData = convertedData.toString(responseEncoding);
    +
    +        if (!responseEncoding || responseEncoding === 'utf8') {
    +          data = utils.stripBOM(convertedData);
    +        }
    +      } else if (responseType === 'stream') {
    +        convertedData = stream.Readable.from(convertedData);
    +      }
    +
    +      return settle(resolve, reject, {
    +        data: convertedData,
    +        status: 200,
    +        statusText: 'OK',
    +        headers: {},
    +        config: config
    +      });
    +    }
    +
    +    if (supportedProtocols.indexOf(protocol) === -1) {
    +      return reject(new AxiosError(
    +        'Unsupported protocol ' + protocol,
    +        AxiosError.ERR_BAD_REQUEST,
    +        config
    +      ));
    +    }
    +
         var headers = config.headers;
         var headerNames = {};
     
    @@ -159,19 +220,6 @@ module.exports = function httpAdapter(config) {
           auth = username + ':' + password;
         }
     
    -    // Parse url
    -    var fullPath = buildFullPath(config.baseURL, config.url);
    -    var parsed = url.parse(fullPath);
    -    var protocol = parsed.protocol || supportedProtocols[0];
    -
    -    if (supportedProtocols.indexOf(protocol) === -1) {
    -      return reject(new AxiosError(
    -        'Unsupported protocol ' + protocol,
    -        AxiosError.ERR_BAD_REQUEST,
    -        config
    -      ));
    -    }
    -
         if (!auth && parsed.auth) {
           var urlAuth = parsed.auth.split(':');
           var urlUsername = urlAuth[0] || '';
    @@ -195,7 +243,7 @@ module.exports = function httpAdapter(config) {
     
         var options = {
           path: buildURL(parsed.path, config.params, config.paramsSerializer).replace(/^\?/, ''),
    -      method: config.method.toUpperCase(),
    +      method: method,
           headers: headers,
           agents: { http: config.httpAgent, https: config.httpsAgent },
           auth: auth,
    @@ -242,7 +290,7 @@ module.exports = function httpAdapter(config) {
           if (req.aborted) return;
     
           // uncompress the response body transparently if required
    -      var stream = res;
    +      var responseStream = res;
     
           // return the last request in case of redirects
           var lastRequest = res.req || req;
    @@ -261,7 +309,7 @@ module.exports = function httpAdapter(config) {
             case 'compress':
             case 'deflate':
               // add the unzipper to the body stream processing pipeline
    -          stream = stream.pipe(zlib.createUnzip());
    +          responseStream = responseStream.pipe(zlib.createUnzip());
     
               // remove the content-encoding in order to not confuse downstream operations
               delete res.headers['content-encoding'];
    @@ -277,31 +325,31 @@ module.exports = function httpAdapter(config) {
             request: lastRequest
           };
     
    -      if (config.responseType === 'stream') {
    -        response.data = stream;
    +      if (responseType === 'stream') {
    +        response.data = responseStream;
             settle(resolve, reject, response);
           } else {
             var responseBuffer = [];
             var totalResponseBytes = 0;
    -        stream.on('data', function handleStreamData(chunk) {
    +        responseStream.on('data', function handleStreamData(chunk) {
               responseBuffer.push(chunk);
               totalResponseBytes += chunk.length;
     
               // make sure the content length is not over the maxContentLength if specified
               if (config.maxContentLength > -1 && totalResponseBytes > config.maxContentLength) {
                 // stream.destroy() emit aborted event before calling reject() on Node.js v16
                 rejected = true;
    -            stream.destroy();
    +            responseStream.destroy();
                 reject(new AxiosError('maxContentLength size of ' + config.maxContentLength + ' exceeded',
                   AxiosError.ERR_BAD_RESPONSE, config, lastRequest));
               }
             });
     
    -        stream.on('aborted', function handlerStreamAborted() {
    +        responseStream.on('aborted', function handlerStreamAborted() {
               if (rejected) {
                 return;
               }
    -          stream.destroy();
    +          responseStream.destroy();
               reject(new AxiosError(
                 'maxContentLength size of ' + config.maxContentLength + ' exceeded',
                 AxiosError.ERR_BAD_RESPONSE,
    @@ -310,17 +358,17 @@ module.exports = function httpAdapter(config) {
               ));
             });
     
    -        stream.on('error', function handleStreamError(err) {
    +        responseStream.on('error', function handleStreamError(err) {
               if (req.aborted) return;
               reject(AxiosError.from(err, null, config, lastRequest));
             });
     
    -        stream.on('end', function handleStreamEnd() {
    +        responseStream.on('end', function handleStreamEnd() {
               try {
                 var responseData = responseBuffer.length === 1 ? responseBuffer[0] : Buffer.concat(responseBuffer);
    -            if (config.responseType !== 'arraybuffer') {
    -              responseData = responseData.toString(config.responseEncoding);
    -              if (!config.responseEncoding || config.responseEncoding === 'utf8') {
    +            if (responseType !== 'arraybuffer') {
    +              responseData = responseData.toString(responseEncoding);
    +              if (!responseEncoding || responseEncoding === 'utf8') {
                     responseData = utils.stripBOM(responseData);
                   }
                 }
    
  • lib/adapters/xhr.js+2 1 modified
    @@ -11,6 +11,7 @@ var transitionalDefaults = require('../defaults/transitional');
     var AxiosError = require('../core/AxiosError');
     var CanceledError = require('../cancel/CanceledError');
     var parseProtocol = require('../helpers/parseProtocol');
    +var platform = require('../platform');
     
     module.exports = function xhrAdapter(config) {
       return new Promise(function dispatchXhrRequest(resolve, reject) {
    @@ -210,7 +211,7 @@ module.exports = function xhrAdapter(config) {
     
         var protocol = parseProtocol(fullPath);
     
    -    if (protocol && [ 'http', 'https', 'file', 'blob' ].indexOf(protocol) === -1) {
    +    if (protocol && platform.protocols.indexOf(protocol) === -1) {
           reject(new AxiosError('Unsupported protocol ' + protocol + ':', AxiosError.ERR_BAD_REQUEST, config));
           return;
         }
    
  • lib/core/AxiosError.js+5 1 modified
    @@ -64,7 +64,9 @@ var descriptors = {};
       'ERR_DEPRECATED',
       'ERR_BAD_RESPONSE',
       'ERR_BAD_REQUEST',
    -  'ERR_CANCELED'
    +  'ERR_CANCELED',
    +  'ERR_NOT_SUPPORT',
    +  'ERR_INVALID_URL'
     // eslint-disable-next-line func-names
     ].forEach(function(code) {
       descriptors[code] = {value: code};
    @@ -83,6 +85,8 @@ AxiosError.from = function(error, code, config, request, response, customProps)
     
       AxiosError.call(axiosError, error.message, code, config, request, response);
     
    +  axiosError.cause = error;
    +
       axiosError.name = error.name;
     
       customProps && Object.assign(axiosError, customProps);
    
  • lib/defaults/index.js+8 4 modified
    @@ -6,6 +6,7 @@ var AxiosError = require('../core/AxiosError');
     var transitionalDefaults = require('./transitional');
     var toFormData = require('../helpers/toFormData');
     var toURLEncodedForm = require('../helpers/toURLEncodedForm');
    +var platform = require('../platform');
     
     var DEFAULT_CONTENT_TYPE = {
       'Content-Type': 'application/x-www-form-urlencoded'
    @@ -101,11 +102,13 @@ var defaults = {
     
       transformResponse: [function transformResponse(data) {
         var transitional = this.transitional || defaults.transitional;
    -    var silentJSONParsing = transitional && transitional.silentJSONParsing;
         var forcedJSONParsing = transitional && transitional.forcedJSONParsing;
    -    var strictJSONParsing = !silentJSONParsing && this.responseType === 'json';
    +    var JSONRequested = this.responseType === 'json';
    +
    +    if (data && utils.isString(data) && ((forcedJSONParsing && !this.responseType) || JSONRequested)) {
    +      var silentJSONParsing = transitional && transitional.silentJSONParsing;
    +      var strictJSONParsing = !silentJSONParsing && JSONRequested;
     
    -    if (strictJSONParsing || (forcedJSONParsing && utils.isString(data) && data.length)) {
           try {
             return JSON.parse(data);
           } catch (e) {
    @@ -134,7 +137,8 @@ var defaults = {
       maxBodyLength: -1,
     
       env: {
    -    FormData: require('./env/FormData')
    +    FormData: platform.classes.FormData,
    +    Blob: platform.classes.Blob
       },
     
       validateStatus: function validateStatus(status) {
    
  • lib/env/classes/FormData.js+0 0 renamed
  • lib/helpers/fromDataURI.js+51 0 added
    @@ -0,0 +1,51 @@
    +'use strict';
    +
    +var AxiosError = require('../core/AxiosError');
    +var parseProtocol = require('./parseProtocol');
    +var platform = require('../platform');
    +
    +var DATA_URL_PATTERN = /^(?:([^;]+);)?(?:[^;]+;)?(base64|),([\s\S]*)$/;
    +
    +/**
    + * Parse data uri to a Buffer or Blob
    + * @param {String} uri
    + * @param {?Boolean} asBlob
    + * @param {?Object} options
    + * @param {?Function} options.Blob
    + * @returns {Buffer|Blob}
    + */
    +module.exports = function fromDataURI(uri, asBlob, options) {
    +  var _Blob = options && options.Blob || platform.classes.Blob;
    +  var protocol = parseProtocol(uri);
    +
    +  if (asBlob === undefined && _Blob) {
    +    asBlob = true;
    +  }
    +
    +  if (protocol === 'data') {
    +    uri = uri.slice(protocol.length);
    +
    +    var match = DATA_URL_PATTERN.exec(uri);
    +
    +    if (!match) {
    +      throw new AxiosError('Invalid URL', AxiosError.ERR_INVALID_URL);
    +    }
    +
    +    var mime = match[1];
    +    var isBase64 = match[2];
    +    var body = match[3];
    +    var buffer = Buffer.from(decodeURIComponent(body), isBase64 ? 'base64' : 'utf8');
    +
    +    if (asBlob) {
    +      if (!_Blob) {
    +        throw new AxiosError('Blob is not supported', AxiosError.ERR_NOT_SUPPORT);
    +      }
    +
    +      return new _Blob([buffer], {type: mime});
    +    }
    +
    +    return buffer;
    +  }
    +
    +  throw new AxiosError('Unsupported protocol ' + protocol, AxiosError.ERR_NOT_SUPPORT);
    +};
    
  • lib/helpers/toFormData.js+1 1 modified
    @@ -1,7 +1,7 @@
     'use strict';
     
     var utils = require('../utils');
    -var envFormData = require('../defaults/env/FormData');
    +var envFormData = require('../env/classes/FormData');
     
     function isVisitable(thing) {
       return utils.isPlainObject(thing) || utils.isArray(thing);
    
  • lib/platform/browser/classes/FormData.js+3 0 added
    @@ -0,0 +1,3 @@
    +'use strict';
    +
    +module.exports = FormData;
    
  • lib/platform/browser/index.js+5 2 modified
    @@ -3,6 +3,9 @@
     module.exports = {
       isBrowser: true,
       classes: {
    -    URLSearchParams: require('./classes/URLSearchParams')
    -  }
    +    URLSearchParams: require('./classes/URLSearchParams'),
    +    FormData: require('./classes/FormData'),
    +    Blob: Blob
    +  },
    +  protocols: ['http', 'https', 'file', 'blob', 'url']
     };
    
  • lib/platform/node/classes/FormData.js+3 0 added
    @@ -0,0 +1,3 @@
    +'use strict';
    +
    +module.exports = require('form-data');
    
  • lib/platform/node/index.js+5 2 modified
    @@ -3,6 +3,9 @@
     module.exports = {
       isNode: true,
       classes: {
    -    URLSearchParams: require('./classes/URLSearchParams')
    -  }
    +    URLSearchParams: require('./classes/URLSearchParams'),
    +    FormData: require('./classes/FormData'),
    +    Blob: typeof Blob !== 'undefined' && Blob || null
    +  },
    +  protocols: [ 'http', 'https', 'file', 'data' ]
     };
    
  • lib/utils.js+8 7 modified
    @@ -229,15 +229,16 @@ function trim(str) {
      *  navigator.product -> 'NativeScript' or 'NS'
      */
     function isStandardBrowserEnv() {
    -  if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' ||
    -                                           navigator.product === 'NativeScript' ||
    -                                           navigator.product === 'NS')) {
    +  var product;
    +  if (typeof navigator !== 'undefined' && (
    +    (product = navigator.product) === 'ReactNative' ||
    +    product === 'NativeScript' ||
    +    product === 'NS')
    +  ) {
         return false;
       }
    -  return (
    -    typeof window !== 'undefined' &&
    -    typeof document !== 'undefined'
    -  );
    +
    +  return typeof window !== 'undefined' && typeof document !== 'undefined';
     }
     
     /**
    
  • package.json+0 1 modified
    @@ -80,7 +80,6 @@
       },
       "browser": {
         "./lib/adapters/http.js": "./lib/adapters/xhr.js",
    -    "./lib/defaults/env/FormData.js": "./lib/helpers/null.js",
         "./lib/platform/node/index.js": "./lib/platform/browser/index.js"
       },
       "jsdelivr": "dist/axios.min.js",
    
  • test/unit/adapters/http.js+43 0 modified
    @@ -1442,4 +1442,47 @@ describe('supports http with nodejs', function () {
             }).catch(done);
         });
       });
    +
    +  describe('Data URL', function () {
    +    it('should support requesting data URL as a Buffer', function (done) {
    +      const buffer = Buffer.from('123');
    +
    +      const dataURI = 'data:application/octet-stream;base64,' + buffer.toString('base64');
    +
    +      axios.get(dataURI).then(({data})=> {
    +        assert.deepStrictEqual(data, buffer);
    +        done();
    +      }).catch(done);
    +    });
    +
    +    it('should support requesting data URL as a String (text)', function (done) {
    +      const buffer = Buffer.from('123', 'utf-8');
    +
    +      const dataURI = 'data:application/octet-stream;base64,' + buffer.toString('base64');
    +
    +      axios.get(dataURI, {responseType: "text"}).then(({data})=> {
    +        assert.deepStrictEqual(data, '123');
    +        done();
    +      }).catch(done);
    +    });
    +
    +    it('should support requesting data URL as a Stream', function (done) {
    +      const buffer = Buffer.from('123', 'utf-8');
    +
    +      const dataURI = 'data:application/octet-stream;base64,' + buffer.toString('base64');
    +
    +      axios.get(dataURI, {responseType: "stream"}).then(({data})=> {
    +        var str = '';
    +
    +        data.on('data', function(response){
    +          str += response.toString();
    +        });
    +
    +        data.on('end', function(){
    +          assert.strictEqual(str, '123');
    +          done();
    +        });
    +      }).catch(done);
    +    });
    +  });
     });
    
  • test/unit/helpers/fromDataURI.js+12 0 added
    @@ -0,0 +1,12 @@
    +var assert = require('assert');
    +var fromDataURI = require('../../../lib/helpers/fromDataURI');
    +
    +describe('helpers::fromDataURI', function () {
    +  it('should return buffer from data uri', function () {
    +    const buffer= Buffer.from('123');
    +
    +    const dataURI = 'data:application/octet-stream;base64,' + buffer.toString('base64');
    +
    +    assert.deepStrictEqual(fromDataURI(dataURI), buffer);
    +  });
    +});
    

Vulnerability mechanics

Synthesis attempt was rejected by the grounding validator. Re-run pending.

References

10

News mentions

0

No linked articles in our index yet.