CVE-2022-0155: Exposure of Private Personal Information to an Unauthorized Actor (CWE-359)
value: follow-redirects is vulnerable to Exposure of Private Personal Information to an Unauthorized Actor
CWE description: The product does not properly prevent a person's private, personal information from being accessed by actors who either (1) are not explicitly authorized to access the information or (2) do not have the implicit consent of the person about whom the information is collected.
CVSS v3 base score: 6.5
Published: 2022-01-10T20:15Z
Repository: https://github.com/follow-redirects/follow-redirects
Language: JavaScript
Files changed: index.js, test/test.js
Commit date: 2022-01-10 17:52:42 +0100
Commit message: Drop Cookie header across domains.

Fixes https://github.com/follow-redirects/follow-redirects/issues/183

Diff: diff --git a/index.js b/index.js
index 6f0c238..ecb05f7 100644
--- a/index.js
+++ b/index.js
@@ -390,13 +390,13 @@ RedirectableRequest.prototype._processResponse = function (response) {
     debug("redirecting to", redirectUrl);
     this._isRedirect = true;
     var redirectUrlParts = url.parse(redirectUrl);
     Object.assign(this._options, redirectUrlParts);
 
-    // Drop the Authorization header if redirecting to another domain
+    // Drop the confidential headers when redirecting to another domain
     if (!(redirectUrlParts.host === currentHost || isSubdomainOf(redirectUrlParts.host, currentHost))) {
-      removeMatchingHeaders(/^authorization$/i, this._options.headers);
+      removeMatchingHeaders(/^(?:authorization|cookie)$/i, this._options.headers);
     }
 
     // Evaluate the beforeRedirect callback
     if (typeof this._options.beforeRedirect === "function") {
       var responseDetails = { headers: response.headers };
diff --git a/test/test.js b/test/test.js
index 4e3db1d..9e8e45f 100644
--- a/test/test.js
+++ b/test/test.js
@@ -1317,219 +1317,212 @@ describe("follow-redirects", function () {
           assert.equal(body.host, "localhost:3600");
         });
     });
   });
 
