Compare commits

...

7 Commits

Author SHA1 Message Date
AlmaLinux RelEng Bot
b8516a1286 import UBI nodejs-24.18.0-3.module+el8.10.0+24639+56878497 2026-08-13 01:53:40 -04:00
AlmaLinux RelEng Bot
61a9b8e4b9 import UBI nodejs-24.18.0-2.module+el8.10.0+24562+090c6525 2026-07-28 12:54:35 -04:00
AlmaLinux RelEng Bot
ef5f06978a import UBI nodejs-24.18.0-1.module+el8.10.0+24481+7a71fac8 2026-07-15 11:05:09 -04:00
AlmaLinux RelEng Bot
e2ce757884 import CS git nodejs-24.14.1-2.el8 2026-04-12 23:21:35 -04:00
a3b6953c17 import UBI nodejs-24.13.0-0.module+el8.10.0+23888+24fa7806 2026-02-10 14:12:02 +00:00
89b6a395f3 import UBI nodejs-24.11.1-0.module+el8.10.0+23730+2ad7beb8 2025-12-18 13:29:55 +00:00
3d1570a8b5 Import from CS git 2025-11-05 08:05:34 +00:00
23 changed files with 9135 additions and 1249 deletions

6
.gitignore vendored
View File

@ -1,2 +1,4 @@
SOURCES/icu4c-64_2-src.tgz
SOURCES/node-v10.24.0-stripped.tar.gz
SOURCES/icu4c-78.3-data-bin-b.zip
SOURCES/icu4c-78.3-data-bin-l.zip
SOURCES/node-v24.18.0-stripped.tar.gz
SOURCES/packaging-scripts.tar.gz

View File

