Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions SPECS/nodejs/CVE-2026-21710.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
From b046ea0fa85136ebb2a112360690650f827eb94c Mon Sep 17 00:00:00 2001
From: Matteo Collina <hello@matteocollina.com>
Date: Thu, 19 Feb 2026 15:49:43 +0100
Subject: [PATCH] http: use null prototype for headersDistinct/trailersDistinct

Use { __proto__: null } instead of {} when initializing the
headersDistinct and trailersDistinct destination objects.

A plain {} inherits from Object.prototype, so when a __proto__
header is received, dest["__proto__"] resolves to Object.prototype
(truthy), causing _addHeaderLineDistinct to call .push() on it,
which throws an uncaught TypeError and crashes the process.

Ref: https://hackerone.com/reports/3560402
PR-URL: https://github.com/nodejs-private/node-private/pull/821
Refs: https://hackerone.com/reports/3560402
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
CVE-ID: CVE-2026-21710
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/nodejs/node/commit/00ad47a28e.patch
---
lib/_http_incoming.js | 4 +--
.../test-http-headers-distinct-proto.js | 36 +++++++++++++++++++
test/parallel/test-http-multiple-headers.js | 16 ++++-----
3 files changed, 46 insertions(+), 10 deletions(-)
create mode 100644 test/parallel/test-http-headers-distinct-proto.js

