follow-redirects 1.14.4 → 1.14.8

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.

Potentially problematic release.


This version of follow-redirects might be problematic. Click here for more details.

Files changed (2) hide show
  1. package/index.js +50 -15
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -18,7 +18,7 @@ events.forEach(function (event) {
18
18
  // Error types with codes
19
19
  var RedirectionError = createErrorType(
20
20
  "ERR_FR_REDIRECTION_FAILURE",
21
- ""
21
+ "Redirected request failed"
22
22
  );
23
23
  var TooManyRedirectsError = createErrorType(
24
24
  "ERR_FR_TOO_MANY_REDIRECTS",
@@ -169,10 +169,16 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
169
169
 
170
170
  // Stops a timeout from triggering
171
171
  function clearTimer() {
172
+ // Clear the timeout
172
173
  if (self._timeout) {
173
174
  clearTimeout(self._timeout);
174
175
  self._timeout = null;
175
176
  }
177
+
178
+ // Clean up all attached listeners
179
+ self.removeListener("abort", clearTimer);
180
+ self.removeListener("error", clearTimer);
181
+ self.removeListener("response", clearTimer);
176
182
  if (callback) {
177
183
  self.removeListener("timeout", callback);
178
184
  }
@@ -196,8 +202,9 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
196
202
 
197
203
  // Clean up on events
198
204
  this.on("socket", destroyOnTimeout);
199
- this.once("response", clearTimer);
200
- this.once("error", clearTimer);
205
+ this.on("abort", clearTimer);
206
+ this.on("error", clearTimer);
207
+ this.on("response", clearTimer);
201
208
 
202
209
  return this;
203
210
  };
@@ -361,19 +368,34 @@ RedirectableRequest.prototype._processResponse = function (response) {
361
368
  }
362
369
 
363
370
  // Drop the Host header, as the redirect might lead to a different host
364
- var previousHostName = removeMatchingHeaders(/^host$/i, this._options.headers) ||
365
- url.parse(this._currentUrl).hostname;
371
+ var currentHostHeader = removeMatchingHeaders(/^host$/i, this._options.headers);
372
+
373
+ // If the redirect is relative, carry over the host of the last request
374
+ var currentUrlParts = url.parse(this._currentUrl);
375
+ var currentHost = currentHostHeader || currentUrlParts.host;
376
+ var currentUrl = /^\w+:/.test(location) ? this._currentUrl :
377
+ url.format(Object.assign(currentUrlParts, { host: currentHost }));
378
+
379
+ // Determine the URL of the redirection
380
+ var redirectUrl;
381
+ try {
382
+ redirectUrl = url.resolve(currentUrl, location);
383
+ }
384
+ catch (cause) {
385
+ this.emit("error", new RedirectionError(cause));
386
+ return;
387
+ }
366
388
 
367
389
  // Create the redirected request
368
- var redirectUrl = url.resolve(this._currentUrl, location);
369
390
  debug("redirecting to", redirectUrl);
370
391
  this._isRedirect = true;
371
392
  var redirectUrlParts = url.parse(redirectUrl);
372
393
  Object.assign(this._options, redirectUrlParts);
373
394
 
374
- // Drop the Authorization header if redirecting to another host
375
- if (redirectUrlParts.hostname !== previousHostName) {
376
- removeMatchingHeaders(/^authorization$/i, this._options.headers);
395
+ // Drop confidential headers when redirecting to another scheme:domain
396
+ if (redirectUrlParts.protocol !== currentUrlParts.protocol ||
397
+ !isSameOrSubdomain(redirectUrlParts.host, currentHost)) {
398
+ removeMatchingHeaders(/^(?:authorization|cookie)$/i, this._options.headers);
377
399
  }
378
400
 
379
401
  // Evaluate the beforeRedirect callback
@@ -394,9 +416,7 @@ RedirectableRequest.prototype._processResponse = function (response) {
394
416
  this._performRequest();
395
417
  }
396
418
  catch (cause) {
397
- var error = new RedirectionError("Redirected request failed: " + cause.message);
398
- error.cause = cause;
399
- this.emit("error", error);
419
+ this.emit("error", new RedirectionError(cause));
400
420
  }
401
421
  }
402
422
  else {
@@ -510,13 +530,20 @@ function removeMatchingHeaders(regex, headers) {
510
530
  delete headers[header];
511
531
  }
512
532
  }
513
- return lastValue;
533
+ return (lastValue === null || typeof lastValue === "undefined") ?
534
+ undefined : String(lastValue).trim();
514
535
  }
515
536
 
516
537
  function createErrorType(code, defaultMessage) {
517
- function CustomError(message) {
538
+ function CustomError(cause) {
518
539
  Error.captureStackTrace(this, this.constructor);
519
- this.message = message || defaultMessage;
540
+ if (!cause) {
541
+ this.message = defaultMessage;
542
+ }
543
+ else {
544
+ this.message = defaultMessage + ": " + cause.message;
545
+ this.cause = cause;
546
+ }
520
547
  }
521
548
  CustomError.prototype = new Error();
522
549
  CustomError.prototype.constructor = CustomError;
@@ -533,6 +560,14 @@ function abortRequest(request) {
533
560
  request.abort();
534
561
  }
535
562
 
563
+ function isSameOrSubdomain(subdomain, domain) {
564
+ if (subdomain === domain) {
565
+ return true;
566
+ }
567
+ const dot = subdomain.length - domain.length - 1;
568
+ return dot > 0 && subdomain[dot] === "." && subdomain.endsWith(domain);
569
+ }
570
+
536
571
  // Exports
537
572
  module.exports = wrap({ http: http, https: https });
538
573
  module.exports.wrap = wrap;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "follow-redirects",
3
- "version": "1.14.4",
3
+ "version": "1.14.8",
4
4
  "description": "HTTP and HTTPS modules that follow redirects.",
5
5
  "license": "MIT",
6
6
  "main": "index.js",