@ -1,2 +1,4 @@
3127155ecf2b75ab4835f501b7478e39c07bb852 SOURCES/icu4c-64_2-src.tgz
be0e0b385a852c376f452b3d94727492e05407e4 SOURCES/node-v10.24.0-stripped.tar.gz
0a91aee64ba400d52034769301104cc843474a22 SOURCES/icu4c-78.3-data-bin-b.zip
913f6682015287483a035af160ea4d919c0a3ca5 SOURCES/icu4c-78.3-data-bin-l.zip
da316582d493179edd255e531bf75e0b21e48512 SOURCES/node-v24.18.0-stripped.tar.gz
eb3cf16f39f950f185ebf460a279bb27293dabe2 SOURCES/packaging-scripts.tar.gz

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,674 @@
From d8919f08bff03ff671a17f7b672edcba8a756c86 Mon Sep 17 00:00:00 2001
From: tjuhasz <tjuhasz@redhat.com>
Date: Tue, 4 Aug 2026 16:36:52 +0200
Subject: [PATCH] deps: update brace-expansion to 5.0.9
Update brace-expansion package in deps/npm/node_modules to version 5.0.9
to address CVE-2026-13149.
Key security fix:
- Adds EXPANSION_MAX_LENGTH to cap the total character count of the
accumulator during expansion, preventing memory exhaustion from
deeply chained brace groups (CVE-2026-14257)
- Refactors expand_ to iterate top-level brace groups instead of
recursing on m.post, preventing stack overflow on deeply chained
input
- Extracts combine() helper that enforces both max count and
maxLength bounds at the single point where output grows
CVE: CVE-2026-13149
---
.../brace-expansion/dist/commonjs/index.js | 246 ++++++++++++------
.../brace-expansion/dist/esm/index.js | 244 +++++++++++------
.../node_modules/brace-expansion/package.json | 6 +-
3 files changed, 336 insertions(+), 160 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..869a6be 100644
--- a/deps/npm/node_modules/brace-expansion/dist/commonjs/index.js
+++ b/deps/npm/node_modules/brace-expansion/dist/commonjs/index.js
@@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
-exports.EXPANSION_MAX = void 0;
+exports.EXPANSION_MAX_LENGTH = exports.EXPANSION_MAX = void 0;
exports.expand = expand;
const balanced_match_1 = require("balanced-match");
const escSlash = '\0SLASH' + Math.random() + '\0';
@@ -19,6 +19,17 @@ const closePattern = /\\}/g;
const commaPattern = /\\,/g;
const periodPattern = /\\\./g;
exports.EXPANSION_MAX = 100_000;
+// `EXPANSION_MAX` caps the *number* of expansions, but not their length. An
+// input like `'{a,b}'.repeat(1500)` stays under that count - its output is
+// truncated to 100k results - while making every result ~1500 characters
+// long. The result set, and the intermediate arrays built while combining
+// brace sets, then grow large enough to exhaust memory and crash the process
+// (CVE-2026-14257). `EXPANSION_MAX_LENGTH` bounds the total number of
+// characters the accumulator may hold at any point, so memory stays flat no
+// matter how many brace groups are chained. The limit sits well above any
+// realistic expansion (100k results hitting `EXPANSION_MAX` measure ~1M
+// characters) so legitimate input is unaffected.
+exports.EXPANSION_MAX_LENGTH = 4_000_000;
function numeric(str) {
return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
}
@@ -68,7 +79,7 @@ function expand(str, options = {}) {
if (!str) {
return [];
}
- const { max = exports.EXPANSION_MAX } = options;
+ const { max = exports.EXPANSION_MAX, maxLength = exports.EXPANSION_MAX_LENGTH } = options;
// I don't know why Bash 4.3 does this, but it does.
// Anything starting with {} will have the first two bytes preserved
// but *only* at the top level, so {},a}b will not expand to anything,
@@ -78,7 +89,7 @@ function expand(str, options = {}) {
if (str.slice(0, 2) === '{}') {
str = '\\{\\}' + str.slice(2);
}
- return expand_(escapeBraces(str), max, true).map(unescapeBraces);
+ return expand_(escapeBraces(str), max, maxLength, true).map(unescapeBraces);
}
function embrace(str) {
return '{' + str + '}';
@@ -92,22 +103,117 @@ function lte(i, y) {
function gte(i, y) {
return 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);
+// Build `{ acc[a] + pre + values[v] }` for every combination, capping the
+// number of results at `max` and the total number of characters at `maxLength`.
+// This is the one place output grows, so bounding it here keeps the single
+// accumulator - and therefore memory - flat regardless of how many brace groups
+// are combined (CVE-2026-14257).
+function combine(acc, pre, values, max, maxLength, dropEmpties) {
+ const out = [];
+ let length = 0;
+ for (let a = 0; a < acc.length; a++) {
+ for (let v = 0; v < values.length; v++) {
+ if (out.length >= max)
+ return out;
+ const expansion = acc[a] + pre + values[v];
+ // Bash drops empty results at the top level. Skip them before they count
+ // against `max`, so `max` bounds the number of *kept* results.
+ if (dropEmpties && !expansion)
+ continue;
+ if (length + expansion.length > maxLength)
+ return out;
+ out.push(expansion);
+ length += expansion.length;
+ }
+ }
+ return out;
+}
+// The expansion values of a single numeric (`1..5`) or alphabetic (`a..e..2`)
+// sequence body.
+function expandSequence(body, isAlphaSequence, max, maxLength) {
+ const n = body.split(/\.\./);
+ const N = [];
+ // A sequence body always splits into two or three parts, but the compiler
+ // can't know that.
+ /* c8 ignore start */
+ if (n[0] === undefined || n[1] === undefined) {
+ return N;
+ }
+ /* c8 ignore stop */
+ const x = numeric(n[0]);
+ const y = numeric(n[1]);
+ const width = Math.max(n[0].length, n[1].length);
+ let incr = n.length === 3 && n[2] !== undefined ?
+ Math.max(Math.abs(numeric(n[2])), 1)
+ : 1;
+ let test = lte;
+ const reverse = y < x;
+ if (reverse) {
+ incr *= -1;
+ test = gte;
+ }
+ const pad = n.some(isPadded);
+ let length = 0;
+ for (let i = x; test(i, y) && N.length < max; i += incr) {
+ let c;
+ if (isAlphaSequence) {
+ c = String.fromCharCode(i);
+ if (c === '\\') {
+ c = '';
+ }
}
+ else {
+ c = String(i);
+ if (pad) {
+ const need = width - c.length;
+ if (need > 0) {
+ const z = new Array(need + 1).join('0');
+ if (i < 0) {
+ c = '-' + z + c.slice(1);
+ }
+ else {
+ c = z + c;
+ }
+ }
+ }
+ }
+ if (length + c.length > maxLength)
+ break;
+ N.push(c);
+ length += c.length;
}
- else {
+ return N;
+}
+function expand_(str, max, maxLength, isTop) {
+ // Consume the string's top-level brace groups left to right, threading a
+ // running set of combined prefixes (`acc`). Expanding the tail iteratively -
+ // rather than recursing on `m.post` once per group - keeps the native stack
+ // depth constant, so deeply chained input (`'{a,b}'.repeat(3000)`) can no
+ // longer overflow the stack, and leaves a single accumulator whose size
+ // `maxLength` bounds directly (CVE-2026-14257).
+ let acc = [''];
+ // Bash drops empty results, but only when the *first* top-level group is a
+ // comma set - a sequence like `{a..\}` may legitimately yield ''. The drop
+ // is on the final strings, so it is applied to whichever `combine` produces
+ // them (the one with no brace set left in the tail).
+ let dropEmpties = false;
+ let firstGroup = true;
+ for (;;) {
+ const m = (0, balanced_match_1.balanced)('{', '}', str);
+ // No brace set left: the rest of the string is literal.
+ if (!m) {
+ return combine(acc, str, [''], max, maxLength, dropEmpties);
+ }
+ // no need to expand pre, since it is guaranteed to be free of brace-sets
+ const pre = m.pre;
+ if (/\$$/.test(pre)) {
+ acc = combine(acc, pre + '{' + m.body + '}', [''], max, maxLength, dropEmpties && !m.post.length);
+ firstGroup = false;
+ if (!m.post.length)
+ break;
+ str = m.post;
+ continue;
+ }
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,86 +222,68 @@ 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];
+ // Nothing here expands, so the whole remaining string is literal.
+ return combine(acc, pre + '{' + m.body + '}' + m.post, [''], max, maxLength, dropEmpties);
}
- let n;
+ if (firstGroup) {
+ dropEmpties = isTop && !isSequence;
+ firstGroup = false;
+ }
+ let values;
if (isSequence) {
- n = m.body.split(/\.\./);
+ values = expandSequence(m.body, isAlphaSequence, max, maxLength);
}
else {
- n = parseCommaParts(m.body);
+ let n = parseCommaParts(m.body);
if (n.length === 1 && n[0] !== undefined) {
// x{{a,b}}y ==> x{a}y x{b}y
- n = expand_(n[0], max, false).map(embrace);
+ n = expand_(n[0], max, maxLength, false).map(embrace);
//XXX is this necessary? Can't seem to hit it in tests.
/* c8 ignore start */
if (n.length === 1) {
- return post.map(p => m.pre + n[0] + p);
+ acc = combine(acc, pre + n[0], [''], max, maxLength, dropEmpties && !m.post.length);
+ if (!m.post.length)
+ break;
+ str = m.post;
+ continue;
}
/* c8 ignore stop */
}
- }
- // at this point, n is the parts, and we know it's not a comma set
- // with a single entry.
- let N;
- if (isSequence && n[0] !== undefined && n[1] !== undefined) {
- const x = numeric(n[0]);
- const y = numeric(n[1]);
- const width = Math.max(n[0].length, n[1].length);
- let incr = n.length === 3 && n[2] !== undefined ?
- Math.max(Math.abs(numeric(n[2])), 1)
- : 1;
- let test = lte;
- const reverse = y < x;
- if (reverse) {
- incr *= -1;
- test = gte;
- }
- const pad = n.some(isPadded);
- N = [];
- for (let i = x; test(i, y) && N.length < max; i += incr) {
- let c;
- if (isAlphaSequence) {
- c = String.fromCharCode(i);
- if (c === '\\') {
- c = '';
- }
+ // Values that `combine` is going to drop as empty produce no result, so
+ // they must not count against `max` - otherwise `{a,,b}` with `max: 2`
+ // would stop at `['a', '']` and yield one result instead of two. Skipping
+ // them outright keeps `values` bounded while leaving `max` a bound on
+ // *kept* results.
+ let dropsEmpties = dropEmpties && !m.post.length && !pre;
+ for (let d = 0; dropsEmpties && d < acc.length; d++) {
+ if (acc[d]) {
+ dropsEmpties = false;
}
- else {
- c = String(i);
- if (pad) {
- const need = width - c.length;
- if (need > 0) {
- const z = new Array(need + 1).join('0');
- if (i < 0) {
- c = '-' + z + c.slice(1);
- }
- else {
- c = z + c;
- }
- }
- }
- }
- N.push(c);
- }
- }
- else {
- N = [];
- for (let j = 0; j < n.length; j++) {
- N.push.apply(N, expand_(n[j], max, false));
}
- }
- for (let j = 0; j < N.length; j++) {
- for (let k = 0; k < post.length && expansions.length < max; k++) {
- const expansion = pre + N[j] + post[k];
- if (!isTop || isSequence || expansion) {
- expansions.push(expansion);
+ values = [];
+ let valuesLength = 0;
+ outer: for (let j = 0; j < n.length; j++) {
+ const expanded = expand_(n[j], max, maxLength, false);
+ for (let k = 0; k < expanded.length; k++) {
+ const v = expanded[k];
+ if (dropsEmpties && !v)
+ continue;
+ if (values.length >= max || valuesLength + v.length > maxLength) {
+ break outer;
+ }
+ values.push(v);
+ valuesLength += v.length;
}
}
}
+ acc = combine(acc, pre, values, max, maxLength, dropEmpties && !m.post.length);
+ if (!m.post.length)
+ break;
+ str = m.post;
}
- return expansions;
+ return acc;
}
//# 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..fd68f57 100644
--- a/deps/npm/node_modules/brace-expansion/dist/esm/index.js
+++ b/deps/npm/node_modules/brace-expansion/dist/esm/index.js
@@ -15,6 +15,17 @@ const closePattern = /\\}/g;
const commaPattern = /\\,/g;
const periodPattern = /\\\./g;
export const EXPANSION_MAX = 100_000;
+// `EXPANSION_MAX` caps the *number* of expansions, but not their length. An
+// input like `'{a,b}'.repeat(1500)` stays under that count - its output is
+// truncated to 100k results - while making every result ~1500 characters
+// long. The result set, and the intermediate arrays built while combining
+// brace sets, then grow large enough to exhaust memory and crash the process
+// (CVE-2026-14257). `EXPANSION_MAX_LENGTH` bounds the total number of
+// characters the accumulator may hold at any point, so memory stays flat no
+// matter how many brace groups are chained. The limit sits well above any
+// realistic expansion (100k results hitting `EXPANSION_MAX` measure ~1M
+// characters) so legitimate input is unaffected.
+export const EXPANSION_MAX_LENGTH = 4_000_000;
function numeric(str) {
return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
}
@@ -64,7 +75,7 @@ export function expand(str, options = {}) {
if (!str) {
return [];
}
- const { max = EXPANSION_MAX } = options;
+ const { max = EXPANSION_MAX, maxLength = EXPANSION_MAX_LENGTH } = options;
// I don't know why Bash 4.3 does this, but it does.
// Anything starting with {} will have the first two bytes preserved
// but *only* at the top level, so {},a}b will not expand to anything,
@@ -74,7 +85,7 @@ export function expand(str, options = {}) {
if (str.slice(0, 2) === '{}') {
str = '\\{\\}' + str.slice(2);
}
- return expand_(escapeBraces(str), max, true).map(unescapeBraces);
+ return expand_(escapeBraces(str), max, maxLength, true).map(unescapeBraces);
}
function embrace(str) {
return '{' + str + '}';
@@ -88,22 +99,117 @@ function lte(i, y) {
function gte(i, y) {
return 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);
+// Build `{ acc[a] + pre + values[v] }` for every combination, capping the
+// number of results at `max` and the total number of characters at `maxLength`.
+// This is the one place output grows, so bounding it here keeps the single
+// accumulator - and therefore memory - flat regardless of how many brace groups
+// are combined (CVE-2026-14257).
+function combine(acc, pre, values, max, maxLength, dropEmpties) {
+ const out = [];
+ let length = 0;
+ for (let a = 0; a < acc.length; a++) {
+ for (let v = 0; v < values.length; v++) {
+ if (out.length >= max)
+ return out;
+ const expansion = acc[a] + pre + values[v];
+ // Bash drops empty results at the top level. Skip them before they count
+ // against `max`, so `max` bounds the number of *kept* results.
+ if (dropEmpties && !expansion)
+ continue;
+ if (length + expansion.length > maxLength)
+ return out;
+ out.push(expansion);
+ length += expansion.length;
+ }
+ }
+ return out;
+}
+// The expansion values of a single numeric (`1..5`) or alphabetic (`a..e..2`)
+// sequence body.
+function expandSequence(body, isAlphaSequence, max, maxLength) {
+ const n = body.split(/\.\./);
+ const N = [];
+ // A sequence body always splits into two or three parts, but the compiler
+ // can't know that.
+ /* c8 ignore start */
+ if (n[0] === undefined || n[1] === undefined) {
+ return N;
+ }
+ /* c8 ignore stop */
+ const x = numeric(n[0]);
+ const y = numeric(n[1]);
+ const width = Math.max(n[0].length, n[1].length);
+ let incr = n.length === 3 && n[2] !== undefined ?
+ Math.max(Math.abs(numeric(n[2])), 1)
+ : 1;
+ let test = lte;
+ const reverse = y < x;
+ if (reverse) {
+ incr *= -1;
+ test = gte;
+ }
+ const pad = n.some(isPadded);
+ let length = 0;
+ for (let i = x; test(i, y) && N.length < max; i += incr) {
+ let c;
+ if (isAlphaSequence) {
+ c = String.fromCharCode(i);
+ if (c === '\\') {
+ c = '';
+ }
}
+ else {
+ c = String(i);
+ if (pad) {
+ const need = width - c.length;
+ if (need > 0) {
+ const z = new Array(need + 1).join('0');
+ if (i < 0) {
+ c = '-' + z + c.slice(1);
+ }
+ else {
+ c = z + c;
+ }
+ }
+ }
+ }
+ if (length + c.length > maxLength)
+ break;
+ N.push(c);
+ length += c.length;
}
- else {
+ return N;
+}
+function expand_(str, max, maxLength, isTop) {
+ // Consume the string's top-level brace groups left to right, threading a
+ // running set of combined prefixes (`acc`). Expanding the tail iteratively -
+ // rather than recursing on `m.post` once per group - keeps the native stack
+ // depth constant, so deeply chained input (`'{a,b}'.repeat(3000)`) can no
+ // longer overflow the stack, and leaves a single accumulator whose size
+ // `maxLength` bounds directly (CVE-2026-14257).
+ let acc = [''];
+ // Bash drops empty results, but only when the *first* top-level group is a
+ // comma set - a sequence like `{a..\}` may legitimately yield ''. The drop
+ // is on the final strings, so it is applied to whichever `combine` produces
+ // them (the one with no brace set left in the tail).
+ let dropEmpties = false;
+ let firstGroup = true;
+ for (;;) {
+ const m = balanced('{', '}', str);
+ // No brace set left: the rest of the string is literal.
+ if (!m) {
+ return combine(acc, str, [''], max, maxLength, dropEmpties);
+ }
+ // no need to expand pre, since it is guaranteed to be free of brace-sets
+ const pre = m.pre;
+ if (/\$$/.test(pre)) {
+ acc = combine(acc, pre + '{' + m.body + '}', [''], max, maxLength, dropEmpties && !m.post.length);
+ firstGroup = false;
+ if (!m.post.length)
+ break;
+ str = m.post;
+ continue;
+ }
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,86 +218,68 @@ 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];
+ // Nothing here expands, so the whole remaining string is literal.
+ return combine(acc, pre + '{' + m.body + '}' + m.post, [''], max, maxLength, dropEmpties);
}
- let n;
+ if (firstGroup) {
+ dropEmpties = isTop && !isSequence;
+ firstGroup = false;
+ }
+ let values;
if (isSequence) {
- n = m.body.split(/\.\./);
+ values = expandSequence(m.body, isAlphaSequence, max, maxLength);
}
else {
- n = parseCommaParts(m.body);
+ let n = parseCommaParts(m.body);
if (n.length === 1 && n[0] !== undefined) {
// x{{a,b}}y ==> x{a}y x{b}y
- n = expand_(n[0], max, false).map(embrace);
+ n = expand_(n[0], max, maxLength, false).map(embrace);
//XXX is this necessary? Can't seem to hit it in tests.
/* c8 ignore start */
if (n.length === 1) {
- return post.map(p => m.pre + n[0] + p);
+ acc = combine(acc, pre + n[0], [''], max, maxLength, dropEmpties && !m.post.length);
+ if (!m.post.length)
+ break;
+ str = m.post;
+ continue;
}
/* c8 ignore stop */
}
- }
- // at this point, n is the parts, and we know it's not a comma set
- // with a single entry.
- let N;
- if (isSequence && n[0] !== undefined && n[1] !== undefined) {
- const x = numeric(n[0]);
- const y = numeric(n[1]);
- const width = Math.max(n[0].length, n[1].length);
- let incr = n.length === 3 && n[2] !== undefined ?
- Math.max(Math.abs(numeric(n[2])), 1)
- : 1;
- let test = lte;
- const reverse = y < x;
- if (reverse) {
- incr *= -1;
- test = gte;
- }
- const pad = n.some(isPadded);
- N = [];
- for (let i = x; test(i, y) && N.length < max; i += incr) {
- let c;
- if (isAlphaSequence) {
- c = String.fromCharCode(i);
- if (c === '\\') {
- c = '';
- }
+ // Values that `combine` is going to drop as empty produce no result, so
+ // they must not count against `max` - otherwise `{a,,b}` with `max: 2`
+ // would stop at `['a', '']` and yield one result instead of two. Skipping
+ // them outright keeps `values` bounded while leaving `max` a bound on
+ // *kept* results.
+ let dropsEmpties = dropEmpties && !m.post.length && !pre;
+ for (let d = 0; dropsEmpties && d < acc.length; d++) {
+ if (acc[d]) {
+ dropsEmpties = false;
}
- else {
- c = String(i);
- if (pad) {
- const need = width - c.length;
- if (need > 0) {
- const z = new Array(need + 1).join('0');
- if (i < 0) {
- c = '-' + z + c.slice(1);
- }
- else {
- c = z + c;
- }
- }
- }
- }
- N.push(c);
- }
- }
- else {
- N = [];
- for (let j = 0; j < n.length; j++) {
- N.push.apply(N, expand_(n[j], max, false));
}
- }
- for (let j = 0; j < N.length; j++) {
- for (let k = 0; k < post.length && expansions.length < max; k++) {
- const expansion = pre + N[j] + post[k];
- if (!isTop || isSequence || expansion) {
- expansions.push(expansion);
+ values = [];
+ let valuesLength = 0;
+ outer: for (let j = 0; j < n.length; j++) {
+ const expanded = expand_(n[j], max, maxLength, false);
+ for (let k = 0; k < expanded.length; k++) {
+ const v = expanded[k];
+ if (dropsEmpties && !v)
+ continue;
+ if (values.length >= max || valuesLength + v.length > maxLength) {
+ break outer;
+ }
+ values.push(v);
+ valuesLength += v.length;
}
}
}
+ acc = combine(acc, pre, values, max, maxLength, dropEmpties && !m.post.length);
+ if (!m.post.length)
+ break;
+ str = m.post;
}
- return expansions;
+ return acc;
}
//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/deps/npm/node_modules/brace-expansion/package.json b/deps/npm/node_modules/brace-expansion/package.json
index 8152480..4376400 100644
--- a/deps/npm/node_modules/brace-expansion/package.json
+++ b/deps/npm/node_modules/brace-expansion/package.json
@@ -1,7 +1,7 @@
{
"name": "brace-expansion",
"description": "Brace expansion as known from sh/bash",
- "version": "5.0.6",
+ "version": "5.0.9",
"files": [
"dist"
],
@@ -46,7 +46,7 @@
},
"license": "MIT",
"engines": {
- "node": "18 || 20 || >=22"
+ "node": "20 || >=22"
},
"tshy": {
"exports": {
@@ -59,6 +59,6 @@
"module": "./dist/esm/index.js",
"repository": {
"type": "git",
- "url": "git+ssh://git@github.com/juliangruber/brace-expansion.git"
+ "url": "git+https://github.com/juliangruber/brace-expansion.git"
}
}
--
2.54.0

View File

@ -0,0 +1,811 @@
From caaad6f1bc0fec6d7bbabae0f6ebfd605eb885f4 Mon Sep 17 00:00:00 2001
From: tjuhasz <tjuhasz@redhat.com>
Date: Tue, 4 Aug 2026 16:26:19 +0200
Subject: [PATCH] deps: update ip-address to 10.4.0
Update ip-address package in deps/npm/node_modules to version 10.4.0
to address CVE-2026-69192 and CVE-2026-54272.
Key security fixes:
- IPv4 leading zero validation: rejects ambiguous octal notation that
could cause disagreement between parsers about which host a string
names (SSRF vector)
- isHostInSubnet: new method used by all classifiers (isLoopback,
isPrivate, isLinkLocal, etc.) so classification is no longer affected
by the CIDR suffix, preventing SSRF filter bypasses
- Improved URL parsing regexes that properly anchor and handle
protocol prefixes
- IPv6 embedded IPv4 awareness: isLoopback/isPrivate/isUnspecified
now check the embedded v4 address for mapped/NAT64 addresses
CVE: CVE-2026-69192, CVE-2026-54272
---
.../node_modules/ip-address/dist/common.js | 49 +++-
deps/npm/node_modules/ip-address/dist/ipv4.js | 44 ++--
deps/npm/node_modules/ip-address/dist/ipv6.js | 233 ++++++++++++++----
.../ip-address/dist/v4/constants.js | 6 +-
.../ip-address/dist/v6/constants.js | 5 +-
deps/npm/node_modules/ip-address/package.json | 10 +-
6 files changed, 262 insertions(+), 85 deletions(-)
diff --git a/deps/npm/node_modules/ip-address/dist/common.js b/deps/npm/node_modules/ip-address/dist/common.js
index 6b76e05..0c15d21 100644
--- a/deps/npm/node_modules/ip-address/dist/common.js
+++ b/deps/npm/node_modules/ip-address/dist/common.js
@@ -1,23 +1,47 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isInSubnet = isInSubnet;
+exports.isHostInSubnet = isHostInSubnet;
exports.isCorrect = isCorrect;
exports.prefixLengthFromMask = prefixLengthFromMask;
+exports.assertByteArray = assertByteArray;
exports.numberToPaddedHex = numberToPaddedHex;
exports.stringToPaddedHex = stringToPaddedHex;
exports.testBit = testBit;
const address_error_1 = require("./address-error");
+/**
+ * Returns whether this address's *network* is contained within `address`,
+ * i.e. whether every address this one can represent also falls inside
+ * `address`. A network wider than `address` is not contained in it, so
+ * `10.0.0.0/8` is not in `10.0.0.0/16`.
+ *
+ * To ask whether the address itself falls inside a range, ignoring any CIDR
+ * suffix it was written with, use {@link isHostInSubnet} instead. That is the
+ * question the special-use classifiers ask.
+ */
function isInSubnet(address) {
if (this.subnetMask < address.subnetMask) {
return false;
}
- if (this.mask(address.subnetMask) === address.mask()) {
- return true;
- }
- return false;
+ return isHostInSubnet.call(this, address);
+}
+/**
+ * Returns whether this address's host bits fall inside `address`, ignoring
+ * this address's own subnet mask.
+ *
+ * This is the primitive the special-use classifiers (`isLoopback`,
+ * `isPrivate`, `isLinkLocal`, `getType`, …) are built on: they answer a
+ * question about the address, so the answer must not change with the CIDR
+ * suffix the caller happened to write. Use this rather than
+ * {@link isInSubnet} when classifying a single address — notably when the
+ * address came from untrusted input and the result backs a trust-boundary
+ * decision such as an SSRF allow/deny filter.
+ */
+function isHostInSubnet(address) {
+ return this.mask(address.subnetMask) === address.mask();
}
function isCorrect(defaultBits) {
- return function () {
+ return function isCorrectForm() {
if (this.addressMinusSuffix !== this.correctForm()) {
return false;
}
@@ -46,6 +70,21 @@ function prefixLengthFromMask(value, totalBits) {
}
return firstZero;
}
+/**
+ * Throws `AddressError` unless `bytes` holds exactly `byteCount` integers,
+ * each from `minimum` to 255. Pass a `minimum` of `-128` where signed bytes
+ * are accepted and folded to unsigned, and `0` where they are not.
+ */
+function assertByteArray(bytes, byteCount, family, minimum) {
+ if (bytes.length !== byteCount) {
+ throw new address_error_1.AddressError(`${family} addresses require exactly ${byteCount} bytes`);
+ }
+ for (let i = 0; i < bytes.length; i++) {
+ if (!Number.isInteger(bytes[i]) || bytes[i] < minimum || bytes[i] > 255) {
+ throw new address_error_1.AddressError(`All bytes must be integers between ${minimum} and 255`);
+ }
+ }
+}
function numberToPaddedHex(number) {
return number.toString(16).padStart(2, '0');
}
diff --git a/deps/npm/node_modules/ip-address/dist/ipv4.js b/deps/npm/node_modules/ip-address/dist/ipv4.js
index 2c0fd18..1a14ddc 100644
--- a/deps/npm/node_modules/ip-address/dist/ipv4.js
+++ b/deps/npm/node_modules/ip-address/dist/ipv4.js
@@ -35,6 +35,7 @@ const isCorrect4 = common.isCorrect(constants.BITS);
*/
class Address4 {
constructor(address) {
+ this.addressMinusSuffix = '';
this.groups = constants.GROUPS;
this.parsedAddress = [];
this.parsedSubnet = '';
@@ -51,6 +52,13 @@ class Address4 {
* @returns {boolean}
*/
this.isInSubnet = common.isInSubnet;
+ /**
+ * Returns true if this address's host bits fall inside the given subnet,
+ * ignoring this address's own subnet mask. See
+ * {@link common.isHostInSubnet}.
+ * @returns {boolean}
+ */
+ this.isHostInSubnet = common.isHostInSubnet;
this.address = address;
const subnet = constants.RE_SUBNET_STRING.exec(address);
if (subnet) {
@@ -78,7 +86,7 @@ class Address4 {
new Address4(address);
return true;
}
- catch (e) {
+ catch {
return false;
}
}
@@ -90,6 +98,11 @@ class Address4 {
*/
parse(address) {
const groups = address.split('.');
+ // Checked before the general match so the error names the actual problem.
+ // Address6 rejects the same notation on its v4-in-v6 path.
+ if (groups.some((group) => /^0\d/.test(group))) {
+ throw new address_error_1.AddressError("IPv4 addresses can't have leading zeroes.");
+ }
if (!address.match(constants.RE_ADDRESS)) {
throw new address_error_1.AddressError('Invalid IPv4 address.');
}
@@ -128,7 +141,6 @@ class Address4 {
static fromAddressAndWildcardMask(address, wildcardMask) {
const wildcard = new Address4(wildcardMask).bigInt();
const allOnes = (BigInt(1) << BigInt(constants.BITS)) - BigInt(1);
- // eslint-disable-next-line no-bitwise
const mask = wildcard ^ allOnes;
const bits = common.prefixLengthFromMask(mask, constants.BITS);
return new Address4(`${address}/${bits}`);
@@ -328,7 +340,7 @@ class Address4 {
* @returns {Address4}
*/
static fromBigInt(bigInt) {
- if (bigInt < 0n || bigInt > 0xffffffffn) {
+ if (bigInt < BigInt(0) || bigInt > BigInt(0xffffffff)) {
throw new address_error_1.AddressError('IPv4 BigInt must be in the range 0 to 2**32 - 1');
}
return Address4.fromHex(bigInt.toString(16).padStart(8, '0'));
@@ -341,15 +353,7 @@ class Address4 {
* @returns {Address4}
*/
static fromByteArray(bytes) {
- if (bytes.length !== 4) {
- throw new address_error_1.AddressError('IPv4 addresses require exactly 4 bytes');
- }
- // Validate that all bytes are within valid range (0-255)
- for (let i = 0; i < bytes.length; i++) {
- if (!Number.isInteger(bytes[i]) || bytes[i] < 0 || bytes[i] > 255) {
- throw new address_error_1.AddressError('All bytes must be integers between 0 and 255');
- }
- }
+ common.assertByteArray(bytes, 4, 'IPv4', 0);
return this.fromUnsignedByteArray(bytes);
}
/**
@@ -403,49 +407,49 @@ class Address4 {
* @returns {boolean}
*/
isMulticast() {
- return this.isInSubnet(MULTICAST_V4);
+ return this.isHostInSubnet(MULTICAST_V4);
}
/**
* Returns true if the address is in one of the [RFC 1918](https://datatracker.ietf.org/doc/html/rfc1918) private address ranges (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`).
* @returns {boolean}
*/
isPrivate() {
- return PRIVATE_V4.some((subnet) => this.isInSubnet(subnet));
+ return PRIVATE_V4.some((subnet) => this.isHostInSubnet(subnet));
}
/**
* Returns true if the address is in the loopback range `127.0.0.0/8` ([RFC 1122](https://datatracker.ietf.org/doc/html/rfc1122)).
* @returns {boolean}
*/
isLoopback() {
- return this.isInSubnet(LOOPBACK_V4);
+ return this.isHostInSubnet(LOOPBACK_V4);
}
/**
* Returns true if the address is in the link-local range `169.254.0.0/16` ([RFC 3927](https://datatracker.ietf.org/doc/html/rfc3927)).
* @returns {boolean}
*/
isLinkLocal() {
- return this.isInSubnet(LINK_LOCAL_V4);
+ return this.isHostInSubnet(LINK_LOCAL_V4);
}
/**
* Returns true if the address is the unspecified address `0.0.0.0`.
* @returns {boolean}
*/
isUnspecified() {
- return this.isInSubnet(UNSPECIFIED_V4);
+ return this.isHostInSubnet(UNSPECIFIED_V4);
}
/**
* Returns true if the address is the limited broadcast address `255.255.255.255` ([RFC 919](https://datatracker.ietf.org/doc/html/rfc919)).
* @returns {boolean}
*/
isBroadcast() {
- return this.isInSubnet(BROADCAST_V4);
+ return this.isHostInSubnet(BROADCAST_V4);
}
/**
* Returns true if the address is in the carrier-grade NAT range `100.64.0.0/10` ([RFC 6598](https://datatracker.ietf.org/doc/html/rfc6598)).
* @returns {boolean}
*/
isCGNAT() {
- return this.isInSubnet(CGNAT_V4);
+ return this.isHostInSubnet(CGNAT_V4);
}
/**
* Returns a zero-padded base-2 string representation of the address
@@ -463,7 +467,7 @@ class Address4 {
*/
groupForV6() {
const segments = this.parsedAddress;
- return this.address.replace(constants.RE_ADDRESS, `<span class="hover-group group-v4 group-6">${segments
+ return this.correctForm().replace(constants.RE_ADDRESS, `<span class="hover-group group-v4 group-6">${segments
.slice(0, 2)
.join('.')}</span>.<span class="hover-group group-v4 group-7">${segments
.slice(2, 4)
diff --git a/deps/npm/node_modules/ip-address/dist/ipv6.js b/deps/npm/node_modules/ip-address/dist/ipv6.js
index a78020e..f64a8dd 100644
--- a/deps/npm/node_modules/ip-address/dist/ipv6.js
+++ b/deps/npm/node_modules/ip-address/dist/ipv6.js
@@ -73,7 +73,6 @@ function paddedHex(octet) {
return parseInt(octet, 16).toString(16).padStart(4, '0');
}
function unsignByte(b) {
- // eslint-disable-next-line no-bitwise
return b & 0xff;
}
/**
@@ -97,6 +96,13 @@ class Address6 {
* @returns {boolean}
*/
this.isInSubnet = common.isInSubnet;
+ /**
+ * Returns true if this address's host bits fall inside the given subnet,
+ * ignoring this address's own subnet mask. See
+ * {@link common.isHostInSubnet}.
+ * @returns {boolean}
+ */
+ this.isHostInSubnet = common.isHostInSubnet;
/**
* Returns true if the address is correct, false otherwise
* @returns {boolean}
@@ -121,7 +127,10 @@ class Address6 {
}
address = address.replace(constants6.RE_SUBNET_STRING, '');
}
- else if (/\//.test(address)) {
+ // RE_SUBNET_STRING anchors on the end of the address, so it strips only
+ // the trailing suffix. A second one left behind (`::/0/1`) is malformed
+ // and must be rejected rather than parsed as an address group.
+ if (/\//.test(address)) {
throw new address_error_1.AddressError('Invalid subnet mask.');
}
const zone = constants6.RE_ZONE_STRING.exec(address);
@@ -145,7 +154,7 @@ class Address6 {
new Address6(address);
return true;
}
- catch (e) {
+ catch {
return false;
}
}
@@ -160,7 +169,7 @@ class Address6 {
* address.correctForm(); // '::e8:d4a5:1000'
*/
static fromBigInt(bigInt) {
- if (bigInt < 0n || bigInt > (1n << BigInt(constants6.BITS)) - 1n) {
+ if (bigInt < BigInt(0) || bigInt > (BigInt(1) << BigInt(constants6.BITS)) - BigInt(1)) {
throw new address_error_1.AddressError('IPv6 BigInt must be in the range 0 to 2**128 - 1');
}
const hex = bigInt.toString(16).padStart(32, '0');
@@ -181,12 +190,15 @@ class Address6 {
* addressAndPort.port; // 8080
*/
static fromURL(url) {
+ var _a;
let host;
let port = null;
let result;
+ // Remove the protocol prefix, if any
+ const stripped = url.replace(/^[a-z][a-z0-9+.-]*:\/\//i, '');
// If we have brackets parse them and find a port
- if (url.indexOf('[') !== -1 && url.indexOf(']:') !== -1) {
- result = constants6.RE_URL_WITH_PORT.exec(url);
+ if (stripped.indexOf('[') !== -1 && stripped.indexOf(']:') !== -1) {
+ result = constants6.RE_URL_WITH_PORT.exec(stripped);
if (result === null) {
return {
error: 'failed to parse address with port',
@@ -196,13 +208,9 @@ class Address6 {
}
host = result[1];
port = result[2];
- // If there's a URL extract the address
}
- else if (url.indexOf('/') !== -1) {
- // Remove the protocol prefix
- url = url.replace(/^[a-z0-9]+:\/\//, '');
- // Parse the address
- result = constants6.RE_URL.exec(url);
+ else {
+ result = constants6.RE_URL.exec(stripped);
if (result === null) {
return {
error: 'failed to parse address from URL',
@@ -210,17 +218,13 @@ class Address6 {
port: null,
};
}
- host = result[1];
- // Otherwise just assign the URL to the host and let the library parse it
- }
- else {
- host = url;
+ host = (_a = result[1]) !== null && _a !== void 0 ? _a : result[2];
}
// If there's a port convert it to an integer
if (port) {
port = parseInt(port, 10);
- // squelch out of range ports
- if (port < 0 || port > 65536) {
+ // squelch out of range ports (valid ports are 0-65535)
+ if (port < 0 || port > 65535) {
port = null;
}
}
@@ -258,7 +262,6 @@ class Address6 {
static fromAddressAndWildcardMask(address, wildcardMask) {
const wildcard = new Address6(wildcardMask).bigInt();
const allOnes = (BigInt(1) << BigInt(constants6.BITS)) - BigInt(1);
- // eslint-disable-next-line no-bitwise
const mask = wildcard ^ allOnes;
const bits = common.prefixLengthFromMask(mask, constants6.BITS);
return new Address6(`${address}/${bits}`);
@@ -493,7 +496,7 @@ class Address6 {
getType() {
for (let i = 0; i < TYPE_SUBNETS.length; i++) {
const entry = TYPE_SUBNETS[i];
- if (this.isInSubnet(entry[0])) {
+ if (this.isHostInSubnet(entry[0])) {
return entry[1];
}
}
@@ -635,20 +638,27 @@ class Address6 {
}
const groups = address.split(':');
const lastGroup = groups.slice(-1)[0];
+ // RE_ADDRESS rejects octets with a leading zero, so a dotted-quad tail is
+ // matched permissively first: that way this notation still gets its own
+ // message with the offending octet highlighted, rather than falling
+ // through as an unrecognized group.
+ const v4Octets = lastGroup.split('.');
+ if (v4Octets.length === constants4.GROUPS &&
+ v4Octets.every((octet) => /^\d{1,3}$/.test(octet))) {
+ if (v4Octets.some((octet) => /^0\d/.test(octet))) {
+ // The prefix groups haven't been through the bad-character check
+ // yet, so escape them before including in the error HTML.
+ const highlighted = v4Octets.map(spanLeadingZeroes4).join('.');
+ const prefix = groups.slice(0, -1).map(helpers.escapeHtml).join(':');
+ const separator = groups.length > 1 ? ':' : '';
+ throw new address_error_1.AddressError("IPv4 addresses can't have leading zeroes.", `${prefix}${separator}${highlighted}`);
+ }
+ }
const address4 = lastGroup.match(constants4.RE_ADDRESS);
if (address4) {
this.parsedAddress4 = address4[0];
- this.address4 = new ipv4_1.Address4(this.parsedAddress4);
- for (let i = 0; i < this.address4.groups; i++) {
- if (/^0[0-9]+/.test(this.address4.parsedAddress[i])) {
- // The prefix groups haven't been through the bad-character check
- // yet, so escape them before including in the error HTML.
- const highlighted = this.address4.parsedAddress.map(spanLeadingZeroes4).join('.');
- const prefix = groups.slice(0, -1).map(helpers.escapeHtml).join(':');
- const separator = groups.length > 1 ? ':' : '';
- throw new address_error_1.AddressError("IPv4 addresses can't have leading zeroes.", `${prefix}${separator}${highlighted}`);
- }
- }
+ const v4Suffix = this.subnetMask >= 96 ? `/${this.subnetMask - 96}` : '';
+ this.address4 = new ipv4_1.Address4(`${this.parsedAddress4}${v4Suffix}`);
this.v4 = true;
groups[groups.length - 1] = this.address4.toGroup6();
address = groups.join(':');
@@ -734,7 +744,11 @@ class Address6 {
return BigInt(`0x${this.parsedAddress.map(paddedHex).join('')}`);
}
/**
- * Return the last two groups of this address as an IPv4 address string
+ * Return the last two groups of this address as an IPv4 address string.
+ * If this address carries a CIDR prefix that covers the trailing 32 bits
+ * (i.e. `subnetMask >= 96`), the resulting `Address4` inherits the
+ * corresponding v4 prefix (`subnetMask - 96`); otherwise it defaults to
+ * `/32`.
* @returns {Address4}
* @example
* var address = new Address6('2001:4860:4001::1825:bf11');
@@ -742,7 +756,18 @@ class Address6 {
*/
to4() {
const binary = this.binaryZeroPad().split('');
- return ipv4_1.Address4.fromHex(BigInt(`0b${binary.slice(96, 128).join('')}`).toString(16).padStart(8, '0'));
+ const hex = BigInt(`0b${binary.slice(96, 128).join('')}`)
+ .toString(16)
+ .padStart(8, '0');
+ if (this.subnetMask >= 96) {
+ const v4Mask = this.subnetMask - 96;
+ const groups = [];
+ for (let i = 0; i < 8; i += 2) {
+ groups.push(parseInt(hex.slice(i, i + 2), 16));
+ }
+ return new ipv4_1.Address4(`${groups.join('.')}/${v4Mask}`);
+ }
+ return ipv4_1.Address4.fromHex(hex);
}
/**
* Return the v4-in-v6 form of the address
@@ -756,7 +781,7 @@ class Address6 {
if (!/:$/.test(correct)) {
infix = ':';
}
- return correct + infix + address4.address;
+ return correct + infix + address4.correctForm();
}
/**
* Decodes the Teredo tunneling fields embedded in this address. Returns the
@@ -788,11 +813,9 @@ class Address6 {
*/
const prefix = this.getBitsBase16(0, 32);
const bitsForUdpPort = this.getBits(80, 96);
- // eslint-disable-next-line no-bitwise
const udpPort = (bitsForUdpPort ^ BigInt('0xffff')).toString();
const server4 = ipv4_1.Address4.fromHex(this.getBitsBase16(32, 64));
const bitsForClient4 = this.getBits(96, 128);
- // eslint-disable-next-line no-bitwise
const client4 = ipv4_1.Address4.fromHex((bitsForClient4 ^ BigInt('0xffffffff')).toString(16).padStart(8, '0'));
const flagsBase2 = this.getBitsBase2(64, 80);
const coneNat = (0, common_1.testBit)(flagsBase2, 15);
@@ -874,12 +897,14 @@ class Address6 {
}
else {
const beforeU = 64 - pl;
- bits =
- prefixBits.slice(0, pl) +
- v4Bits.slice(0, beforeU) +
- '00000000' +
- v4Bits.slice(beforeU) +
- '0'.repeat(128 - 72 - (32 - beforeU));
+ bits = [
+ prefixBits.slice(0, pl),
+ v4Bits.slice(0, beforeU),
+ // Bits 64 to 71 are the reserved u octet and are always zero.
+ '00000000',
+ v4Bits.slice(beforeU),
+ '0'.repeat(128 - 72 - (32 - beforeU)),
+ ].join('');
}
const hex = BigInt(`0b${bits}`).toString(16).padStart(32, '0');
const groups = [];
@@ -902,7 +927,7 @@ class Address6 {
if (pl !== 32 && pl !== 40 && pl !== 48 && pl !== 56 && pl !== 64 && pl !== 96) {
throw new address_error_1.AddressError('NAT64 prefix length must be 32, 40, 48, 56, 64, or 96');
}
- if (!this.isInSubnet(prefix6)) {
+ if (!this.isHostInSubnet(prefix6)) {
return null;
}
const bits = this.binaryZeroPad();
@@ -927,9 +952,9 @@ class Address6 {
* @returns {Array}
*/
toByteArray() {
- const valueWithoutPadding = this.bigInt().toString(16);
- const leadingPad = '0'.repeat(valueWithoutPadding.length % 2);
- const value = `${leadingPad}${valueWithoutPadding}`;
+ const value = this.bigInt()
+ .toString(16)
+ .padStart(constants6.BITS / 4, '0');
const bytes = [];
for (let i = 0, length = value.length; i < length; i += 2) {
bytes.push(parseInt(value.substring(i, i + 2), 16));
@@ -943,24 +968,39 @@ class Address6 {
* @returns {Array}
*/
toUnsignedByteArray() {
+ // toByteArray() emits 0 to 255, so unsigning it is an identity mapping and
+ // the two methods return equal arrays. 11.0.0 keeps one of them and makes
+ // this a deprecated alias; test/common-test.ts fails at that version.
return this.toByteArray().map(unsignByte);
}
/**
* Convert a byte array to an Address6 object.
*
+ * Accepts unsigned bytes (0 to 255) or signed bytes (-128 to 127, as an
+ * `Int8Array` or a Java `byte[]` holds them), folding signed values to their
+ * unsigned equivalent. Throws `AddressError` unless given exactly 16
+ * integers from -128 to 255.
+ *
* To convert from a Node.js `Buffer`, spread it: `Address6.fromByteArray([...buf])`.
* @returns {Address6}
*/
static fromByteArray(bytes) {
+ // Address4.fromByteArray takes unsigned bytes only. 11.0.0 aligns this
+ // method with it, at which point the -128 floor here, unsignByte, and the
+ // mapping below all go; test/common-test.ts fails at that version.
+ common.assertByteArray(bytes, 16, 'IPv6', -128);
return this.fromUnsignedByteArray(bytes.map(unsignByte));
}
/**
* Convert an unsigned byte array to an Address6 object.
*
+ * Throws `AddressError` unless given exactly 16 integers from 0 to 255.
+ *
* To convert from a Node.js `Buffer`, spread it: `Address6.fromUnsignedByteArray([...buf])`.
* @returns {Address6}
*/
static fromUnsignedByteArray(bytes) {
+ common.assertByteArray(bytes, 16, 'IPv6', 0);
const BYTE_MAX = BigInt('256');
let result = BigInt('0');
let multiplier = BigInt('1');
@@ -982,7 +1022,11 @@ class Address6 {
* @returns {boolean}
*/
isLinkLocal() {
- // Zeroes are required, i.e. we can't check isInSubnet with 'fe80::/10'
+ const embedded = this.embeddedIPv4();
+ if (embedded) {
+ return embedded.isLinkLocal();
+ }
+ // Zeroes are required, i.e. we can't check isHostInSubnet with 'fe80::/10'
if (this.getBitsBase2(0, 64) ===
'1111111010000000000000000000000000000000000000000000000000000000') {
return true;
@@ -994,6 +1038,10 @@ class Address6 {
* @returns {boolean}
*/
isMulticast() {
+ const embedded = this.embeddedIPv4();
+ if (embedded) {
+ return embedded.isMulticast();
+ }
const type = this.getType();
return type === 'Multicast' || type.startsWith('Multicast ');
}
@@ -1016,27 +1064,54 @@ class Address6 {
* @returns {boolean}
*/
isMapped4() {
- return this.isInSubnet(IPV4_MAPPED_SUBNET);
+ return this.isHostInSubnet(IPV4_MAPPED_SUBNET);
+ }
+ /**
+ * If this address embeds a routable IPv4 address — i.e. it is IPv4-mapped
+ * (`::ffff:0:0/96`) or sits in the NAT64 well-known prefix (`64:ff9b::/96`,
+ * [RFC 6052](https://datatracker.ietf.org/doc/html/rfc6052)) — return that
+ * embedded address as an {@link Address4}; otherwise return null.
+ *
+ * The special-property checks (`isLoopback`, `isLinkLocal`, `isMulticast`,
+ * `isUnspecified`, `isPrivate`, `isCGNAT`, `isBroadcast`) call this first and
+ * delegate to the embedded {@link Address4} when present, so a literal such as
+ * `::ffff:127.0.0.1` is classified by what it actually reaches (loopback)
+ * rather than by its IPv6 wrapper (which `getType()` reports as IPv4-mapped).
+ * This matters wherever the checks back a trust-boundary decision (e.g. an
+ * SSRF allow/deny filter): without normalization, `::ffff:10.0.0.1`,
+ * `::ffff:169.254.169.254`, `64:ff9b::7f00:1`, etc. would all read as
+ * non-internal.
+ * @returns {Address4 | null}
+ */
+ embeddedIPv4() {
+ if (this.isMapped4() || this.isHostInSubnet(NAT64_WELL_KNOWN_SUBNET)) {
+ return this.to4();
+ }
+ return null;
}
/**
* Returns true if the address is a Teredo address, false otherwise
* @returns {boolean}
*/
isTeredo() {
- return this.isInSubnet(TEREDO_SUBNET);
+ return this.isHostInSubnet(TEREDO_SUBNET);
}
/**
* Returns true if the address is a 6to4 address, false otherwise
* @returns {boolean}
*/
is6to4() {
- return this.isInSubnet(SIX_TO_FOUR_SUBNET);
+ return this.isHostInSubnet(SIX_TO_FOUR_SUBNET);
}
/**
* Returns true if the address is a loopback address, false otherwise
* @returns {boolean}
*/
isLoopback() {
+ const embedded = this.embeddedIPv4();
+ if (embedded) {
+ return embedded.isLoopback();
+ }
return this.getType() === 'Loopback';
}
/**
@@ -1044,13 +1119,64 @@ class Address6 {
* @returns {boolean}
*/
isULA() {
- return this.isInSubnet(ULA_SUBNET);
+ return this.isHostInSubnet(ULA_SUBNET);
+ }
+ /**
+ * Returns true if the address is private, i.e. a Unique Local Address in
+ * `fc00::/7` ([RFC 4193](https://datatracker.ietf.org/doc/html/rfc4193)) or an
+ * IPv4-mapped / NAT64 address whose embedded IPv4 address is in one of the
+ * [RFC 1918](https://datatracker.ietf.org/doc/html/rfc1918) private ranges
+ * (e.g. `::ffff:10.0.0.1`). This is the IPv6 counterpart to
+ * {@link Address4.isPrivate}; use it instead of {@link isULA} when you need to
+ * catch mapped RFC 1918 addresses as well as native ULAs.
+ * @returns {boolean}
+ */
+ isPrivate() {
+ const embedded = this.embeddedIPv4();
+ if (embedded) {
+ return embedded.isPrivate();
+ }
+ return this.isULA();
+ }
+ /**
+ * Returns true if the address is an IPv4-mapped / NAT64 address whose embedded
+ * IPv4 address is in the carrier-grade NAT range `100.64.0.0/10`
+ * ([RFC 6598](https://datatracker.ietf.org/doc/html/rfc6598)), false
+ * otherwise. There is no native IPv6 CGNAT range, so this only ever returns
+ * true for an embedded IPv4 address (e.g. `::ffff:100.64.0.1`).
+ * @returns {boolean}
+ */
+ isCGNAT() {
+ const embedded = this.embeddedIPv4();
+ if (embedded) {
+ return embedded.isCGNAT();
+ }
+ return false;
+ }
+ /**
+ * Returns true if the address is an IPv4-mapped / NAT64 address whose embedded
+ * IPv4 address is the limited broadcast address `255.255.255.255`
+ * ([RFC 919](https://datatracker.ietf.org/doc/html/rfc919)), false otherwise.
+ * There is no IPv6 broadcast, so this only ever returns true for an embedded
+ * IPv4 address (e.g. `::ffff:255.255.255.255`).
+ * @returns {boolean}
+ */
+ isBroadcast() {
+ const embedded = this.embeddedIPv4();
+ if (embedded) {
+ return embedded.isBroadcast();
+ }
+ return false;
}
/**
* Returns true if the address is the unspecified address `::`.
* @returns {boolean}
*/
isUnspecified() {
+ const embedded = this.embeddedIPv4();
+ if (embedded) {
+ return embedded.isUnspecified();
+ }
return this.getType() === 'Unspecified';
}
/**
@@ -1058,7 +1184,7 @@ class Address6 {
* @returns {boolean}
*/
isDocumentation() {
- return this.isInSubnet(DOCUMENTATION_SUBNET);
+ return this.isHostInSubnet(DOCUMENTATION_SUBNET);
}
// #endregion
// #region HTML
@@ -1214,4 +1340,5 @@ const SIX_TO_FOUR_SUBNET = new Address6('2002::/16');
const ULA_SUBNET = new Address6('fc00::/7');
const DOCUMENTATION_SUBNET = new Address6('2001:db8::/32');
const IPV4_MAPPED_SUBNET = new Address6('::ffff:0:0/96');
+const NAT64_WELL_KNOWN_SUBNET = new Address6('64:ff9b::/96');
//# sourceMappingURL=ipv6.js.map
\ No newline at end of file
diff --git a/deps/npm/node_modules/ip-address/dist/v4/constants.js b/deps/npm/node_modules/ip-address/dist/v4/constants.js
index 6fa2518..158288b 100644
--- a/deps/npm/node_modules/ip-address/dist/v4/constants.js
+++ b/deps/npm/node_modules/ip-address/dist/v4/constants.js
@@ -3,6 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
exports.RE_SUBNET_STRING = exports.RE_ADDRESS = exports.GROUPS = exports.BITS = void 0;
exports.BITS = 32;
exports.GROUPS = 4;
-exports.RE_ADDRESS = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/g;
+// Each octet is 0-255 written without a leading zero. A leading zero is
+// octal to the WHATWG URL parser, inet_aton, and getaddrinfo, but decimal to
+// parseInt(part, 10), so accepting the notation would make this library
+// disagree with the network stack about which host a string names.
+exports.RE_ADDRESS = /^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$/g;
exports.RE_SUBNET_STRING = /\/\d{1,2}$/;
//# sourceMappingURL=constants.js.map
\ No newline at end of file
diff --git a/deps/npm/node_modules/ip-address/dist/v6/constants.js b/deps/npm/node_modules/ip-address/dist/v6/constants.js
index 1a8cd1d..4616cad 100644
--- a/deps/npm/node_modules/ip-address/dist/v6/constants.js
+++ b/deps/npm/node_modules/ip-address/dist/v6/constants.js
@@ -44,6 +44,7 @@ exports.TYPES = {
'ff05::1:3/128': 'Multicast (All DHCP servers in this site)',
'::/128': 'Unspecified',
'::1/128': 'Loopback',
+ '::ffff:0:0/96': 'IPv4-mapped',
'ff00::/8': 'Multicast',
'fe80::/10': 'Link-local unicast',
'fc00::/7': 'Unique local',
@@ -76,6 +77,6 @@ exports.RE_SUBNET_STRING = /\/\d{1,3}(?=%|$)/;
* @static
*/
exports.RE_ZONE_STRING = /%.*$/;
-exports.RE_URL = /^\[{0,1}([0-9a-f:]+)\]{0,1}/;
-exports.RE_URL_WITH_PORT = /\[([0-9a-f:]+)\]:([0-9]{1,5})/;
+exports.RE_URL = /^(?:\[([0-9a-f:.]+)\]|([0-9a-f:.]+))(?:[/?#].*)?$/i;
+exports.RE_URL_WITH_PORT = /^\[([0-9a-f:.]+)\]:([0-9]{1,5})(?:[/?#].*)?$/i;
//# sourceMappingURL=constants.js.map
\ No newline at end of file
diff --git a/deps/npm/node_modules/ip-address/package.json b/deps/npm/node_modules/ip-address/package.json
index 47d109e..dc778c2 100644
--- a/deps/npm/node_modules/ip-address/package.json
+++ b/deps/npm/node_modules/ip-address/package.json
@@ -16,7 +16,7 @@
"bigint",
"browser"
],
- "version": "10.2.0",
+ "version": "10.4.0",
"author": "Beau Gunderson <beau@beaugunderson.com> (https://beaugunderson.com/)",
"license": "MIT",
"main": "dist/ip-address.js",
@@ -25,6 +25,9 @@
"docs": "tsx scripts/build-readme.ts",
"build": "rm -rf dist; mkdir dist; tsc",
"prepack": "npm run docs && npm run build",
+ "prepare": "git config core.hooksPath hooks || true",
+ "lint": "prettier --check . && eslint . --ext .ts,.js --max-warnings 0",
+ "lint:fix": "prettier --write . && eslint . --ext .ts,.js --max-warnings 0 --fix",
"test-ci": "c8 --experimental-monocart mocha",
"test": "mocha",
"watch": "mocha --watch"
@@ -54,7 +57,7 @@
],
"repository": {
"type": "git",
- "url": "git://github.com/beaugunderson/ip-address.git"
+ "url": "https://github.com/beaugunderson/ip-address.git"
},
"overrides": {
"diff": "^8.0.3",
@@ -70,11 +73,10 @@
"chai": "^6.2.2",
"eslint": "^8.57.1",
"eslint_d": "^14.0.4",
- "eslint-config-airbnb": "^19.0.4",
+ "eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-filenames": "^1.3.2",
"eslint-plugin-import": "^2.32.0",
- "eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-prettier": "^5.5.5",
"eslint-plugin-sort-imports-es6-autofix": "^0.6.0",
"mocha": "^11.7.5",
--
2.54.0

View File

@ -1,31 +0,0 @@
From 2cd4c12776af3da588231d3eb498e6451c30eae5 Mon Sep 17 00:00:00 2001
From: Zuzana Svetlikova <zsvetlik@redhat.com>
Date: Thu, 27 Apr 2017 14:25:42 +0200
Subject: [PATCH] Disable running gyp on shared deps
Signed-off-by: rpm-build <rpm-build>
---
Makefile | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/Makefile b/Makefile
index 73feb4c..45bbceb 100644
--- a/Makefile
+++ b/Makefile
@@ -123,10 +123,9 @@ with-code-cache:
test-code-cache: with-code-cache
$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=$(BUILDTYPE_LOWER) code-cache
-out/Makefile: common.gypi deps/uv/uv.gyp deps/http_parser/http_parser.gyp \
- deps/zlib/zlib.gyp deps/v8/gypfiles/toolchain.gypi \
- deps/v8/gypfiles/features.gypi deps/v8/gypfiles/v8.gyp node.gyp \
- config.gypi
+out/Makefile: common.gypi deps/http_parser/http_parser.gyp \
+ deps/v8/gypfiles/toolchain.gypi deps/v8/gypfiles/features.gypi \
+ deps/v8/gypfiles/v8.gyp node.gyp config.gypi
$(PYTHON) tools/gyp_node.py -f make
config.gypi: configure configure.py
--
2.26.2

View File

@ -0,0 +1,46 @@
From e93d9b5fdcd8e5744de629461c03a07de2252f8f Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Fri, 17 Apr 2020 12:59:44 +0200
Subject: [PATCH] Remove unused OpenSSL config
The build process will try to create these config files, even when
using the system OpenSSL and will thus fail since we strip this path
from the tarball.
Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
Signed-off-by: rpm-build <rpm-build>
---
node.gyp | 17 -----------------
1 file changed, 17 deletions(-)
diff --git a/node.gyp b/node.gyp
index 1147495..da6ea50 100644
--- a/node.gyp
+++ b/node.gyp
@@ -822,23 +822,6 @@
],
},
],
- }, {
- 'variables': {
- 'opensslconfig_internal': '<(obj_dir)/deps/openssl/openssl.cnf',
- 'opensslconfig': './deps/openssl/nodejs-openssl.cnf',
- },
- 'actions': [
- {
- 'action_name': 'reset_openssl_cnf',
- 'inputs': [ '<(opensslconfig)', ],
- 'outputs': [ '<(opensslconfig_internal)', ],
- 'action': [
- '<(python)', 'tools/copyfile.py',
- '<(opensslconfig)',
- '<(opensslconfig_internal)',
- ],
- },
- ],
}],
],
}, # node_core_target_name
--
2.47.0

File diff suppressed because it is too large Load Diff

View File

@ -1,84 +0,0 @@
From e7afb2d6e2a6c8f9c9c32e12a10c3c5c4902a251 Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Tue, 1 May 2018 08:05:30 -0400
Subject: [PATCH] Suppress NPM message to run global update
Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
Signed-off-by: rpm-build <rpm-build>
---
deps/npm/bin/npm-cli.js | 54 -----------------------------------------
1 file changed, 54 deletions(-)
diff --git a/deps/npm/bin/npm-cli.js b/deps/npm/bin/npm-cli.js
index c0d9be0..0f0892e 100755
--- a/deps/npm/bin/npm-cli.js
+++ b/deps/npm/bin/npm-cli.js
@@ -71,65 +71,11 @@
npm.command = 'help'
}
- var isGlobalNpmUpdate = conf.global && ['install', 'update'].includes(npm.command) && npm.argv.includes('npm')
-
// now actually fire up npm and run the command.
// this is how to use npm programmatically:
conf._exit = true
npm.load(conf, function (er) {
if (er) return errorHandler(er)
- if (
- !isGlobalNpmUpdate &&
- npm.config.get('update-notifier') &&
- !unsupported.checkVersion(process.version).unsupported
- ) {
- const pkg = require('../package.json')
- let notifier = require('update-notifier')({pkg})
- const isCI = require('ci-info').isCI
- if (
- notifier.update &&
- notifier.update.latest !== pkg.version &&
- !isCI
- ) {
- const color = require('ansicolors')
- const useColor = npm.config.get('color')
- const useUnicode = npm.config.get('unicode')
- const old = notifier.update.current
- const latest = notifier.update.latest
- let type = notifier.update.type
- if (useColor) {
- switch (type) {
- case 'major':
- type = color.red(type)
- break
- case 'minor':
- type = color.yellow(type)
- break
- case 'patch':
- type = color.green(type)
- break
- }
- }
- const changelog = `https://github.com/npm/cli/releases/tag/v${latest}`
- notifier.notify({
- message: `New ${type} version of ${pkg.name} available! ${
- useColor ? color.red(old) : old
- } ${useUnicode ? '→' : '->'} ${
- useColor ? color.green(latest) : latest
- }\n` +
- `${
- useColor ? color.yellow('Changelog:') : 'Changelog:'
- } ${
- useColor ? color.cyan(changelog) : changelog
- }\n` +
- `Run ${
- useColor
- ? color.green(`npm install -g ${pkg.name}`)
- : `npm i -g ${pkg.name}`
- } to update!`
- })
- }
- }
npm.commands[npm.command](npm.argv, function (err) {
// https://genius.com/Lin-manuel-miranda-your-obedient-servant-lyrics
if (
--
2.26.2

View File

@ -0,0 +1,84 @@
From 98738d27288bd9ca634e29181ef665e812e7bbd3 Mon Sep 17 00:00:00 2001
From: Michael Dawson <midawson@redhat.com>
Date: Fri, 23 Feb 2024 13:43:56 +0100
Subject: [PATCH] Disable FIPS options
On RHEL, FIPS should be configured only on system level.
Additionally, the related options may cause segfault when used on RHEL.
This patch causes the option processing to end sooner
than the problematic code gets executed.
Additionally, the JS-level options to mess with FIPS settings
are similarly disabled.
Upstream report: https://github.com/nodejs/node/pull/48950
RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=2226726
---
lib/crypto.js | 10 ++++++++++
lib/internal/errors.js | 6 ++++++
src/crypto/crypto_util.cc | 2 ++
3 files changed, 18 insertions(+)
diff --git a/lib/crypto.js b/lib/crypto.js
index 41adecc..b2627ac 100644
--- a/lib/crypto.js
+++ b/lib/crypto.js
@@ -36,7 +36,10 @@ const {
assertCrypto();
const {
+ // RHEL specific error
+ ERR_CRYPTO_FIPS_SYSTEM_CONTROLLED,
+
ERR_CRYPTO_FIPS_FORCED,
ERR_WORKER_UNSUPPORTED_OPERATION,
} = require('internal/errors').codes;
const constants = internalBinding('constants').crypto;
@@ -251,6 +254,13 @@ function getFips() {
}
function setFips(val) {
+ // in RHEL FIPS enable/disable should only be done at system level
+ if (getFips() != val) {
+ throw new ERR_CRYPTO_FIPS_SYSTEM_CONTROLLED();
+ } else {
+ return;
+ }
+
if (getOptionValue('--force-fips')) {
if (val) return;
throw new ERR_CRYPTO_FIPS_FORCED();
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index a722360..04d8a53 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -1111,6 +1111,12 @@ module.exports = {
//
// Note: Node.js specific errors must begin with the prefix ERR_
+// insert RHEL specific erro
+E('ERR_CRYPTO_FIPS_SYSTEM_CONTROLLED',
+ 'Cannot set FIPS mode. FIPS should be enabled/disabled at system level. See' +
+ 'https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/security_hardening/assembly_installing-the-system-in-fips-mode_security-hardening for more details.\n',
+ Error);
+
E('ERR_ACCESS_DENIED',
function(msg, permission = '', resource = '') {
this.permission = permission;
diff --git a/src/crypto/crypto_util.cc b/src/crypto/crypto_util.cc
index 5734d8f..ef9d1b1 100644
--- a/src/crypto/crypto_util.cc
+++ b/src/crypto/crypto_util.cc
@@ -86,6 +86,8 @@ bool ProcessFipsOptions() {
/* Override FIPS settings in configuration file, if needed. */
if (per_process::cli_options->enable_fips_crypto ||
per_process::cli_options->force_fips_crypto) {
+ fprintf(stderr, "ERROR: Using options related to FIPS is not recommended, configure FIPS in openssl instead. See https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/security_hardening/assembly_installing-the-system-in-fips-mode_security-hardening for more details.\n");
+ return false;
#if OPENSSL_VERSION_MAJOR >= 3
if (!ncrypto::testFipsEnabled()) return false;
return ncrypto::setFipsEnabled(true, nullptr);
--
2.43.2

View File

@ -1,122 +0,0 @@
From 0028cc74dac4dd24b8599ade85cb49fdafa9f559 Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Fri, 6 Dec 2019 16:40:25 -0500
Subject: [PATCH] build: auto-load ICU data from --with-icu-default-data-dir
When compiled with `--with-intl=small` and
`--with-icu-default-data-dir=PATH`, Node.js will use PATH as a
fallback location for the ICU data.
We will first perform an access check using fopen(PATH, 'r') to
ensure that the file is readable. If it is, we'll set the
icu_data_directory and proceed. There's a slight overhead for the
fopen() check, but it should be barely measurable.
This will be useful for Linux distribution packagers who want to
be able to ship a minimal node binary in a container image but
also be able to add on the full i18n support where needed. With
this patch, it becomes possible to ship the interpreter as
/usr/bin/node in one package for the distribution and to ship the
data files in another package (without a strict dependency
between the two). This means that users of the distribution will
not need to explicitly direct Node.js to locate the ICU data. It
also means that in environments where full internationalization is
not required, they do not need to carry the extra content (with
the associated storage costs).
Refs: https://github.com/nodejs/node/issues/3460
Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
Signed-off-by: rpm-build <rpm-build>
---
configure.py | 9 +++++++++
node.gypi | 7 +++++++
src/node.cc | 20 ++++++++++++++++++++
3 files changed, 36 insertions(+)
diff --git a/configure.py b/configure.py
index 89f7bf5..d611a88 100755
--- a/configure.py
+++ b/configure.py
@@ -433,6 +433,14 @@ intl_optgroup.add_option('--with-icu-source',
'the icu4c source archive. '
'v%d.x or later recommended.' % icu_versions['minimum_icu'])
+intl_optgroup.add_option('--with-icu-default-data-dir',
+ action='store',
+ dest='with_icu_default_data_dir',
+ help='Path to the icuXXdt{lb}.dat file. If unspecified, ICU data will '
+ 'only be read if the NODE_ICU_DATA environment variable or the '
+ '--icu-data-dir runtime argument is used. This option has effect '
+ 'only when Node.js is built with --with-intl=small-icu.')
+
parser.add_option('--with-ltcg',
action='store_true',
dest='with_ltcg',
@@ -1359,6 +1367,7 @@ def configure_intl(o):
locs.add('root') # must have root
o['variables']['icu_locales'] = string.join(locs,',')
# We will check a bit later if we can use the canned deps/icu-small
+ o['variables']['icu_default_data'] = options.with_icu_default_data_dir or ''
elif with_intl == 'full-icu':
# full ICU
o['variables']['v8_enable_i18n_support'] = 1
diff --git a/node.gypi b/node.gypi
index 466a174..65b97d6 100644
--- a/node.gypi
+++ b/node.gypi
@@ -113,6 +113,13 @@
'conditions': [
[ 'icu_small=="true"', {
'defines': [ 'NODE_HAVE_SMALL_ICU=1' ],
+ 'conditions': [
+ [ 'icu_default_data!=""', {
+ 'defines': [
+ 'NODE_ICU_DEFAULT_DATA_DIR="<(icu_default_data)"',
+ ],
+ }],
+ ],
}]],
}],
[ 'node_use_bundled_v8=="true" and \
diff --git a/src/node.cc b/src/node.cc
index 7c01187..c9840e3 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -92,6 +92,7 @@
#if defined(NODE_HAVE_I18N_SUPPORT)
#include <unicode/uvernum.h>
+#include <unicode/utypes.h>
#endif
#if defined(LEAK_SANITIZER)
@@ -2643,6 +2644,25 @@ void Init(std::vector<std::string>* argv,
// If the parameter isn't given, use the env variable.
if (per_process_opts->icu_data_dir.empty())
SafeGetenv("NODE_ICU_DATA", &per_process_opts->icu_data_dir);
+
+#ifdef NODE_ICU_DEFAULT_DATA_DIR
+ // If neither the CLI option nor the environment variable was specified,
+ // fall back to the configured default
+ if (per_process_opts->icu_data_dir.empty()) {
+ // Check whether the NODE_ICU_DEFAULT_DATA_DIR contains the right data
+ // file and can be read.
+ static const char full_path[] =
+ NODE_ICU_DEFAULT_DATA_DIR "/" U_ICUDATA_NAME ".dat";
+
+ FILE* f = fopen(full_path, "rb");
+
+ if (f != nullptr) {
+ fclose(f);
+ per_process_opts->icu_data_dir = NODE_ICU_DEFAULT_DATA_DIR;
+ }
+ }
+#endif // NODE_ICU_DEFAULT_DATA_DIR
+
// Initialize ICU.
// If icu_data_dir is empty here, it will load the 'minimal' data.
if (!i18n::InitializeICUDirectory(per_process_opts->icu_data_dir)) {
--
2.26.2

View File

@ -1,13 +0,0 @@
diff --git a/deps/npm/node_modules/y18n/index.js b/deps/npm/node_modules/y18n/index.js
index d720681628..727362aac0 100644
--- a/deps/npm/node_modules/y18n/index.js
+++ b/deps/npm/node_modules/y18n/index.js
@@ -11,7 +11,7 @@ function Y18N (opts) {
this.fallbackToLanguage = typeof opts.fallbackToLanguage === 'boolean' ? opts.fallbackToLanguage : true
// internal stuff.
- this.cache = {}
+ this.cache = Object.create(null)
this.writeQueue = []
}

View File

@ -1,189 +0,0 @@
#!/bin/sh
# Uses Argbash to generate command argument parsing. To update
# arguments, make sure to call
# `argbash nodejs-tarball.sh -o nodejs-tarball.sh`
# ARG_POSITIONAL_SINGLE([version],[Node.js release version],[""])
# ARG_DEFAULTS_POS([])
# ARG_HELP([Tool to aid in Node.js packaging of new releases])
# ARGBASH_GO()
# needed because of Argbash --> m4_ignore([
### START OF CODE GENERATED BY Argbash v2.8.1 one line above ###
# Argbash is a bash code generator used to get arguments parsing right.
# Argbash is FREE SOFTWARE, see https://argbash.io for more info
die()
{
local _ret=$2
test -n "$_ret" || _ret=1
test "$_PRINT_HELP" = yes && print_help >&2
echo "$1" >&2
exit ${_ret}
}
begins_with_short_option()
{
local first_option all_short_options='h'
first_option="${1:0:1}"
test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0
}
# THE DEFAULTS INITIALIZATION - POSITIONALS
_positionals=()
_arg_version=""
# THE DEFAULTS INITIALIZATION - OPTIONALS
print_help()
{
printf '%s\n' "Tool to aid in Node.js packaging of new releases"
printf 'Usage: %s [-h|--help] [<version>]\n' "$0"
printf '\t%s\n' "<version>: Node.js release version (default: '""')"
printf '\t%s\n' "-h, --help: Prints help"
}
parse_commandline()
{
_positionals_count=0
while test $# -gt 0
do
_key="$1"
case "$_key" in
-h|--help)
print_help
exit 0
;;
-h*)
print_help
exit 0
;;
*)
_last_positional="$1"
_positionals+=("$_last_positional")
_positionals_count=$((_positionals_count + 1))
;;
esac
shift
done
}
handle_passed_args_count()
{
test "${_positionals_count}" -le 1 || _PRINT_HELP=yes die "FATAL ERROR: There were spurious positional arguments --- we expect between 0 and 1, but got ${_positionals_count} (the last one was: '${_last_positional}')." 1
}
assign_positional_args()
{
local _positional_name _shift_for=$1
_positional_names="_arg_version "
shift "$_shift_for"
for _positional_name in ${_positional_names}
do
test $# -gt 0 || break
eval "$_positional_name=\${1}" || die "Error during argument parsing, possibly an Argbash bug." 1
shift
done
}
parse_commandline "$@"
handle_passed_args_count
assign_positional_args 1 "${_positionals[@]}"
# OTHER STUFF GENERATED BY Argbash
### END OF CODE GENERATED BY Argbash (sortof) ### ])
# [ <-- needed because of Argbash
set -e
echo $_arg_version
if [ x$_arg_version != x ]; then
version=$_arg_version
else
version=$(rpm -q --specfile --qf='%{version}\n' nodejs.spec | head -n1)
fi
rm -f node-v${version}.tar.gz node-v${version}-stripped.tar.gz
wget http://nodejs.org/dist/v${version}/node-v${version}.tar.gz \
http://nodejs.org/dist/v${version}/SHASUMS256.txt
sha256sum -c SHASUMS256.txt --ignore-missing
tar -zxf node-v${version}.tar.gz
rm -rf node-v${version}/deps/openssl
tar -zcf node-v${version}-stripped.tar.gz node-v${version}
# Download the matching version of ICU
rm -f icu4c*-src.tgz icu.md5
ICUMD5=$(cat node-v${version}/tools/icu/current_ver.dep |jq -r '.[0].md5')
wget $(cat node-v${version}/tools/icu/current_ver.dep |jq -r '.[0].url')
ICUTARBALL=$(ls -1 icu4c*-src.tgz)
echo "$ICUMD5 $ICUTARBALL" > icu.md5
md5sum -c icu.md5
rm -f icu.md5 SHASUMS256.txt
rhpkg new-sources node-v${version}-stripped.tar.gz icu4c*-src.tgz
rm -f node-v${version}.tar.gz
set +e
# Determine the bundled versions of the various packages
echo "Bundled software versions"
echo "-------------------------"
echo
echo "libnode shared object version"
echo "========================="
grep "define NODE_MODULE_VERSION" node-v${version}/src/node_version.h
echo
echo "V8"
echo "========================="
grep "define V8_MAJOR_VERSION" node-v${version}/deps/v8/include/v8-version.h
grep "define V8_MINOR_VERSION" node-v${version}/deps/v8/include/v8-version.h
grep "define V8_BUILD_NUMBER" node-v${version}/deps/v8/include/v8-version.h
grep "define V8_PATCH_LEVEL" node-v${version}/deps/v8/include/v8-version.h
echo
echo "c-ares"
echo "========================="
grep "define ARES_VERSION_MAJOR" node-v${version}/deps/cares/include/ares_version.h
grep "define ARES_VERSION_MINOR" node-v${version}/deps/cares/include/ares_version.h
grep "define ARES_VERSION_PATCH" node-v${version}/deps/cares/include/ares_version.h
echo
echo "http-parser"
echo "========================="
grep "define HTTP_PARSER_VERSION_MAJOR" node-v${version}/deps/http_parser/http_parser.h
grep "define HTTP_PARSER_VERSION_MINOR" node-v${version}/deps/http_parser/http_parser.h
grep "define HTTP_PARSER_VERSION_PATCH" node-v${version}/deps/http_parser/http_parser.h
echo
echo "libuv"
echo "========================="
grep "define UV_VERSION_MAJOR" node-v${version}/deps/uv/include/uv/version.h
grep "define UV_VERSION_MINOR" node-v${version}/deps/uv/include/uv/version.h
grep "define UV_VERSION_PATCH" node-v${version}/deps/uv/include/uv/version.h
echo
echo "nghttp2"
echo "========================="
grep "define NGHTTP2_VERSION " node-v${version}/deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h
echo
echo "ICU"
echo "========================="
grep "url" node-v${version}/tools/icu/current_ver.dep
echo
echo "punycode"
echo "========================="
grep "'version'" node-v${version}/lib/punycode.js
echo
echo "npm"
echo "========================="
grep "\"version\":" node-v${version}/deps/npm/package.json
echo
echo "Make sure these versions match what is in the RPM spec file"
rm -rf node-v${version}
# ] <-- needed because of Argbash

9
SOURCES/nodejs.pc.in Normal file
View File

@ -0,0 +1,9 @@
prefix=@PREFIX@
includedir=@INCLUDEDIR@
libdir=@LIBDIR@
Name: @PKGCONFNAME@
Description: JavaScript Runtime
Version: @NODEJS_VERSION@
Libs: -L${libdir} -lnode
Cflags: -I${includedir}/node

167
SOURCES/nodejs.srpm.macros Normal file
View File

@ -0,0 +1,167 @@
# ============================================================================
# Vendored dependencies management
# --- Version macros definition
# Parse and normalize version string into several macros.
# By default, stores the whole string in `%<name>_evr` macro,
# then automatically strips any epoch and/or release parts
# (specified in the standard "E:V-R" format)
# and defines `%<name>_epoch`, `%<name>_version`, and `%<name>_release` macros.
#
# With the `-p` option, the version is additionally split into
# `%<name>_version_major`, `%<name>_version_minor`, and `%<name>_version_patch` macros.
#
# Any would-be empty macro will evaluate to `%{nil}`.
#
# Options:
# -p : Also define the partial macros.
#
# Arguments:
# 1: Name of the dependency. Any `-' will be replaced by `_' in the macro names.
# 2: The EVR string to parse.
%nodejs_define_version(p) %{lua:
local function hasflag(flag) return (rpm.expand("%{-" .. flag .. "}") ~= "") end
local function readflag(flag)
if not hasflag(flag) then return nil end
local value = rpm.expand("%{-" .. flag .. "*}")
if (value == '""') or (value == "''") then value = '' end
return value
end
local function explicitset(rpmvar, value)
if (value == nil) or (value == "") then value = "%{nil}" end
rpm.define(rpmvar .. " " .. value)
end
\
local arg = {}; for a = 1, tonumber(rpm.expand("%#")) do
table.insert(arg, rpm.expand("%" .. a))
end
local opt = {
["p"] = readflag("p"),
}
\
local component = arg[1] or error("No name provided!")
local evr = arg[2] or error("No version string provided!")
\
local name = component:gsub("-", "_") -- macro-safe name
\
explicitset(name .. "_evr", evr)
\
local _, epoch_end, epoch = evr:find("^(%d+):")
explicitset(name .. "_epoch", epoch)
\
local release_start, _, release = evr:find("%-([^-]+)$")
explicitset(name .. "_release", release)
\
local version_start, version_end = 0, -1
if epoch_end then version_start = epoch_end + 1 end
if release_start then version_end = release_start -1 end
\
local version = evr:sub(version_start, version_end)
explicitset(name .. "_version", version)
\
if opt.p then
local parts = {}; for p in version:gmatch("[^.]+") do table.insert(parts, p) end
explicitset(name .. "_version_major", parts[1])
explicitset(name .. "_version_minor", parts[2])
explicitset(name .. "_version_patch", parts[3])
end
}
# --- Declare vendored dependency
# Emits bcond-controlled RPM tags for a (potentially) vendored dependency.
#
# By default, it emits `Provides: bundled(<name>) = <version>` for given arguments.
# If de-vendoring option is provided, also defines a bcond that controls whether to de-vendor or not.
# The default is to de-vendor when possible unless a global bcond (`all_deps_bundled`) is set.
#
# Options:
# -a : Autoversion try using `<name>_version` macro if the version argument is empty.
# -n[npmname,...] : Also provide the respective npm module name when vendoring.
# -p[pkgname,...] : Use pkgconfig to BuildRequire de-vendored dependency.
# -r[rpmname,...] : Also explicitly declare run time requirement.
# -s[rpmname,...] : BuildRequire de-vendored dependency by RPM name.
#
# All above options accept optional parameter overriding the component name in respective tag.
# If needed, multiple values can be requested by separating them with a comma.
#
# When a name is used in a macro context (for example, in the -a option),
# the same name-mangling as for nodejs_define_version is used;
# no need to adjust it by hand.
#
# Arguments:
# 1: Name of the vendored component. Should be appropriate for `Provides: bundled(<name>)` tag.
# 2: Version of the vendored component. Ignored if de-vendored.
%nodejs_declare_bundled(an::p::r::s::) %{lua:
local function read(rpmvar)
if not rpmvar then return nil end
local macro_string = "%{" .. rpmvar .. "}"
if rpm.expand(macro_string) == macro_string then return nil end
return rpm.expand("%{?" .. rpmvar .. "}")
end
local function hasflag(flag) return (rpm.expand("%{-" .. flag .. "}") ~= "") end
local function readflag(flag)
if not hasflag(flag) then return nil end
local value = rpm.expand("%{-" .. flag .. "*}")
if (value == '""') or (value == "''") then value = '' end
return value
end
\
local arg = {}; for a = 1, tonumber(rpm.expand("%#")) do
table.insert(arg, rpm.expand("%" .. a))
end
local opt = {
["a"] = hasflag("a"),
["n"] = readflag("n"),
["p"] = readflag("p"),
["r"] = readflag("r"),
["s"] = readflag("s"),
}
\
local component = arg[1] or error("Vendored component was not named!")
local version = arg[2] or (opt.a and read(component:gsub("-", "_") .. "_version")) or error("Missing component version!")
\
local mapvalues = function(fn, tbl)
local output = {}; for _, val in ipairs(tbl) do table.insert(output, fn(val)) end; return output
end
local splitnames = function(input)
local output = {}; for m in input:gmatch("[^,]+") do table.insert(output, m) end; return output
end
local nl = string.char(10); -- \n does not work in rpmlua
\
local possible_to_devendor = opt.p or opt.s
local should_devendor = possible_to_devendor and rpm.expand("%{with all_deps_bundled}") == "0"
\
local bcond_name = "bundled_" .. component:gsub("-", "_")
if should_devendor
then rpm.expand("%bcond_with " .. bcond_name)
else rpm.expand("%bcond_without " .. bcond_name)
end
\
if rpm.expand("%with " .. bcond_name) == "1" then
local provides = {string.format("bundled(%s) = %s", component, version)}
if opt.n then
local names = {component}; if opt.n ~= "" then names = splitnames(opt.n) end
for _, name in ipairs(names) do
table.insert(provides, string.format("npm(%s) = %s", name, version))
end
end
print("Provides: " .. table.concat(provides, ", "))
else
\
local buildrequire, require = nil, nil
if opt.p then
local format = function(n) return string.format("pkgconfig(%s)", n) end
local names = {component}; if opt.p ~= "" then names = splitnames(opt.p) end
buildrequire = "BuildRequires: " .. table.concat(mapvalues(format, names), ", ")
elseif opt.s then
local names = {component}; if opt.s ~= "" then names = splitnames(opt.s) end
buildrequire = "BuildRequires: " .. table.concat(names, ", ")
end
if opt.r then
local names = {component}; if opt.r ~= "" then names = splitnames(opt.r) end
require = "Requires: " .. table.concat(names, ", ")
end
\
print(table.concat({buildrequire, require}, nl))
end
}

View File

@ -1,2 +0,0 @@
%__nodejs_native_requires %{_rpmconfigdir}/nodejs_native.req
%__nodejs_native_path ^/usr/lib.*/node_modules/.*\\.node$

View File

@ -1 +0,0 @@
prefix=/usr/local

7
SOURCES/npmrc.in Normal file
View File

@ -0,0 +1,7 @@
# This is the distribution-level configuration file for npm.
# To configure npm on a system level, use the globalconfig below (defaults to @SYSCONFDIR@/npmrc).
# vim:set filetype=dosini:
globalconfig=@SYSCONFDIR@/npmrc
prefix=/usr/local
update-notifier=false

61
SOURCES/test-runner.sh Executable file
View File

@ -0,0 +1,61 @@
#!/bin/bash
NODE_BIN="$1"
PARENT_TEST_FOLDER="$2"
TEST_LIST_FILE="$3"
# At most 10 min per test
TIMEOUT_DURATION=600
# Exit code
FINAL_RESULT=0
ARCH=$(uname -m)
echo "Started test run:"
# Run the list of test
while IFS= read -r test_line; do
# ignore commented lines
if [[ "$test_line" =~ ^# ]]; then
continue
fi
# If test has specified ARCH which it should be skipped
# Extract it
TEST_PATH=$(echo "$test_line" | awk '{print $1}')
IGNORE_ARCHES=$(echo "$test_line" |\
awk '{for (i=2; i<=NF; i++) printf "%s ", $i; print ""}')
# Skip test for specified ARCH
for ARCH_IGNORE in $IGNORE_ARCHES; do
if [[ "$ARCH_IGNORE" == "$ARCH" ]]; then
echo "Skipping test, current arch is in ignore: $TEST_PATH ($ARCH_IGNORE)"
continue 2
fi
done
# Construct test path
TEST_SCRIPT="$PARENT_TEST_FOLDER/$TEST_PATH"
if [ ! -f "$TEST_SCRIPT" ]; then
echo "Test script not found: $TEST_SCRIPT"
continue
fi
TEST_OUTPUT=$(timeout "$TIMEOUT_DURATION" "$NODE_BIN" "$TEST_SCRIPT" 2>&1)
TEST_RESULT=$?
# Handle test result
if [ $TEST_RESULT -ne 0 ]; then
FINAL_RESULT=1
if [ $TEST_RESULT -eq 124 ]; then
echo "Test timed out: $TEST_SCRIPT"
else
echo "Test failed: $TEST_SCRIPT"
fi
echo "Test failure message:"
echo "$TEST_OUTPUT"
fi
done < "$TEST_LIST_FILE"
if [ $FINAL_RESULT -eq 0 ]; then
echo "All tests succesfully passed."
fi
exit $FINAL_RESULT

2743
SOURCES/test-should-pass.txt Normal file

File diff suppressed because it is too large Load Diff

9
SOURCES/v8.pc.in Normal file
View File

@ -0,0 +1,9 @@
prefix=@PREFIX@
includedir=@INCLUDEDIR@
libdir=@LIBDIR@
Name: @PKGCONFNAME@
Description: JavaScript Runtime
Version: @V8_VERSION@
Libs: -L${libdir} -lv8
Cflags: -I${includedir}

File diff suppressed because it is too large Load Diff