Patch CVE-2022-25883 in nodejs v16 (#5823)

This commit is contained in:
Olivia Crain 2023-07-12 19:05:49 -05:00 коммит произвёл GitHub
Родитель 9ea5f187b3
Коммит e10b21f9eb
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 331 добавлений и 1 удалений

Просмотреть файл

@ -0,0 +1,326 @@
From 12b07816ba23e72527d8c36108889d7ee7cbfe4f Mon Sep 17 00:00:00 2001
From: Olivia Crain <olivia@olivia.dev>
Date: Wed, 12 Jul 2023 13:09:19 -0700
Subject: [PATCH] Patch CVE-2022-25883 in vendored semver source
Backport of the following commits from npm/node-semver upstream:
npm/node-semver@717534ee353682f3bcf33e60a8af4292626d4441
npm/node-semver@cc6fde2d34b95cb600d126649d926901bd2a9703
npm/node-semver@abdd93d55496d22e3c15a454a5cf13f101e48bce
npm/node-semver@99d8287516a1d2abf0286033e2e26eca6b69c09f
---
.../node_modules/semver/classes/comparator.js | 3 +-
deps/npm/node_modules/semver/classes/range.js | 65 +++++++++++--------
.../npm/node_modules/semver/classes/semver.js | 2 +-
.../node_modules/semver/functions/coerce.js | 2 +-
.../node_modules/semver/internal/constants.js | 5 ++
deps/npm/node_modules/semver/internal/re.js | 38 +++++++++--
6 files changed, 82 insertions(+), 33 deletions(-)
diff --git a/deps/npm/node_modules/semver/classes/comparator.js b/deps/npm/node_modules/semver/classes/comparator.js
index 62cd204d..c909446d 100644
--- a/deps/npm/node_modules/semver/classes/comparator.js
+++ b/deps/npm/node_modules/semver/classes/comparator.js
@@ -16,6 +16,7 @@ class Comparator {
}
}
+ comp = comp.trim().split(/\s+/).join(' ')
debug('comparator', comp, options)
this.options = options
this.loose = !!options.loose
@@ -129,7 +130,7 @@ class Comparator {
module.exports = Comparator
const parseOptions = require('../internal/parse-options')
-const { re, t } = require('../internal/re')
+const { safeRe: re, t } = require('../internal/re')
const cmp = require('../functions/cmp')
const debug = require('../internal/debug')
const SemVer = require('./semver')
diff --git a/deps/npm/node_modules/semver/classes/range.js b/deps/npm/node_modules/semver/classes/range.js
index 7dc24bc7..1d084ff9 100644
--- a/deps/npm/node_modules/semver/classes/range.js
+++ b/deps/npm/node_modules/semver/classes/range.js
@@ -26,9 +26,16 @@ class Range {
this.loose = !!options.loose
this.includePrerelease = !!options.includePrerelease
- // First, split based on boolean or ||
+ // First reduce all whitespace as much as possible so we do not have to rely
+ // on potentially slow regexes like \s*. This is then stored and used for
+ // future error messages as well.
this.raw = range
- this.set = range
+ .trim()
+ .split(/\s+/)
+ .join(' ')
+
+ // First, split on ||
+ this.set = this.raw
.split('||')
// map the range to a 2d array of comparators
.map(r => this.parseRange(r.trim()))
@@ -38,7 +45,7 @@ class Range {
.filter(c => c.length)
if (!this.set.length) {
- throw new TypeError(`Invalid SemVer Range: ${range}`)
+ throw new TypeError(`Invalid SemVer Range: ${this.raw}`)
}
// if we have any that are not the null set, throw out null sets.
@@ -64,9 +71,7 @@ class Range {
format () {
this.range = this.set
- .map((comps) => {
- return comps.join(' ').trim()
- })
+ .map((comps) => comps.join(' ').trim())
.join('||')
.trim()
return this.range
@@ -77,8 +82,6 @@ class Range {
}
parseRange (range) {
- range = range.trim()
-
// memoize range parsing for performance.
// this is a very hot path, and fully deterministic.
const memoOpts = Object.keys(this.options).join(',')
@@ -93,18 +96,18 @@ class Range {
const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE]
range = range.replace(hr, hyphenReplace(this.options.includePrerelease))
debug('hyphen replace', range)
+
// `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace)
debug('comparator trim', range)
// `~ 1.2.3` => `~1.2.3`
range = range.replace(re[t.TILDETRIM], tildeTrimReplace)
+ debug('tilde trim', range)
// `^ 1.2.3` => `^1.2.3`
range = range.replace(re[t.CARETTRIM], caretTrimReplace)
-
- // normalize spaces
- range = range.split(/\s+/).join(' ')
+ debug('caret trim', range)
// At this point, the range is completely trimmed and
// ready to be split into comparators.
@@ -200,7 +203,7 @@ const Comparator = require('./comparator')
const debug = require('../internal/debug')
const SemVer = require('./semver')
const {
- re,
+ safeRe: re,
t,
comparatorTrimReplace,
tildeTrimReplace,
@@ -252,10 +255,13 @@ const isX = id => !id || id.toLowerCase() === 'x' || id === '*'
// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0-0
// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0-0
// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0-0
-const replaceTildes = (comp, options) =>
- comp.trim().split(/\s+/).map((c) => {
- return replaceTilde(c, options)
- }).join(' ')
+const replaceTildes = (comp, options) => {
+ return comp
+ .trim()
+ .split(/\s+/)
+ .map((c) => replaceTilde(c, options))
+ .join(' ')
+}
const replaceTilde = (comp, options) => {
const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE]
@@ -291,10 +297,13 @@ const replaceTilde = (comp, options) => {
// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0-0
// ^1.2.3 --> >=1.2.3 <2.0.0-0
// ^1.2.0 --> >=1.2.0 <2.0.0-0
-const replaceCarets = (comp, options) =>
- comp.trim().split(/\s+/).map((c) => {
- return replaceCaret(c, options)
- }).join(' ')
+const replaceCarets = (comp, options) => {
+ return comp
+ .trim()
+ .split(/\s+/)
+ .map((c) => replaceCaret(c, options))
+ .join(' ')
+}
const replaceCaret = (comp, options) => {
debug('caret', comp, options)
@@ -351,9 +360,10 @@ const replaceCaret = (comp, options) => {
const replaceXRanges = (comp, options) => {
debug('replaceXRanges', comp, options)
- return comp.split(/\s+/).map((c) => {
- return replaceXRange(c, options)
- }).join(' ')
+ return comp
+ .split(/\s+/)
+ .map((c) => replaceXRange(c, options))
+ .join(' ')
}
const replaceXRange = (comp, options) => {
@@ -436,12 +446,15 @@ const replaceXRange = (comp, options) => {
const replaceStars = (comp, options) => {
debug('replaceStars', comp, options)
// Looseness is ignored here. star is always as loose as it gets!
- return comp.trim().replace(re[t.STAR], '')
+ return comp
+ .trim()
+ .replace(re[t.STAR], '')
}
const replaceGTE0 = (comp, options) => {
debug('replaceGTE0', comp, options)
- return comp.trim()
+ return comp
+ .trim()
.replace(re[options.includePrerelease ? t.GTE0PRE : t.GTE0], '')
}
@@ -479,7 +492,7 @@ const hyphenReplace = incPr => ($0,
to = `<=${to}`
}
- return (`${from} ${to}`).trim()
+ return `${from} ${to}`.trim()
}
const testSet = (set, version, options) => {
diff --git a/deps/npm/node_modules/semver/classes/semver.js b/deps/npm/node_modules/semver/classes/semver.js
index af629551..ad4e8775 100644
--- a/deps/npm/node_modules/semver/classes/semver.js
+++ b/deps/npm/node_modules/semver/classes/semver.js
@@ -1,6 +1,6 @@
const debug = require('../internal/debug')
const { MAX_LENGTH, MAX_SAFE_INTEGER } = require('../internal/constants')
-const { re, t } = require('../internal/re')
+const { safeRe: re, t } = require('../internal/re')
const parseOptions = require('../internal/parse-options')
const { compareIdentifiers } = require('../internal/identifiers')
diff --git a/deps/npm/node_modules/semver/functions/coerce.js b/deps/npm/node_modules/semver/functions/coerce.js
index 2e01452f..febbff9c 100644
--- a/deps/npm/node_modules/semver/functions/coerce.js
+++ b/deps/npm/node_modules/semver/functions/coerce.js
@@ -1,6 +1,6 @@
const SemVer = require('../classes/semver')
const parse = require('./parse')
-const { re, t } = require('../internal/re')
+const { safeRe: re, t } = require('../internal/re')
const coerce = (version, options) => {
if (version instanceof SemVer) {
diff --git a/deps/npm/node_modules/semver/internal/constants.js b/deps/npm/node_modules/semver/internal/constants.js
index 4f0de59b..30685a44 100644
--- a/deps/npm/node_modules/semver/internal/constants.js
+++ b/deps/npm/node_modules/semver/internal/constants.js
@@ -9,9 +9,14 @@ const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER ||
// Max safe segment length for coercion.
const MAX_SAFE_COMPONENT_LENGTH = 16
+// Max safe length for a build identifier. The max length minus 6 characters for
+// the shortest version with a build 0.0.0+BUILD.
+const MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6
+
module.exports = {
SEMVER_SPEC_VERSION,
MAX_LENGTH,
MAX_SAFE_INTEGER,
MAX_SAFE_COMPONENT_LENGTH,
+ MAX_SAFE_BUILD_LENGTH,
}
diff --git a/deps/npm/node_modules/semver/internal/re.js b/deps/npm/node_modules/semver/internal/re.js
index ed88398a..21150b3e 100644
--- a/deps/npm/node_modules/semver/internal/re.js
+++ b/deps/npm/node_modules/semver/internal/re.js
@@ -1,19 +1,49 @@
-const { MAX_SAFE_COMPONENT_LENGTH } = require('./constants')
+const {
+ MAX_SAFE_COMPONENT_LENGTH,
+ MAX_SAFE_BUILD_LENGTH,
+ MAX_LENGTH,
+} = require('./constants')
const debug = require('./debug')
exports = module.exports = {}
// The actual regexps go on exports.re
const re = exports.re = []
+const safeRe = exports.safeRe = []
const src = exports.src = []
const t = exports.t = {}
let R = 0
+const LETTERDASHNUMBER = '[a-zA-Z0-9-]'
+
+// Replace some greedy regex tokens to prevent regex dos issues. These regex are
+// used internally via the safeRe object since all inputs in this library get
+// normalized first to trim and collapse all extra whitespace. The original
+// regexes are exported for userland consumption and lower level usage. A
+// future breaking change could export the safer regex only with a note that
+// all input should have extra whitespace removed.
+const safeRegexReplacements = [
+ ['\\s', 1],
+ ['\\d', MAX_LENGTH],
+ [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH],
+]
+
+const makeSafeRegex = (value) => {
+ for (const [token, max] of safeRegexReplacements) {
+ value = value
+ .split(`${token}*`).join(`${token}{0,${max}}`)
+ .split(`${token}+`).join(`${token}{1,${max}}`)
+ }
+ return value
+}
+
const createToken = (name, value, isGlobal) => {
+ const safe = makeSafeRegex(value)
const index = R++
debug(name, index, value)
t[name] = index
src[index] = value
re[index] = new RegExp(value, isGlobal ? 'g' : undefined)
+ safeRe[index] = new RegExp(safe, isGlobal ? 'g' : undefined)
}
// The following Regular Expressions can be used for tokenizing,
@@ -23,13 +53,13 @@ const createToken = (name, value, isGlobal) => {
// A single `0`, or a non-zero digit followed by zero or more digits.
createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*')
-createToken('NUMERICIDENTIFIERLOOSE', '[0-9]+')
+createToken('NUMERICIDENTIFIERLOOSE', '\\d+')
// ## Non-numeric Identifier
// Zero or more digits, followed by a letter or hyphen, and then zero or
// more letters, digits, or hyphens.
-createToken('NONNUMERICIDENTIFIER', '\\d*[a-zA-Z-][a-zA-Z0-9-]*')
+createToken('NONNUMERICIDENTIFIER', `\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`)
// ## Main Version
// Three dot-separated numeric identifiers.
@@ -64,7 +94,7 @@ createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE]
// ## Build Metadata Identifier
// Any combination of digits, letters, or hyphens.
-createToken('BUILDIDENTIFIER', '[0-9A-Za-z-]+')
+createToken('BUILDIDENTIFIER', `${LETTERDASHNUMBER}+`)
// ## Build Metadata
// Plus sign, followed by one or more period-separated build metadata
--
2.34.1

Просмотреть файл

@ -5,7 +5,7 @@ Name: nodejs
# 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: 16.20.1
Release: 1%{?dist}
Release: 2%{?dist}
License: BSD AND MIT AND Public Domain AND NAIST-2003 AND Artistic-2.0
Vendor: Microsoft Corporation
Distribution: Mariner
@ -16,6 +16,7 @@ URL: https://github.com/nodejs/node
# !!! => use clean-source-tarball.sh script to create a clean and reproducible source tarball.
Source0: https://nodejs.org/download/release/v%{version}/node-v%{version}.tar.xz
Patch0: disable-tlsv1-tlsv1-1.patch
Patch1: CVE-2022-25883.patch
BuildRequires: brotli-devel
BuildRequires: c-ares-devel
BuildRequires: coreutils >= 8.22
@ -113,6 +114,9 @@ make cctest
%{_datadir}/systemtap/tapset/node.stp
%changelog
* Wed Jul 12 2023 Olivia Crain <oliviacrain@microsoft.com> - 16.20.1-2
- Backport upstream patches to fix CVE-2022-25883
* Wed Jun 28 2023 David Steele <davidsteele@microsoft.com> - 16.20.1-1
- Upgrade to nodejs to 16.20.1 and npm to 8.19.4