diff options
author | Polampalli, Archana <archana.polampalli@windriver.com> | 2023-08-31 04:57:43 +0000 |
---|---|---|
committer | Armin Kuster <akuster808@gmail.com> | 2023-09-04 11:59:59 -0400 |
commit | d3ee870fb0acbda1c27aa58311547733cccb1df2 (patch) | |
tree | 361549c4a346a05c7a0ddec7592f111dc74663b7 | |
parent | 71d9cabed74b29741babe26a657adf6ecedb3bf4 (diff) | |
download | meta-openembedded-d3ee870fb0acbda1c27aa58311547733cccb1df2.tar.gz |
nodejs: fix CVE-2022-25883
Versions of the package semver before 7.5.2 are vulnerable to Regular Expression
Denial of Service (ReDoS) via the function new Range, when untrusted user data is
provided as a range.
References:
https://nvd.nist.gov/vuln/detail/CVE-2022-25883
Upstream patches:
https://github.com/npm/node-semver/commit/717534ee353682f3bcf33e60a8af4292626d4441
Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com>
Signed-off-by: Armin Kuster <akuster808@gmail.com>
-rw-r--r-- | meta-oe/recipes-devtools/nodejs/nodejs/CVE-2022-25883.patch | 262 | ||||
-rw-r--r-- | meta-oe/recipes-devtools/nodejs/nodejs_16.20.2.bb | 1 |
2 files changed, 263 insertions, 0 deletions
diff --git a/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2022-25883.patch b/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2022-25883.patch new file mode 100644 index 0000000000..4c73b556f9 --- /dev/null +++ b/meta-oe/recipes-devtools/nodejs/nodejs/CVE-2022-25883.patch | |||
@@ -0,0 +1,262 @@ | |||
1 | From 717534ee353682f3bcf33e60a8af4292626d4441 Mon Sep 17 00:00:00 2001 | ||
2 | From: Luke Karrys <luke@lukekarrys.com> | ||
3 | Date: Thu, 15 Jun 2023 12:21:14 -0700 | ||
4 | Subject: [PATCH] fix: better handling of whitespace (#564) | ||
5 | |||
6 | CVE: CVE-2022-25883 | ||
7 | |||
8 | Upstream-Status: Backport [https://github.com/npm/node-semver/commit/717534ee353682f3bcf33e60a8af4292626d4441] | ||
9 | |||
10 | Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com> | ||
11 | --- | ||
12 | .../node_modules/semver/classes/comparator.js | 3 +- | ||
13 | deps/npm/node_modules/semver/classes/range.js | 64 +++++++++++-------- | ||
14 | .../npm/node_modules/semver/classes/semver.js | 2 +- | ||
15 | .../node_modules/semver/functions/coerce.js | 2 +- | ||
16 | deps/npm/node_modules/semver/internal/re.js | 11 ++++ | ||
17 | deps/npm/node_modules/semver/package.json | 2 +- | ||
18 | 6 files changed, 53 insertions(+), 31 deletions(-) | ||
19 | |||
20 | diff --git a/deps/npm/node_modules/semver/classes/comparator.js b/deps/npm/node_modules/semver/classes/comparator.js | ||
21 | index 62cd204..c909446 100644 | ||
22 | --- a/deps/npm/node_modules/semver/classes/comparator.js | ||
23 | +++ b/deps/npm/node_modules/semver/classes/comparator.js | ||
24 | @@ -16,6 +16,7 @@ class Comparator { | ||
25 | } | ||
26 | } | ||
27 | |||
28 | + comp = comp.trim().split(/\s+/).join(' ') | ||
29 | debug('comparator', comp, options) | ||
30 | this.options = options | ||
31 | this.loose = !!options.loose | ||
32 | @@ -129,7 +130,7 @@ class Comparator { | ||
33 | module.exports = Comparator | ||
34 | |||
35 | const parseOptions = require('../internal/parse-options') | ||
36 | -const { re, t } = require('../internal/re') | ||
37 | +const { safeRe: re, t } = require('../internal/re') | ||
38 | const cmp = require('../functions/cmp') | ||
39 | const debug = require('../internal/debug') | ||
40 | const SemVer = require('./semver') | ||
41 | diff --git a/deps/npm/node_modules/semver/classes/range.js b/deps/npm/node_modules/semver/classes/range.js | ||
42 | index 7dc24bc..8e2e1f9 100644 | ||
43 | --- a/deps/npm/node_modules/semver/classes/range.js | ||
44 | +++ b/deps/npm/node_modules/semver/classes/range.js | ||
45 | @@ -26,19 +26,26 @@ class Range { | ||
46 | this.loose = !!options.loose | ||
47 | this.includePrerelease = !!options.includePrerelease | ||
48 | |||
49 | - // First, split based on boolean or || | ||
50 | + // First reduce all whitespace as much as possible so we do not have to rely | ||
51 | + // on potentially slow regexes like \s*. This is then stored and used for | ||
52 | + // future error messages as well. | ||
53 | this.raw = range | ||
54 | - this.set = range | ||
55 | + .trim() | ||
56 | + .split(/\s+/) | ||
57 | + .join(' ') | ||
58 | + | ||
59 | + // First, split on || | ||
60 | + this.set = this.raw | ||
61 | .split('||') | ||
62 | // map the range to a 2d array of comparators | ||
63 | - .map(r => this.parseRange(r.trim())) | ||
64 | + .map(r => this.parseRange(r)) | ||
65 | // throw out any comparator lists that are empty | ||
66 | // this generally means that it was not a valid range, which is allowed | ||
67 | // in loose mode, but will still throw if the WHOLE range is invalid. | ||
68 | .filter(c => c.length) | ||
69 | |||
70 | if (!this.set.length) { | ||
71 | - throw new TypeError(`Invalid SemVer Range: ${range}`) | ||
72 | + throw new TypeError(`Invalid SemVer Range: ${this.raw}`) | ||
73 | } | ||
74 | |||
75 | // if we have any that are not the null set, throw out null sets. | ||
76 | @@ -64,9 +71,7 @@ class Range { | ||
77 | |||
78 | format () { | ||
79 | this.range = this.set | ||
80 | - .map((comps) => { | ||
81 | - return comps.join(' ').trim() | ||
82 | - }) | ||
83 | + .map((comps) => comps.join(' ').trim()) | ||
84 | .join('||') | ||
85 | .trim() | ||
86 | return this.range | ||
87 | @@ -77,8 +82,6 @@ class Range { | ||
88 | } | ||
89 | |||
90 | parseRange (range) { | ||
91 | - range = range.trim() | ||
92 | - | ||
93 | // memoize range parsing for performance. | ||
94 | // this is a very hot path, and fully deterministic. | ||
95 | const memoOpts = Object.keys(this.options).join(',') | ||
96 | @@ -103,9 +106,6 @@ class Range { | ||
97 | // `^ 1.2.3` => `^1.2.3` | ||
98 | range = range.replace(re[t.CARETTRIM], caretTrimReplace) | ||
99 | |||
100 | - // normalize spaces | ||
101 | - range = range.split(/\s+/).join(' ') | ||
102 | - | ||
103 | // At this point, the range is completely trimmed and | ||
104 | // ready to be split into comparators. | ||
105 | |||
106 | @@ -200,7 +200,7 @@ const Comparator = require('./comparator') | ||
107 | const debug = require('../internal/debug') | ||
108 | const SemVer = require('./semver') | ||
109 | const { | ||
110 | - re, | ||
111 | + safeRe: re, | ||
112 | t, | ||
113 | comparatorTrimReplace, | ||
114 | tildeTrimReplace, | ||
115 | @@ -252,10 +252,13 @@ const isX = id => !id || id.toLowerCase() === 'x' || id === '*' | ||
116 | // ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0-0 | ||
117 | // ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0-0 | ||
118 | // ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0-0 | ||
119 | -const replaceTildes = (comp, options) => | ||
120 | - comp.trim().split(/\s+/).map((c) => { | ||
121 | - return replaceTilde(c, options) | ||
122 | - }).join(' ') | ||
123 | +const replaceTildes = (comp, options) => { | ||
124 | + return comp | ||
125 | + .trim() | ||
126 | + .split(/\s+/) | ||
127 | + .map((c) => replaceTilde(c, options)) | ||
128 | + .join(' ') | ||
129 | +} | ||
130 | |||
131 | const replaceTilde = (comp, options) => { | ||
132 | const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE] | ||
133 | @@ -291,10 +294,13 @@ const replaceTilde = (comp, options) => { | ||
134 | // ^1.2, ^1.2.x --> >=1.2.0 <2.0.0-0 | ||
135 | // ^1.2.3 --> >=1.2.3 <2.0.0-0 | ||
136 | // ^1.2.0 --> >=1.2.0 <2.0.0-0 | ||
137 | -const replaceCarets = (comp, options) => | ||
138 | - comp.trim().split(/\s+/).map((c) => { | ||
139 | - return replaceCaret(c, options) | ||
140 | - }).join(' ') | ||
141 | +const replaceCarets = (comp, options) => { | ||
142 | + return comp | ||
143 | + .trim() | ||
144 | + .split(/\s+/) | ||
145 | + .map((c) => replaceCaret(c, options)) | ||
146 | + .join(' ') | ||
147 | +} | ||
148 | |||
149 | const replaceCaret = (comp, options) => { | ||
150 | debug('caret', comp, options) | ||
151 | @@ -351,9 +357,10 @@ const replaceCaret = (comp, options) => { | ||
152 | |||
153 | const replaceXRanges = (comp, options) => { | ||
154 | debug('replaceXRanges', comp, options) | ||
155 | - return comp.split(/\s+/).map((c) => { | ||
156 | - return replaceXRange(c, options) | ||
157 | - }).join(' ') | ||
158 | + return comp | ||
159 | + .split(/\s+/) | ||
160 | + .map((c) => replaceXRange(c, options)) | ||
161 | + .join(' ') | ||
162 | } | ||
163 | |||
164 | const replaceXRange = (comp, options) => { | ||
165 | @@ -436,12 +443,15 @@ const replaceXRange = (comp, options) => { | ||
166 | const replaceStars = (comp, options) => { | ||
167 | debug('replaceStars', comp, options) | ||
168 | // Looseness is ignored here. star is always as loose as it gets! | ||
169 | - return comp.trim().replace(re[t.STAR], '') | ||
170 | + return comp | ||
171 | + .trim() | ||
172 | + .replace(re[t.STAR], '') | ||
173 | } | ||
174 | |||
175 | const replaceGTE0 = (comp, options) => { | ||
176 | debug('replaceGTE0', comp, options) | ||
177 | - return comp.trim() | ||
178 | + return comp | ||
179 | + .trim() | ||
180 | .replace(re[options.includePrerelease ? t.GTE0PRE : t.GTE0], '') | ||
181 | } | ||
182 | |||
183 | @@ -479,7 +489,7 @@ const hyphenReplace = incPr => ($0, | ||
184 | to = `<=${to}` | ||
185 | } | ||
186 | |||
187 | - return (`${from} ${to}`).trim() | ||
188 | + return `${from} ${to}`.trim() | ||
189 | } | ||
190 | |||
191 | const testSet = (set, version, options) => { | ||
192 | diff --git a/deps/npm/node_modules/semver/classes/semver.js b/deps/npm/node_modules/semver/classes/semver.js | ||
193 | index af62955..ad4e877 100644 | ||
194 | --- a/deps/npm/node_modules/semver/classes/semver.js | ||
195 | +++ b/deps/npm/node_modules/semver/classes/semver.js | ||
196 | @@ -1,6 +1,6 @@ | ||
197 | const debug = require('../internal/debug') | ||
198 | const { MAX_LENGTH, MAX_SAFE_INTEGER } = require('../internal/constants') | ||
199 | -const { re, t } = require('../internal/re') | ||
200 | +const { safeRe: re, t } = require('../internal/re') | ||
201 | |||
202 | const parseOptions = require('../internal/parse-options') | ||
203 | const { compareIdentifiers } = require('../internal/identifiers') | ||
204 | diff --git a/deps/npm/node_modules/semver/functions/coerce.js b/deps/npm/node_modules/semver/functions/coerce.js | ||
205 | index 2e01452..febbff9 100644 | ||
206 | --- a/deps/npm/node_modules/semver/functions/coerce.js | ||
207 | +++ b/deps/npm/node_modules/semver/functions/coerce.js | ||
208 | @@ -1,6 +1,6 @@ | ||
209 | const SemVer = require('../classes/semver') | ||
210 | const parse = require('./parse') | ||
211 | -const { re, t } = require('../internal/re') | ||
212 | +const { safeRe: re, t } = require('../internal/re') | ||
213 | |||
214 | const coerce = (version, options) => { | ||
215 | if (version instanceof SemVer) { | ||
216 | diff --git a/deps/npm/node_modules/semver/internal/re.js b/deps/npm/node_modules/semver/internal/re.js | ||
217 | index ed88398..f73ef1a 100644 | ||
218 | --- a/deps/npm/node_modules/semver/internal/re.js | ||
219 | +++ b/deps/npm/node_modules/semver/internal/re.js | ||
220 | @@ -4,16 +4,27 @@ exports = module.exports = {} | ||
221 | |||
222 | // The actual regexps go on exports.re | ||
223 | const re = exports.re = [] | ||
224 | +const safeRe = exports.safeRe = [] | ||
225 | const src = exports.src = [] | ||
226 | const t = exports.t = {} | ||
227 | let R = 0 | ||
228 | |||
229 | const createToken = (name, value, isGlobal) => { | ||
230 | + // Replace all greedy whitespace to prevent regex dos issues. These regex are | ||
231 | + // used internally via the safeRe object since all inputs in this library get | ||
232 | + // normalized first to trim and collapse all extra whitespace. The original | ||
233 | + // regexes are exported for userland consumption and lower level usage. A | ||
234 | + // future breaking change could export the safer regex only with a note that | ||
235 | + // all input should have extra whitespace removed. | ||
236 | + const safe = value | ||
237 | + .split('\\s*').join('\\s{0,1}') | ||
238 | + .split('\\s+').join('\\s') | ||
239 | const index = R++ | ||
240 | debug(name, index, value) | ||
241 | t[name] = index | ||
242 | src[index] = value | ||
243 | re[index] = new RegExp(value, isGlobal ? 'g' : undefined) | ||
244 | + safeRe[index] = new RegExp(safe, isGlobal ? 'g' : undefined) | ||
245 | } | ||
246 | |||
247 | // The following Regular Expressions can be used for tokenizing, | ||
248 | diff --git a/deps/npm/node_modules/semver/package.json b/deps/npm/node_modules/semver/package.json | ||
249 | index 7898f59..d8ae619 100644 | ||
250 | --- a/deps/npm/node_modules/semver/package.json | ||
251 | +++ b/deps/npm/node_modules/semver/package.json | ||
252 | @@ -40,7 +40,7 @@ | ||
253 | "range.bnf" | ||
254 | ], | ||
255 | "tap": { | ||
256 | - "check-coverage": true, | ||
257 | + "timeout": 30, | ||
258 | "coverage-map": "map.js" | ||
259 | }, | ||
260 | "engines": { | ||
261 | -- | ||
262 | 2.40.0 | ||
diff --git a/meta-oe/recipes-devtools/nodejs/nodejs_16.20.2.bb b/meta-oe/recipes-devtools/nodejs/nodejs_16.20.2.bb index 4761bfb14f..16593a0fe6 100644 --- a/meta-oe/recipes-devtools/nodejs/nodejs_16.20.2.bb +++ b/meta-oe/recipes-devtools/nodejs/nodejs_16.20.2.bb | |||
@@ -26,6 +26,7 @@ SRC_URI = "http://nodejs.org/dist/v${PV}/node-v${PV}.tar.xz \ | |||
26 | file://0001-liftoff-Correct-function-signatures.patch \ | 26 | file://0001-liftoff-Correct-function-signatures.patch \ |
27 | file://0001-mips-Use-32bit-cast-for-operand-on-mips32.patch \ | 27 | file://0001-mips-Use-32bit-cast-for-operand-on-mips32.patch \ |
28 | file://0001-Nodejs-Fixed-pipes-DeprecationWarning.patch \ | 28 | file://0001-Nodejs-Fixed-pipes-DeprecationWarning.patch \ |
29 | file://CVE-2022-25883.patch \ | ||
29 | " | 30 | " |
30 | SRC_URI:append:class-target = " \ | 31 | SRC_URI:append:class-target = " \ |
31 | file://0001-Using-native-binaries.patch \ | 32 | file://0001-Using-native-binaries.patch \ |