Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/needle.js
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,13 @@ Needle.prototype.send_request = function(count, method, uri, config, post_data,
// else
delete config.headers['cookie'];
}

// the host and port match, but a scheme change (e.g. https -> http) is
// still cross-origin, so authentication headers must not be carried over.
if (!utils.protocols_match(headers.location, uri)) {
delete config.headers['authorization'];
delete config.headers['proxy-authorization'];
}
} else {
delete config.headers['cookie'];
delete config.headers['authorization'];
Expand Down
8 changes: 8 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ function host_and_ports_match(url1, url2) {
== String(b.port || (b.protocol == 'https:' ? 443 : 80));
}

function protocols_match(url1, url2) {
if (url1.indexOf('http') < 0) url1 = 'http://' + url1;
if (url2.indexOf('http') < 0) url2 = 'http://' + url2;

return url.parse(url1).protocol == url.parse(url2).protocol;
}

// returns false if a no_proxy host or pattern matches given url
function should_proxy_to(uri) {
var no_proxy = get_env_var(['NO_PROXY'], true);
Expand Down Expand Up @@ -103,6 +110,7 @@ module.exports = {
resolve_url: resolve_url,
get_env_var: get_env_var,
host_and_ports_match: host_and_ports_match,
protocols_match: protocols_match,
should_proxy_to: should_proxy_to,
parse_content_type: parse_content_type,
is_stream: is_stream,
Expand Down
40 changes: 40 additions & 0 deletions test/redirect_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -462,4 +462,44 @@ describe('redirects', function() {

})

describe('and redirected to the same host and port over a downgraded scheme', function() {

// The origin is the scheme, host and port together, so a redirect that only
// changes the scheme (https -> http on the same host and port) is still
// cross-origin and must not carry the original credential headers along.

before(function() {
needle.defaults({ follow_max: 1 });
})

after(function() {
needle.defaults({ follow_max: 0 });
})

it('strips credential headers from the redirected request', function(done) {
code = 301;
location = 'http://127.0.0.1:' + ports.https + '/hello';
spies.http.reset();

var opts = {
rejectUnauthorized: false,
headers: {
authorization: 'Bearer secret-token',
cookie: 'sid=secret',
'proxy-authorization': 'Basic secret'
}
};

needle.get('https://127.0.0.1:' + ports.https + '/hello', opts, function() {
spies.http.callCount.should.be.above(0);
var sent = spies.http.args[0][0].headers;
should.not.exist(sent['authorization']);
should.not.exist(sent['cookie']);
should.not.exist(sent['proxy-authorization']);
done();
});
})

})

});