axios 0.31.0 → 0.31.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -145,8 +145,9 @@ module.exports = function httpAdapter(config) {
145
145
  }
146
146
 
147
147
  try {
148
+ var envOption = utils.hasOwnProperty(config, 'env') ? config.env : undefined;
148
149
  convertedData = fromDataURI(config.url, responseType === 'blob', {
149
- Blob: config.env && config.env.Blob
150
+ Blob: envOption && envOption.Blob
150
151
  });
151
152
  } catch (err) {
152
153
  throw AxiosError.from(err, AxiosError.ERR_BAD_REQUEST, config);
@@ -200,7 +201,8 @@ module.exports = function httpAdapter(config) {
200
201
  }
201
202
 
202
203
  // support for https://www.npmjs.com/package/form-data api
203
- if (utils.isFormData(data) && utils.isFunction(data.getHeaders)) {
204
+ if (utils.isFormData(data) && utils.isFunction(data.getHeaders) &&
205
+ data.getHeaders !== Object.prototype.getHeaders) {
204
206
  Object.assign(headers, data.getHeaders());
205
207
  } else if (data && !utils.isStream(data)) {
206
208
  if (Buffer.isBuffer(data)) {
@@ -282,7 +284,7 @@ module.exports = function httpAdapter(config) {
282
284
  var transport;
283
285
  var isHttpsRequest = isHttps.test(options.protocol);
284
286
  options.agent = isHttpsRequest ? config.httpsAgent : config.httpAgent;
285
- if (config.transport) {
287
+ if (utils.hasOwnProperty(config, 'transport') && config.transport) {
286
288
  transport = config.transport;
287
289
  } else if (config.maxRedirects === 0) {
288
290
  transport = isHttpsRequest ? https : http;
@@ -348,8 +350,47 @@ module.exports = function httpAdapter(config) {
348
350
  };
349
351
 
350
352
  if (responseType === 'stream') {
351
- response.data = responseStream;
352
- settle(resolve, reject, response);
353
+ // Enforce maxContentLength on streamed responses too (GHSA-vf2m-468p-8v99).
354
+ // Previously the stream path bypassed the size guard because the check only
355
+ // ran on the buffering branch.
356
+ if (config.maxContentLength > -1) {
357
+ var maxContentLength = config.maxContentLength;
358
+ var streamedBytes = 0;
359
+ var limiter = new stream.Transform({
360
+ transform: function transformChunk(chunk, encoding, callback) {
361
+ streamedBytes += chunk.length;
362
+ if (streamedBytes > maxContentLength) {
363
+ callback(new AxiosError(
364
+ 'maxContentLength size of ' + maxContentLength + ' exceeded',
365
+ AxiosError.ERR_BAD_RESPONSE,
366
+ config,
367
+ lastRequest
368
+ ));
369
+ return;
370
+ }
371
+ callback(null, chunk);
372
+ }
373
+ });
374
+ limiter.on('error', function handleLimiterError() {
375
+ rejected = true;
376
+ responseStream.destroy();
377
+ });
378
+ responseStream.on('error', function forwardError(err) {
379
+ limiter.destroy(err);
380
+ });
381
+ response.data = limiter;
382
+ settle(resolve, reject, response);
383
+ // Defer piping via setImmediate so the caller's `.then` (a microtask)
384
+ // has run and attached any `error`/`data` listeners before chunks flow
385
+ // through the transform. `process.nextTick` would drain before those
386
+ // microtasks and lose the error event.
387
+ setImmediate(function startPipe() {
388
+ responseStream.pipe(limiter);
389
+ });
390
+ } else {
391
+ response.data = responseStream;
392
+ settle(resolve, reject, response);
393
+ }
353
394
  } else {
354
395
  var responseBuffer = [];
355
396
  var totalResponseBytes = 0;
@@ -474,7 +515,42 @@ module.exports = function httpAdapter(config) {
474
515
  if (utils.isStream(data)) {
475
516
  data.on('error', function handleStreamError(err) {
476
517
  reject(AxiosError.from(err, config, null, req));
477
- }).pipe(req);
518
+ });
519
+
520
+ // follow-redirects enforces options.maxBodyLength for stream uploads, but the
521
+ // native http/https transport (used when maxRedirects === 0) does not.
522
+ // Count bytes ourselves so the limit is always honored (GHSA-5c9x-8gcm-mpgx).
523
+ var nativeTransport = transport === http || transport === https;
524
+ if (nativeTransport && config.maxBodyLength > -1) {
525
+ var maxBodyLength = config.maxBodyLength;
526
+ var uploadedBytes = 0;
527
+ var bodyLimiter = new stream.Transform({
528
+ transform: function transformChunk(chunk, encoding, callback) {
529
+ uploadedBytes += chunk.length;
530
+ if (uploadedBytes > maxBodyLength) {
531
+ callback(new AxiosError(
532
+ 'Request body larger than maxBodyLength limit',
533
+ AxiosError.ERR_BAD_REQUEST,
534
+ config,
535
+ req
536
+ ));
537
+ return;
538
+ }
539
+ callback(null, chunk);
540
+ }
541
+ });
542
+ bodyLimiter.on('error', function handleLimiterError(err) {
543
+ if (rejected) return;
544
+ rejected = true;
545
+ try { data.unpipe(bodyLimiter); } catch (e) { /* noop */ }
546
+ try { bodyLimiter.unpipe(req); } catch (e) { /* noop */ }
547
+ req.destroy();
548
+ reject(err);
549
+ });
550
+ data.pipe(bodyLimiter).pipe(req);
551
+ } else {
552
+ data.pipe(req);
553
+ }
478
554
  } else {
479
555
  req.end(data);
480
556
  }
@@ -18,7 +18,8 @@ module.exports = function xhrAdapter(config) {
18
18
  var requestData = config.data;
19
19
  var requestHeaders = config.headers;
20
20
  var responseType = config.responseType;
21
- var withXSRFToken = config.withXSRFToken;
21
+ // Guard against prototype pollution (GHSA-xx6v-rp6x-q39c): only honor own properties.
22
+ var withXSRFToken = utils.hasOwnProperty(config, 'withXSRFToken') ? config.withXSRFToken : undefined;
22
23
  var onCanceled;
23
24
  function done() {
24
25
  if (config.cancelToken) {
@@ -146,8 +147,11 @@ module.exports = function xhrAdapter(config) {
146
147
  // Specifically not if we're in a web worker, or react-native.
147
148
  if (utils.isStandardBrowserEnv()) {
148
149
  // Add xsrf header
149
- withXSRFToken && utils.isFunction(withXSRFToken) && (withXSRFToken = withXSRFToken(config));
150
- if (withXSRFToken || (withXSRFToken !== false && isURLSameOrigin(fullPath))) {
150
+ if (utils.isFunction(withXSRFToken)) {
151
+ withXSRFToken = withXSRFToken(config);
152
+ }
153
+ // Strict boolean check (GHSA-xx6v-rp6x-q39c): only `true` short-circuits the same-origin guard.
154
+ if (withXSRFToken === true || (withXSRFToken !== false && isURLSameOrigin(fullPath))) {
151
155
  // Add xsrf header
152
156
  var xsrfValue = config.xsrfHeaderName && config.xsrfCookieName && cookies.read(config.xsrfCookieName);
153
157
  if (xsrfValue) {
@@ -66,7 +66,8 @@ var descriptors = {};
66
66
  'ERR_BAD_REQUEST',
67
67
  'ERR_CANCELED',
68
68
  'ERR_NOT_SUPPORT',
69
- 'ERR_INVALID_URL'
69
+ 'ERR_INVALID_URL',
70
+ 'ERR_FORM_DATA_DEPTH_EXCEEDED'
70
71
  // eslint-disable-next-line func-names
71
72
  ].forEach(function(code) {
72
73
  descriptors[code] = {value: code};
@@ -13,7 +13,17 @@ var utils = require('../utils');
13
13
  module.exports = function mergeConfig(config1, config2) {
14
14
  // eslint-disable-next-line no-param-reassign
15
15
  config2 = config2 || {};
16
- var config = {};
16
+ // Use a null-prototype object so a polluted Object.prototype cannot leak
17
+ // values (e.g. transport, adapter) into the returned config via inheritance.
18
+ var config = Object.create(null);
19
+
20
+ function getOwn(source, prop) {
21
+ return utils.hasOwnProperty(source, prop) ? source[prop] : undefined;
22
+ }
23
+
24
+ function hasOwn(source, prop) {
25
+ return utils.hasOwnProperty(source, prop);
26
+ }
17
27
 
18
28
  function getMergedValue(target, source) {
19
29
  if (utils.isPlainObject(target) && utils.isPlainObject(source)) {
@@ -30,34 +40,34 @@ module.exports = function mergeConfig(config1, config2) {
30
40
 
31
41
  // eslint-disable-next-line consistent-return
32
42
  function mergeDeepProperties(prop) {
33
- if (!utils.isUndefined(config2[prop])) {
34
- return getMergedValue(config1[prop], config2[prop]);
35
- } else if (!utils.isUndefined(config1[prop])) {
43
+ if (hasOwn(config2, prop) && !utils.isUndefined(config2[prop])) {
44
+ return getMergedValue(getOwn(config1, prop), config2[prop]);
45
+ } else if (hasOwn(config1, prop) && !utils.isUndefined(config1[prop])) {
36
46
  return getMergedValue(undefined, config1[prop]);
37
47
  }
38
48
  }
39
49
 
40
50
  // eslint-disable-next-line consistent-return
41
51
  function valueFromConfig2(prop) {
42
- if (!utils.isUndefined(config2[prop])) {
52
+ if (hasOwn(config2, prop) && !utils.isUndefined(config2[prop])) {
43
53
  return getMergedValue(undefined, config2[prop]);
44
54
  }
45
55
  }
46
56
 
47
57
  // eslint-disable-next-line consistent-return
48
58
  function defaultToConfig2(prop) {
49
- if (!utils.isUndefined(config2[prop])) {
59
+ if (hasOwn(config2, prop) && !utils.isUndefined(config2[prop])) {
50
60
  return getMergedValue(undefined, config2[prop]);
51
- } else if (!utils.isUndefined(config1[prop])) {
61
+ } else if (hasOwn(config1, prop) && !utils.isUndefined(config1[prop])) {
52
62
  return getMergedValue(undefined, config1[prop]);
53
63
  }
54
64
  }
55
65
 
56
66
  // eslint-disable-next-line consistent-return
57
67
  function mergeDirectKeys(prop) {
58
- if (prop in config2) {
59
- return getMergedValue(config1[prop], config2[prop]);
60
- } else if (prop in config1) {
68
+ if (hasOwn(config2, prop)) {
69
+ return getMergedValue(getOwn(config1, prop), config2[prop]);
70
+ } else if (hasOwn(config1, prop)) {
61
71
  return getMergedValue(undefined, config1[prop]);
62
72
  }
63
73
  }
@@ -89,17 +89,20 @@ var defaults = {
89
89
  var isFileList;
90
90
 
91
91
  if (isObjectPayload) {
92
+ var formSerializer = utils.hasOwnProperty(this, 'formSerializer') ? this.formSerializer : undefined;
93
+ var envOption = utils.hasOwnProperty(this, 'env') ? this.env : undefined;
94
+
92
95
  if (contentType.indexOf('application/x-www-form-urlencoded') !== -1) {
93
- return toURLEncodedForm(data, this.formSerializer).toString();
96
+ return toURLEncodedForm(data, formSerializer).toString();
94
97
  }
95
98
 
96
99
  if ((isFileList = utils.isFileList(data)) || contentType.indexOf('multipart/form-data') > -1) {
97
- var _FormData = this.env && this.env.FormData;
100
+ var _FormData = envOption && envOption.FormData;
98
101
 
99
102
  return toFormData(
100
103
  isFileList ? {'files[]': data} : data,
101
104
  _FormData && new _FormData(),
102
- this.formSerializer
105
+ formSerializer
103
106
  );
104
107
  }
105
108
  }
package/lib/env/data.js CHANGED
@@ -1,3 +1,3 @@
1
1
  module.exports = {
2
- "version": "0.31.0"
2
+ "version": "0.31.1"
3
3
  };
@@ -3,16 +3,17 @@
3
3
  var toFormData = require('./toFormData');
4
4
 
5
5
  function encode(str) {
6
+ // Do not map `%00` back to a raw null byte (GHSA-xhjh-pmcv-23jw): that reversed
7
+ // the safe percent-encoding from encodeURIComponent and enabled null byte injection.
6
8
  var charMap = {
7
9
  '!': '%21',
8
10
  "'": '%27',
9
11
  '(': '%28',
10
12
  ')': '%29',
11
13
  '~': '%7E',
12
- '%20': '+',
13
- '%00': '\x00'
14
+ '%20': '+'
14
15
  };
15
- return encodeURIComponent(str).replace(/[!'\(\)~]|%20|%00/g, function replacer(match) {
16
+ return encodeURIComponent(str).replace(/[!'\(\)~]|%20/g, function replacer(match) {
16
17
  return charMap[match];
17
18
  });
18
19
  }
@@ -69,6 +69,7 @@ function toFormData(obj, formData, options) {
69
69
  var dots = options.dots;
70
70
  var indexes = options.indexes;
71
71
  var _Blob = options.Blob || typeof Blob !== 'undefined' && Blob;
72
+ var maxDepth = options.maxDepth === undefined ? 100 : options.maxDepth;
72
73
  var useBlob = _Blob && isSpecCompliant(formData);
73
74
 
74
75
  if (!utils.isFunction(visitor)) {
@@ -145,9 +146,19 @@ function toFormData(obj, formData, options) {
145
146
  isVisitable: isVisitable
146
147
  });
147
148
 
148
- function build(value, path) {
149
+ function build(value, path, depth) {
149
150
  if (utils.isUndefined(value)) return;
150
151
 
152
+ // eslint-disable-next-line no-param-reassign
153
+ depth = depth || 0;
154
+
155
+ if (depth > maxDepth) {
156
+ throw new AxiosError(
157
+ 'Maximum object depth of ' + maxDepth + ' exceeded (got ' + depth + ' levels)',
158
+ AxiosError.ERR_FORM_DATA_DEPTH_EXCEEDED
159
+ );
160
+ }
161
+
151
162
  if (stack.indexOf(value) !== -1) {
152
163
  throw Error('Circular reference detected in ' + path.join('.'));
153
164
  }
@@ -160,7 +171,7 @@ function toFormData(obj, formData, options) {
160
171
  );
161
172
 
162
173
  if (result === true) {
163
- build(el, path ? path.concat(key) : [key]);
174
+ build(el, path ? path.concat(key) : [key], depth + 1);
164
175
  }
165
176
  });
166
177
 
@@ -171,7 +182,7 @@ function toFormData(obj, formData, options) {
171
182
  throw new TypeError('data must be an object');
172
183
  }
173
184
 
174
- build(obj);
185
+ build(obj, null, 0);
175
186
 
176
187
  return formData;
177
188
  }
package/lib/utils.js CHANGED
@@ -206,11 +206,17 @@ function isStream(val) {
206
206
  */
207
207
  function isFormData(thing) {
208
208
  var pattern = '[object FormData]';
209
- return thing && (
210
- (typeof FormData === 'function' && thing instanceof FormData) ||
211
- toString.call(thing) === pattern ||
212
- (isFunction(thing.toString) && thing.toString() === pattern)
213
- );
209
+ if (!thing) return false;
210
+ if (typeof FormData === 'function' && thing instanceof FormData) return true;
211
+ // Reject non-objects (strings, numbers, booleans) up front — Object.getPrototypeOf
212
+ // throws a TypeError on primitives in ES5 environments.
213
+ if (!isObject(thing)) return false;
214
+ // Reject plain objects inheriting directly from Object.prototype so prototype-pollution gadgets can't spoof FormData (GHSA-6chq-wfr3-2hj9).
215
+ var proto = Object.getPrototypeOf(thing);
216
+ if (!proto || proto === Object.prototype) return false;
217
+ if (!isFunction(thing.append)) return false;
218
+ return toString.call(thing) === pattern ||
219
+ (isFunction(thing.toString) && thing.toString() === pattern);
214
220
  }
215
221
 
216
222
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "axios",
3
- "version": "0.31.0",
3
+ "version": "0.31.1",
4
4
  "description": "Promise based HTTP client for the browser and node.js",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -97,4 +97,4 @@
97
97
  "threshold": "5kB"
98
98
  }
99
99
  ]
100
- }
100
+ }