Le 05/10/2026 à 11:27, Salvatore Bonaccorso a écrit :
Hi Xavier,

On Sat, Oct 03, 2026 at 07:47:47AM +0200, Xavier wrote:
Le 02/10/2026 à 16:51, Salvatore Bonaccorso a écrit :
Source: node-shell-quote
Version: 1.10.0-1
X-Debbugs-CC: [email protected]
Severity: grave
Tags: security upstream

Hi,

The following vulnerability was published for node-shell-quote.

CVE-2026-102422[0]:

Hi,

here is the debdiff. If you don't consider it as urgent, of course I can
push it to release.debian.org.

We have node-shell-quote ineed in dsa-needed list, and issue
warranting a DSA. But while at it, can you as well include the fix for
the no-dsa marked one, CVE-2026-13311? Or is there a reason we should
rather ignore it?

Regards,
Salvatore

Hi,

done. I also fixed the debdiff, base was not good
diff --git a/debian/changelog b/debian/changelog
index 05e6b17..273622b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,12 @@
+node-shell-quote (1.7.4+~1.7.1-1+deb13u2) trixie; urgency=medium
+
+  * Team upload
+  * Reject line terminators in tokens after a comment
+    (Closes: #1149713, CVE-2026-102422)
+  * Finalize parse tokens in linear time (Closes: #1140921, CVE-2026-13311)
+
+ -- Xavier Guimard <[email protected]>  Mon, 05 Oct 2026 14:56:23 +0200
+
 node-shell-quote (1.7.4+~1.7.1-1+deb13u1) trixie-security; urgency=medium
 
   * Team upload
diff --git a/debian/patches/CVE-2026-102422.patch 
b/debian/patches/CVE-2026-102422.patch
new file mode 100644
index 0000000..c4ef5c8
--- /dev/null
+++ b/debian/patches/CVE-2026-102422.patch
@@ -0,0 +1,83 @@
+Description: reject line terminators in tokens after a comment
+Author: Jordan Harband <[email protected]>
+Origin: upstream, https://github.com/ljharb/shell-quote/commit/6002b2ed
+Bug: https://www.cve.org/CVERecord?id=CVE-2026-102422
+Bug-Debian: https://bugs.debian.org/1149713
+Forwarded: not-needed
+Applied-Upstream: 1.12.0, commit:6002b2ed
+Reviewed-By: Xavier Guimard <[email protected]>
+
+--- a/README.md
++++ b/README.md
+@@ -111,8 +111,9 @@
+ `parse` emits: `{ op }` (where `op` is one of the control operators
+ `||`, `&&`, `;;`, `|&`, `<(`, `<<<`, `>>`, `>&`, `<&`, `&`, `;`, `(`,
+ `)`, `|`, `<`, `>`), `{ op: 'glob', pattern }`, or `{ comment }`. Any
+-other object shape, an unrecognized `op`, or a `pattern`/`comment`
+-containing line terminators throws a `TypeError`.
++other object shape, an unrecognized `op`, a `pattern`/`comment`
++containing line terminators, or a string containing line terminators
++anywhere after a `{ comment }` throws a `TypeError`.
+ 
+ ## parse(cmd, env={})
+ 
+--- a/index.js
++++ b/index.js
+@@ -22,7 +22,11 @@
+ var GLOB_SHELL_SPECIAL = /[\s#!"$&'():;<=>@\\^`|]/g;
+ 
+ exports.quote = function (xs) {
++      var sawComment = false;
+       return xs.map(function (s) {
++              if (sawComment && typeof s === 'string' && 
LINE_TERMINATORS.test(s)) {
++                      throw new TypeError('a token after a `comment` must not 
contain line terminators');
++              }
+               if (s && typeof s === 'object') {
+                       if (s.op === 'glob') {
+                               if (typeof s.pattern !== 'string') {
+@@ -43,6 +47,7 @@
+                               if (LINE_TERMINATORS.test(s.comment)) {
+                                       throw new TypeError('`comment` must not 
contain line terminators');
+                               }
++                              sawComment = true;
+                               return '#' + s.comment;
+                       }
+                       throw new TypeError('unrecognized object token shape');
+--- a/test/quote.js
++++ b/test/quote.js
+@@ -2,6 +2,7 @@
+ 
+ var test = require('tape');
+ var quote = require('../').quote;
++var parse = require('../').parse;
+ 
+ test('quote', function (t) {
+       t.equal(quote(['a', 'b', 'c d']), 'a b \'c d\'');
+@@ -98,6 +99,27 @@
+       t.end();
+ });
+ 
++test('quote comment: rejects line terminators in later tokens', function (t) {
++      t['throws'](function () { quote(['echo', { comment: 'x' }, 'a\nid;#']); 
}, TypeError, 'newline after a comment');
++      t['throws'](function () { quote(['echo', { comment: 'x' }, 'a\rb']); }, 
TypeError, 'CR after a comment');
++      t['throws'](function () { quote(['echo', { comment: 'x' }, 
'a\u2028b']); }, TypeError, 'U+2028 after a comment');
++      t['throws'](function () { quote(['echo', { comment: 'x' }, 
'a\u2029b']); }, TypeError, 'U+2029 after a comment');
++      t['throws'](
++              function () { quote(['echo', { comment: 'x' }, 'ok', 
'it\'s\nid;#']); },
++              TypeError,
++              'newline in any later token, not just the next one'
++      );
++      t['throws'](
++              function () { quote(parse('curl 
http://x/#frag').concat('a\nid;#')); },
++              TypeError,
++              'a mid-word `#` from parse, followed by an appended token'
++      );
++
++      t.equal(quote(['echo', 'a\nb', { comment: 'x' }]), 'echo \'a\nb\' #x', 
'a line terminator before a comment is fine');
++      t.equal(quote(['echo', { comment: 'x' }, 'y']), 'echo #x y', 'later 
tokens without line terminators are unchanged');
++      t.end();
++});
++
+ test('quote rejects unrecognized object shapes', function (t) {
+       t['throws'](function () { quote([{}]); }, TypeError, 'empty object');
+       t['throws'](function () { quote([{ foo: 'bar' }]); }, TypeError, 
'unknown key');
diff --git a/debian/patches/CVE-2026-13311.patch 
b/debian/patches/CVE-2026-13311.patch
new file mode 100644
index 0000000..37474ea
--- /dev/null
+++ b/debian/patches/CVE-2026-13311.patch
@@ -0,0 +1,80 @@
+Description: `parse`: finalize tokens in linear time
+ The two finalizing reduces used `prev.concat(arg)` as the accumulator,
+ reallocating and copying the whole array each iteration, so `parse()` ran
+ in O(n²) in the token count: a small (~128 KB) input could block the event
+ loop for tens of seconds (CWE-407 DoS).
+ Push into the accumulator instead.
+Author: Jordan Harband <[email protected]>
+Origin: upstream, https://github.com/ljharb/shell-quote/commit/7ff54885
+Bug: 
https://github.com/ljharb/shell-quote/security/advisories/GHSA-395f-4hp3-45gv
+Bug-Debian: https://bugs.debian.org/1140921
+Forwarded: not-needed
+Applied-Upstream: 1.9.0, commit:7ff54885
+Reviewed-By: Xavier Guimard <[email protected]>
+Last-Update: 2026-10-05
+
+--- a/index.js
++++ b/index.js
+@@ -223,7 +223,10 @@
+               if (arg === undefined) {
+                       return prev;
+               }
+-              return prev.concat(arg);
++              [].concat(arg).forEach(function (entry) {
++                      prev[prev.length] = entry;
++              });
++              return prev;
+       }, []);
+ }
+ 
+@@ -234,17 +237,19 @@
+       }
+       return mapped.reduce(function (acc, s) {
+               if (typeof s === 'object') {
+-                      return acc.concat(s);
++                      acc[acc.length] = s;
++                      return acc;
+               }
+               var xs = s.split(RegExp('(' + TOKEN + '.*?' + TOKEN + ')', 
'g'));
+               if (xs.length === 1) {
+-                      return acc.concat(xs[0]);
++                      acc[acc.length] = xs[0];
++                      return acc;
+               }
+-              return acc.concat(xs.filter(Boolean).map(function (x) {
+-                      if (RegExp('^' + TOKEN).test(x)) {
+-                              return JSON.parse(x.split(TOKEN)[1]);
+-                      }
+-                      return x;
+-              }));
++              xs.filter(Boolean).forEach(function (x) {
++                      acc[acc.length] = RegExp('^' + TOKEN).test(x)
++                              ? JSON.parse(x.split(TOKEN)[1])
++                              : x;
++              });
++              return acc;
+       }, []);
+ };
+--- a/test/parse.js
++++ b/test/parse.js
+@@ -23,3 +23,20 @@
+ 
+       t.end();
+ });
++
++test('parse stays linear in token count (GHSA-395f-4hp3-45gv)', function (t) {
++      // the old concat-in-reduce finalizer was O(n^2): this many tokens took
++      // ~minutes, so under the unfixed code this test hangs rather than 
passes
++      var n = 2e5;
++      var input = new Array(n + 1).join('x '); // avoid String#repeat for old 
engines
++
++      var words = parse(input);
++      t.equal(words.length, n, 'every token is returned');
++      t.equal(words[0], 'x', 'first token is correct');
++      t.equal(words[n - 1], 'x', 'last token is correct');
++
++      var withEnv = parse(input, function () { return 'v'; });
++      t.equal(withEnv.length, n, 'env-function path returns every token');
++
++      t.end();
++});
diff --git a/debian/patches/series b/debian/patches/series
index fba295f..3038468 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1 +1,3 @@
 CVE-2026-9277.patch
+CVE-2026-102422.patch
+CVE-2026-13311.patch
-- 
Pkg-javascript-devel mailing list
[email protected]
https://alioth-lists.debian.net/cgi-bin/mailman/listinfo/pkg-javascript-devel

Reply via email to