Files
node/test/parallel/test-http-client-path-toctou.js
Matteo Collina acb79bca7e http: validate ClientRequest path on set
The `path` property on `ClientRequest` was only validated at
construction time. Add a getter/setter so that the same
`INVALID_PATH_REGEX` check runs whenever `req.path` is reassigned,
preventing invalid characters from reaching `_implicitHeader()`.

PR-URL: https://github.com/nodejs/node/pull/62030
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
Reviewed-By: Tim Perry <pimterry@gmail.com>
2026-03-02 19:10:56 +00:00

69 lines
1.7 KiB
JavaScript

'use strict';
require('../common');
const assert = require('assert');
const http = require('http');
// Test that mutating req.path after construction to include
// invalid characters (e.g. CRLF) throws ERR_UNESCAPED_CHARACTERS.
// Regression test for a TOCTOU vulnerability where path was only
// validated at construction time but could be mutated before
// _implicitHeader() flushed it to the socket.
// Use a createConnection that returns nothing to avoid actual connection.
const req = new http.ClientRequest({
host: '127.0.0.1',
port: 1,
path: '/valid',
method: 'GET',
createConnection: () => {},
});
// Attempting to set path with CRLF must throw
assert.throws(
() => { req.path = '/evil\r\nX-Injected: true\r\n\r\n'; },
{
code: 'ERR_UNESCAPED_CHARACTERS',
name: 'TypeError',
message: 'Request path contains unescaped characters',
}
);
// Path must be unchanged after failed mutation
assert.strictEqual(req.path, '/valid');
// Attempting to set path with lone CR must throw
assert.throws(
() => { req.path = '/evil\rpath'; },
{
code: 'ERR_UNESCAPED_CHARACTERS',
name: 'TypeError',
}
);
// Attempting to set path with lone LF must throw
assert.throws(
() => { req.path = '/evil\npath'; },
{
code: 'ERR_UNESCAPED_CHARACTERS',
name: 'TypeError',
}
);
// Attempting to set path with null byte must throw
assert.throws(
() => { req.path = '/evil\0path'; },
{
code: 'ERR_UNESCAPED_CHARACTERS',
name: 'TypeError',
}
);
// Valid path mutation should succeed
req.path = '/also-valid';
assert.strictEqual(req.path, '/also-valid');
req.path = '/path?query=1&other=2';
assert.strictEqual(req.path, '/path?query=1&other=2');
req.destroy();