3 var express
= require('../')
4 , request
= require('supertest')
5 , assert
= require('assert')
6 , url
= require('url');
8 describe('res', function(){
9 describe('.location(url)', function(){
10 it('should set the header', function(done
){
13 app
.use(function(req
, res
){
14 res
.location('http://google.com/').end();
19 .expect('Location', 'http://google.com/')
23 it('should preserve trailing slashes when not present', function(done
){
26 app
.use(function(req
, res
){
27 res
.location('http://google.com').end();
32 .expect('Location', 'http://google.com')
36 it('should encode "url"', function (done
) {
39 app
.use(function (req
, res
) {
40 res
.location('https://google.com?q=\u2603 ยง10').end()
45 .expect('Location', 'https://google.com?q=%E2%98%83%20%C2%A710')
49 describe('when url is "back"', function () {
50 it('should set location from "Referer" header', function (done
) {
53 app
.use(function (req
, res
) {
54 res
.location('back').end()
59 .set('Referer', '/some/page.html')
60 .expect('Location', '/some/page.html')
64 it('should set location from "Referrer" header', function (done
) {
67 app
.use(function (req
, res
) {
68 res
.location('back').end()
73 .set('Referrer', '/some/page.html')
74 .expect('Location', '/some/page.html')
78 it('should prefer "Referrer" header', function (done
) {
81 app
.use(function (req
, res
) {
82 res
.location('back').end()
87 .set('Referer', '/some/page1.html')
88 .set('Referrer', '/some/page2.html')
89 .expect('Location', '/some/page2.html')
93 it('should set the header to "/" without referrer', function (done
) {
96 app
.use(function (req
, res
) {
97 res
.location('back').end()
102 .expect('Location', '/')
107 it('should encode data uri', function (done
) {
109 app
.use(function (req
, res
) {
110 res
.location('data:text/javascript,export default () => { }').end();
115 .expect('Location', 'data:text/javascript,export%20default%20()%20=%3E%20%7B%20%7D')
119 it('should encode data uri', function (done
) {
121 app
.use(function (req
, res
) {
122 res
.location('data:text/javascript,export default () => { }').end();
127 .expect('Location', 'data:text/javascript,export%20default%20()%20=%3E%20%7B%20%7D')
131 it('should consistently handle non-string input: boolean', function (done
) {
133 app
.use(function (req
, res
) {
134 res
.location(true).end();
139 .expect('Location', 'true')
143 it('should consistently handle non-string inputs: object', function (done
) {
145 app
.use(function (req
, res
) {
146 res
.location({}).end();
151 .expect('Location', '[object%20Object]')
155 it('should consistently handle non-string inputs: array', function (done
) {
157 app
.use(function (req
, res
) {
158 res
.location([]).end();
163 .expect('Location', '')
167 it('should consistently handle empty string input', function (done
) {
169 app
.use(function (req
, res
) {
170 res
.location('').end();
175 .expect('Location', '')
180 if (typeof URL
!== 'undefined') {
181 it('should accept an instance of URL', function (done
) {
184 app
.use(function(req
, res
){
185 res
.location(new URL('http://google.com/')).end();
190 .expect('Location', 'http://google.com/')
196 describe('location header encoding', function() {
197 function createRedirectServerForDomain (domain
) {
199 app
.use(function (req
, res
) {
200 var host
= url
.parse(req
.query
.q
, false, true).host
;
201 // This is here to show a basic check one might do which
202 // would pass but then the location header would still be bad
203 if (host
!== domain
) {
204 res
.status(400).end('Bad host: ' + host
+ ' !== ' + domain
);
206 res
.location(req
.query
.q
).end();
211 function testRequestedRedirect (app
, inputUrl
, expected
, expectedHost
, done
) {
213 // Encode uri because old supertest does not and is required
214 // to test older node versions. New supertest doesn't re-encode
215 // so this works in both.
216 .get('/?q=' + encodeURIComponent(inputUrl
))
217 .expect('') // No body.
219 .expect('Location', expected
)
220 .end(function (err
, res
) {
222 console
.log('headers:', res
.headers
)
223 console
.error('error', res
.error
, err
);
224 return done(err
, res
);
227 // Parse the hosts from the input URL and the Location header
228 var inputHost
= url
.parse(inputUrl
, false, true).host
;
229 var locationHost
= url
.parse(res
.headers
['location'], false, true).host
;
231 assert
.strictEqual(locationHost
, expectedHost
);
233 // Assert that the hosts are the same
234 if (inputHost
!== locationHost
) {
235 return done(new Error('Hosts do not match: ' + inputHost
+ " !== " + locationHost
));
238 return done(null, res
);
242 it('should not touch already-encoded sequences in "url"', function (done
) {
243 var app
= createRedirectServerForDomain('google.com');
244 testRequestedRedirect(
246 'https://google.com?q=%A710',
247 'https://google.com?q=%A710',
253 it('should consistently handle relative urls', function (done
) {
254 var app
= createRedirectServerForDomain(null);
255 testRequestedRedirect(
264 it('should not encode urls in such a way that they can bypass redirect allow lists', function (done
) {
265 var app
= createRedirectServerForDomain('google.com');
266 testRequestedRedirect(
268 'http://google.com\\@apple.com',
269 'http://google.com\\@apple.com',
275 it('should not be case sensitive', function (done
) {
276 var app
= createRedirectServerForDomain('google.com');
277 testRequestedRedirect(
279 'HTTP://google.com\\@apple.com',
280 'HTTP://google.com\\@apple.com',
286 it('should work with https', function (done
) {
287 var app
= createRedirectServerForDomain('google.com');
288 testRequestedRedirect(
290 'https://google.com\\@apple.com',
291 'https://google.com\\@apple.com',
297 it('should correctly encode schemaless paths', function (done
) {
298 var app
= createRedirectServerForDomain('google.com');
299 testRequestedRedirect(
301 '//google.com\\@apple.com/',
302 '//google.com\\@apple.com/',
308 it('should percent encode backslashes in the path', function (done
) {
309 var app
= createRedirectServerForDomain('google.com');
310 testRequestedRedirect(
312 'https://google.com/foo\\bar\\baz',
313 'https://google.com/foo%5Cbar%5Cbaz',
319 it('should encode backslashes in the path after the first backslash that triggered path parsing', function (done
) {
320 var app
= createRedirectServerForDomain('google.com');
321 testRequestedRedirect(
323 'https://google.com\\@app\\l\\e.com',
324 'https://google.com\\@app%5Cl%5Ce.com',
330 it('should escape header splitting for old node versions', function (done
) {
331 var app
= createRedirectServerForDomain('google.com');
332 testRequestedRedirect(
334 'http://google.com\\@apple.com/%0d%0afoo:%20bar',
335 'http://google.com\\@apple.com/%0d%0afoo:%20bar',
341 it('should encode unicode correctly', function (done
) {
342 var app
= createRedirectServerForDomain(null);
343 testRequestedRedirect(
352 it('should encode unicode correctly even with a bad host', function (done
) {
353 var app
= createRedirectServerForDomain('google.com');
354 testRequestedRedirect(
356 'http://google.com\\@apple.com/%e2%98%83',
357 'http://google.com\\@apple.com/%e2%98%83',
363 it('should work correctly despite using deprecated url.parse', function (done
) {
364 var app
= createRedirectServerForDomain('google.com');
365 testRequestedRedirect(
367 'https://google.com\'.bb.com/1.html',
368 'https://google.com\'.bb.com/1.html',
374 it('should encode file uri path', function (done
) {
375 var app
= createRedirectServerForDomain('');
376 testRequestedRedirect(
378 'file:///etc\\passwd',
379 'file:///etc%5Cpasswd',