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
29 changes: 29 additions & 0 deletions SPECS/nodejs24/CVE-2025-15504.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
From 1860997ed7f80ad2248526ae5cb82621024948cd Mon Sep 17 00:00:00 2001
From: Romain Thomas <me@romainthomas.fr>
Date: Thu, 1 Jan 2026 12:50:49 +0100
Subject: [PATCH] Fix #1277

Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: https://github.com/lief-project/LIEF/commit/8bba61609ff8dfd8046a67497bfa6e2d06f7d8d4.patch
---
deps/LIEF/src/ELF/Parser.tcc | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/deps/LIEF/src/ELF/Parser.tcc b/deps/LIEF/src/ELF/Parser.tcc
index 03c2c5e7..88ca4bbd 100644
--- a/deps/LIEF/src/ELF/Parser.tcc
+++ b/deps/LIEF/src/ELF/Parser.tcc
@@ -129,7 +129,9 @@ ok_error_t Parser::parse_binary() {
uint64_t offset = addr - seg->virtual_address();
strm->setpos(offset);
binary_->gnu_hash_ = GnuHash::parse<ELF_T>(*strm, dynsymcount);
- binary_->sizing_info_->gnu_hash = binary_->gnu_hash_->original_size();
+ if (binary_->gnu_hash_ != nullptr) {
+ binary_->sizing_info_->gnu_hash = binary_->gnu_hash_->original_size();
+ }
}
}
}
--
2.45.4

356 changes: 356 additions & 0 deletions SPECS/nodejs24/CVE-2026-33671.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,356 @@
From f720787b6ff1ad2479c9c9319a7baf844818de7d Mon Sep 17 00:00:00 2001
From: AllSpark <allspark@microsoft.com>
Date: Mon, 13 Apr 2026 14:33:16 +0000
Subject: [PATCH] Backport: picomatch safeguard against risky quantified
extglobs; add DEFAULT_MAX_EXTGLOB_RECURSION and analyze repeated extglobs in
parse to literalize or simplify where necessary

Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: AI Backport of https://github.com/micromatch/picomatch/commit/5eceecd27543b8e056b9307d69e105ea03618a7d.patch
---
.../node_modules/picomatch/lib/constants.js | 1 +
.../node_modules/picomatch/lib/parse.js | 302 ++++++++++++++++++
2 files changed, 303 insertions(+)

diff --git a/deps/npm/node_modules/tinyglobby/node_modules/picomatch/lib/constants.js b/deps/npm/node_modules/tinyglobby/node_modules/picomatch/lib/constants.js
index 2bdb5a07..738c745a 100644
--- a/deps/npm/node_modules/tinyglobby/node_modules/picomatch/lib/constants.js
+++ b/deps/npm/node_modules/tinyglobby/node_modules/picomatch/lib/constants.js
@@ -87,6 +87,7 @@ const POSIX_REGEX_SOURCE = {
};