-  describe("when the client passes an Authorization header", function () {
-    it("ignores it when null", function () {
-      app.get("/a", redirectsTo(302, "http://localhost:3600/b"));
-      app.get("/b", function (req, res) {
-        res.end(JSON.stringify(req.headers));
-      });
-
-      var opts = url.parse("http://127.0.0.1:3600/a");
-      opts.headers = {
-        host: "localhost",
-        authorization: null,
-      };
-
-      return server.start(app)
-        .then(asPromise(function (resolve, reject) {
-          http.get(opts, resolve).on("error", reject);
-        }))
-        .then(asPromise(function (resolve, reject, res) {
-          res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
-        }))
-        .then(function (str) {
-          var body = JSON.parse(str);
-          assert.equal(body.host, "localhost:3600");
-          assert.equal(body.authorization, undefined);
+  [
+    "Authorization",
+    "Cookie",
+  ].forEach(function (header) {
+    describe("when the client passes an header named " + header, function () {
+      it("ignores it when null", function () {
+        app.get("/a", redirectsTo(302, "http://localhost:3600/b"));
+        app.get("/b", function (req, res) {
+          res.end(JSON.stringify(req.headers));
         });
-    });
 
-    it("keeps the header when redirected to the same host", function () {
-      app.get("/a", redirectsTo(302, "/b"));
-      app.get("/b", function (req, res) {
-        res.end(JSON.stringify(req.headers));
-      });
+        var opts = url.parse("http://127.0.0.1:3600/a");
+        opts.headers = { host: "localhost" };
+        opts.headers[header] = null;
 
-      var opts = url.parse("http://localhost:3600/a");
-      opts.headers = {
-        authorization: "bearer my-token-1234",
-      };
+        return server.start(app)
+          .then(asPromise(function (resolve, reject) {
+            http.get(opts, resolve).on("error", reject);
+          }))
+          .then(asPromise(function (resolve, reject, res) {
+            res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
+          }))
+          .then(function (str) {
+            var body = JSON.parse(str);
+            assert.equal(body.host, "localhost:3600");
+            assert.equal(body[header.toLowerCase()], undefined);
+          });
+      });
 
-      return server.start(app)
-        .then(asPromise(function (resolve, reject) {
-          http.get(opts, resolve).on("error", reject);
-        }))
-        .then(asPromise(function (resolve, reject, res) {
-          res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
-        }))
-        .then(function (str) {
-          var body = JSON.parse(str);
-          assert.equal(body.host, "localhost:3600");
-          assert.equal(body.authorization, "bearer my-token-1234");
+      it("keeps the header when redirected to the same host", function () {
+        app.get("/a", redirectsTo(302, "/b"));
+        app.get("/b", function (req, res) {
+          res.end(JSON.stringify(req.headers));
         });
-    });
 
-    it("keeps the header when redirected to the same host via header", function () {
-      app.get("/a", redirectsTo(302, "http://localhost:3600/b"));
-      app.get("/b", function (req, res) {
-        res.end(JSON.stringify(req.headers));
-      });
+        var opts = url.parse("http://localhost:3600/a");
+        opts.headers = {};
+        opts.headers[header] = "the header value";
 
-      var opts = url.parse("http://127.0.0.1:3600/a");
-      opts.headers = {
-        host: "localhost:3600",
-        authorization: "bearer my-token-1234",
-      };
+        return server.start(app)
+          .then(asPromise(function (resolve, reject) {
+            http.get(opts, resolve).on("error", reject);
+          }))
+          .then(asPromise(function (resolve, reject, res) {
+            res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
+          }))
+          .then(function (str) {
+            var body = JSON.parse(str);
+            assert.equal(body.host, "localhost:3600");
+            assert.equal(body[header.toLowerCase()], "the header value");
+          });
+      });
 
-      return server.start(app)
-        .then(asPromise(function (resolve, reject) {
-          http.get(opts, resolve).on("error", reject);
-        }))
-        .then(asPromise(function (resolve, reject, res) {
-          res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
-        }))
-        .then(function (str) {
-          var body = JSON.parse(str);
-          assert.equal(body.host, "localhost:3600");
-          assert.equal(body.authorization, "bearer my-token-1234");
+      it("keeps the header when redirected to the same host via header", function () {
+        app.get("/a", redirectsTo(302, "http://localhost:3600/b"));
+        app.get("/b", function (req, res) {
+          res.end(JSON.stringify(req.headers));
         });
-    });
 
-    it("keeps the header when redirected to the same host via header", function () {
-      app.get("/a", redirectsTo(302, "http://localhost:3600/b"));
-      app.get("/b", function (req, res) {
-        res.end(JSON.stringify(req.headers));
-      });
+        var opts = url.parse("http://127.0.0.1:3600/a");
+        opts.headers = { host: "localhost:3600" };
+        opts.headers[header] = "the header value";
 
-      var opts = url.parse("http://127.0.0.1:3600/a");
-      opts.headers = {
-        host: "localhost:3600",
-        authorization: "bearer my-token-1234",
-      };
+        return server.start(app)
+          .then(asPromise(function (resolve, reject) {
+            http.get(opts, resolve).on("error", reject);
+          }))
+          .then(asPromise(function (resolve, reject, res) {
+            res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
+          }))
+          .then(function (str) {
+            var body = JSON.parse(str);
+            assert.equal(body.host, "localhost:3600");
+            assert.equal(body[header.toLowerCase()], "the header value");
+          });
+      });
 
-      return server.start(app)
-        .then(asPromise(function (resolve, reject) {
-          http.get(opts, resolve).on("error", reject);
-        }))
-        .then(asPromise(function (resolve, reject, res) {
-          res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
-        }))
-        .then(function (str) {
-          var body = JSON.parse(str);
-          assert.equal(body.host, "localhost:3600");
-          assert.equal(body.authorization, "bearer my-token-1234");
+      it("keeps the header when redirected to the same host via header", function () {
+        app.get("/a", redirectsTo(302, "http://localhost:3600/b"));
+        app.get("/b", function (req, res) {
+          res.end(JSON.stringify(req.headers));
         });
-    });
 
-    it("keeps the header when redirected to a subdomain", function () {
-      app.get("/a", redirectsTo(302, "http://sub.localhost:3600/b"));
-      app.get("/b", function (req, res) {
-        res.end(JSON.stringify(req.headers));
-      });
+        var opts = url.parse("http://127.0.0.1:3600/a");
+        opts.headers = { host: "localhost:3600" };
+        opts.headers[header] = "the header value";
 
-      var opts = url.parse("http://localhost:3600/a");
-      opts.headers = {
-        authorization: "bearer my-token-1234",
-      };
-      // Intercept the hostname, as no DNS entry is defined for it
-      opts.beforeRedirect = function (options) {
-        assert.equal(options.hostname, "sub.localhost");
-        options.hostname = "localhost";
-      };
+        return server.start(app)
+          .then(asPromise(function (resolve, reject) {
+            http.get(opts, resolve).on("error", reject);
+          }))
+          .then(asPromise(function (resolve, reject, res) {
+            res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
+          }))
+          .then(function (str) {
+            var body = JSON.parse(str);
+            assert.equal(body.host, "localhost:3600");
+            assert.equal(body[header.toLowerCase()], "the header value");
+          });
+      });
 
-      return server.start(app)
-        .then(asPromise(function (resolve, reject) {
-          http.get(opts, resolve).on("error", reject);
-        }))
-        .then(asPromise(function (resolve, reject, res) {
-          res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
-        }))
-        .then(function (str) {
-          var body = JSON.parse(str);
-          assert.equal(body.host, "localhost:3600");
-          assert.equal(body.authorization, "bearer my-token-1234");
+      it("keeps the header when redirected to a subdomain", function () {
+        app.get("/a", redirectsTo(302, "http://sub.localhost:3600/b"));
+        app.get("/b", function (req, res) {
+          res.end(JSON.stringify(req.headers));
         });
-    });
 
-    it("drops the header when redirected to a different host (same hostname and different port)", function () {
-      app.get("/a", redirectsTo(302, "http://localhost:3600/b"));
-      app.get("/b", function (req, res) {
-        res.end(JSON.stringify(req.headers));
-      });
+        var opts = url.parse("http://localhost:3600/a");
+        opts.headers = {};
+        opts.headers[header] = "the header value";
 
-      var opts = url.parse("http://127.0.0.1:3600/a");
-      opts.headers = {
-        host: "localhost",
-        authorization: "bearer my-token-1234",
-      };
+        // Intercept the hostname, as no DNS entry is defined for it
+        opts.beforeRedirect = function (options) {
+          assert.equal(options.hostname, "sub.localhost");
+          options.hostname = "localhost";
+        };
 
-      return server.start(app)
-        .then(asPromise(function (resolve, reject) {
-          http.get(opts, resolve).on("error", reject);
-        }))
-        .then(asPromise(function (resolve, reject, res) {
-          res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
-        }))
-        .then(function (str) {
-          var body = JSON.parse(str);
-          assert.equal(body.host, "localhost:3600");
-          assert.equal(body.authorization, undefined);
+        return server.start(app)
+          .then(asPromise(function (resolve, reject) {
+            http.get(opts, resolve).on("error", reject);
+          }))
+          .then(asPromise(function (resolve, reject, res) {
+            res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
+          }))
+          .then(function (str) {
+            var body = JSON.parse(str);
+            assert.equal(body.host, "localhost:3600");
+            assert.equal(body[header.toLowerCase()], "the header value");
+          });
+      });
+
+      it("drops the header when redirected to a different host (same hostname and different port)", function () {
+        app.get("/a", redirectsTo(302, "http://localhost:3600/b"));
+        app.get("/b", function (req, res) {
+          res.end(JSON.stringify(req.headers));
         });
-    });
 
-    it("drops the header when redirected to a different host", function () {
-      app.get("/a", redirectsTo(302, "http://127.0.0.1:3600/b"));
-      app.get("/b", function (req, res) {
-        res.end(JSON.stringify(req.headers));
-      });
+        var opts = url.parse("http://127.0.0.1:3600/a");
+        opts.headers = { host: "localhost" };
+        opts.headers[header] = "the header value";
 
-      var opts = url.parse("http://localhost:3600/a");
-      opts.headers = {
-        authorization: "bearer my-token-1234",
-      };
+        return server.start(app)
+          .then(asPromise(function (resolve, reject) {
+            http.get(opts, resolve).on("error", reject);
+          }))
+          .then(asPromise(function (resolve, reject, res) {
+            res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
+          }))
+          .then(function (str) {
+            var body = JSON.parse(str);
+            assert.equal(body.host, "localhost:3600");
+            assert.equal(body[header.toLowerCase()], undefined);
+          });
+      });
 
-      return server.start(app)
-        .then(asPromise(function (resolve, reject) {
-          http.get(opts, resolve).on("error", reject);
-        }))
-        .then(asPromise(function (resolve, reject, res) {
-          res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
-        }))
-        .then(function (str) {
-          var body = JSON.parse(str);
-          assert.equal(body.host, "127.0.0.1:3600");
-          assert.equal(body.authorization, undefined);
+      it("drops the header when redirected to a different host", function () {
+        app.get("/a", redirectsTo(302, "http://127.0.0.1:3600/b"));
+        app.get("/b", function (req, res) {
+          res.end(JSON.stringify(req.headers));
         });
-    });
 
-    it("drops the header when redirected from a different host via header", function () {
-      app.get("/a", redirectsTo(302, "http://127.0.0.1:3600/b"));
-      app.get("/b", function (req, res) {
-        res.end(JSON.stringify(req.headers));
-      });
+        var opts = url.parse("http://localhost:3600/a");
+        opts.headers = {};
+        opts.headers[header] = "the header value";
 
-      var opts = url.parse("http://127.0.0.1:3600/a");
-      opts.headers = {
-        host: "localhost",
-        authorization: "bearer my-token-1234",
-      };
+        return server.start(app)
+          .then(asPromise(function (resolve, reject) {
+            http.get(opts, resolve).on("error", reject);
+          }))
+          .then(asPromise(function (resolve, reject, res) {
+            res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
+          }))
+          .then(function (str) {
+            var body = JSON.parse(str);
+            assert.equal(body.host, "127.0.0.1:3600");
+            assert.equal(body[header.toLowerCase()], undefined);
+          });
+      });
 
-      return server.start(app)
-        .then(asPromise(function (resolve, reject) {
-          http.get(opts, resolve).on("error", reject);
-        }))
-        .then(asPromise(function (resolve, reject, res) {
-          res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
-        }))
-        .then(function (str) {
-          var body = JSON.parse(str);
-          assert.equal(body.host, "127.0.0.1:3600");
-          assert.equal(body.authorization, undefined);
+      it("drops the header when redirected from a different host via header", function () {
+        app.get("/a", redirectsTo(302, "http://127.0.0.1:3600/b"));
+        app.get("/b", function (req, res) {
+          res.end(JSON.stringify(req.headers));
         });
+
+        var opts = url.parse("http://127.0.0.1:3600/a");
+        opts.headers = { host: "localhost" };
+        opts.headers[header] = "the header value";
+
+        return server.start(app)
+          .then(asPromise(function (resolve, reject) {
+            http.get(opts, resolve).on("error", reject);
+          }))
+          .then(asPromise(function (resolve, reject, res) {
+            res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
+          }))
+          .then(function (str) {
+            var body = JSON.parse(str);
+            assert.equal(body.host, "127.0.0.1:3600");
+            assert.equal(body[header.toLowerCase()], undefined);
+          });
+      });
     });
   });
 
   describe("when the followRedirects option is set to false", function () {
     it("does not redirect", function () {

Vulnerable code:     // Drop the Authorization header if redirecting to another domain
      removeMatchingHeaders(/^authorization$/i, this._options.headers);
  describe("when the client passes an Authorization header", function () {
    it("ignores it when null", function () {
      app.get("/a", redirectsTo(302, "http://localhost:3600/b"));
      app.get("/b", function (req, res) {
        res.end(JSON.stringify(req.headers));
      });

      var opts = url.parse("http://127.0.0.1:3600/a");
      opts.headers = {
        host: "localhost",
        authorization: null,
      };

      return server.start(app)
        .then(asPromise(function (resolve, reject) {
          http.get(opts, resolve).on("error", reject);
        }))
        .then(asPromise(function (resolve, reject, res) {
          res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
        }))
        .then(function (str) {
          var body = JSON.parse(str);
          assert.equal(body.host, "localhost:3600");
          assert.equal(body.authorization, undefined);
    });
    it("keeps the header when redirected to the same host", function () {
      app.get("/a", redirectsTo(302, "/b"));
      app.get("/b", function (req, res) {
        res.end(JSON.stringify(req.headers));
      });
      var opts = url.parse("http://localhost:3600/a");
      opts.headers = {
        authorization: "bearer my-token-1234",
      };
      return server.start(app)
        .then(asPromise(function (resolve, reject) {
          http.get(opts, resolve).on("error", reject);
        }))
        .then(asPromise(function (resolve, reject, res) {
          res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
        }))
        .then(function (str) {
          var body = JSON.parse(str);
          assert.equal(body.host, "localhost:3600");
          assert.equal(body.authorization, "bearer my-token-1234");
    });
    it("keeps the header when redirected to the same host via header", function () {
      app.get("/a", redirectsTo(302, "http://localhost:3600/b"));
      app.get("/b", function (req, res) {
        res.end(JSON.stringify(req.headers));
      });
      var opts = url.parse("http://127.0.0.1:3600/a");
      opts.headers = {
        host: "localhost:3600",
        authorization: "bearer my-token-1234",
      };
      return server.start(app)
        .then(asPromise(function (resolve, reject) {
          http.get(opts, resolve).on("error", reject);
        }))
        .then(asPromise(function (resolve, reject, res) {
          res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
        }))
        .then(function (str) {
          var body = JSON.parse(str);
          assert.equal(body.host, "localhost:3600");
          assert.equal(body.authorization, "bearer my-token-1234");
    });
    it("keeps the header when redirected to the same host via header", function () {
      app.get("/a", redirectsTo(302, "http://localhost:3600/b"));
      app.get("/b", function (req, res) {
        res.end(JSON.stringify(req.headers));
      });
      var opts = url.parse("http://127.0.0.1:3600/a");
      opts.headers = {
        host: "localhost:3600",
        authorization: "bearer my-token-1234",
      };
      return server.start(app)
        .then(asPromise(function (resolve, reject) {
          http.get(opts, resolve).on("error", reject);
        }))
        .then(asPromise(function (resolve, reject, res) {
          res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
        }))
        .then(function (str) {
          var body = JSON.parse(str);
          assert.equal(body.host, "localhost:3600");
          assert.equal(body.authorization, "bearer my-token-1234");
    });
    it("keeps the header when redirected to a subdomain", function () {
      app.get("/a", redirectsTo(302, "http://sub.localhost:3600/b"));
      app.get("/b", function (req, res) {
        res.end(JSON.stringify(req.headers));
      });
      var opts = url.parse("http://localhost:3600/a");
      opts.headers = {
        authorization: "bearer my-token-1234",
      };
      // Intercept the hostname, as no DNS entry is defined for it
      opts.beforeRedirect = function (options) {
        assert.equal(options.hostname, "sub.localhost");
        options.hostname = "localhost";
      };
      return server.start(app)
        .then(asPromise(function (resolve, reject) {
          http.get(opts, resolve).on("error", reject);
        }))
        .then(asPromise(function (resolve, reject, res) {
          res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
        }))
        .then(function (str) {
          var body = JSON.parse(str);
          assert.equal(body.host, "localhost:3600");
          assert.equal(body.authorization, "bearer my-token-1234");
    });
    it("drops the header when redirected to a different host (same hostname and different port)", function () {
      app.get("/a", redirectsTo(302, "http://localhost:3600/b"));
      app.get("/b", function (req, res) {
        res.end(JSON.stringify(req.headers));
      });
      var opts = url.parse("http://127.0.0.1:3600/a");
      opts.headers = {
        host: "localhost",
        authorization: "bearer my-token-1234",
      };
      return server.start(app)
        .then(asPromise(function (resolve, reject) {
          http.get(opts, resolve).on("error", reject);
        }))
        .then(asPromise(function (resolve, reject, res) {
          res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
        }))
        .then(function (str) {
          var body = JSON.parse(str);
          assert.equal(body.host, "localhost:3600");
          assert.equal(body.authorization, undefined);
    });
    it("drops the header when redirected to a different host", function () {
      app.get("/a", redirectsTo(302, "http://127.0.0.1:3600/b"));
      app.get("/b", function (req, res) {
        res.end(JSON.stringify(req.headers));
      });
      var opts = url.parse("http://localhost:3600/a");
      opts.headers = {
        authorization: "bearer my-token-1234",
      };
      return server.start(app)
        .then(asPromise(function (resolve, reject) {
          http.get(opts, resolve).on("error", reject);
        }))
        .then(asPromise(function (resolve, reject, res) {
          res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
        }))
        .then(function (str) {
          var body = JSON.parse(str);
          assert.equal(body.host, "127.0.0.1:3600");
          assert.equal(body.authorization, undefined);
    });
    it("drops the header when redirected from a different host via header", function () {
      app.get("/a", redirectsTo(302, "http://127.0.0.1:3600/b"));
      app.get("/b", function (req, res) {
        res.end(JSON.stringify(req.headers));
      });
      var opts = url.parse("http://127.0.0.1:3600/a");
      opts.headers = {
        host: "localhost",
        authorization: "bearer my-token-1234",
      };
      return server.start(app)
        .then(asPromise(function (resolve, reject) {
          http.get(opts, resolve).on("error", reject);
        }))
        .then(asPromise(function (resolve, reject, res) {
          res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
        }))
        .then(function (str) {
          var body = JSON.parse(str);
          assert.equal(body.host, "127.0.0.1:3600");
          assert.equal(body.authorization, undefined);
Fixed code:     // Drop the confidential headers when redirecting to another domain
      removeMatchingHeaders(/^(?:authorization|cookie)$/i, this._options.headers);
  [
    "Authorization",
    "Cookie",
  ].forEach(function (header) {
    describe("when the client passes an header named " + header, function () {
      it("ignores it when null", function () {
        app.get("/a", redirectsTo(302, "http://localhost:3600/b"));
        app.get("/b", function (req, res) {
          res.end(JSON.stringify(req.headers));
        var opts = url.parse("http://127.0.0.1:3600/a");
        opts.headers = { host: "localhost" };
        opts.headers[header] = null;
        return server.start(app)
          .then(asPromise(function (resolve, reject) {
            http.get(opts, resolve).on("error", reject);
          }))
          .then(asPromise(function (resolve, reject, res) {
            res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
          }))
          .then(function (str) {
            var body = JSON.parse(str);
            assert.equal(body.host, "localhost:3600");
            assert.equal(body[header.toLowerCase()], undefined);
          });
      });
      it("keeps the header when redirected to the same host", function () {
        app.get("/a", redirectsTo(302, "/b"));
        app.get("/b", function (req, res) {
          res.end(JSON.stringify(req.headers));
        var opts = url.parse("http://localhost:3600/a");
        opts.headers = {};
        opts.headers[header] = "the header value";
        return server.start(app)
          .then(asPromise(function (resolve, reject) {
            http.get(opts, resolve).on("error", reject);
          }))
          .then(asPromise(function (resolve, reject, res) {
            res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
          }))
          .then(function (str) {
            var body = JSON.parse(str);
            assert.equal(body.host, "localhost:3600");
            assert.equal(body[header.toLowerCase()], "the header value");
          });
      });
      it("keeps the header when redirected to the same host via header", function () {
        app.get("/a", redirectsTo(302, "http://localhost:3600/b"));
        app.get("/b", function (req, res) {
          res.end(JSON.stringify(req.headers));
        var opts = url.parse("http://127.0.0.1:3600/a");
        opts.headers = { host: "localhost:3600" };
        opts.headers[header] = "the header value";
        return server.start(app)
          .then(asPromise(function (resolve, reject) {
            http.get(opts, resolve).on("error", reject);
          }))
          .then(asPromise(function (resolve, reject, res) {
            res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
          }))
          .then(function (str) {
            var body = JSON.parse(str);
            assert.equal(body.host, "localhost:3600");
            assert.equal(body[header.toLowerCase()], "the header value");
          });
      });
      it("keeps the header when redirected to the same host via header", function () {
        app.get("/a", redirectsTo(302, "http://localhost:3600/b"));
        app.get("/b", function (req, res) {
          res.end(JSON.stringify(req.headers));
        var opts = url.parse("http://127.0.0.1:3600/a");
        opts.headers = { host: "localhost:3600" };
        opts.headers[header] = "the header value";
        return server.start(app)
          .then(asPromise(function (resolve, reject) {
            http.get(opts, resolve).on("error", reject);
          }))
          .then(asPromise(function (resolve, reject, res) {
            res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
          }))
          .then(function (str) {
            var body = JSON.parse(str);
            assert.equal(body.host, "localhost:3600");
            assert.equal(body[header.toLowerCase()], "the header value");
          });
      });
      it("keeps the header when redirected to a subdomain", function () {
        app.get("/a", redirectsTo(302, "http://sub.localhost:3600/b"));
        app.get("/b", function (req, res) {
          res.end(JSON.stringify(req.headers));
        var opts = url.parse("http://localhost:3600/a");
        opts.headers = {};
        opts.headers[header] = "the header value";
        // Intercept the hostname, as no DNS entry is defined for it
        opts.beforeRedirect = function (options) {
          assert.equal(options.hostname, "sub.localhost");
          options.hostname = "localhost";
        };
        return server.start(app)
          .then(asPromise(function (resolve, reject) {
            http.get(opts, resolve).on("error", reject);
          }))
          .then(asPromise(function (resolve, reject, res) {
            res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
          }))
          .then(function (str) {
            var body = JSON.parse(str);
            assert.equal(body.host, "localhost:3600");
            assert.equal(body[header.toLowerCase()], "the header value");
          });
      });

      it("drops the header when redirected to a different host (same hostname and different port)", function () {
        app.get("/a", redirectsTo(302, "http://localhost:3600/b"));
        app.get("/b", function (req, res) {
          res.end(JSON.stringify(req.headers));
        var opts = url.parse("http://127.0.0.1:3600/a");
        opts.headers = { host: "localhost" };
        opts.headers[header] = "the header value";
        return server.start(app)
          .then(asPromise(function (resolve, reject) {
            http.get(opts, resolve).on("error", reject);
          }))
          .then(asPromise(function (resolve, reject, res) {
            res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
          }))
          .then(function (str) {
            var body = JSON.parse(str);
            assert.equal(body.host, "localhost:3600");
            assert.equal(body[header.toLowerCase()], undefined);
          });
      });
      it("drops the header when redirected to a different host", function () {
        app.get("/a", redirectsTo(302, "http://127.0.0.1:3600/b"));
        app.get("/b", function (req, res) {
          res.end(JSON.stringify(req.headers));
        var opts = url.parse("http://localhost:3600/a");
        opts.headers = {};
        opts.headers[header] = "the header value";
        return server.start(app)
          .then(asPromise(function (resolve, reject) {
            http.get(opts, resolve).on("error", reject);
          }))
          .then(asPromise(function (resolve, reject, res) {
            res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
          }))
          .then(function (str) {
            var body = JSON.parse(str);
            assert.equal(body.host, "127.0.0.1:3600");
            assert.equal(body[header.toLowerCase()], undefined);
          });
      });
      it("drops the header when redirected from a different host via header", function () {
        app.get("/a", redirectsTo(302, "http://127.0.0.1:3600/b"));
        app.get("/b", function (req, res) {
          res.end(JSON.stringify(req.headers));

        var opts = url.parse("http://127.0.0.1:3600/a");
        opts.headers = { host: "localhost" };
        opts.headers[header] = "the header value";

        return server.start(app)
          .then(asPromise(function (resolve, reject) {
            http.get(opts, resolve).on("error", reject);
          }))
          .then(asPromise(function (resolve, reject, res) {
            res.pipe(concat({ encoding: "string" }, resolve)).on("error", reject);
          }))
          .then(function (str) {
            var body = JSON.parse(str);
            assert.equal(body.host, "127.0.0.1:3600");
            assert.equal(body[header.toLowerCase()], undefined);
          });
      });