Skip to content

test: fix static file tests for supertest 7+ URL normalization - #6856

Open
guimard wants to merge 1 commit into
expressjs:masterfrom
guimard:fix-static-tests-supertest-url-normalization
Open

guimard wants to merge 1 commit into
expressjs:masterfrom
guimard:fix-static-tests-supertest-url-normalization

Conversation

@guimard

@guimard guimard commented Oct 25, 2025

Copy link
Copy Markdown

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.

Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or

(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or

(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.

(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.

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.
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedsuperagent@​8.1.2 ⏵ 10.2.398 +110010087 +1100
Updatedsupertest@​6.3.4 ⏵ 7.1.499100100 +187 +1100

View full report

@jonchurch

Copy link
Copy Markdown
Member

To be clear, the changes are only relevant upon upgrading to supertest v7, which is where we get superagent 9.0.2

It is not clear from the PR that this is both a dep bump, and then a code change to address test breakage in said dep bump

elsewhere we have intentionally not taken supertest 7, in order to avoid specifically this issue ala pillarjs/send#231

@jonchurch jonchurch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idk if upgrading supertest is worth the additional complexity demonstrated in this PR

It's something worth thinking about though, we haven't thought too deeply about using something else as a test client, but we have been concerned in the past about this exact class of bug/drift in supertest

Comment thread test/express.static.js
Comment on lines +281 to +288
}, function (res) {
assert.strictEqual(res.statusCode, 404)
var body = ''
res.on('data', function (chunk) { body += chunk })
res.on('end', function () {
assert.strictEqual(body, 'Not Found')
server.close(done)
})

@jonchurch jonchurch Oct 30, 2025 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the assertions throw, they prevent cleanup (server.close) so the suite hangs forever with an open server.

They're also throwing in callbacks, so mocha only sees them due to its global uncaught exception handler.
They'd need to be try/catch'd, otherwise we can never clean up or reliaby associate the failure w/ the test.

(ideally we assert in only one place, so moved the first one)

This feedback applies to all of the updated test cases in the PR

Suggested change
}, function (res) {
assert.strictEqual(res.statusCode, 404)
var body = ''
res.on('data', function (chunk) { body += chunk })
res.on('end', function () {
assert.strictEqual(body, 'Not Found')
server.close(done)
})
}, function (res) {
var body = ''
res.on('data', function (chunk) { body += chunk })
res.on('end', function () {
try {
assert.strictEqual(res.statusCode, 404)
assert.strictEqual(body, 'Not Found')
server.close(done)
} catch(err) {
server.close(() => done(err))
}
})

Comment thread package.json
"nyc": "^17.1.0",
"pbkdf2-password": "1.2.1",
"supertest": "^6.3.0",
"superagent": "^10.2.3",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"superagent": "^10.2.3",

Unused?

@jonchurch jonchurch added dependencies Pull requests that update a dependency file tests labels Oct 30, 2025
@nidhishgajjar

This comment was marked as spam.

@Qodo-Free-For-OSS

Copy link
Copy Markdown

Hi, Some new http.request-based tests close the server immediately after checking statusCode without consuming or resuming the response stream, which can leave sockets open and cause flaky/hanging test runs.

Severity: remediation recommended | Category: reliability

How to fix: Call res.resume() before close

Agent prompt to fix - you can give this to your LLM of choice:

Issue description

Some http.request tests do not consume the response body before closing the server.

Issue Context

Other rewritten tests already drain the response via data/end. Status-only assertions should still free the socket.

Fix Focus Areas

  • test/express.static.js[615-630]
  • test/express.static.js[633-648]
  • test/acceptance/downloads.js[44-58]

Notes

In each status-only http.request callback, add res.resume() (or res.on('data', ...); res.on('end', ...)) before calling server.close(done).


Spotted by Qodo code review - free for open-source projects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants