Skip to content
This repository was archived by the owner on Aug 18, 2022. It is now read-only.

Commit 273260e

Browse files
ivanphzkat
authored andcommitted
fix(redirect): Remove authorization header on redirect to different host (#2)
Fixes: #1 Fixes: https://github.com/zkat/pacote/issues/87 Matches the behavior of request: https://github.com/request/request/blob/b12a6245/lib/redirect.js#L134-L138
1 parent 1aa876e commit 273260e

File tree

3 files changed

+96
-1
lines changed

3 files changed

+96
-1
lines changed

src/index.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const Headers = require('./headers')
1919
const Request = require('./request')
2020
const getNodeRequestOptions = Request.getNodeRequestOptions
2121
const FetchError = require('./fetch-error')
22+
const isURL = /^https?:/
2223

2324
/**
2425
* Fetch function
@@ -86,6 +87,18 @@ function fetch (uri, opts) {
8687
reject(new FetchError(`redirect location header missing at: ${request.url}`, 'invalid-redirect'))
8788
return
8889
}
90+
// Remove authorization if changing hostnames (but not if just
91+
// changing ports or protocols). This matches the behavior of request:
92+
// https://github.com/request/request/blob/b12a6245/lib/redirect.js#L134-L138
93+
let redirectURL = ''
94+
if (!isURL.test(res.headers.location)) {
95+
redirectURL = url.parse(url.resolve(request.url, res.headers.location))
96+
} else {
97+
redirectURL = url.parse(res.headers.location)
98+
}
99+
if (url.parse(request.url).hostname !== redirectURL.hostname) {
100+
request.headers.delete('authorization')
101+
}
89102

90103
// per fetch spec, for POST request with 301/302 response, or any request with 303 response, use GET when following redirect
91104
if (res.statusCode === 303 ||

test/server.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,31 @@ module.exports = class TestServer {
252252
res.end();
253253
}
254254

255+
if (p === '/redirect/host/different') {
256+
res.statusCode = 301;
257+
res.setHeader('Location', 'http://127.0.0.1:30001/inspect');
258+
res.end();
259+
}
260+
261+
if (p === '/redirect/host/same') {
262+
res.statusCode = 301;
263+
res.setHeader('Location', 'localhost:30001/inspect');
264+
res.end();
265+
}
266+
267+
if (p === '/redirect/host/relativeuri') {
268+
res.statusCode = 301;
269+
res.setHeader('Location', '/inspect')
270+
res.end()
271+
}
272+
273+
if (p === '/redirect/host/protocolrelative') {
274+
res.statusCode = 301;
275+
res.setHeader('Location', '//localhost:30001/inspect')
276+
res.end()
277+
}
278+
279+
255280
if (p === '/error/400') {
256281
res.statusCode = 400;
257282
res.setHeader('Content-Type', 'text/plain');

test/test.js

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const supportToString = ({
4141

4242
const local = new TestServer();
4343
const base = `http://${local.hostname}:${local.port}/`;
44+
const redirectBase = `http://127.0.0.1:${local.port}/`;
4445
let url, opts;
4546

4647
before(done => {
@@ -251,7 +252,63 @@ describe('node-fetch', () => {
251252
});
252253
});
253254

254-
it('should follow POST request redirect code 301 with GET', function() {
255+
it('should remove authorization header on redirect if hostname changed', function () {
256+
url = `${base}redirect/host/different`
257+
opts = {
258+
headers: new Headers({ 'authorization': 'abc' })
259+
};
260+
return fetch(url, opts).then(res => {
261+
expect(res.url).to.equal(`${redirectBase}inspect`);
262+
return res.json()
263+
.then(res => {
264+
expect(res.headers['authorization']).to.be.undefined;
265+
});
266+
});
267+
});
268+
269+
it('should preserve authorization header on redirect if hostname did not change', function () {
270+
url = `${base}redirect/host/same`
271+
opts = {
272+
headers: new Headers({ 'authorization': 'abc' })
273+
};
274+
return fetch(url, opts).then(res => {
275+
expect(res.url).to.equal(`${base}inspect`);
276+
return res.json()
277+
.then(res => {
278+
expect(res.headers['authorization']).to.equal('abc');
279+
});
280+
});
281+
});
282+
283+
it('should preserve authorization header on redirect if url is relative', function () {
284+
url = `${base}redirect/host/relativeuri`
285+
opts = {
286+
headers: new Headers({ 'authorization': 'abc' })
287+
};
288+
return fetch(url, opts).then(res => {
289+
expect(res.url).to.equal(`${base}inspect`);
290+
return res.json()
291+
.then(res => {
292+
expect(res.headers['authorization']).to.equal('abc');
293+
});
294+
});
295+
});
296+
297+
it('should preserve authorization header on redirect if url is protocol relative', function () {
298+
url = `${base}redirect/host/protocolrelative`
299+
opts = {
300+
headers: new Headers({ 'authorization': 'abc' })
301+
};
302+
return fetch(url, opts).then(res => {
303+
expect(res.url).to.equal(`${base}inspect`);
304+
return res.json()
305+
.then(res => {
306+
expect(res.headers['authorization']).to.equal('abc');
307+
});
308+
});
309+
});
310+
311+
it('should follow POST request redirect code 301 with GET', function () {
255312
url = `${base}redirect/301`;
256313
opts = {
257314
method: 'POST'

0 commit comments

Comments
 (0)