diff --git a/0001-CVE-2026-13149-Brace-Expansion-DOS.patch b/0001-CVE-2026-13149-Brace-Expansion-DOS.patch new file mode 100644 index 0000000..a711155 --- /dev/null +++ b/0001-CVE-2026-13149-Brace-Expansion-DOS.patch @@ -0,0 +1,159 @@ +From 193b73ce0138d3d2d62acd97fe2268bcdfcd8da6 Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Tue, 14 Jul 2026 09:01:08 +0000 +Subject: [PATCH] deps: update brace-expansion to 5.0.7 + +A run of non-expanding {} groups expanded post once per group before the +early returns that never use it, doubling the work on every group. expand_ +ran in O(2^n) and blocked for minutes on a ~90 byte input. + +Defer expanding post until a brace set is known to expand, and turn the +{a},b} restart into a loop so a long run of {} groups can't exhaust the +call stack. + +Backport of upstream fix: +https://github.com/juliangruber/brace-expansion/commit/c7e33ec13ac1a684c116720843ce24e208611754 + +CVE: CVE-2026-13149 +--- + .../brace-expansion/dist/commonjs/index.js | 38 ++++++++++++------- + .../brace-expansion/dist/esm/index.js | 38 ++++++++++++------- + .../node_modules/brace-expansion/package.json | 2 +- + 3 files changed, 49 insertions(+), 29 deletions(-) + +diff --git a/deps/npm/node_modules/brace-expansion/dist/commonjs/index.js b/deps/npm/node_modules/brace-expansion/dist/commonjs/index.js +index 33063dd..e4e5bd8 100644 +--- a/deps/npm/node_modules/brace-expansion/dist/commonjs/index.js ++++ b/deps/npm/node_modules/brace-expansion/dist/commonjs/index.js +@@ -95,19 +95,23 @@ function gte(i, y) { + function expand_(str, max, isTop) { + /** @type {string[]} */ + const expansions = []; +- const m = (0, balanced_match_1.balanced)('{', '}', str); +- if (!m) +- return [str]; +- // no need to expand pre, since it is guaranteed to be free of brace-sets +- const pre = m.pre; +- const post = m.post.length ? expand_(m.post, max, false) : ['']; +- if (/\$$/.test(m.pre)) { +- for (let k = 0; k < post.length && k < max; k++) { +- const expansion = pre + '{' + m.body + '}' + post[k]; +- expansions.push(expansion); ++ // The `{a},b}` rewrite below restarts expansion on a rewritten string with ++ // the same `max` and `isTop = true`. Loop instead of recursing so a long run ++ // of non-expanding `{}` groups can't exhaust the call stack. ++ for (;;) { ++ const m = (0, balanced_match_1.balanced)('{', '}', str); ++ if (!m) ++ return [str]; ++ // no need to expand pre, since it is guaranteed to be free of brace-sets ++ const pre = m.pre; ++ if (/\$$/.test(m.pre)) { ++ const post = m.post.length ? expand_(m.post, max, false) : ['']; ++ for (let k = 0; k < post.length && k < max; k++) { ++ const expansion = pre + '{' + m.body + '}' + post[k]; ++ expansions.push(expansion); ++ } ++ return expansions; + } +- } +- else { + const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); + const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); + const isSequence = isNumericSequence || isAlphaSequence; +@@ -116,10 +120,16 @@ function expand_(str, max, isTop) { + // {a},b} + if (m.post.match(/,(?!,).*\}/)) { + str = m.pre + '{' + m.body + escClose + m.post; +- return expand_(str, max, true); ++ isTop = true; ++ continue; + } + return [str]; + } ++ // Only expand post once we know this brace set actually expands. Computing ++ // it before the early returns above expanded post a second time on every ++ // non-expanding `{}`, which is what made inputs like `a{},{},{}...` blow up ++ // exponentially. ++ const post = m.post.length ? expand_(m.post, max, false) : ['']; + let n; + if (isSequence) { + n = m.body.split(/\.\./); +@@ -195,7 +205,7 @@ function expand_(str, max, isTop) { + } + } + } ++ return expansions; + } +- return expansions; + } + //# sourceMappingURL=index.js.map +\ No newline at end of file +diff --git a/deps/npm/node_modules/brace-expansion/dist/esm/index.js b/deps/npm/node_modules/brace-expansion/dist/esm/index.js +index 32399e7..b2d2aa9 100644 +--- a/deps/npm/node_modules/brace-expansion/dist/esm/index.js ++++ b/deps/npm/node_modules/brace-expansion/dist/esm/index.js +@@ -91,19 +91,23 @@ function gte(i, y) { + function expand_(str, max, isTop) { + /** @type {string[]} */ + const expansions = []; +- const m = balanced('{', '}', str); +- if (!m) +- return [str]; +- // no need to expand pre, since it is guaranteed to be free of brace-sets +- const pre = m.pre; +- const post = m.post.length ? expand_(m.post, max, false) : ['']; +- if (/\$$/.test(m.pre)) { +- for (let k = 0; k < post.length && k < max; k++) { +- const expansion = pre + '{' + m.body + '}' + post[k]; +- expansions.push(expansion); ++ // The `{a},b}` rewrite below restarts expansion on a rewritten string with ++ // the same `max` and `isTop = true`. Loop instead of recursing so a long run ++ // of non-expanding `{}` groups can't exhaust the call stack. ++ for (;;) { ++ const m = balanced('{', '}', str); ++ if (!m) ++ return [str]; ++ // no need to expand pre, since it is guaranteed to be free of brace-sets ++ const pre = m.pre; ++ if (/\$$/.test(m.pre)) { ++ const post = m.post.length ? expand_(m.post, max, false) : ['']; ++ for (let k = 0; k < post.length && k < max; k++) { ++ const expansion = pre + '{' + m.body + '}' + post[k]; ++ expansions.push(expansion); ++ } ++ return expansions; + } +- } +- else { + const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); + const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); + const isSequence = isNumericSequence || isAlphaSequence; +@@ -112,10 +116,16 @@ function expand_(str, max, isTop) { + // {a},b} + if (m.post.match(/,(?!,).*\}/)) { + str = m.pre + '{' + m.body + escClose + m.post; +- return expand_(str, max, true); ++ isTop = true; ++ continue; + } + return [str]; + } ++ // Only expand post once we know this brace set actually expands. Computing ++ // it before the early returns above expanded post a second time on every ++ // non-expanding `{}`, which is what made inputs like `a{},{},{}...` blow up ++ // exponentially. ++ const post = m.post.length ? expand_(m.post, max, false) : ['']; + let n; + if (isSequence) { + n = m.body.split(/\.\./); +@@ -191,7 +201,7 @@ function expand_(str, max, isTop) { + } + } + } ++ return expansions; + } +- return expansions; + } + //# sourceMappingURL=index.js.map +\ No newline at end of file diff --git a/nodejs24.spec b/nodejs24.spec index 5699790..b9476d8 100644 --- a/nodejs24.spec +++ b/nodejs24.spec @@ -155,6 +155,9 @@ Source101: nodejs.srpm.macros # Sourced from: # https://github.com/nodejs/node/commit/fd350185539242b7d383ebf38f6041f10b472b39 0001-CVE-2026-59873-CVE-2026-59874-upgrade-bundled-tar-to-7.5.19.patch +# Sourced from: +# https://github.com/juliangruber/brace-expansion/commit/c7e33ec13ac1a684c116720843ce24e208611754 +0001-CVE-2026-13149-Brace-Expansion-DOS.patch %description Node.js is a platform built on Chrome's JavaScript runtime