module.exports = {
+ DEFAULT_MAX_EXTGLOB_RECURSION: 0,
MAX_LENGTH: 1024 * 64,
POSIX_REGEX_SOURCE,

diff --git a/deps/npm/node_modules/tinyglobby/node_modules/picomatch/lib/parse.js b/deps/npm/node_modules/tinyglobby/node_modules/picomatch/lib/parse.js
index 8fd8ff49..8c087b3f 100644
--- a/deps/npm/node_modules/tinyglobby/node_modules/picomatch/lib/parse.js
+++ b/deps/npm/node_modules/tinyglobby/node_modules/picomatch/lib/parse.js
@@ -45,6 +45,278 @@ const syntaxError = (type, char) => {
return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`;
};

+const splitTopLevel = input => {
+ const parts = [];
+ let bracket = 0;
+ let paren = 0;
+ let quote = 0;
+ let value = '';
+ let escaped = false;
+
+ for (const ch of input) {
+ if (escaped === true) {
+ value += ch;
+ escaped = false;
+ continue;
+ }
+
+ if (ch === '\\') {
+ value += ch;
+ escaped = true;
+ continue;
+ }
+
+ if (ch === '"') {
+ quote = quote === 1 ? 0 : 1;
+ value += ch;
+ continue;
+ }
+
+ if (quote === 0) {
+ if (ch === '[') {
+ bracket++;
+ } else if (ch === ']' && bracket > 0) {
+ bracket--;
+ } else if (bracket === 0) {
+ if (ch === '(') {
+ paren++;
+ } else if (ch === ')' && paren > 0) {
+ paren--;
+ } else if (ch === '|' && paren === 0) {
+ parts.push(value);
+ value = '';
+ continue;
+ }
+ }
+ }
+
+ value += ch;
+ }
+
+ parts.push(value);
+ return parts;
+};
+
+const isPlainBranch = branch => {
+ let escaped = false;
+
+ for (const ch of branch) {
+ if (escaped === true) {
+ escaped = false;
+ continue;
+ }
+
+ if (ch === '\\') {
+ escaped = true;
+ continue;
+ }
+
+ if (/[?*+@!()[\]{}]/.test(ch)) {
+ return false;
+ }
+ }
+
+ return true;
+};
+
+const normalizeSimpleBranch = branch => {
+ let value = branch.trim();
+ let changed = true;
+
+ while (changed === true) {
+ changed = false;
+
+ if (/^@\([^\\()[\]{}|]+\)$/.test(value)) {
+ value = value.slice(2, -1);
+ changed = true;
+ }
+ }
+
+ if (!isPlainBranch(value)) {
+ return;
+ }
+
+ return value.replace(/\\(.)/g, '$1');
+};
+
+const hasRepeatedCharPrefixOverlap = branches => {
+ const values = branches.map(normalizeSimpleBranch).filter(Boolean);
+
+ for (let i = 0; i < values.length; i++) {
+ for (let j = i + 1; j < values.length; j++) {
+ const a = values[i];
+ const b = values[j];
+ const char = a[0];
+
+ if (!char || a !== char.repeat(a.length) || b !== char.repeat(b.length)) {
+ continue;
+ }
+
+ if (a === b || a.startsWith(b) || b.startsWith(a)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+};
+
+const parseRepeatedExtglob = (pattern, requireEnd = true) => {
+ if ((pattern[0] !== '+' && pattern[0] !== '*') || pattern[1] !== '(') {
+ return;
+ }
+
+ let bracket = 0;
+ let paren = 0;
+ let quote = 0;
+ let escaped = false;
+
+ for (let i = 1; i < pattern.length; i++) {
+ const ch = pattern[i];
+
+ if (escaped === true) {
+ escaped = false;
+ continue;
+ }
+
+ if (ch === '\\') {
+ escaped = true;
+ continue;
+ }
+
+ if (ch === '"') {
+ quote = quote === 1 ? 0 : 1;
+ continue;
+ }
+
+ if (quote === 1) {
+ continue;
+ }
+
+ if (ch === '[') {
+ bracket++;
+ continue;
+ }
+
+ if (ch === ']' && bracket > 0) {
+ bracket--;
+ continue;
+ }
+
+ if (bracket > 0) {
+ continue;
+ }
+
+ if (ch === '(') {
+ paren++;
+ continue;
+ }
+
+ if (ch === ')') {
+ paren--;
+
+ if (paren === 0) {
+ if (requireEnd === true && i !== pattern.length - 1) {
+ return;
+ }
+
+ return {
+ type: pattern[0],
+ body: pattern.slice(2, i),
+ end: i
+ };
+ }
+ }
+ }
+};
+
+const getStarExtglobSequenceOutput = pattern => {
+ let index = 0;
+ const chars = [];
+
+ while (index < pattern.length) {
+ const match = parseRepeatedExtglob(pattern.slice(index), false);
+
+ if (!match || match.type !== '*') {
+ return;
+ }
+
+ const branches = splitTopLevel(match.body).map(branch => branch.trim());
+ if (branches.length !== 1) {
+ return;
+ }
+
+ const branch = normalizeSimpleBranch(branches[0]);
+ if (!branch || branch.length !== 1) {
+ return;
+ }
+
+ chars.push(branch);
+ index += match.end + 1;
+ }
+
+ if (chars.length < 1) {
+ return;
+ }
+
+ const source = chars.length === 1
+ ? utils.escapeRegex(chars[0])
+ : `[${chars.map(ch => utils.escapeRegex(ch)).join('')}]`;
+
+ return `${source}*`;
+};
+
+const repeatedExtglobRecursion = pattern => {
+ let depth = 0;
+ let value = pattern.trim();
+ let match = parseRepeatedExtglob(value);
+
+ while (match) {
+ depth++;
+ value = match.body.trim();
+ match = parseRepeatedExtglob(value);
+ }
+
+ return depth;
+};
+
+const analyzeRepeatedExtglob = (body, options) => {
+ if (options.maxExtglobRecursion === false) {
+ return { risky: false };
+ }
+
+ const max =
+ typeof options.maxExtglobRecursion === 'number'
+ ? options.maxExtglobRecursion
+ : constants.DEFAULT_MAX_EXTGLOB_RECURSION;
+
+ const branches = splitTopLevel(body).map(branch => branch.trim());
+
+ if (branches.length > 1) {
+ if (
+ branches.some(branch => branch === '') ||
+ branches.some(branch => /^[*?]+$/.test(branch)) ||
+ hasRepeatedCharPrefixOverlap(branches)
+ ) {
+ return { risky: true };
+ }
+ }
+
+ for (const branch of branches) {
+ const safeOutput = getStarExtglobSequenceOutput(branch);
+ if (safeOutput) {
+ return { risky: true, safeOutput };
+ }
+
+ if (repeatedExtglobRecursion(branch) > max) {
+ return { risky: true };
+ }
+ }
+
+ return { risky: false };
+};
+
+
/**
* Parse the given input string.
* @param {String} input
@@ -225,6 +497,8 @@ const parse = (input, options) => {
token.prev = prev;
token.parens = state.parens;
token.output = state.output;
+ token.startIndex = state.index;
+ token.tokensIndex = tokens.length;
const output = (opts.capture ? '(' : '') + token.open;

increment('parens');
@@ -234,6 +508,34 @@ const parse = (input, options) => {
};

const extglobClose = token => {
+ const literal = input.slice(token.startIndex, state.index + 1);
+ const body = input.slice(token.startIndex + 2, state.index);
+ const analysis = analyzeRepeatedExtglob(body, opts);
+
+ if ((token.type === 'plus' || token.type === 'star') && analysis.risky) {
+ const safeOutput = analysis.safeOutput
+ ? (token.output ? '' : ONE_CHAR) + (opts.capture ? `(${analysis.safeOutput})` : analysis.safeOutput)
+ : undefined;
+ const open = tokens[token.tokensIndex];
+
+ open.type = 'text';
+ open.value = literal;
+ open.output = safeOutput || utils.escapeRegex(literal);
+
+ for (let i = token.tokensIndex + 1; i < tokens.length; i++) {
+ tokens[i].value = '';
+ tokens[i].output = '';
+ delete tokens[i].suffix;
+ }
+
+ state.output = token.output + open.output;
+ state.backtrack = true;
+
+ push({ type: 'paren', extglob: true, value, output: '' });
+ decrement('parens');
+ return;
+ }
+
let output = token.close + (opts.capture ? ')' : '');
let rest;

--
2.45.4

Loading
Loading