axios 0.31.0 → 0.32.0

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.
@@ -18,7 +18,10 @@ 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: only honor own properties.
22
+ var withXSRFToken = utils.hasOwnProperty(config, 'withXSRFToken')
23
+ ? config.withXSRFToken
24
+ : undefined;
22
25
  var onCanceled;
23
26
  function done() {
24
27
  if (config.cancelToken) {
@@ -39,13 +42,23 @@ module.exports = function xhrAdapter(config) {
39
42
  // HTTP basic authentication
40
43
  if (config.auth) {
41
44
  var username = config.auth.username || '';
42
- var password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : '';
45
+ var password = config.auth.password
46
+ ? unescape(encodeURIComponent(config.auth.password))
47
+ : '';
43
48
  requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
44
49
  }
45
50
 
46
- var fullPath = buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls);
51
+ var fullPath = buildFullPath(
52
+ config.baseURL,
53
+ config.url,
54
+ config.allowAbsoluteUrls
55
+ );
47
56
 
48
- request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
57
+ request.open(
58
+ config.method.toUpperCase(),
59
+ buildURL(fullPath, config.params, config.paramsSerializer),
60
+ true
61
+ );
49
62
 
50
63
  // Set the request timeout in MS
51
64
  request.timeout = config.timeout;
@@ -55,9 +68,14 @@ module.exports = function xhrAdapter(config) {
55
68
  return;
56
69
  }
57
70
  // Prepare the response
58
- var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
59
- var responseData = !responseType || responseType === 'text' || responseType === 'json' ?
60
- request.responseText : request.response;
71
+ var responseHeaders =
72
+ 'getAllResponseHeaders' in request
73
+ ? parseHeaders(request.getAllResponseHeaders())
74
+ : null;
75
+ var responseData =
76
+ !responseType || responseType === 'text' || responseType === 'json'
77
+ ? request.responseText
78
+ : request.response;
61
79
  var response = {
62
80
  data: responseData,
63
81
  status: request.status,
@@ -67,13 +85,17 @@ module.exports = function xhrAdapter(config) {
67
85
  request: request
68
86
  };
69
87
 
70
- settle(function _resolve(value) {
71
- resolve(value);
72
- done();
73
- }, function _reject(err) {
74
- reject(err);
75
- done();
76
- }, response);
88
+ settle(
89
+ function _resolve(value) {
90
+ resolve(value);
91
+ done();
92
+ },
93
+ function _reject(err) {
94
+ reject(err);
95
+ done();
96
+ },
97
+ response
98
+ );
77
99
 
78
100
  // Clean up request
79
101
  request = null;
@@ -93,7 +115,10 @@ module.exports = function xhrAdapter(config) {
93
115
  // handled by onerror instead
94
116
  // With one exception: request that using file: protocol, most browsers
95
117
  // will return status as 0 even though it's a successful request
96
- if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
118
+ if (
119
+ request.status === 0 &&
120
+ !(request.responseURL && request.responseURL.indexOf('file:') === 0)
121
+ ) {
97
122
  return;
98
123
  }
99
124
  // readystate handler is calling before onerror or ontimeout handlers,
@@ -108,7 +133,14 @@ module.exports = function xhrAdapter(config) {
108
133
  return;
109
134
  }
110
135
 
111
- reject(new AxiosError('Request aborted', AxiosError.ECONNABORTED, config, request));
136
+ reject(
137
+ new AxiosError(
138
+ 'Request aborted',
139
+ AxiosError.ECONNABORTED,
140
+ config,
141
+ request
142
+ )
143
+ );
112
144
 
113
145
  // Clean up request
114
146
  request = null;
@@ -118,7 +150,14 @@ module.exports = function xhrAdapter(config) {
118
150
  request.onerror = function handleError() {
119
151
  // Real errors are hidden from us by the browser
120
152
  // onerror should only fire if it's a network error
121
- reject(new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request));
153
+ reject(
154
+ new AxiosError(
155
+ 'Network Error',
156
+ AxiosError.ERR_NETWORK,
157
+ config,
158
+ request
159
+ )
160
+ );
122
161
 
123
162
  // Clean up request
124
163
  request = null;
@@ -126,16 +165,23 @@ module.exports = function xhrAdapter(config) {
126
165
 
127
166
  // Handle timeout
128
167
  request.ontimeout = function handleTimeout() {
129
- var timeoutErrorMessage = config.timeout ? 'timeout of ' + config.timeout + 'ms exceeded' : 'timeout exceeded';
168
+ var timeoutErrorMessage = config.timeout
169
+ ? 'timeout of ' + config.timeout + 'ms exceeded'
170
+ : 'timeout exceeded';
130
171
  var transitional = config.transitional || transitionalDefaults;
131
172
  if (config.timeoutErrorMessage) {
132
173
  timeoutErrorMessage = config.timeoutErrorMessage;
133
174
  }
134
- reject(new AxiosError(
135
- timeoutErrorMessage,
136
- transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED,
137
- config,
138
- request));
175
+ reject(
176
+ new AxiosError(
177
+ timeoutErrorMessage,
178
+ transitional.clarifyTimeoutError
179
+ ? AxiosError.ETIMEDOUT
180
+ : AxiosError.ECONNABORTED,
181
+ config,
182
+ request
183
+ )
184
+ );
139
185
 
140
186
  // Clean up request
141
187
  request = null;
@@ -146,10 +192,19 @@ module.exports = function xhrAdapter(config) {
146
192
  // Specifically not if we're in a web worker, or react-native.
147
193
  if (utils.isStandardBrowserEnv()) {
148
194
  // Add xsrf header
149
- withXSRFToken && utils.isFunction(withXSRFToken) && (withXSRFToken = withXSRFToken(config));
150
- if (withXSRFToken || (withXSRFToken !== false && isURLSameOrigin(fullPath))) {
195
+ if (utils.isFunction(withXSRFToken)) {
196
+ withXSRFToken = withXSRFToken(config);
197
+ }
198
+ // Strict boolean check: only `true` short-circuits the same-origin guard.
199
+ if (
200
+ withXSRFToken === true ||
201
+ (withXSRFToken !== false && isURLSameOrigin(fullPath))
202
+ ) {
151
203
  // Add xsrf header
152
- var xsrfValue = config.xsrfHeaderName && config.xsrfCookieName && cookies.read(config.xsrfCookieName);
204
+ var xsrfValue =
205
+ config.xsrfHeaderName &&
206
+ config.xsrfCookieName &&
207
+ cookies.read(config.xsrfCookieName);
153
208
  if (xsrfValue) {
154
209
  requestHeaders[config.xsrfHeaderName] = xsrfValue;
155
210
  }
@@ -159,7 +214,10 @@ module.exports = function xhrAdapter(config) {
159
214
  // Add headers to the request
160
215
  if ('setRequestHeader' in request) {
161
216
  utils.forEach(requestHeaders, function setRequestHeader(val, key) {
162
- if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
217
+ if (
218
+ typeof requestData === 'undefined' &&
219
+ key.toLowerCase() === 'content-type'
220
+ ) {
163
221
  // Remove Content-Type if data is undefined
164
222
  delete requestHeaders[key];
165
223
  } else {
@@ -196,30 +254,46 @@ module.exports = function xhrAdapter(config) {
196
254
  if (!request) {
197
255
  return;
198
256
  }
199
- reject(!cancel || cancel.type ? new CanceledError(null, config, request) : cancel);
257
+ reject(
258
+ !cancel || cancel.type
259
+ ? new CanceledError(null, config, request)
260
+ : cancel
261
+ );
200
262
  request.abort();
201
263
  request = null;
202
264
  };
203
265
 
204
266
  config.cancelToken && config.cancelToken.subscribe(onCanceled);
205
267
  if (config.signal) {
206
- config.signal.aborted ? onCanceled() : config.signal.addEventListener('abort', onCanceled);
268
+ config.signal.aborted
269
+ ? onCanceled()
270
+ : config.signal.addEventListener('abort', onCanceled);
207
271
  }
208
272
  }
209
273
 
210
274
  // false, 0 (zero number), and '' (empty string) are valid JSON values
211
- if (!requestData && requestData !== false && requestData !== 0 && requestData !== '') {
275
+ if (
276
+ !requestData &&
277
+ requestData !== false &&
278
+ requestData !== 0 &&
279
+ requestData !== ''
280
+ ) {
212
281
  requestData = null;
213
282
  }
214
283
 
215
284
  var protocol = parseProtocol(fullPath);
216
285
 
217
286
  if (protocol && platform.protocols.indexOf(protocol) === -1) {
218
- reject(new AxiosError('Unsupported protocol ' + protocol + ':', AxiosError.ERR_BAD_REQUEST, config));
287
+ reject(
288
+ new AxiosError(
289
+ 'Unsupported protocol ' + protocol + ':',
290
+ AxiosError.ERR_BAD_REQUEST,
291
+ config
292
+ )
293
+ );
219
294
  return;
220
295
  }
221
296
 
222
-
223
297
  // Send the request
224
298
  request.send(requestData);
225
299
  });
@@ -1,6 +1,81 @@
1
1
  'use strict';
2
2
 
3
3
  var utils = require('../utils');
4
+ var DEFAULT_REDACT_KEYS = require('../helpers/defaultRedactKeys');
5
+
6
+ var REDACTED_VALUE = '[REDACTED ****]';
7
+
8
+ function makeValueDescriptor(value) {
9
+ var descriptor = Object.create(null);
10
+ descriptor.value = value;
11
+ return descriptor;
12
+ }
13
+
14
+ function getRedactKeys(config) {
15
+ // An empty array is treated as "no override" so an upstream `redact: []` cannot
16
+ // silently disable redaction. To opt out, pass non-string values or unset keys.
17
+ var override = config && utils.isArray(config.redact) && config.redact.length ? config.redact : null;
18
+ var redact = override || DEFAULT_REDACT_KEYS;
19
+ var keys = {};
20
+
21
+ utils.forEach(redact, function eachRedactKey(key) {
22
+ if (typeof key === 'string') {
23
+ keys[key.toLowerCase()] = true;
24
+ }
25
+ });
26
+
27
+ return keys;
28
+ }
29
+
30
+ function shouldRedact(key, keys) {
31
+ return typeof key === 'string' && keys[key.toLowerCase()];
32
+ }
33
+
34
+ var CIRCULAR_VALUE = '[Circular]';
35
+
36
+ function serializeConfigValue(value, keys, key, seen) {
37
+ var result;
38
+
39
+ if (shouldRedact(key, keys)) {
40
+ return REDACTED_VALUE;
41
+ }
42
+
43
+ if (utils.isArray(value)) {
44
+ if (seen.indexOf(value) !== -1) {
45
+ return CIRCULAR_VALUE;
46
+ }
47
+ seen.push(value);
48
+ result = [];
49
+ utils.forEach(value, function eachArrayValue(item, index) {
50
+ result[index] = serializeConfigValue(item, keys, index, seen);
51
+ });
52
+ seen.pop();
53
+ return result;
54
+ }
55
+
56
+ if (utils.isPlainObject(value)) {
57
+ if (seen.indexOf(value) !== -1) {
58
+ return CIRCULAR_VALUE;
59
+ }
60
+ seen.push(value);
61
+ result = {};
62
+ utils.forEach(value, function eachObjectValue(item, itemKey) {
63
+ result[itemKey] = serializeConfigValue(item, keys, itemKey, seen);
64
+ });
65
+ seen.pop();
66
+ return result;
67
+ }
68
+
69
+ return value;
70
+ }
71
+
72
+ function serializeConfig(config) {
73
+ if (!config) {
74
+ return config;
75
+ }
76
+
77
+ return serializeConfigValue(config, getRedactKeys(config), undefined, []);
78
+ }
4
79
 
5
80
  /**
6
81
  * Create an Error with the specified message, config, error code, request and response.
@@ -44,7 +119,7 @@ utils.inherits(AxiosError, Error, {
44
119
  columnNumber: this.columnNumber,
45
120
  stack: this.stack,
46
121
  // Axios
47
- config: this.config,
122
+ config: serializeConfig(this.config),
48
123
  code: this.code,
49
124
  status: this.response && this.response.status ? this.response.status : null
50
125
  };
@@ -52,7 +127,7 @@ utils.inherits(AxiosError, Error, {
52
127
  });
53
128
 
54
129
  var prototype = AxiosError.prototype;
55
- var descriptors = {};
130
+ var descriptors = Object.create(null);
56
131
 
57
132
  [
58
133
  'ERR_BAD_OPTION_VALUE',
@@ -66,14 +141,15 @@ var descriptors = {};
66
141
  'ERR_BAD_REQUEST',
67
142
  'ERR_CANCELED',
68
143
  'ERR_NOT_SUPPORT',
69
- 'ERR_INVALID_URL'
144
+ 'ERR_INVALID_URL',
145
+ 'ERR_FORM_DATA_DEPTH_EXCEEDED'
70
146
  // eslint-disable-next-line func-names
71
147
  ].forEach(function(code) {
72
- descriptors[code] = {value: code};
148
+ descriptors[code] = makeValueDescriptor(code);
73
149
  });
74
150
 
75
151
  Object.defineProperties(AxiosError, descriptors);
76
- Object.defineProperty(prototype, 'isAxiosError', {value: true});
152
+ Object.defineProperty(prototype, 'isAxiosError', makeValueDescriptor(true));
77
153
 
78
154
  // eslint-disable-next-line func-names
79
155
  AxiosError.from = function(error, code, config, request, response, customProps) {
@@ -46,11 +46,16 @@ module.exports = function dispatchRequest(config) {
46
46
  normalizeHeaderName(config.headers, 'Content-Type');
47
47
 
48
48
  // Flatten headers
49
- config.headers = utils.merge(
50
- config.headers.common || {},
51
- config.headers[config.method] || {},
52
- config.headers
53
- );
49
+ var commonHeaders = utils.hasOwnProperty(config.headers, 'common') && config.headers.common
50
+ ? config.headers.common
51
+ : {};
52
+ var methodHeaders = config.method &&
53
+ utils.hasOwnProperty(config.headers, config.method) &&
54
+ config.headers[config.method]
55
+ ? config.headers[config.method]
56
+ : {};
57
+
58
+ config.headers = utils.merge(commonHeaders, methodHeaders, config.headers);
54
59
 
55
60
  utils.forEach(
56
61
  ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
@@ -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,6 +99,7 @@ module.exports = function mergeConfig(config1, config2) {
89
99
  'httpsAgent': defaultToConfig2,
90
100
  'cancelToken': defaultToConfig2,
91
101
  'socketPath': defaultToConfig2,
102
+ 'allowedSocketPaths': defaultToConfig2,
92
103
  'responseEncoding': defaultToConfig2,
93
104
  'validateStatus': mergeDirectKeys
94
105
  };
@@ -8,6 +8,7 @@ var toFormData = require('../helpers/toFormData');
8
8
  var toURLEncodedForm = require('../helpers/toURLEncodedForm');
9
9
  var platform = require('../platform');
10
10
  var formDataToJSON = require('../helpers/formDataToJSON');
11
+ var defaultRedactKeys = require('../helpers/defaultRedactKeys');
11
12
 
12
13
  var DEFAULT_CONTENT_TYPE = {
13
14
  'Content-Type': 'application/x-www-form-urlencoded'
@@ -89,17 +90,20 @@ var defaults = {
89
90
  var isFileList;
90
91
 
91
92
  if (isObjectPayload) {
93
+ var formSerializer = utils.hasOwnProperty(this, 'formSerializer') ? this.formSerializer : undefined;
94
+ var envOption = utils.hasOwnProperty(this, 'env') ? this.env : undefined;
95
+
92
96
  if (contentType.indexOf('application/x-www-form-urlencoded') !== -1) {
93
- return toURLEncodedForm(data, this.formSerializer).toString();
97
+ return toURLEncodedForm(data, formSerializer).toString();
94
98
  }
95
99
 
96
100
  if ((isFileList = utils.isFileList(data)) || contentType.indexOf('multipart/form-data') > -1) {
97
- var _FormData = this.env && this.env.FormData;
101
+ var _FormData = envOption && envOption.FormData;
98
102
 
99
103
  return toFormData(
100
104
  isFileList ? {'files[]': data} : data,
101
105
  _FormData && new _FormData(),
102
- this.formSerializer
106
+ formSerializer
103
107
  );
104
108
  }
105
109
  }
@@ -148,6 +152,8 @@ var defaults = {
148
152
  maxContentLength: -1,
149
153
  maxBodyLength: -1,
150
154
 
155
+ redact: defaultRedactKeys.slice(),
156
+
151
157
  env: {
152
158
  FormData: platform.classes.FormData,
153
159
  Blob: platform.classes.Blob
package/lib/env/data.js CHANGED
@@ -1,3 +1,3 @@
1
1
  module.exports = {
2
- "version": "0.31.0"
2
+ "version": "0.32.0"
3
3
  };
@@ -3,18 +3,22 @@
3
3
  var toFormData = require('./toFormData');
4
4
 
5
5
  function encode(str) {
6
+ // Do not map `%00` back to a raw null byte: 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 charMap[match];
17
- });
16
+ return encodeURIComponent(str).replace(
17
+ /[!'\(\)~]|%20/g,
18
+ function replacer(match) {
19
+ return charMap[match];
20
+ }
21
+ );
18
22
  }
19
23
 
20
24
  function AxiosURLSearchParams(params, options) {
@@ -30,13 +34,17 @@ prototype.append = function append(name, value) {
30
34
  };
31
35
 
32
36
  prototype.toString = function toString(encoder) {
33
- var _encode = encoder ? function(value) {
34
- return encoder.call(this, value, encode);
35
- } : encode;
36
-
37
- return this._pairs.map(function each(pair) {
38
- return _encode(pair[0]) + '=' + _encode(pair[1]);
39
- }, '').join('&');
37
+ var _encode = encoder
38
+ ? function(value) {
39
+ return encoder.call(this, value, encode);
40
+ }
41
+ : encode;
42
+
43
+ return this._pairs
44
+ .map(function each(pair) {
45
+ return _encode(pair[0]) + '=' + _encode(pair[1]);
46
+ }, '')
47
+ .join('&');
40
48
  };
41
49
 
42
50
  module.exports = AxiosURLSearchParams;
@@ -32,8 +32,21 @@ module.exports = (
32
32
  },
33
33
 
34
34
  read: function read(name) {
35
- var match = document.cookie.match(new RegExp('(^|;\\s*)(' + name + ')=([^;]*)'));
36
- return (match ? decodeURIComponent(match[3]) : null);
35
+ var nameEQ = name + '=';
36
+ var cookies = document.cookie.split(';');
37
+ var cookie;
38
+
39
+ for (var i = 0; i < cookies.length; i++) {
40
+ cookie = cookies[i];
41
+ while (cookie.charAt(0) === ' ') {
42
+ cookie = cookie.substring(1);
43
+ }
44
+ if (cookie.indexOf(nameEQ) === 0) {
45
+ return decodeURIComponent(cookie.substring(nameEQ.length));
46
+ }
47
+ }
48
+
49
+ return null;
37
50
  },
38
51
 
39
52
  remove: function remove(name) {
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = ['authorization', 'proxy-authorization', 'cookie', 'set-cookie', 'x-api-key', 'password'];
@@ -40,6 +40,58 @@ function parseNoProxyEntry(entry) {
40
40
  return [entryHost, entryPort];
41
41
  }
42
42
 
43
+ function parseIPv4Octets(hostname) {
44
+ var octets = hostname.split('.');
45
+
46
+ if (octets.length !== 4) {
47
+ return null;
48
+ }
49
+
50
+ for (var i = 0; i < octets.length; i++) {
51
+ if (!/^\d+$/.test(octets[i]) || Number(octets[i]) > 255) {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ return octets;
57
+ }
58
+
59
+ // Recognises the canonical IPv4-mapped IPv6 forms the Node URL parser produces:
60
+ // ::ffff:127.0.0.1 (dotted-quad tail)
61
+ // ::ffff:7f00:1 (compressed two-group hex tail)
62
+ // Fully-expanded forms like 0:0:0:0:0:ffff:7f00:1 or single-group tails like
63
+ // ::ffff:1 are not normalised here. URL inputs are canonicalised by the parser
64
+ // before reaching this helper, but hand-crafted no_proxy entries in those
65
+ // shapes will not match an IPv4 listing.
66
+ function normalizeIPv4MappedIPv6(hostname) {
67
+ // Match against the lowercased form so a hand-crafted no_proxy entry like
68
+ // `[::FFFF:7F00:1]` still resolves to its IPv4 alias. Callers that route via
69
+ // URL parsing already lowercase, but the helper stays robust on its own.
70
+ var lower = hostname.toLowerCase();
71
+ var dottedMatch = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/.exec(lower);
72
+
73
+ if (dottedMatch) {
74
+ var octets = parseIPv4Octets(dottedMatch[1]);
75
+ return octets ? octets.join('.') : hostname;
76
+ }
77
+
78
+ var hexMatch = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(lower);
79
+
80
+ if (hexMatch) {
81
+ var high = parseInt(hexMatch[1], 16);
82
+ var low = parseInt(hexMatch[2], 16);
83
+
84
+ return [
85
+ (high >> 8) & 0xff,
86
+ high & 0xff,
87
+ (low >> 8) & 0xff,
88
+ low & 0xff
89
+ ].join('.');
90
+ }
91
+
92
+ return hostname;
93
+ }
94
+
43
95
  function normalizeNoProxyHost(hostname) {
44
96
  if (!hostname) {
45
97
  return hostname;
@@ -49,7 +101,9 @@ function normalizeNoProxyHost(hostname) {
49
101
  hostname = hostname.slice(1, -1);
50
102
  }
51
103
 
52
- return hostname.replace(/\.+$/, '');
104
+ hostname = hostname.replace(/\.+$/, '');
105
+
106
+ return normalizeIPv4MappedIPv6(hostname);
53
107
  }
54
108
 
55
109
  function isLoopbackIPv4(hostname) {