Files
node/test/parallel/test-http-server-headers-null-proto.js
T
Matteo Collina 21436f0405 http: make req.headers have a null prototype
Makes IncomingMessage.prototype.headers and trailers have a null
prototype, matching the existing behavior of headersDistinct and
trailersDistinct.

Fixes prototype pollution concerns where headers like __proto__
could be interpreted as prototype manipulation.

Refs: https://github.com/nodejs/node/issues/61771

PR-URL: https://github.com/nodejs/node/pull/62900
Reviewed-By: Jordan Harband <ljharb@gmail.com>
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
Reviewed-By: Tim Perry <pimterry@gmail.com>
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Daijiro Wachi <daijiro.wachi@gmail.com>
Reviewed-By: René <contact.9a5d6388@renegade334.me.uk>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
2026-04-25 09:28:53 +00:00

107 lines
2.6 KiB
JavaScript

'use strict';
const common = require('../common');
const assert = require('assert');
const http = require('http');
const net = require('net');
// Test 1: req.headers has a null prototype
{
const server = http.createServer(common.mustCall((req, res) => {
assert.strictEqual(Object.getPrototypeOf(req.headers), null);
res.end();
}));
server.listen(0, common.mustCall(() => {
const port = server.address().port;
const client = net.connect(port, common.mustCall(() => {
client.write(
'GET / HTTP/1.1\r\n' +
'Host: localhost\r\n' +
'Connection: close\r\n' +
'\r\n',
);
}));
client.on('end', common.mustCall(() => {
server.close();
}));
client.resume();
}));
}
// Test 2: req.trailers has a null prototype
{
const server = http.createServer(common.mustCall((req, res) => {
req.on('end', common.mustCall(() => {
assert.strictEqual(Object.getPrototypeOf(req.trailers), null);
assert.strictEqual(req.trailers['x-client-trailer'], 'bar');
}));
res.setHeader('Transfer-Encoding', 'chunked');
res.setHeader('Trailer', 'X-Trailer');
res.write('Hello');
res.addTrailers({
'X-Trailer': 'test',
});
res.end();
}));
server.listen(0, common.mustCall(() => {
const port = server.address().port;
const client = net.connect(port, common.mustCall(() => {
client.write(
'GET / HTTP/1.1\r\n' +
'Host: localhost\r\n' +
'Transfer-Encoding: chunked\r\n' +
'Trailer: X-Client-Trailer\r\n' +
'Connection: close\r\n' +
'\r\n' +
'0\r\n' +
'X-Client-Trailer: bar\r\n' +
'\r\n',
);
}));
client.on('data', () => {});
client.on('end', common.mustCall(() => {
server.close();
}));
}));
}
// Test 3: req.headers with __proto__ header (should not pollute prototype)
{
const server = http.createServer(common.mustCall((req, res) => {
assert.strictEqual(Object.getPrototypeOf(req.headers), null);
// The __proto__ header should be stored as a regular property
assert.strictEqual(
Object.getOwnPropertyDescriptor(req.headers, '__proto__').value, 'test',
);
res.end();
}));
server.listen(0, common.mustCall(() => {
const port = server.address().port;
const client = net.connect(port, common.mustCall(() => {
client.write(
'GET / HTTP/1.1\r\n' +
'Host: localhost\r\n' +
'__proto__: test\r\n' +
'Connection: close\r\n' +
'\r\n',
);
}));
client.on('end', common.mustCall(() => {
server.close();
}));
client.resume();
}));
}