diff --git a/lib/_http_incoming.js b/lib/_http_incoming.js
index e45ae819..77433e55 100644
--- a/lib/_http_incoming.js
+++ b/lib/_http_incoming.js
@@ -131,7 +131,7 @@ ObjectDefineProperty(IncomingMessage.prototype, 'headersDistinct', {
__proto__: null,
get: function() {
if (!this[kHeadersDistinct]) {
- this[kHeadersDistinct] = {};
+ this[kHeadersDistinct] = { __proto__: null };

const src = this.rawHeaders;
const dst = this[kHeadersDistinct];
@@ -171,7 +171,7 @@ ObjectDefineProperty(IncomingMessage.prototype, 'trailersDistinct', {
__proto__: null,
get: function() {
if (!this[kTrailersDistinct]) {
- this[kTrailersDistinct] = {};
+ this[kTrailersDistinct] = { __proto__: null };

const src = this.rawTrailers;
const dst = this[kTrailersDistinct];
diff --git a/test/parallel/test-http-headers-distinct-proto.js b/test/parallel/test-http-headers-distinct-proto.js
new file mode 100644
index 00000000..bd4cb82b
--- /dev/null
+++ b/test/parallel/test-http-headers-distinct-proto.js
@@ -0,0 +1,36 @@
+'use strict';
+
+const common = require('../common');
+const assert = require('assert');
+const http = require('http');
+const net = require('net');
+
+// Regression test: sending a __proto__ header must not crash the server
+// when accessing req.headersDistinct or req.trailersDistinct.
+
+const server = http.createServer(common.mustCall((req, res) => {
+ const headers = req.headersDistinct;
+ assert.strictEqual(Object.getPrototypeOf(headers), null);
+ assert.deepStrictEqual(Object.getOwnPropertyDescriptor(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();
+}));
diff --git a/test/parallel/test-http-multiple-headers.js b/test/parallel/test-http-multiple-headers.js
index 1ebd290a..1188af5d 100644
--- a/test/parallel/test-http-multiple-headers.js
+++ b/test/parallel/test-http-multiple-headers.js
@@ -27,13 +27,13 @@ const server = createServer(
host,
'transfer-encoding': 'chunked'
});
- assert.deepStrictEqual(req.headersDistinct, {
+ assert.deepStrictEqual(req.headersDistinct, Object.assign({ __proto__: null }, {
'connection': ['close'],
'x-req-a': ['eee', 'fff', 'ggg', 'hhh'],
'x-req-b': ['iii; jjj; kkk; lll'],
'host': [host],
- 'transfer-encoding': ['chunked']
- });
+ 'transfer-encoding': ['chunked'],
+ }));

req.on('end', function() {
assert.deepStrictEqual(req.rawTrailers, [
@@ -46,7 +46,7 @@ const server = createServer(
);
assert.deepStrictEqual(
req.trailersDistinct,
- { 'x-req-x': ['xxx', 'yyy'], 'x-req-y': ['zzz; www'] }
+ Object.assign({ __proto__: null }, { 'x-req-x': ['xxx', 'yyy'], 'x-req-y': ['zzz; www'] })
);

res.setHeader('X-Res-a', 'AAA');
@@ -129,14 +129,14 @@ server.listen(0, common.mustCall(() => {
'x-res-d': 'JJJ; KKK; LLL',
'transfer-encoding': 'chunked'
});
- assert.deepStrictEqual(res.headersDistinct, {
+ assert.deepStrictEqual(res.headersDistinct, Object.assign({ __proto__: null }, {
'x-res-a': [ 'AAA', 'BBB', 'CCC' ],
'x-res-b': [ 'DDD; EEE; FFF; GGG' ],
'connection': [ 'close' ],
'x-res-c': [ 'HHH', 'III' ],
'x-res-d': [ 'JJJ; KKK; LLL' ],
- 'transfer-encoding': [ 'chunked' ]
- });
+ 'transfer-encoding': [ 'chunked' ],
+ }));

res.on('end', function() {
assert.deepStrictEqual(res.rawTrailers, [
@@ -150,7 +150,7 @@ server.listen(0, common.mustCall(() => {
);
assert.deepStrictEqual(
res.trailersDistinct,
- { 'x-res-x': ['XXX', 'YYY'], 'x-res-y': ['ZZZ; WWW'] }
+ Object.assign({ __proto__: null }, { 'x-res-x': ['XXX', 'YYY'], 'x-res-y': ['ZZZ; WWW'] })
);
server.close();
});
--
2.45.4

32 changes: 32 additions & 0 deletions SPECS/nodejs/CVE-2026-33750.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
From 62fac79ac5c1e3fa7806fa649e56e8b9991abd7b Mon Sep 17 00:00:00 2001
From: Alec Fenichel <alec.fenichel@gmail.com>
Date: Fri, 27 Mar 2026 04:40:32 -0400
Subject: [PATCH] Backport fix for GHSA-f886-m6hf-6m8v to v2 (#96)

Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/juliangruber/brace-expansion/commit/311ac0d54994158c0a384e286a7d6cbb17ee8ed5.patch
---
deps/minimatch/src/node_modules/brace-expansion/index.js | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/deps/minimatch/src/node_modules/brace-expansion/index.js b/deps/minimatch/src/node_modules/brace-expansion/index.js
index a27f81ce..376d060c 100644
--- a/deps/minimatch/src/node_modules/brace-expansion/index.js
+++ b/deps/minimatch/src/node_modules/brace-expansion/index.js
@@ -148,7 +148,7 @@ function expand(str, isTop) {
var y = numeric(n[1]);
var width = Math.max(n[0].length, n[1].length)
var incr = n.length == 3
- ? Math.abs(numeric(n[2]))
+ ? Math.max(Math.abs(numeric(n[2])), 1)
: 1;
var test = lte;
var reverse = y < x;
@@ -200,4 +200,3 @@ function expand(str, isTop) {

return expansions;
}
-
--
2.45.4

7 changes: 6 additions & 1 deletion SPECS/nodejs/nodejs18.spec
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Name: nodejs18
# WARNINGS: MUST check and update the 'npm_version' macro for every version update of this package.
# The version of NPM can be found inside the sources under 'deps/npm/package.json'.
Version: 18.20.3
Release: 12%{?dist}
Release: 13%{?dist}
License: BSD and MIT and Public Domain and NAIST-2003 and Artistic-2.0
Group: Applications/System
Vendor: Microsoft Corporation
Expand All @@ -31,6 +31,8 @@ Patch11: CVE-2025-5889.patch
Patch12: CVE-2025-5222.patch
Patch13: CVE-2025-55131.patch
Patch14: CVE-2026-27135.patch
Patch15: CVE-2026-21710.patch
Patch16: CVE-2026-33750.patch
BuildRequires: brotli-devel
BuildRequires: coreutils >= 8.22
BuildRequires: gcc
Expand Down Expand Up @@ -131,6 +133,9 @@ make cctest
%{_datadir}/systemtap/tapset/node.stp

%changelog
* Thu Apr 02 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 18.20.3-13
- Patch for CVE-2026-33750, CVE-2026-21710

* Fri Mar 20 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 18.20.3-12
- Patch for CVE-2026-27135

Expand Down
Loading