File CVE-2024-30261-undici-fetch-integrity.patch of Package nodejs-electron
Port https://github.com/nodejs/undici/commit/d542b8c to apply to amalgamated undici code in nodejs (the original copy in deps/undici/src is not used and i'm not bothering with patching it)
These chunks were manually cherry-picked from upstream nodejs commit https://github.com/nodejs/node/commit/60d24938 to match the undici changes.
--- a/third_party/electron_node/deps/undici/undici.js
+++ b/third_party/electron_node/deps/undici/undici.js
@@ -992,9 +992,12 @@ var require_util2 = __commonJS({
var { isBlobLike, toUSVString, ReadableStreamFrom } = require_util();
var assert = require("assert");
var { isUint8Array } = require("util/types");
+ var supportedHashes = [];
var crypto;
try {
crypto = require("crypto");
+ const possibleRelevantHashes = ["sha256", "sha384", "sha512"];
+ supportedHashes = crypto.getHashes().filter((hash) => possibleRelevantHashes.includes(hash));
} catch {
}
function responseURL(response) {
@@ -1277,46 +1280,38 @@ var require_util2 = __commonJS({
if (parsedMetadata.length === 0) {
return true;
}
- const list = parsedMetadata.sort((c, d) => d.algo.localeCompare(c.algo));
- const strongest = list[0].algo;
- const metadata = list.filter((item) => item.algo === strongest);
+ const strongest = getStrongestMetadata(parsedMetadata);
+ const metadata = filterMetadataListByAlgorithm(parsedMetadata, strongest);
for (const item of metadata) {
const algorithm = item.algo;
- let expectedValue = item.hash;
- if (expectedValue.endsWith("==")) {
- expectedValue = expectedValue.slice(0, -2);
- }
+ const expectedValue = item.hash;
let actualValue = crypto.createHash(algorithm).update(bytes).digest("base64");
- if (actualValue.endsWith("==")) {
- actualValue = actualValue.slice(0, -2);
- }
- if (actualValue === expectedValue) {
- return true;
- }
- let actualBase64URL = crypto.createHash(algorithm).update(bytes).digest("base64url");
- if (actualBase64URL.endsWith("==")) {
- actualBase64URL = actualBase64URL.slice(0, -2);
+ if (actualValue[actualValue.length - 1] === "=") {
+ if (actualValue[actualValue.length - 2] === "=") {
+ actualValue = actualValue.slice(0, -2);
+ } else {
+ actualValue = actualValue.slice(0, -1);
+ }
}
- if (actualBase64URL === expectedValue) {
+ if (compareBase64Mixed(actualValue, expectedValue)) {
return true;
}
}
return false;
}
__name(bytesMatch, "bytesMatch");
- var parseHashWithOptions = /((?<algo>sha256|sha384|sha512)-(?<hash>[A-z0-9+/]{1}.*={0,2}))( +[\x21-\x7e]?)?/i;
+ var parseHashWithOptions = /(?<algo>sha256|sha384|sha512)-((?<hash>[A-Za-z0-9+/]+|[A-Za-z0-9_-]+)={0,2}(?:\s|$)( +[!-~]*)?)?/i;
function parseMetadata(metadata) {
const result = [];
let empty = true;
- const supportedHashes = crypto.getHashes();
for (const token of metadata.split(" ")) {
empty = false;
const parsedToken = parseHashWithOptions.exec(token);
- if (parsedToken === null || parsedToken.groups === void 0) {
+ if (parsedToken === null || parsedToken.groups === void 0 || parsedToken.groups.algo === void 0) {
continue;
}
- const algorithm = parsedToken.groups.algo;
- if (supportedHashes.includes(algorithm.toLowerCase())) {
+ const algorithm = parsedToken.groups.algo.toLowerCase();
+ if (supportedHashes.includes(algorithm)) {
result.push(parsedToken.groups);
}
}
@@ -1326,6 +1321,54 @@ var require_util2 = __commonJS({
return result;
}
__name(parseMetadata, "parseMetadata");
+ function getStrongestMetadata(metadataList) {
+ let algorithm = metadataList[0].algo;
+ if (algorithm[3] === "5") {
+ return algorithm;
+ }
+ for (let i = 1; i < metadataList.length; ++i) {
+ const metadata = metadataList[i];
+ if (metadata.algo[3] === "5") {
+ algorithm = "sha512";
+ break;
+ } else if (algorithm[3] === "3") {
+ continue;
+ } else if (metadata.algo[3] === "3") {
+ algorithm = "sha384";
+ }
+ }
+ return algorithm;
+ }
+ __name(getStrongestMetadata, "getStrongestMetadata");
+ function filterMetadataListByAlgorithm(metadataList, algorithm) {
+ if (metadataList.length === 1) {
+ return metadataList;
+ }
+ let pos = 0;
+ for (let i = 0; i < metadataList.length; ++i) {
+ if (metadataList[i].algo === algorithm) {
+ metadataList[pos++] = metadataList[i];
+ }
+ }
+ metadataList.length = pos;
+ return metadataList;
+ }
+ __name(filterMetadataListByAlgorithm, "filterMetadataListByAlgorithm");
+ function compareBase64Mixed(actualValue, expectedValue) {
+ if (actualValue.length !== expectedValue.length) {
+ return false;
+ }
+ for (let i = 0; i < actualValue.length; ++i) {
+ if (actualValue[i] !== expectedValue[i]) {
+ if (actualValue[i] === "+" && expectedValue[i] === "-" || actualValue[i] === "/" && expectedValue[i] === "_") {
+ continue;
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+ __name(compareBase64Mixed, "compareBase64Mixed");
function tryUpgradeRequestToAPotentiallyTrustworthyURL(request) {
}
__name(tryUpgradeRequestToAPotentiallyTrustworthyURL, "tryUpgradeRequestToAPotentiallyTrustworthyURL");