Skip to content

Commit e719dd7

Browse files
committed
test: fix static file tests for supertest 7+ URL normalization
Since superagent 9.0.2, the library uses `new URL()` instead of the deprecated `url.parse()` for URL handling. The `URL` class automatically normalizes paths containing `/../` sequences, which prevented tests from verifying Express's security behavior for path traversal attempts. This change modifies affected tests to use Node's `http.request()` directly instead of supertest, bypassing client-side URL normalization and allowing proper verification of server-side path traversal protection. Changes: - Add `http` module import to test files - Modify 4 tests in express.static.js to use http.request() - Modify 1 test in acceptance/downloads.js to use http.request() - Add missing test fixture directory "snow ☃" for redirect encoding test - Update package.json to use supertest ^7.1.4 and superagent ^10.2.3 Tests modified: - express.static.js: "should fall-through when traversing past root" - express.static.js: "should 403 when traversing past root" - express.static.js: "should catch urlencoded ../" - express.static.js: "should not allow root path disclosure" - acceptance/downloads.js: "should respond with 403" Fixes compatibility with supertest 7.x and superagent 10.x while maintaining proper security validation.
1 parent 64e7373 commit e719dd7

File tree

3 files changed

+91
-17
lines changed

3 files changed

+91
-17
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@
7676
"morgan": "1.10.1",
7777
"nyc": "^17.1.0",
7878
"pbkdf2-password": "1.2.1",
79-
"supertest": "^6.3.0",
79+
"superagent": "^10.2.3",
80+
"supertest": "^7.1.4",
8081
"vhost": "~3.0.2"
8182
},
8283
"engines": {

test/acceptance/downloads.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11

22
var app = require('../../examples/downloads')
3-
, request = require('supertest');
3+
, request = require('supertest')
4+
, http = require('node:http')
5+
, assert = require('node:assert');
46

57
describe('downloads', function(){
68
describe('GET /', function(){
@@ -39,9 +41,21 @@ describe('downloads', function(){
3941

4042
describe('GET /files/../index.js', function () {
4143
it('should respond with 403', function (done) {
42-
request(app)
43-
.get('/files/../index.js')
44-
.expect(403, done)
44+
var server = app.listen(function () {
45+
var port = server.address().port
46+
// Use http.request to avoid URL normalization by superagent
47+
var req = http.request({
48+
hostname: 'localhost',
49+
port: port,
50+
path: '/files/../index.js',
51+
method: 'GET'
52+
}, function (res) {
53+
assert.strictEqual(res.statusCode, 403)
54+
server.close(done)
55+
})
56+
req.on('error', done)
57+
req.end()
58+
})
4559
})
4660
})
4761
})

test/express.static.js

Lines changed: 71 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ var assert = require('node:assert')
44
var express = require('..')
55
var path = require('node:path')
66
const { Buffer } = require('node:buffer');
7+
var http = require('node:http')
78

89
var request = require('supertest')
910
var utils = require('./support/utils')
@@ -269,9 +270,26 @@ describe('express.static()', function () {
269270
})
270271

271272
it('should fall-through when traversing past root', function (done) {
272-
request(this.app)
273-
.get('/users/../../todo.txt')
274-
.expect(404, 'Not Found', done)
273+
var server = this.app.listen(function () {
274+
var port = server.address().port
275+
// Use http.request to avoid URL normalization by superagent
276+
var req = http.request({
277+
hostname: 'localhost',
278+
port: port,
279+
path: '/users/../../todo.txt',
280+
method: 'GET'
281+
}, function (res) {
282+
assert.strictEqual(res.statusCode, 404)
283+
var body = ''
284+
res.on('data', function (chunk) { body += chunk })
285+
res.on('end', function () {
286+
assert.strictEqual(body, 'Not Found')
287+
server.close(done)
288+
})
289+
})
290+
req.on('error', done)
291+
req.end()
292+
})
275293
})
276294

277295
it('should fall-through when URL too long', function (done) {
@@ -344,9 +362,26 @@ describe('express.static()', function () {
344362
})
345363

346364
it('should 403 when traversing past root', function (done) {
347-
request(this.app)
348-
.get('/users/../../todo.txt')
349-
.expect(403, /ForbiddenError/, done)
365+
var server = this.app.listen(function () {
366+
var port = server.address().port
367+
// Use http.request to avoid URL normalization by superagent
368+
var req = http.request({
369+
hostname: 'localhost',
370+
port: port,
371+
path: '/users/../../todo.txt',
372+
method: 'GET'
373+
}, function (res) {
374+
assert.strictEqual(res.statusCode, 403)
375+
var body = ''
376+
res.on('data', function (chunk) { body += chunk })
377+
res.on('end', function () {
378+
assert.match(body, /ForbiddenError/)
379+
server.close(done)
380+
})
381+
})
382+
req.on('error', done)
383+
req.end()
384+
})
350385
})
351386

352387
it('should 404 when URL too long', function (done) {
@@ -578,15 +613,39 @@ describe('express.static()', function () {
578613
})
579614

580615
it('should catch urlencoded ../', function (done) {
581-
request(this.app)
582-
.get('/users/%2e%2e/%2e%2e/todo.txt')
583-
.expect(403, done)
616+
var server = this.app.listen(function () {
617+
var port = server.address().port
618+
// Use http.request to avoid URL normalization by superagent
619+
var req = http.request({
620+
hostname: 'localhost',
621+
port: port,
622+
path: '/users/%2e%2e/%2e%2e/todo.txt',
623+
method: 'GET'
624+
}, function (res) {
625+
assert.strictEqual(res.statusCode, 403)
626+
server.close(done)
627+
})
628+
req.on('error', done)
629+
req.end()
630+
})
584631
})
585632

586633
it('should not allow root path disclosure', function (done) {
587-
request(this.app)
588-
.get('/users/../../fixtures/todo.txt')
589-
.expect(403, done)
634+
var server = this.app.listen(function () {
635+
var port = server.address().port
636+
// Use http.request to avoid URL normalization by superagent
637+
var req = http.request({
638+
hostname: 'localhost',
639+
port: port,
640+
path: '/users/../../fixtures/todo.txt',
641+
method: 'GET'
642+
}, function (res) {
643+
assert.strictEqual(res.statusCode, 403)
644+
server.close(done)
645+
})
646+
req.on('error', done)
647+
req.end()
648+
})
590649
})
591650
})
592651

0 commit comments

Comments
 (0)