@prodgator/report 3.19.1 → 3.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/cli.js +793 -127
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -239,10 +239,10 @@ var require_utils = __commonJS({
|
|
|
239
239
|
REGEX_SPECIAL_CHARS_GLOBAL
|
|
240
240
|
} = require_constants();
|
|
241
241
|
exports2.isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val);
|
|
242
|
-
exports2.hasRegexChars = (
|
|
243
|
-
exports2.isRegexChar = (
|
|
244
|
-
exports2.escapeRegex = (
|
|
245
|
-
exports2.toPosixSlashes = (
|
|
242
|
+
exports2.hasRegexChars = (str2) => REGEX_SPECIAL_CHARS.test(str2);
|
|
243
|
+
exports2.isRegexChar = (str2) => str2.length === 1 && exports2.hasRegexChars(str2);
|
|
244
|
+
exports2.escapeRegex = (str2) => str2.replace(REGEX_SPECIAL_CHARS_GLOBAL, "\\$1");
|
|
245
|
+
exports2.toPosixSlashes = (str2) => str2.replace(REGEX_BACKSLASH, "/");
|
|
246
246
|
exports2.isWindows = () => {
|
|
247
247
|
if (typeof navigator !== "undefined" && navigator.platform) {
|
|
248
248
|
const platform = navigator.platform.toLowerCase();
|
|
@@ -253,8 +253,8 @@ var require_utils = __commonJS({
|
|
|
253
253
|
}
|
|
254
254
|
return false;
|
|
255
255
|
};
|
|
256
|
-
exports2.removeBackslashes = (
|
|
257
|
-
return
|
|
256
|
+
exports2.removeBackslashes = (str2) => {
|
|
257
|
+
return str2.replace(REGEX_REMOVE_BACKSLASH, (match) => {
|
|
258
258
|
return match === "\\" ? "" : match;
|
|
259
259
|
});
|
|
260
260
|
};
|
|
@@ -344,7 +344,7 @@ var require_scan = __commonJS({
|
|
|
344
344
|
const slashes = [];
|
|
345
345
|
const tokens = [];
|
|
346
346
|
const parts = [];
|
|
347
|
-
let
|
|
347
|
+
let str2 = input;
|
|
348
348
|
let index = -1;
|
|
349
349
|
let start = 0;
|
|
350
350
|
let lastIndex = 0;
|
|
@@ -363,10 +363,10 @@ var require_scan = __commonJS({
|
|
|
363
363
|
let code;
|
|
364
364
|
let token = { value: "", depth: 0, isGlob: false };
|
|
365
365
|
const eos = () => index >= length;
|
|
366
|
-
const peek = () =>
|
|
366
|
+
const peek = () => str2.charCodeAt(index + 1);
|
|
367
367
|
const advance = () => {
|
|
368
368
|
prev = code;
|
|
369
|
-
return
|
|
369
|
+
return str2.charCodeAt(++index);
|
|
370
370
|
};
|
|
371
371
|
while (index < length) {
|
|
372
372
|
code = advance();
|
|
@@ -543,24 +543,24 @@ var require_scan = __commonJS({
|
|
|
543
543
|
isExtglob = false;
|
|
544
544
|
isGlob = false;
|
|
545
545
|
}
|
|
546
|
-
let base =
|
|
546
|
+
let base = str2;
|
|
547
547
|
let prefix = "";
|
|
548
548
|
let glob2 = "";
|
|
549
549
|
if (start > 0) {
|
|
550
|
-
prefix =
|
|
551
|
-
|
|
550
|
+
prefix = str2.slice(0, start);
|
|
551
|
+
str2 = str2.slice(start);
|
|
552
552
|
lastIndex -= start;
|
|
553
553
|
}
|
|
554
554
|
if (base && isGlob === true && lastIndex > 0) {
|
|
555
|
-
base =
|
|
556
|
-
glob2 =
|
|
555
|
+
base = str2.slice(0, lastIndex);
|
|
556
|
+
glob2 = str2.slice(lastIndex);
|
|
557
557
|
} else if (isGlob === true) {
|
|
558
558
|
base = "";
|
|
559
|
-
glob2 =
|
|
559
|
+
glob2 = str2;
|
|
560
560
|
} else {
|
|
561
|
-
base =
|
|
561
|
+
base = str2;
|
|
562
562
|
}
|
|
563
|
-
if (base && base !== "" && base !== "/" && base !==
|
|
563
|
+
if (base && base !== "" && base !== "/" && base !== str2) {
|
|
564
564
|
if (isPathSeparator(base.charCodeAt(base.length - 1))) {
|
|
565
565
|
base = base.slice(0, -1);
|
|
566
566
|
}
|
|
@@ -1275,15 +1275,15 @@ var require_parse = __commonJS({
|
|
|
1275
1275
|
}
|
|
1276
1276
|
let output = ")";
|
|
1277
1277
|
if (brace.dots === true) {
|
|
1278
|
-
const
|
|
1278
|
+
const arr2 = tokens.slice();
|
|
1279
1279
|
const range = [];
|
|
1280
|
-
for (let i =
|
|
1280
|
+
for (let i = arr2.length - 1; i >= 0; i--) {
|
|
1281
1281
|
tokens.pop();
|
|
1282
|
-
if (
|
|
1282
|
+
if (arr2[i].type === "brace") {
|
|
1283
1283
|
break;
|
|
1284
1284
|
}
|
|
1285
|
-
if (
|
|
1286
|
-
range.unshift(
|
|
1285
|
+
if (arr2[i].type !== "dots") {
|
|
1286
|
+
range.unshift(arr2[i].value);
|
|
1287
1287
|
}
|
|
1288
1288
|
}
|
|
1289
1289
|
output = expandRange(range, opts);
|
|
@@ -1607,8 +1607,8 @@ var require_parse = __commonJS({
|
|
|
1607
1607
|
if (opts2.noglobstar === true) return star;
|
|
1608
1608
|
return `(${capture}(?:(?!${START_ANCHOR}${opts2.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
|
|
1609
1609
|
};
|
|
1610
|
-
const create = (
|
|
1611
|
-
switch (
|
|
1610
|
+
const create = (str2) => {
|
|
1611
|
+
switch (str2) {
|
|
1612
1612
|
case "*":
|
|
1613
1613
|
return `${nodot}${ONE_CHAR}${star}`;
|
|
1614
1614
|
case ".*":
|
|
@@ -1626,7 +1626,7 @@ var require_parse = __commonJS({
|
|
|
1626
1626
|
case "**/.*":
|
|
1627
1627
|
return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`;
|
|
1628
1628
|
default: {
|
|
1629
|
-
const match = /^(.*?)\.(\w+)$/.exec(
|
|
1629
|
+
const match = /^(.*?)\.(\w+)$/.exec(str2);
|
|
1630
1630
|
if (!match) return;
|
|
1631
1631
|
const source2 = create(match[1]);
|
|
1632
1632
|
if (!source2) return;
|
|
@@ -1657,9 +1657,9 @@ var require_picomatch = __commonJS({
|
|
|
1657
1657
|
var picomatch = (glob2, options, returnState = false) => {
|
|
1658
1658
|
if (Array.isArray(glob2)) {
|
|
1659
1659
|
const fns = glob2.map((input) => picomatch(input, options, returnState));
|
|
1660
|
-
const arrayMatcher = (
|
|
1660
|
+
const arrayMatcher = (str2) => {
|
|
1661
1661
|
for (const isMatch of fns) {
|
|
1662
|
-
const state2 = isMatch(
|
|
1662
|
+
const state2 = isMatch(str2);
|
|
1663
1663
|
if (state2) return state2;
|
|
1664
1664
|
}
|
|
1665
1665
|
return false;
|
|
@@ -1735,7 +1735,7 @@ var require_picomatch = __commonJS({
|
|
|
1735
1735
|
const regex = glob2 instanceof RegExp ? glob2 : picomatch.makeRe(glob2, options);
|
|
1736
1736
|
return regex.test(utils.basename(input, { windows: posix }));
|
|
1737
1737
|
};
|
|
1738
|
-
picomatch.isMatch = (
|
|
1738
|
+
picomatch.isMatch = (str2, patterns, options) => picomatch(patterns, options)(str2);
|
|
1739
1739
|
picomatch.parse = (pattern, options) => {
|
|
1740
1740
|
if (Array.isArray(pattern)) return pattern.map((p) => picomatch.parse(p, options));
|
|
1741
1741
|
return parse(pattern, { ...options, fastpaths: false });
|
|
@@ -1826,7 +1826,7 @@ var require_dist = __commonJS({
|
|
|
1826
1826
|
enumerable: true
|
|
1827
1827
|
}) : target, mod));
|
|
1828
1828
|
var path = __toESM2(require("path"));
|
|
1829
|
-
var
|
|
1829
|
+
var fs3 = __toESM2(require("fs"));
|
|
1830
1830
|
function cleanPath(path$1) {
|
|
1831
1831
|
let normalized = (0, path.normalize)(path$1);
|
|
1832
1832
|
if (normalized.length > 1 && normalized[normalized.length - 1] === path.sep) normalized = normalized.substring(0, normalized.length - 1);
|
|
@@ -2123,7 +2123,7 @@ var require_dist = __commonJS({
|
|
|
2123
2123
|
symlinks: /* @__PURE__ */ new Map(),
|
|
2124
2124
|
visited: [""].slice(0, 0),
|
|
2125
2125
|
controller: new Aborter(),
|
|
2126
|
-
fs: options.fs ||
|
|
2126
|
+
fs: options.fs || fs3
|
|
2127
2127
|
};
|
|
2128
2128
|
this.joinPath = build$7(this.root, options);
|
|
2129
2129
|
this.pushDirectory = build$6(this.root, options);
|
|
@@ -2358,7 +2358,7 @@ var require_dist2 = __commonJS({
|
|
|
2358
2358
|
value: mod,
|
|
2359
2359
|
enumerable: true
|
|
2360
2360
|
}) : target, mod));
|
|
2361
|
-
var
|
|
2361
|
+
var fs3 = require("fs");
|
|
2362
2362
|
var path = require("path");
|
|
2363
2363
|
var url = require("url");
|
|
2364
2364
|
var fdir = require_dist();
|
|
@@ -2607,12 +2607,12 @@ var require_dist2 = __commonJS({
|
|
|
2607
2607
|
opts.cwd = (opts.cwd instanceof URL ? (0, url.fileURLToPath)(opts.cwd) : (0, path.resolve)(opts.cwd || process.cwd())).replace(BACKSLASHES, "/");
|
|
2608
2608
|
opts.ignore = ensureStringArray(opts.ignore);
|
|
2609
2609
|
opts.fs && (opts.fs = {
|
|
2610
|
-
readdir: opts.fs.readdir ||
|
|
2611
|
-
readdirSync: opts.fs.readdirSync ||
|
|
2612
|
-
realpath: opts.fs.realpath ||
|
|
2613
|
-
realpathSync: opts.fs.realpathSync ||
|
|
2614
|
-
stat: opts.fs.stat ||
|
|
2615
|
-
statSync: opts.fs.statSync ||
|
|
2610
|
+
readdir: opts.fs.readdir || fs3.readdir,
|
|
2611
|
+
readdirSync: opts.fs.readdirSync || fs3.readdirSync,
|
|
2612
|
+
realpath: opts.fs.realpath || fs3.realpath,
|
|
2613
|
+
realpathSync: opts.fs.realpathSync || fs3.realpathSync,
|
|
2614
|
+
stat: opts.fs.stat || fs3.stat,
|
|
2615
|
+
statSync: opts.fs.statSync || fs3.statSync
|
|
2616
2616
|
});
|
|
2617
2617
|
if (opts.debug) log("globbing with options:", opts);
|
|
2618
2618
|
return opts;
|
|
@@ -2626,12 +2626,12 @@ var require_dist2 = __commonJS({
|
|
|
2626
2626
|
return patterns.length > 0 ? buildCrawler(options, patterns) : [];
|
|
2627
2627
|
}
|
|
2628
2628
|
async function glob2(globInput, options) {
|
|
2629
|
-
const [crawler,
|
|
2630
|
-
return crawler ? formatPaths(await crawler.withPromise(),
|
|
2629
|
+
const [crawler, relative4] = getCrawler(globInput, options);
|
|
2630
|
+
return crawler ? formatPaths(await crawler.withPromise(), relative4) : [];
|
|
2631
2631
|
}
|
|
2632
2632
|
function globSync(globInput, options) {
|
|
2633
|
-
const [crawler,
|
|
2634
|
-
return crawler ? formatPaths(crawler.sync(),
|
|
2633
|
+
const [crawler, relative4] = getCrawler(globInput, options);
|
|
2634
|
+
return crawler ? formatPaths(crawler.sync(), relative4) : [];
|
|
2635
2635
|
}
|
|
2636
2636
|
exports2.convertPathToPattern = convertPathToPattern;
|
|
2637
2637
|
exports2.escapePath = escapePath;
|
|
@@ -3341,29 +3341,29 @@ var EntityDecoder = class {
|
|
|
3341
3341
|
* @param {string} str
|
|
3342
3342
|
* @returns {string}
|
|
3343
3343
|
*/
|
|
3344
|
-
decode(
|
|
3345
|
-
if (typeof
|
|
3346
|
-
if (
|
|
3347
|
-
const original =
|
|
3344
|
+
decode(str2) {
|
|
3345
|
+
if (typeof str2 !== "string" || str2.length === 0) return str2;
|
|
3346
|
+
if (str2.indexOf("&") === -1) return str2;
|
|
3347
|
+
const original = str2;
|
|
3348
3348
|
const chunks = [];
|
|
3349
|
-
const len =
|
|
3349
|
+
const len = str2.length;
|
|
3350
3350
|
let last = 0;
|
|
3351
3351
|
let i = 0;
|
|
3352
3352
|
const limitExpansions = this._maxTotalExpansions > 0;
|
|
3353
3353
|
const limitLength = this._maxExpandedLength > 0;
|
|
3354
3354
|
const checkLimits = limitExpansions || limitLength;
|
|
3355
3355
|
while (i < len) {
|
|
3356
|
-
if (
|
|
3356
|
+
if (str2.charCodeAt(i) !== 38) {
|
|
3357
3357
|
i++;
|
|
3358
3358
|
continue;
|
|
3359
3359
|
}
|
|
3360
3360
|
let j = i + 1;
|
|
3361
|
-
while (j < len &&
|
|
3362
|
-
if (j >= len ||
|
|
3361
|
+
while (j < len && str2.charCodeAt(j) !== 59 && j - i <= 32) j++;
|
|
3362
|
+
if (j >= len || str2.charCodeAt(j) !== 59) {
|
|
3363
3363
|
i++;
|
|
3364
3364
|
continue;
|
|
3365
3365
|
}
|
|
3366
|
-
const token =
|
|
3366
|
+
const token = str2.slice(i + 1, j);
|
|
3367
3367
|
if (token.length === 0) {
|
|
3368
3368
|
i++;
|
|
3369
3369
|
continue;
|
|
@@ -3395,7 +3395,7 @@ var EntityDecoder = class {
|
|
|
3395
3395
|
i++;
|
|
3396
3396
|
continue;
|
|
3397
3397
|
}
|
|
3398
|
-
if (i > last) chunks.push(
|
|
3398
|
+
if (i > last) chunks.push(str2.slice(last, i));
|
|
3399
3399
|
chunks.push(replacement);
|
|
3400
3400
|
last = j + 1;
|
|
3401
3401
|
i = last;
|
|
@@ -3421,8 +3421,8 @@ var EntityDecoder = class {
|
|
|
3421
3421
|
}
|
|
3422
3422
|
}
|
|
3423
3423
|
}
|
|
3424
|
-
if (last < len) chunks.push(
|
|
3425
|
-
const result = chunks.length === 0 ?
|
|
3424
|
+
if (last < len) chunks.push(str2.slice(last));
|
|
3425
|
+
const result = chunks.length === 0 ? str2 : chunks.join("");
|
|
3426
3426
|
return this._postCheck(result, original);
|
|
3427
3427
|
}
|
|
3428
3428
|
// -------------------------------------------------------------------------
|
|
@@ -3741,7 +3741,7 @@ var getRegexes = (xmlVersion = "1.0", asciiOnly = false) => {
|
|
|
3741
3741
|
if (asciiOnly) return regexesAscii;
|
|
3742
3742
|
return xmlVersion === "1.1" ? regexes11 : regexes10;
|
|
3743
3743
|
};
|
|
3744
|
-
var qName = (
|
|
3744
|
+
var qName = (str2, { xmlVersion = "1.0", asciiOnly = false } = {}) => getRegexes(xmlVersion, asciiOnly).qName.test(str2);
|
|
3745
3745
|
|
|
3746
3746
|
// node_modules/fast-xml-parser/src/xmlparser/DocTypeReader.js
|
|
3747
3747
|
var DocTypeReader = class {
|
|
@@ -4189,13 +4189,13 @@ var CHAR_0 = 48;
|
|
|
4189
4189
|
var CHAR_9 = 57;
|
|
4190
4190
|
var CHAR_MINUS = 45;
|
|
4191
4191
|
var MINUS_SET = /* @__PURE__ */ new Set([8722, 65293, 65123]);
|
|
4192
|
-
function anynum(
|
|
4193
|
-
if (typeof
|
|
4194
|
-
const len =
|
|
4195
|
-
if (len === 0) return
|
|
4192
|
+
function anynum(str2) {
|
|
4193
|
+
if (typeof str2 !== "string") return str2;
|
|
4194
|
+
const len = str2.length;
|
|
4195
|
+
if (len === 0) return str2;
|
|
4196
4196
|
let firstHit = -1;
|
|
4197
4197
|
for (let i = 0; i < len; i++) {
|
|
4198
|
-
const cc =
|
|
4198
|
+
const cc = str2.charCodeAt(i);
|
|
4199
4199
|
if (cc >= CHAR_0 && cc <= CHAR_9 || cc === CHAR_MINUS) continue;
|
|
4200
4200
|
if (cc < TABLE_OFFSET) {
|
|
4201
4201
|
if (MINUS_SET.has(cc)) {
|
|
@@ -4206,7 +4206,7 @@ function anynum(str) {
|
|
|
4206
4206
|
}
|
|
4207
4207
|
if (cc >= 55296 && cc <= 56319) {
|
|
4208
4208
|
if (i + 1 < len) {
|
|
4209
|
-
const low =
|
|
4209
|
+
const low = str2.charCodeAt(i + 1);
|
|
4210
4210
|
if (low >= 56320 && low <= 57343) {
|
|
4211
4211
|
const cp = 65536 + (cc - 55296 << 10) + (low - 56320);
|
|
4212
4212
|
if (HIGH_MAP.has(cp)) {
|
|
@@ -4222,22 +4222,22 @@ function anynum(str) {
|
|
|
4222
4222
|
break;
|
|
4223
4223
|
}
|
|
4224
4224
|
}
|
|
4225
|
-
if (firstHit === -1) return
|
|
4225
|
+
if (firstHit === -1) return str2;
|
|
4226
4226
|
const chars = [];
|
|
4227
|
-
if (firstHit > 0) chars.push(
|
|
4227
|
+
if (firstHit > 0) chars.push(str2.slice(0, firstHit));
|
|
4228
4228
|
for (let i = firstHit; i < len; i++) {
|
|
4229
|
-
const cc =
|
|
4229
|
+
const cc = str2.charCodeAt(i);
|
|
4230
4230
|
if (cc >= CHAR_0 && cc <= CHAR_9 || cc === CHAR_MINUS) {
|
|
4231
|
-
chars.push(
|
|
4231
|
+
chars.push(str2[i]);
|
|
4232
4232
|
continue;
|
|
4233
4233
|
}
|
|
4234
4234
|
if (cc < TABLE_OFFSET) {
|
|
4235
|
-
chars.push(MINUS_SET.has(cc) ? "-" :
|
|
4235
|
+
chars.push(MINUS_SET.has(cc) ? "-" : str2[i]);
|
|
4236
4236
|
continue;
|
|
4237
4237
|
}
|
|
4238
4238
|
if (cc >= 55296 && cc <= 56319) {
|
|
4239
4239
|
if (i + 1 < len) {
|
|
4240
|
-
const low =
|
|
4240
|
+
const low = str2.charCodeAt(i + 1);
|
|
4241
4241
|
if (low >= 56320 && low <= 57343) {
|
|
4242
4242
|
const cp = 65536 + (cc - 55296 << 10) + (low - 56320);
|
|
4243
4243
|
const d2 = HIGH_MAP.get(cp);
|
|
@@ -4248,7 +4248,7 @@ function anynum(str) {
|
|
|
4248
4248
|
}
|
|
4249
4249
|
}
|
|
4250
4250
|
}
|
|
4251
|
-
chars.push(
|
|
4251
|
+
chars.push(str2[i]);
|
|
4252
4252
|
continue;
|
|
4253
4253
|
}
|
|
4254
4254
|
if (MINUS_SET.has(cc)) {
|
|
@@ -4256,7 +4256,7 @@ function anynum(str) {
|
|
|
4256
4256
|
continue;
|
|
4257
4257
|
}
|
|
4258
4258
|
const d = TABLE[cc - TABLE_OFFSET];
|
|
4259
|
-
chars.push(d !== NOT_DIGIT ? String.fromCharCode(d + 48) :
|
|
4259
|
+
chars.push(d !== NOT_DIGIT ? String.fromCharCode(d + 48) : str2[i]);
|
|
4260
4260
|
}
|
|
4261
4261
|
return chars.join("");
|
|
4262
4262
|
}
|
|
@@ -4279,12 +4279,12 @@ var consider = {
|
|
|
4279
4279
|
// "null", "infinity" (Infinity type), "string" ("Infinity" (the string literal))
|
|
4280
4280
|
unicode: false
|
|
4281
4281
|
};
|
|
4282
|
-
function toNumber(
|
|
4282
|
+
function toNumber(str2, options = {}) {
|
|
4283
4283
|
options = Object.assign({}, consider, options);
|
|
4284
|
-
if (!
|
|
4285
|
-
let trimmedStr =
|
|
4286
|
-
if (trimmedStr.length === 0) return
|
|
4287
|
-
else if (options.skipLike !== void 0 && options.skipLike.test(trimmedStr)) return
|
|
4284
|
+
if (!str2 || typeof str2 !== "string") return str2;
|
|
4285
|
+
let trimmedStr = str2.trim();
|
|
4286
|
+
if (trimmedStr.length === 0) return str2;
|
|
4287
|
+
else if (options.skipLike !== void 0 && options.skipLike.test(trimmedStr)) return str2;
|
|
4288
4288
|
else if (trimmedStr === "0") return 0;
|
|
4289
4289
|
if (options.unicode) {
|
|
4290
4290
|
trimmedStr = anynum_default(trimmedStr);
|
|
@@ -4297,9 +4297,9 @@ function toNumber(str, options = {}) {
|
|
|
4297
4297
|
} else if (options.octal && octRegex.test(trimmedStr)) {
|
|
4298
4298
|
return parse_int(trimmedStr, 8);
|
|
4299
4299
|
} else if (!isFinite(trimmedStr)) {
|
|
4300
|
-
return handleInfinity(
|
|
4300
|
+
return handleInfinity(str2, Number(trimmedStr), options);
|
|
4301
4301
|
} else if (trimmedStr.includes("e") || trimmedStr.includes("E")) {
|
|
4302
|
-
return resolveEnotation(
|
|
4302
|
+
return resolveEnotation(str2, trimmedStr, options);
|
|
4303
4303
|
} else {
|
|
4304
4304
|
const match = numRegex.exec(trimmedStr);
|
|
4305
4305
|
if (match) {
|
|
@@ -4308,38 +4308,38 @@ function toNumber(str, options = {}) {
|
|
|
4308
4308
|
let numTrimmedByZeros = trimZeros(match[3]);
|
|
4309
4309
|
const decimalAdjacentToLeadingZeros = sign ? (
|
|
4310
4310
|
// 0., -00., 000.
|
|
4311
|
-
|
|
4312
|
-
) :
|
|
4311
|
+
str2[leadingZeros.length + 1] === "."
|
|
4312
|
+
) : str2[leadingZeros.length] === ".";
|
|
4313
4313
|
if (!options.leadingZeros && (leadingZeros.length > 1 || leadingZeros.length === 1 && !decimalAdjacentToLeadingZeros)) {
|
|
4314
|
-
return
|
|
4314
|
+
return str2;
|
|
4315
4315
|
} else {
|
|
4316
4316
|
const num = Number(trimmedStr);
|
|
4317
4317
|
const parsedStr = String(num);
|
|
4318
4318
|
if (num === 0) return num;
|
|
4319
4319
|
if (parsedStr.search(/[eE]/) !== -1) {
|
|
4320
4320
|
if (options.eNotation) return num;
|
|
4321
|
-
else return
|
|
4321
|
+
else return str2;
|
|
4322
4322
|
} else if (trimmedStr.indexOf(".") !== -1) {
|
|
4323
4323
|
if (parsedStr === "0") return num;
|
|
4324
4324
|
else if (parsedStr === numTrimmedByZeros) return num;
|
|
4325
4325
|
else if (parsedStr === `${sign}${numTrimmedByZeros}`) return num;
|
|
4326
|
-
else return
|
|
4326
|
+
else return str2;
|
|
4327
4327
|
}
|
|
4328
4328
|
let n = leadingZeros ? numTrimmedByZeros : trimmedStr;
|
|
4329
4329
|
if (leadingZeros) {
|
|
4330
|
-
return n === parsedStr || sign + n === parsedStr ? num :
|
|
4330
|
+
return n === parsedStr || sign + n === parsedStr ? num : str2;
|
|
4331
4331
|
} else {
|
|
4332
|
-
return n === parsedStr || n === sign + parsedStr ? num :
|
|
4332
|
+
return n === parsedStr || n === sign + parsedStr ? num : str2;
|
|
4333
4333
|
}
|
|
4334
4334
|
}
|
|
4335
4335
|
} else {
|
|
4336
|
-
return
|
|
4336
|
+
return str2;
|
|
4337
4337
|
}
|
|
4338
4338
|
}
|
|
4339
4339
|
}
|
|
4340
4340
|
var eNotationRegx = /^([-+])?(0*)(\d*(\.\d*)?[eE][-\+]?\d+)$/;
|
|
4341
|
-
function resolveEnotation(
|
|
4342
|
-
if (!options.eNotation) return
|
|
4341
|
+
function resolveEnotation(str2, trimmedStr, options) {
|
|
4342
|
+
if (!options.eNotation) return str2;
|
|
4343
4343
|
const notation = trimmedStr.match(eNotationRegx);
|
|
4344
4344
|
if (notation) {
|
|
4345
4345
|
let sign = notation[1] || "";
|
|
@@ -4347,21 +4347,21 @@ function resolveEnotation(str, trimmedStr, options) {
|
|
|
4347
4347
|
const leadingZeros = notation[2];
|
|
4348
4348
|
const eAdjacentToLeadingZeros = sign ? (
|
|
4349
4349
|
// 0E.
|
|
4350
|
-
|
|
4351
|
-
) :
|
|
4352
|
-
if (leadingZeros.length > 1 && eAdjacentToLeadingZeros) return
|
|
4350
|
+
str2[leadingZeros.length + 1] === eChar
|
|
4351
|
+
) : str2[leadingZeros.length] === eChar;
|
|
4352
|
+
if (leadingZeros.length > 1 && eAdjacentToLeadingZeros) return str2;
|
|
4353
4353
|
else if (leadingZeros.length === 1 && (notation[3].startsWith(`.${eChar}`) || notation[3][0] === eChar)) {
|
|
4354
4354
|
return Number(trimmedStr);
|
|
4355
4355
|
} else if (leadingZeros.length > 0) {
|
|
4356
4356
|
if (options.leadingZeros && !eAdjacentToLeadingZeros) {
|
|
4357
4357
|
trimmedStr = (notation[1] || "") + notation[3];
|
|
4358
4358
|
return Number(trimmedStr);
|
|
4359
|
-
} else return
|
|
4359
|
+
} else return str2;
|
|
4360
4360
|
} else {
|
|
4361
4361
|
return Number(trimmedStr);
|
|
4362
4362
|
}
|
|
4363
4363
|
} else {
|
|
4364
|
-
return
|
|
4364
|
+
return str2;
|
|
4365
4365
|
}
|
|
4366
4366
|
}
|
|
4367
4367
|
function trimZeros(numStr) {
|
|
@@ -4377,14 +4377,14 @@ function trimZeros(numStr) {
|
|
|
4377
4377
|
return numStr;
|
|
4378
4378
|
}
|
|
4379
4379
|
function parse_int(numStr, base) {
|
|
4380
|
-
const
|
|
4381
|
-
if (base === 2 || base === 8) numStr =
|
|
4380
|
+
const str2 = numStr.trim();
|
|
4381
|
+
if (base === 2 || base === 8) numStr = str2.substring(2);
|
|
4382
4382
|
if (parseInt) return parseInt(numStr, base);
|
|
4383
4383
|
else if (Number.parseInt) return Number.parseInt(numStr, base);
|
|
4384
4384
|
else if (window && window.parseInt) return window.parseInt(numStr, base);
|
|
4385
4385
|
else throw new Error("parseInt, Number.parseInt, window.parseInt are not supported");
|
|
4386
4386
|
}
|
|
4387
|
-
function handleInfinity(
|
|
4387
|
+
function handleInfinity(str2, num, options) {
|
|
4388
4388
|
const isPositive = num === Infinity;
|
|
4389
4389
|
switch (options.infinity.toLowerCase()) {
|
|
4390
4390
|
case "null":
|
|
@@ -4396,7 +4396,7 @@ function handleInfinity(str, num, options) {
|
|
|
4396
4396
|
return isPositive ? "Infinity" : "-Infinity";
|
|
4397
4397
|
case "original":
|
|
4398
4398
|
default:
|
|
4399
|
-
return
|
|
4399
|
+
return str2;
|
|
4400
4400
|
}
|
|
4401
4401
|
}
|
|
4402
4402
|
|
|
@@ -5080,21 +5080,21 @@ var Matcher = class {
|
|
|
5080
5080
|
* @returns {string}
|
|
5081
5081
|
*/
|
|
5082
5082
|
toString(separator, includeNamespace = true) {
|
|
5083
|
-
const
|
|
5084
|
-
const isDefault =
|
|
5083
|
+
const sep4 = separator || this.separator;
|
|
5084
|
+
const isDefault = sep4 === this.separator && includeNamespace === true;
|
|
5085
5085
|
if (isDefault) {
|
|
5086
5086
|
if (this._pathStringCache !== null) {
|
|
5087
5087
|
return this._pathStringCache;
|
|
5088
5088
|
}
|
|
5089
5089
|
const result = this.path.map(
|
|
5090
5090
|
(n) => n.namespace ? `${n.namespace}:${n.tag}` : n.tag
|
|
5091
|
-
).join(
|
|
5091
|
+
).join(sep4);
|
|
5092
5092
|
this._pathStringCache = result;
|
|
5093
5093
|
return result;
|
|
5094
5094
|
}
|
|
5095
5095
|
return this.path.map(
|
|
5096
5096
|
(n) => includeNamespace && n.namespace ? `${n.namespace}:${n.tag}` : n.tag
|
|
5097
|
-
).join(
|
|
5097
|
+
).join(sep4);
|
|
5098
5098
|
}
|
|
5099
5099
|
/**
|
|
5100
5100
|
* Get path as array of tag names.
|
|
@@ -6478,12 +6478,12 @@ function tagExpWithClosingIndex(xmlData, i, closingChar = ">") {
|
|
|
6478
6478
|
}
|
|
6479
6479
|
}
|
|
6480
6480
|
}
|
|
6481
|
-
function findClosingIndex(xmlData,
|
|
6482
|
-
const closingIndex = xmlData.indexOf(
|
|
6481
|
+
function findClosingIndex(xmlData, str2, i, errMsg) {
|
|
6482
|
+
const closingIndex = xmlData.indexOf(str2, i);
|
|
6483
6483
|
if (closingIndex === -1) {
|
|
6484
6484
|
throw new Error(errMsg);
|
|
6485
6485
|
} else {
|
|
6486
|
-
return closingIndex +
|
|
6486
|
+
return closingIndex + str2.length - 1;
|
|
6487
6487
|
}
|
|
6488
6488
|
}
|
|
6489
6489
|
function findClosingChar(xmlData, char, i, errMsg) {
|
|
@@ -6614,11 +6614,11 @@ function stripAttributePrefix(attrs, prefix) {
|
|
|
6614
6614
|
function prettify(node, options, matcher, readonlyMatcher) {
|
|
6615
6615
|
return compress(node, options, matcher, readonlyMatcher);
|
|
6616
6616
|
}
|
|
6617
|
-
function compress(
|
|
6617
|
+
function compress(arr2, options, matcher, readonlyMatcher) {
|
|
6618
6618
|
let text;
|
|
6619
6619
|
const compressedObj = {};
|
|
6620
|
-
for (let i = 0; i <
|
|
6621
|
-
const tagObj =
|
|
6620
|
+
for (let i = 0; i < arr2.length; i++) {
|
|
6621
|
+
const tagObj = arr2[i];
|
|
6622
6622
|
const property = propName(tagObj);
|
|
6623
6623
|
if (property !== void 0 && property !== options.textNodeName) {
|
|
6624
6624
|
const rawAttrs = stripAttributePrefix(
|
|
@@ -6935,8 +6935,568 @@ async function junitAttestation(patterns, opts) {
|
|
|
6935
6935
|
}
|
|
6936
6936
|
|
|
6937
6937
|
// src/core/attestations.ts
|
|
6938
|
+
var import_crypto3 = require("crypto");
|
|
6939
|
+
var import_fs3 = require("fs");
|
|
6940
|
+
var import_os = require("os");
|
|
6941
|
+
var import_path3 = require("path");
|
|
6942
|
+
|
|
6943
|
+
// src/core/blame.ts
|
|
6944
|
+
var import_child_process = require("child_process");
|
|
6938
6945
|
var import_crypto2 = require("crypto");
|
|
6946
|
+
var import_fs2 = require("fs");
|
|
6939
6947
|
var import_path2 = require("path");
|
|
6948
|
+
var import_url = require("url");
|
|
6949
|
+
function createBlameBudget(limits = {}, now = Date.now()) {
|
|
6950
|
+
return {
|
|
6951
|
+
deadline: now + (limits.maxMs ?? 6e4),
|
|
6952
|
+
resultsLeft: limits.maxResults ?? 500,
|
|
6953
|
+
deepensLeft: limits.maxDeepen ?? 3,
|
|
6954
|
+
gitMissing: false,
|
|
6955
|
+
snippetDeadline: null,
|
|
6956
|
+
snippetMs: limits.maxSnippetMs ?? 3e4,
|
|
6957
|
+
snippetsLeft: limits.maxSnippets ?? 5e3
|
|
6958
|
+
};
|
|
6959
|
+
}
|
|
6960
|
+
var BLAME_SIDECAR_SUFFIX = ".prodgator-blame.json";
|
|
6961
|
+
var MAX_RANGE_LINES = 50;
|
|
6962
|
+
var DEEPEN_BY = 200;
|
|
6963
|
+
var MAX_CALL_MS = 3e4;
|
|
6964
|
+
var MAX_RUNS = 20;
|
|
6965
|
+
var SKIP_KINDS = /* @__PURE__ */ new Set(["pass", "notApplicable", "informational"]);
|
|
6966
|
+
var GitError = class extends Error {
|
|
6967
|
+
constructor(message, stderr, code) {
|
|
6968
|
+
super(message);
|
|
6969
|
+
this.stderr = stderr;
|
|
6970
|
+
this.code = code;
|
|
6971
|
+
}
|
|
6972
|
+
};
|
|
6973
|
+
var defaultIsExecutable = (platform) => (file) => {
|
|
6974
|
+
try {
|
|
6975
|
+
if (!(0, import_fs2.statSync)(file).isFile()) return false;
|
|
6976
|
+
if (platform !== "win32") (0, import_fs2.accessSync)(file, import_fs2.constants.X_OK);
|
|
6977
|
+
return true;
|
|
6978
|
+
} catch {
|
|
6979
|
+
return false;
|
|
6980
|
+
}
|
|
6981
|
+
};
|
|
6982
|
+
var defaultRealpath = (p) => {
|
|
6983
|
+
try {
|
|
6984
|
+
return import_fs2.realpathSync.native(p);
|
|
6985
|
+
} catch {
|
|
6986
|
+
return p;
|
|
6987
|
+
}
|
|
6988
|
+
};
|
|
6989
|
+
function searchGit(workspace, o) {
|
|
6990
|
+
const platform = o.platform ?? process.platform;
|
|
6991
|
+
const win = platform === "win32";
|
|
6992
|
+
const p = win ? import_path2.win32 : import_path2.posix;
|
|
6993
|
+
const env = o.env ?? process.env;
|
|
6994
|
+
const isExecutable = o.isExecutable ?? defaultIsExecutable(platform);
|
|
6995
|
+
const realpath = o.realpath ?? defaultRealpath;
|
|
6996
|
+
const pathKey = (win ? Object.keys(env).find((k) => k.toUpperCase() === "PATH") : void 0) ?? "PATH";
|
|
6997
|
+
const pathValue = env[pathKey];
|
|
6998
|
+
const pathExt = win ? Object.entries(env).find(([k]) => k.toUpperCase() === "PATHEXT")?.[1] : void 0;
|
|
6999
|
+
const exts = win ? [...new Set((pathExt ?? ".COM;.EXE").split(";").map((e) => e.trim().toLowerCase()).filter((e) => e === ".exe" || e === ".com"))] : [""];
|
|
7000
|
+
if (win && !exts.length) exts.push(".exe");
|
|
7001
|
+
const roots = [.../* @__PURE__ */ new Set([p.resolve(workspace), p.resolve(realpath(workspace))])];
|
|
7002
|
+
const inside = (dir) => roots.some((root) => {
|
|
7003
|
+
const rel = p.relative(root, dir);
|
|
7004
|
+
return !(rel === ".." || rel.startsWith(`..${p.sep}`) || p.isAbsolute(rel));
|
|
7005
|
+
});
|
|
7006
|
+
const absolute = (dir) => win ? /^([A-Za-z]:[\\/]|\\\\[^\\])/.test(dir) : dir.startsWith("/");
|
|
7007
|
+
const kept = [];
|
|
7008
|
+
let git = null;
|
|
7009
|
+
for (const raw of (pathValue ?? "").split(win ? ";" : ":")) {
|
|
7010
|
+
const dir = win ? raw.trim().replace(/^"(.*)"$/, "$1") : raw;
|
|
7011
|
+
if (!dir || dir === "." || !absolute(dir)) continue;
|
|
7012
|
+
const resolved = p.resolve(dir);
|
|
7013
|
+
if (inside(resolved) || inside(p.resolve(realpath(resolved)))) continue;
|
|
7014
|
+
kept.push(raw);
|
|
7015
|
+
for (const ext of git ? [] : exts) {
|
|
7016
|
+
const candidate = p.join(resolved, `git${ext}`);
|
|
7017
|
+
if (isExecutable(candidate)) {
|
|
7018
|
+
git = candidate;
|
|
7019
|
+
break;
|
|
7020
|
+
}
|
|
7021
|
+
}
|
|
7022
|
+
}
|
|
7023
|
+
return { git, path: kept.join(win ? ";" : ":"), pathKey };
|
|
7024
|
+
}
|
|
7025
|
+
function findGit(workspace, o = {}) {
|
|
7026
|
+
return searchGit(workspace, o).git;
|
|
7027
|
+
}
|
|
7028
|
+
function gitEnv(workspace, o = {}) {
|
|
7029
|
+
const search = searchGit(workspace, o);
|
|
7030
|
+
const env = { ...o.env ?? process.env };
|
|
7031
|
+
for (const k of Object.keys(env)) if (k.toUpperCase() === "PATH") delete env[k];
|
|
7032
|
+
env[search.pathKey] = search.path;
|
|
7033
|
+
return env;
|
|
7034
|
+
}
|
|
7035
|
+
function gitRunnerFor(lookup = {}, spawn = import_child_process.execFile) {
|
|
7036
|
+
const found = /* @__PURE__ */ new Map();
|
|
7037
|
+
return {
|
|
7038
|
+
run: (args, { cwd, timeoutMs }) => new Promise((resolveRun, reject) => {
|
|
7039
|
+
if (!found.has(cwd)) found.set(cwd, { git: findGit(cwd, lookup), env: gitEnv(cwd, lookup) });
|
|
7040
|
+
const { git, env: baseEnv } = found.get(cwd);
|
|
7041
|
+
if (!git) {
|
|
7042
|
+
reject(new GitError("git not found", "", "ENOENT"));
|
|
7043
|
+
return;
|
|
7044
|
+
}
|
|
7045
|
+
spawn(git, args, {
|
|
7046
|
+
cwd,
|
|
7047
|
+
timeout: Math.max(1, timeoutMs),
|
|
7048
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
7049
|
+
windowsHide: true,
|
|
7050
|
+
encoding: "utf8",
|
|
7051
|
+
// English messages (for missing-path detection) and never a credential prompt.
|
|
7052
|
+
env: { ...baseEnv, LC_ALL: "C", GIT_TERMINAL_PROMPT: "0", GIT_OPTIONAL_LOCKS: "0" }
|
|
7053
|
+
}, (err, stdout, stderr) => {
|
|
7054
|
+
if (err) reject(new GitError("git failed", String(stderr ?? ""), err.code));
|
|
7055
|
+
else resolveRun(String(stdout));
|
|
7056
|
+
});
|
|
7057
|
+
})
|
|
7058
|
+
};
|
|
7059
|
+
}
|
|
7060
|
+
var execGit = gitRunnerFor();
|
|
7061
|
+
function normalizeSnippet(lines) {
|
|
7062
|
+
return lines.map((l) => l.trim().replace(/\s+/g, " ")).filter(Boolean).join("\n");
|
|
7063
|
+
}
|
|
7064
|
+
function snippetHashOf(lines) {
|
|
7065
|
+
const snippet = normalizeSnippet(lines);
|
|
7066
|
+
return snippet ? (0, import_crypto2.createHash)("sha256").update(snippet).digest("hex").slice(0, 32) : void 0;
|
|
7067
|
+
}
|
|
7068
|
+
var isObj = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
7069
|
+
var arr = (v) => Array.isArray(v) ? v : [];
|
|
7070
|
+
var str = (v) => typeof v === "string" && v.trim() ? v.trim() : null;
|
|
7071
|
+
var SCA_TOOLS = [
|
|
7072
|
+
/\bgrype\b|anchore\/grype/i,
|
|
7073
|
+
/osv-scanner|google\/osv-scanner/i,
|
|
7074
|
+
/dependency-check|dependencycheck/i,
|
|
7075
|
+
/snyk open source|snyk-open-source/i,
|
|
7076
|
+
/\bnpm[- ]audit\b/i,
|
|
7077
|
+
/\byarn[- ]audit\b/i,
|
|
7078
|
+
/\bpnpm[- ]audit\b/i,
|
|
7079
|
+
/\bpip[- ]audit\b/i,
|
|
7080
|
+
/\bcargo[- ]audit\b/i,
|
|
7081
|
+
/\bbundler[- ]audit\b/i,
|
|
7082
|
+
/\bgovulncheck\b/i
|
|
7083
|
+
];
|
|
7084
|
+
var SECRET_TOOLS = [/gitleaks/i, /trufflehog/i, /detect-secrets/i, /ggshield|gitguardian/i, /secret[- ]scanning/i, /secretlint/i, /noseyparker|nosey parker/i];
|
|
7085
|
+
var MANIFESTS = [
|
|
7086
|
+
/(^|\/)(package-lock\.json|npm-shrinkwrap\.json|yarn\.lock|pnpm-lock\.yaml|bun\.lockb?|package\.json)$/i,
|
|
7087
|
+
/(^|\/)(go\.sum|go\.mod)$/i,
|
|
7088
|
+
/(^|\/)(Gemfile\.lock|Gemfile|gems\.locked)$/i,
|
|
7089
|
+
/(^|\/)(requirements[^/]*\.txt|poetry\.lock|Pipfile\.lock|Pipfile|pdm\.lock|uv\.lock|pyproject\.toml|setup\.py)$/i,
|
|
7090
|
+
/(^|\/)(pom\.xml|build\.gradle(\.kts)?|gradle\.lockfile|[^/]+\.jar)$/i,
|
|
7091
|
+
/(^|\/)(Cargo\.lock|Cargo\.toml)$/i,
|
|
7092
|
+
/(^|\/)(packages\.lock\.json|packages\.config|[^/]+\.csproj|[^/]+\.fsproj|[^/]+\.vbproj|[^/]+\.deps\.json)$/i,
|
|
7093
|
+
/(^|\/)(composer\.lock|composer\.json)$/i,
|
|
7094
|
+
/(^|\/)pubspec\.lock$/i,
|
|
7095
|
+
/(^|\/)Package\.resolved$/i,
|
|
7096
|
+
/(^|\/)mix\.lock$/i,
|
|
7097
|
+
// OS package databases inside an image.
|
|
7098
|
+
/(^|\/)lib\/apk\/db\/installed$/i,
|
|
7099
|
+
/(^|\/)var\/lib\/dpkg\/status(\.d\/[^/]+)?$/i,
|
|
7100
|
+
/(^|\/)var\/lib\/rpm\/(Packages|rpmdb\.sqlite|Packages\.db)$/i
|
|
7101
|
+
];
|
|
7102
|
+
var isManifest = (path) => !!path && MANIFESTS.some((re) => re.test(path));
|
|
7103
|
+
var CVE = /^CVE-\d{4}-\d{4,}$/;
|
|
7104
|
+
var GHSA = /^GHSA(-[23456789CFGHJMPQRVWX]{4}){3}$/;
|
|
7105
|
+
var ADVISORY_PREFIX = /^(RUSTSEC|GO|PYSEC|OSV|GSD|MAL|SNYK|RUBYSEC|DLA|DSA|USN|ALSA|RHSA|ELSA|ALAS\d*|BIT|CGA|MGASA|OESA|OPENSUSE|SUSE)-/;
|
|
7106
|
+
var LEADING_VULN_ID = /^(CVE-\d{4}-\d{4,}|GHSA(?:-[23456789cfghjmpqrvwx]{4}){3})-([^-].*)$/i;
|
|
7107
|
+
var CHECKOV_SCA_ID = /^([A-Z]+-[A-Z0-9-]+?)_(.+)@([^@]+)$/i;
|
|
7108
|
+
function normalizeVulnId(v) {
|
|
7109
|
+
if (typeof v !== "string") return null;
|
|
7110
|
+
const id = v.trim().toUpperCase();
|
|
7111
|
+
if (!id || id.length > 64 || !/^[A-Z0-9._:-]+$/.test(id)) return null;
|
|
7112
|
+
if (id.startsWith("CVE-") && !CVE.test(id)) return null;
|
|
7113
|
+
if (id.startsWith("GHSA-") && !GHSA.test(id)) return null;
|
|
7114
|
+
return id;
|
|
7115
|
+
}
|
|
7116
|
+
var isAdvisoryId = (id) => !!id && (CVE.test(id) || GHSA.test(id) || ADVISORY_PREFIX.test(id));
|
|
7117
|
+
function vulnFromRuleId(ruleId) {
|
|
7118
|
+
if (!ruleId) return { id: null, rest: null, pkgVersion: null };
|
|
7119
|
+
const whole = normalizeVulnId(ruleId);
|
|
7120
|
+
if (isAdvisoryId(whole)) return { id: whole, rest: null, pkgVersion: null };
|
|
7121
|
+
const lead = LEADING_VULN_ID.exec(ruleId);
|
|
7122
|
+
if (lead) return { id: normalizeVulnId(lead[1]), rest: lead[2], pkgVersion: null };
|
|
7123
|
+
const ck = CHECKOV_SCA_ID.exec(ruleId);
|
|
7124
|
+
const ckId = ck ? normalizeVulnId(ck[1]) : null;
|
|
7125
|
+
if (ck && isAdvisoryId(ckId)) return { id: ckId, rest: ck[2], pkgVersion: ck[3] };
|
|
7126
|
+
return { id: null, rest: null, pkgVersion: null };
|
|
7127
|
+
}
|
|
7128
|
+
var canon = (k) => k.toLowerCase().replace(/[-_ ]/g, "");
|
|
7129
|
+
var prop = (bag, names) => {
|
|
7130
|
+
if (!isObj(bag)) return void 0;
|
|
7131
|
+
for (const key of Object.keys(bag)) if (names.includes(canon(key))) return bag[key];
|
|
7132
|
+
return void 0;
|
|
7133
|
+
};
|
|
7134
|
+
var firstStr = (v) => Array.isArray(v) ? v.map(str).find(Boolean) ?? null : str(v);
|
|
7135
|
+
var nameValue = (v) => {
|
|
7136
|
+
if (isObj(v)) return str(v.name);
|
|
7137
|
+
const s = firstStr(v);
|
|
7138
|
+
return s && s.length <= 300 && !/\s/.test(s) ? s : null;
|
|
7139
|
+
};
|
|
7140
|
+
var concreteVersion = (v) => v && v.length <= 100 && /^[A-Za-z0-9][A-Za-z0-9._+:~-]*$/.test(v) ? v : null;
|
|
7141
|
+
function parsePurl(purl) {
|
|
7142
|
+
if (typeof purl !== "string" || purl.length > 2048 || !purl.startsWith("pkg:")) return null;
|
|
7143
|
+
const withoutSub = purl.slice(4).split("#")[0].split("?")[0];
|
|
7144
|
+
const at = withoutSub.lastIndexOf("@");
|
|
7145
|
+
const parts = (at > 0 ? withoutSub.slice(0, at) : withoutSub).split("/").filter(Boolean);
|
|
7146
|
+
if (parts.length < 2 || !/^[a-z][a-z0-9.+-]*$/.test(parts[0].toLowerCase()) || !parts[parts.length - 1]) return null;
|
|
7147
|
+
let version = null;
|
|
7148
|
+
if (at > 0) {
|
|
7149
|
+
const raw = withoutSub.slice(at + 1);
|
|
7150
|
+
try {
|
|
7151
|
+
version = decodeURIComponent(raw) || null;
|
|
7152
|
+
} catch {
|
|
7153
|
+
version = raw || null;
|
|
7154
|
+
}
|
|
7155
|
+
}
|
|
7156
|
+
return { version };
|
|
7157
|
+
}
|
|
7158
|
+
var PROP = {
|
|
7159
|
+
purl: ["purl", "purls", "packageurl", "packageurls"],
|
|
7160
|
+
name: ["packagename", "pkgname", "package", "componentname", "dependency", "dependencyname", "artifactname", "libraryname"],
|
|
7161
|
+
version: ["packageversion", "pkgversion", "installedversion", "componentversion", "dependencyversion", "currentversion", "version"],
|
|
7162
|
+
fixed: ["fixedversion", "fixedversions", "fixversion", "fixversions", "patchedversion", "patchedversions", "fixedin"]
|
|
7163
|
+
};
|
|
7164
|
+
function fromProperties(bag) {
|
|
7165
|
+
if (!isObj(bag)) return null;
|
|
7166
|
+
const purlText = firstStr(prop(bag, PROP.purl));
|
|
7167
|
+
const parsed = parsePurl(purlText);
|
|
7168
|
+
const pkgObj = isObj(prop(bag, ["package"])) ? prop(bag, ["package"]) : null;
|
|
7169
|
+
const name = nameValue(prop(bag, PROP.name)) ?? (parsed ? "purl" : null);
|
|
7170
|
+
const versionRaw = firstStr(prop(bag, PROP.version.filter((n) => n !== "version"))) ?? (name ? firstStr(prop(bag, ["version"])) : null) ?? str(pkgObj?.version);
|
|
7171
|
+
const fixed = firstStr(prop(bag, PROP.fixed));
|
|
7172
|
+
if (!parsed && !name && !versionRaw && !fixed) return null;
|
|
7173
|
+
return { purl: parsed ? purlText : null, name, version: concreteVersion(versionRaw) ?? parsed?.version ?? null, fixedVersion: fixed };
|
|
7174
|
+
}
|
|
7175
|
+
function mergeParts(parts) {
|
|
7176
|
+
const out = { purl: null, name: null, version: null, fixedVersion: null };
|
|
7177
|
+
for (const p of parts) {
|
|
7178
|
+
if (!p) continue;
|
|
7179
|
+
for (const k of Object.keys(out)) if (!out[k] && p[k]) out[k] = p[k];
|
|
7180
|
+
}
|
|
7181
|
+
return out;
|
|
7182
|
+
}
|
|
7183
|
+
var PACKAGE_MESSAGES = [/^Package: *(\S+)\s*$/m, /^Package '([^']+)@([^'@]+)' is vulnerable to '/, /vulnerability in ([\w.-]+) package: ([^,\s]+), version ([^\s,]+) was found/];
|
|
7184
|
+
var SNYK_DESCRIPTION = /^\(([^)]*)\)\s+(\S+)@(\S+)$/;
|
|
7185
|
+
function isDependencyResult(result, rule, run2, path, line) {
|
|
7186
|
+
if (isManifest(path) && line === 1) return true;
|
|
7187
|
+
const driver = isObj(run2.tool?.driver) ? run2.tool.driver : {};
|
|
7188
|
+
const props = isObj(result.properties) ? result.properties : {};
|
|
7189
|
+
const toolName = str(props.scanner_name) ?? str(props.scanner_details?.tool_name) ?? str(driver.name);
|
|
7190
|
+
const nested = toolName !== str(driver.name);
|
|
7191
|
+
const toolNames = [toolName, nested ? null : str(driver.fullName), nested ? null : str(driver.informationUri)].filter((n) => !!n);
|
|
7192
|
+
const tags = [...arr(rule.properties?.tags), ...arr(result.properties?.tags)].filter((t) => typeof t === "string").map((t) => t.toLowerCase());
|
|
7193
|
+
const toolType = tags.map((t) => /^tool_type::(\w+)$/.exec(t)?.[1]).find(Boolean) ?? null;
|
|
7194
|
+
if (toolNames.some((n) => SECRET_TOOLS.some((re) => re.test(n))) || toolType === "secrets" || tags.includes("secret") || tags.includes("secrets")) return false;
|
|
7195
|
+
if (toolType === "sast" || toolType === "iac" || tags.includes("misconfiguration")) return false;
|
|
7196
|
+
if (toolNames.some((n) => SCA_TOOLS.some((re) => re.test(n))) || toolType === "sca" || toolType === "container") return true;
|
|
7197
|
+
const parts = mergeParts([fromProperties(result.properties), fromProperties(rule.properties)]);
|
|
7198
|
+
const explicitName = [result.properties, rule.properties].some((b) => !!nameValue(prop(b, ["packagename", "pkgname"])));
|
|
7199
|
+
if (parts.purl || explicitName || parts.name && (parts.version || parts.fixedVersion)) return true;
|
|
7200
|
+
const message = typeof result.message?.text === "string" ? result.message.text : "";
|
|
7201
|
+
if (PACKAGE_MESSAGES.some((re) => re.test(message))) return true;
|
|
7202
|
+
const ruleVuln = vulnFromRuleId(str(result.ruleId) ?? str(rule.id));
|
|
7203
|
+
const snyk = SNYK_DESCRIPTION.exec(str(rule.fullDescription?.text) ?? "");
|
|
7204
|
+
const vulnIds = [
|
|
7205
|
+
ruleVuln.id,
|
|
7206
|
+
...[...arr(rule.deprecatedIds), ...arr(rule.properties?.aliases)].map(normalizeVulnId).filter(isAdvisoryId),
|
|
7207
|
+
...snyk ? snyk[1].split(/[,\s]+/).map(normalizeVulnId).filter(isAdvisoryId) : []
|
|
7208
|
+
].filter(Boolean);
|
|
7209
|
+
if (!vulnIds.length) return false;
|
|
7210
|
+
if (ruleVuln.rest && ruleVuln.pkgVersion) return true;
|
|
7211
|
+
if (isManifest(path) || str(run2.properties?.imageName)) return true;
|
|
7212
|
+
return tags.some((t) => ["vulnerability", "dependency", "dependencies", "sca", "supply-chain", "security"].includes(t));
|
|
7213
|
+
}
|
|
7214
|
+
function hasFingerprints(result) {
|
|
7215
|
+
return [result.partialFingerprints, result.fingerprints].some((source) => isObj(source) && Object.entries(source).some(([key, value]) => typeof value === "string" && (/(hash|fingerprint)/i.test(key) || /\/v\d+$/.test(key))));
|
|
7216
|
+
}
|
|
7217
|
+
var rulesById = /* @__PURE__ */ new WeakMap();
|
|
7218
|
+
var hasRuleId = (result, run2) => !!(str(result.ruleId) ?? str(ruleFor(result, run2).id));
|
|
7219
|
+
function ruleFor(result, run2) {
|
|
7220
|
+
const driverRules = arr(run2.tool?.driver?.rules);
|
|
7221
|
+
const ref = isObj(result.rule) ? result.rule : {};
|
|
7222
|
+
const idx = Number.isInteger(result.ruleIndex) ? result.ruleIndex : Number.isInteger(ref.index) ? ref.index : null;
|
|
7223
|
+
if (idx !== null && !isObj(ref.toolComponent) && isObj(driverRules[idx])) return driverRules[idx];
|
|
7224
|
+
const id = str(result.ruleId) ?? str(ref.id);
|
|
7225
|
+
let byId = rulesById.get(run2);
|
|
7226
|
+
if (!byId) {
|
|
7227
|
+
byId = /* @__PURE__ */ new Map();
|
|
7228
|
+
for (const r of [...driverRules, ...arr(run2.tool?.extensions).flatMap((e) => arr(e?.rules))]) {
|
|
7229
|
+
if (isObj(r) && !byId.has(r.id)) byId.set(r.id, r);
|
|
7230
|
+
}
|
|
7231
|
+
rulesById.set(run2, byId);
|
|
7232
|
+
}
|
|
7233
|
+
return byId.get(id) ?? {};
|
|
7234
|
+
}
|
|
7235
|
+
function uriToPath(uri) {
|
|
7236
|
+
if (/^file:/i.test(uri)) {
|
|
7237
|
+
try {
|
|
7238
|
+
return (0, import_url.fileURLToPath)(uri);
|
|
7239
|
+
} catch {
|
|
7240
|
+
return null;
|
|
7241
|
+
}
|
|
7242
|
+
}
|
|
7243
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(uri)) return null;
|
|
7244
|
+
try {
|
|
7245
|
+
return decodeURIComponent(uri);
|
|
7246
|
+
} catch {
|
|
7247
|
+
return uri;
|
|
7248
|
+
}
|
|
7249
|
+
}
|
|
7250
|
+
function categoryPath(uri) {
|
|
7251
|
+
let p = uri.trim();
|
|
7252
|
+
try {
|
|
7253
|
+
p = decodeURIComponent(p);
|
|
7254
|
+
} catch {
|
|
7255
|
+
}
|
|
7256
|
+
p = p.replace(/\\/g, "/").replace(/^file:\/\//i, "");
|
|
7257
|
+
p = p.replace(/^\/github\/workspace\//, "").replace(/^\/home\/runner\/work\/[^/]+\/[^/]+\//, "");
|
|
7258
|
+
p = p.replace(/^\.\//, "").replace(/^\/+/, "").replace(/\/{2,}/g, "/");
|
|
7259
|
+
return !p || p.split("/").some((s) => s === "..") ? null : p;
|
|
7260
|
+
}
|
|
7261
|
+
var outsideRel = (rel) => !rel || rel === ".." || rel.startsWith(`..${import_path2.sep}`) || (0, import_path2.isAbsolute)(rel) || /^[A-Za-z]:/.test(rel);
|
|
7262
|
+
function locate(result, run2, workspace) {
|
|
7263
|
+
const physical = arr(result.locations).map((l) => l?.physicalLocation).find(isObj);
|
|
7264
|
+
const al = isObj(physical?.artifactLocation) ? physical.artifactLocation : {};
|
|
7265
|
+
const fromIndex = Number.isInteger(al.index) ? arr(run2.artifacts)[al.index]?.location : null;
|
|
7266
|
+
const loc = typeof al.uri === "string" ? al : isObj(fromIndex) ? fromIndex : null;
|
|
7267
|
+
const uri = loc && typeof loc.uri === "string" && loc.uri.trim() ? loc.uri.trim() : null;
|
|
7268
|
+
const rawStart = physical?.region?.startLine;
|
|
7269
|
+
const start = Number.isInteger(rawStart) && rawStart >= 1 ? rawStart : null;
|
|
7270
|
+
const rawEnd = physical?.region?.endLine;
|
|
7271
|
+
const end = start === null ? 0 : Math.min(Number.isInteger(rawEnd) && rawEnd >= start ? rawEnd : start, start + MAX_RANGE_LINES - 1);
|
|
7272
|
+
const located = (kind, path2 = null) => ({ kind, path: path2, categoryPath: uri ? categoryPath(uri) : null, start, end });
|
|
7273
|
+
if (!uri || start === null) return located("none");
|
|
7274
|
+
let p = uriToPath(uri);
|
|
7275
|
+
if (p === null) return located("outside");
|
|
7276
|
+
const base = typeof loc?.uriBaseId === "string" ? run2.originalUriBaseIds?.[loc.uriBaseId]?.uri : null;
|
|
7277
|
+
const basePath = typeof base === "string" ? uriToPath(base) : null;
|
|
7278
|
+
if (!(0, import_path2.isAbsolute)(p) && !p.startsWith("/") && basePath) p = (0, import_path2.resolve)(workspace, basePath, p);
|
|
7279
|
+
p = p.replace(/^\/github\/workspace\//, "");
|
|
7280
|
+
const rel = (0, import_path2.relative)(workspace, (0, import_path2.resolve)(workspace, p));
|
|
7281
|
+
if (outsideRel(rel)) return located("outside");
|
|
7282
|
+
const path = rel.split(import_path2.sep).join("/");
|
|
7283
|
+
if (path.split("/").includes(".git")) return located("outside");
|
|
7284
|
+
return located("ok", path);
|
|
7285
|
+
}
|
|
7286
|
+
function parsePorcelain(out) {
|
|
7287
|
+
const commits = /* @__PURE__ */ new Map();
|
|
7288
|
+
const lines = [];
|
|
7289
|
+
const order = [];
|
|
7290
|
+
let current = null;
|
|
7291
|
+
for (const raw of out.split("\n")) {
|
|
7292
|
+
const header = /^([0-9a-f]{40,64}) \d+ \d+(?: \d+)?$/.exec(raw);
|
|
7293
|
+
if (header) {
|
|
7294
|
+
current = header[1];
|
|
7295
|
+
if (!commits.has(current)) commits.set(current, { time: 0, boundary: false });
|
|
7296
|
+
continue;
|
|
7297
|
+
}
|
|
7298
|
+
if (!current) continue;
|
|
7299
|
+
if (raw.startsWith(" ")) {
|
|
7300
|
+
lines.push(raw.slice(1));
|
|
7301
|
+
order.push(current);
|
|
7302
|
+
continue;
|
|
7303
|
+
}
|
|
7304
|
+
const info = commits.get(current);
|
|
7305
|
+
if (raw.startsWith("committer-time ")) info.time = Number(raw.slice(15)) || 0;
|
|
7306
|
+
else if (raw === "boundary") info.boundary = true;
|
|
7307
|
+
}
|
|
7308
|
+
if (!lines.length) return { ok: false, reason: "error" };
|
|
7309
|
+
if (lines.some((l) => l.includes("\0"))) return { ok: false, reason: "binary" };
|
|
7310
|
+
let best = order[0];
|
|
7311
|
+
for (const c of order) if (commits.get(c).time > commits.get(best).time) best = c;
|
|
7312
|
+
return { ok: true, commit: best, boundary: commits.get(best).boundary, lines };
|
|
7313
|
+
}
|
|
7314
|
+
function failureReason(text) {
|
|
7315
|
+
if (/no such path|has only \d+ lines?|does not exist|outside repository|no such file/i.test(text)) return "missing";
|
|
7316
|
+
if (/binary/i.test(text)) return "binary";
|
|
7317
|
+
return "error";
|
|
7318
|
+
}
|
|
7319
|
+
async function buildBlameSidecar(sarif, opts) {
|
|
7320
|
+
const started = Date.now();
|
|
7321
|
+
const budget = opts.budget ?? createBlameBudget(opts, started);
|
|
7322
|
+
if (budget.gitMissing) return null;
|
|
7323
|
+
const git = opts.git ?? execGit;
|
|
7324
|
+
const cwd = opts.cwd;
|
|
7325
|
+
const safeDirectory = cwd.replace(/\\/g, "/");
|
|
7326
|
+
const gitRun = (args, timeoutMs) => git.run(["-c", `safe.directory=${safeDirectory}`, "-c", "safe.bareRepository=explicit", ...args], { cwd, timeoutMs });
|
|
7327
|
+
const callMs = () => Math.min(MAX_CALL_MS, budget.deadline - Date.now());
|
|
7328
|
+
let doc;
|
|
7329
|
+
let sarifSha256;
|
|
7330
|
+
try {
|
|
7331
|
+
sarifSha256 = (0, import_crypto2.createHash)("sha256").update(sarif).digest("hex");
|
|
7332
|
+
doc = JSON.parse(Buffer.from(sarif.buffer, sarif.byteOffset, sarif.byteLength).toString("utf8"));
|
|
7333
|
+
if (!isObj(doc)) return null;
|
|
7334
|
+
} catch {
|
|
7335
|
+
return null;
|
|
7336
|
+
}
|
|
7337
|
+
let head;
|
|
7338
|
+
let shallow;
|
|
7339
|
+
try {
|
|
7340
|
+
head = (await gitRun(["rev-parse", "HEAD"], MAX_CALL_MS)).trim();
|
|
7341
|
+
if (!/^[0-9a-f]{40,64}$/.test(head)) return null;
|
|
7342
|
+
shallow = (await gitRun(["rev-parse", "--is-shallow-repository"], MAX_CALL_MS).catch(() => "false")).trim() === "true";
|
|
7343
|
+
} catch (e) {
|
|
7344
|
+
if (e?.code === "ENOENT") budget.gitMissing = true;
|
|
7345
|
+
return null;
|
|
7346
|
+
}
|
|
7347
|
+
const cache = /* @__PURE__ */ new Map();
|
|
7348
|
+
const missingFiles = /* @__PURE__ */ new Set();
|
|
7349
|
+
const shallowBoundary = /* @__PURE__ */ new Map();
|
|
7350
|
+
let canDeepen = shallow;
|
|
7351
|
+
const blameRange = (path, start, end) => {
|
|
7352
|
+
const key = `${start},${end}\0${path}`;
|
|
7353
|
+
let hit = cache.get(key);
|
|
7354
|
+
if (!hit) {
|
|
7355
|
+
hit = (async () => {
|
|
7356
|
+
if (missingFiles.has(path)) return { ok: false, reason: "missing" };
|
|
7357
|
+
const timeoutMs = callMs();
|
|
7358
|
+
if (timeoutMs <= 0) return { ok: false, reason: "limit" };
|
|
7359
|
+
try {
|
|
7360
|
+
return parsePorcelain(await gitRun(["blame", "--no-textconv", "-w", "-M", "-C", "--porcelain", "-L", `${start},${end}`, head, "--", path], timeoutMs));
|
|
7361
|
+
} catch (e) {
|
|
7362
|
+
if (Date.now() >= budget.deadline) return { ok: false, reason: "limit" };
|
|
7363
|
+
const stderr = e instanceof GitError ? e.stderr : "";
|
|
7364
|
+
if (/no such path/i.test(stderr)) missingFiles.add(path);
|
|
7365
|
+
const lineCount = Number(/has only (\d+) lines?/i.exec(stderr)?.[1]) || void 0;
|
|
7366
|
+
return { ok: false, reason: failureReason(stderr), lineCount };
|
|
7367
|
+
}
|
|
7368
|
+
})();
|
|
7369
|
+
cache.set(key, hit);
|
|
7370
|
+
}
|
|
7371
|
+
return hit;
|
|
7372
|
+
};
|
|
7373
|
+
const blame = async (path, start, end) => {
|
|
7374
|
+
const first = await blameRange(path, start, end);
|
|
7375
|
+
const lineCount = first.ok ? null : first.lineCount;
|
|
7376
|
+
return lineCount && start <= lineCount && end > lineCount ? blameRange(path, start, lineCount) : first;
|
|
7377
|
+
};
|
|
7378
|
+
const isShallowEdge = (commit, timeoutMs) => {
|
|
7379
|
+
let hit = shallowBoundary.get(commit);
|
|
7380
|
+
if (!hit) {
|
|
7381
|
+
hit = gitRun(["cat-file", "commit", commit], timeoutMs).then((raw) => /^parent [0-9a-f]+$/m.test(raw), () => true);
|
|
7382
|
+
shallowBoundary.set(commit, hit);
|
|
7383
|
+
}
|
|
7384
|
+
return hit;
|
|
7385
|
+
};
|
|
7386
|
+
const deepen = async () => {
|
|
7387
|
+
if (!canDeepen || budget.deepensLeft <= 0) return false;
|
|
7388
|
+
budget.deepensLeft--;
|
|
7389
|
+
try {
|
|
7390
|
+
await gitRun(["-c", "http.lowSpeedLimit=1000", "-c", "http.lowSpeedTime=10", "fetch", `--deepen=${DEEPEN_BY}`, "--no-tags", "--quiet"], Math.max(1, budget.deadline - Date.now()));
|
|
7391
|
+
} catch {
|
|
7392
|
+
canDeepen = false;
|
|
7393
|
+
return false;
|
|
7394
|
+
}
|
|
7395
|
+
cache.clear();
|
|
7396
|
+
shallowBoundary.clear();
|
|
7397
|
+
return true;
|
|
7398
|
+
};
|
|
7399
|
+
const linesAtHead = (path, timeoutMs) => gitRun(["show", "--no-textconv", `${head}:./${path}`], timeoutMs).then(
|
|
7400
|
+
(text) => text.includes("\0") ? null : text.split("\n").map((l) => l.replace(/\r$/, "")),
|
|
7401
|
+
() => null
|
|
7402
|
+
);
|
|
7403
|
+
const toHash = [];
|
|
7404
|
+
const hashSnippets = async () => {
|
|
7405
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
7406
|
+
for (const t of toHash) {
|
|
7407
|
+
const group = byPath.get(t.path);
|
|
7408
|
+
if (group) group.push(t);
|
|
7409
|
+
else byPath.set(t.path, [t]);
|
|
7410
|
+
}
|
|
7411
|
+
if (!byPath.size) return;
|
|
7412
|
+
const deadline = budget.snippetDeadline ??= Date.now() + budget.snippetMs;
|
|
7413
|
+
for (const [path, group] of byPath) {
|
|
7414
|
+
const timeoutMs = Math.min(MAX_CALL_MS, deadline - Date.now());
|
|
7415
|
+
if (budget.snippetsLeft <= 0 || timeoutMs <= 0) return;
|
|
7416
|
+
const counted = group.slice(0, budget.snippetsLeft);
|
|
7417
|
+
budget.snippetsLeft -= counted.length;
|
|
7418
|
+
const lines = await linesAtHead(path, timeoutMs);
|
|
7419
|
+
if (!lines) continue;
|
|
7420
|
+
const count = lines.at(-1) === "" ? lines.length - 1 : lines.length;
|
|
7421
|
+
for (const { entry, start, end } of counted) {
|
|
7422
|
+
const hash = start > count ? void 0 : snippetHashOf(lines.slice(start - 1, Math.min(end, count)));
|
|
7423
|
+
if (hash) entry.snippetHash = hash;
|
|
7424
|
+
}
|
|
7425
|
+
}
|
|
7426
|
+
};
|
|
7427
|
+
const results = [];
|
|
7428
|
+
const runs = arr(doc.runs).slice(0, MAX_RUNS);
|
|
7429
|
+
for (let r = 0; r < runs.length; r++) {
|
|
7430
|
+
const run2 = isObj(runs[r]) ? runs[r] : {};
|
|
7431
|
+
const list2 = arr(run2.results);
|
|
7432
|
+
for (let i = 0; i < list2.length; i++) {
|
|
7433
|
+
const result = list2[i];
|
|
7434
|
+
if (!isObj(result) || SKIP_KINDS.has(result.kind)) continue;
|
|
7435
|
+
const entry = { run: r, result: i, status: "skipped" };
|
|
7436
|
+
results.push(entry);
|
|
7437
|
+
try {
|
|
7438
|
+
const located = locate(result, run2, cwd);
|
|
7439
|
+
if (isDependencyResult(result, ruleFor(result, run2), run2, located.categoryPath, located.start)) {
|
|
7440
|
+
Object.assign(entry, { status: "not_applicable", reason: "dependency" });
|
|
7441
|
+
continue;
|
|
7442
|
+
}
|
|
7443
|
+
if (located.kind === "none") {
|
|
7444
|
+
entry.reason = "no_location";
|
|
7445
|
+
continue;
|
|
7446
|
+
}
|
|
7447
|
+
if (located.kind === "outside" || !located.path || located.start === null) {
|
|
7448
|
+
entry.reason = "missing";
|
|
7449
|
+
continue;
|
|
7450
|
+
}
|
|
7451
|
+
const { path, start, end } = { path: located.path, start: located.start, end: located.end };
|
|
7452
|
+
if (!hasFingerprints(result) && hasRuleId(result, run2) && toHash.length < budget.snippetsLeft && (budget.snippetDeadline === null || Date.now() < budget.snippetDeadline)) {
|
|
7453
|
+
toHash.push({ entry, path, start, end });
|
|
7454
|
+
}
|
|
7455
|
+
if (budget.resultsLeft <= 0 || Date.now() >= budget.deadline) {
|
|
7456
|
+
entry.reason = "limit";
|
|
7457
|
+
continue;
|
|
7458
|
+
}
|
|
7459
|
+
budget.resultsLeft--;
|
|
7460
|
+
let blamed = await blame(path, start, end);
|
|
7461
|
+
while (blamed.ok && shallow && blamed.boundary) {
|
|
7462
|
+
const timeoutMs = callMs();
|
|
7463
|
+
if (timeoutMs <= 0) {
|
|
7464
|
+
entry.reason = "limit";
|
|
7465
|
+
break;
|
|
7466
|
+
}
|
|
7467
|
+
if (!await isShallowEdge(blamed.commit, timeoutMs)) break;
|
|
7468
|
+
if (!await deepen()) {
|
|
7469
|
+
entry.reason = Date.now() >= budget.deadline ? "limit" : "shallow";
|
|
7470
|
+
break;
|
|
7471
|
+
}
|
|
7472
|
+
blamed = await blame(path, start, end);
|
|
7473
|
+
}
|
|
7474
|
+
if (entry.reason) continue;
|
|
7475
|
+
if (!blamed.ok) {
|
|
7476
|
+
entry.reason = blamed.reason;
|
|
7477
|
+
continue;
|
|
7478
|
+
}
|
|
7479
|
+
Object.assign(entry, { status: "found", commit: blamed.commit });
|
|
7480
|
+
} catch {
|
|
7481
|
+
entry.reason = "error";
|
|
7482
|
+
}
|
|
7483
|
+
}
|
|
7484
|
+
}
|
|
7485
|
+
try {
|
|
7486
|
+
await hashSnippets();
|
|
7487
|
+
} catch {
|
|
7488
|
+
}
|
|
7489
|
+
return {
|
|
7490
|
+
format: "prodgator-blame",
|
|
7491
|
+
version: 1,
|
|
7492
|
+
sarifSha256,
|
|
7493
|
+
head,
|
|
7494
|
+
results,
|
|
7495
|
+
stats: { blamed: results.filter((e) => e.status === "found").length, skipped: results.filter((e) => e.status === "skipped").length, ms: Date.now() - started }
|
|
7496
|
+
};
|
|
7497
|
+
}
|
|
7498
|
+
|
|
7499
|
+
// src/core/attestations.ts
|
|
6940
7500
|
var KINDS = ["test-results", "coverage", "sbom", "scan", "custom", "provenance"];
|
|
6941
7501
|
async function parseAttestationsInput(value, workspace) {
|
|
6942
7502
|
if (!value) return [];
|
|
@@ -6967,7 +7527,7 @@ function contentTypeForScanFile(format, path) {
|
|
|
6967
7527
|
if (format === "sarif") return "application/sarif+json";
|
|
6968
7528
|
if (format === "cyclonedx") return "application/vnd.cyclonedx+json";
|
|
6969
7529
|
if (format === "spdx") return "application/spdx+json";
|
|
6970
|
-
return (0,
|
|
7530
|
+
return (0, import_path3.extname)(path).toLowerCase() === ".json" ? "application/json" : "application/octet-stream";
|
|
6971
7531
|
}
|
|
6972
7532
|
function sarifCounts(json) {
|
|
6973
7533
|
const counts = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
@@ -6998,12 +7558,77 @@ function sarifCounts(json) {
|
|
|
6998
7558
|
}
|
|
6999
7559
|
return counts;
|
|
7000
7560
|
}
|
|
7001
|
-
|
|
7561
|
+
var BLAME_INPUT_WARNING = "data.blame is set by the report client itself; the value given was ignored";
|
|
7562
|
+
var isReservedSidecarPath = (path) => path.toLowerCase().endsWith(BLAME_SIDECAR_SUFFIX);
|
|
7563
|
+
var BLAME_NO_GIT_NOTE = "git is not installed, so code findings were sent without blame data.";
|
|
7564
|
+
var blameTooLargeWarning = (file) => `${file} is too large to add blame data to (over ${MAX_SARIF_BYTES} bytes), so its code findings were sent without it`;
|
|
7565
|
+
var BlameSidecars = class {
|
|
7566
|
+
constructor(limits, build) {
|
|
7567
|
+
this.build = build;
|
|
7568
|
+
this.budget = createBlameBudget(limits);
|
|
7569
|
+
}
|
|
7570
|
+
dir = null;
|
|
7571
|
+
count = 0;
|
|
7572
|
+
budget;
|
|
7573
|
+
/** The sidecar as an artifact, or why there is none (null when git is not installed: the note covers it). */
|
|
7574
|
+
async add(sarif, path, workspace) {
|
|
7575
|
+
let sidecar;
|
|
7576
|
+
try {
|
|
7577
|
+
sidecar = await this.build(sarif, { cwd: workspace, budget: this.budget });
|
|
7578
|
+
} catch {
|
|
7579
|
+
return "blame data was not added (blame stopped with an error)";
|
|
7580
|
+
}
|
|
7581
|
+
if (this.budget.gitMissing) return null;
|
|
7582
|
+
if (!sidecar) return "blame data was not added (the workspace is not a git checkout, or the SARIF file could not be read)";
|
|
7583
|
+
try {
|
|
7584
|
+
this.dir ??= await import_fs3.promises.mkdtemp((0, import_path3.join)((0, import_os.tmpdir)(), "prodgator-blame-"));
|
|
7585
|
+
const name = `${++this.count}${BLAME_SIDECAR_SUFFIX}`;
|
|
7586
|
+
const target = (0, import_path3.join)(this.dir, name);
|
|
7587
|
+
const text = JSON.stringify(sidecar);
|
|
7588
|
+
await import_fs3.promises.writeFile(target, text, { flag: "wx" });
|
|
7589
|
+
return {
|
|
7590
|
+
// Uploaded from the directory this client made (its own root for the upload check), never from the checkout.
|
|
7591
|
+
path: path + BLAME_SIDECAR_SUFFIX,
|
|
7592
|
+
absolute: target,
|
|
7593
|
+
workspace: this.dir,
|
|
7594
|
+
source: name,
|
|
7595
|
+
size: Buffer.byteLength(text),
|
|
7596
|
+
sha256: (0, import_crypto3.createHash)("sha256").update(text).digest("hex"),
|
|
7597
|
+
contentType: "application/json"
|
|
7598
|
+
};
|
|
7599
|
+
} catch {
|
|
7600
|
+
return "blame data was not added (the blame file could not be written)";
|
|
7601
|
+
}
|
|
7602
|
+
}
|
|
7603
|
+
/** Removes the temporary directory (best effort). Call after the uploads. */
|
|
7604
|
+
async cleanup() {
|
|
7605
|
+
const dir = this.dir;
|
|
7606
|
+
this.dir = null;
|
|
7607
|
+
if (dir) await import_fs3.promises.rm(dir, { recursive: true, force: true }).catch(() => {
|
|
7608
|
+
});
|
|
7609
|
+
}
|
|
7610
|
+
};
|
|
7611
|
+
async function resolveAttestationFiles(attestations, workspace, deps = {}) {
|
|
7612
|
+
const blame = deps.blame ? new BlameSidecars(deps.blame === true ? {} : deps.blame, deps.buildBlameSidecar ?? buildBlameSidecar) : null;
|
|
7613
|
+
try {
|
|
7614
|
+
return await resolveFiles(attestations, workspace, blame);
|
|
7615
|
+
} catch (e) {
|
|
7616
|
+
await blame?.cleanup();
|
|
7617
|
+
throw e;
|
|
7618
|
+
}
|
|
7619
|
+
}
|
|
7620
|
+
async function resolveFiles(attestations, workspace, blame) {
|
|
7002
7621
|
const files = [];
|
|
7003
7622
|
const warnings = [];
|
|
7004
7623
|
const out = [];
|
|
7624
|
+
let warnedTooLargeForBlame = false;
|
|
7005
7625
|
for (const input of attestations) {
|
|
7006
7626
|
let a = input;
|
|
7627
|
+
if ("blame" in a.data) {
|
|
7628
|
+
const { blame: _given, ...rest } = a.data;
|
|
7629
|
+
a = { ...a, data: rest };
|
|
7630
|
+
warnings.push(BLAME_INPUT_WARNING);
|
|
7631
|
+
}
|
|
7007
7632
|
if (a.kind === "scan" && "mode" in a.data && a.data.mode !== "full" && a.data.mode !== "diff") {
|
|
7008
7633
|
const { mode: _ignored, ...rest } = a.data;
|
|
7009
7634
|
a = { ...a, data: rest };
|
|
@@ -7018,6 +7643,7 @@ async function resolveAttestationFiles(attestations, workspace) {
|
|
|
7018
7643
|
const scanFormat = typeof format === "string" && SCAN_FILE_FORMATS.includes(format) ? format : void 0;
|
|
7019
7644
|
const wantCounts = a.kind === "scan" && scanFormat === "sarif" && a.data.critical === void 0 && a.data.high === void 0 && a.data.medium === void 0 && a.data.low === void 0;
|
|
7020
7645
|
const notAttached = `and was not attached to "${a.name}"`;
|
|
7646
|
+
const wantBlame = !!blame && a.kind === "scan" && scanFormat === "sarif";
|
|
7021
7647
|
const opened = await openWorkspaceFile(workspace, file);
|
|
7022
7648
|
if (!opened.ok) {
|
|
7023
7649
|
const missing = opened.reason === "missing" || opened.reason === "not-file";
|
|
@@ -7025,18 +7651,26 @@ async function resolveAttestationFiles(attestations, workspace) {
|
|
|
7025
7651
|
out.push(a);
|
|
7026
7652
|
continue;
|
|
7027
7653
|
}
|
|
7654
|
+
if (isReservedSidecarPath(opened.file.path)) {
|
|
7655
|
+
await opened.handle.close();
|
|
7656
|
+
warnings.push(`${opened.file.path} ends in ${BLAME_SIDECAR_SUFFIX}, a name reserved for blame data, ${notAttached}`);
|
|
7657
|
+
out.push(a);
|
|
7658
|
+
continue;
|
|
7659
|
+
}
|
|
7028
7660
|
let sha256;
|
|
7029
7661
|
let counts = null;
|
|
7662
|
+
let sarifBytes = null;
|
|
7030
7663
|
try {
|
|
7031
7664
|
if (opened.size === 0) {
|
|
7032
7665
|
warnings.push(`${file} was not found (or is empty) ${notAttached}`);
|
|
7033
7666
|
out.push(a);
|
|
7034
7667
|
continue;
|
|
7035
7668
|
}
|
|
7036
|
-
if (wantCounts && opened.size <= MAX_SARIF_BYTES) {
|
|
7669
|
+
if ((wantCounts || wantBlame) && opened.size <= MAX_SARIF_BYTES) {
|
|
7037
7670
|
const bytes = await opened.handle.readFile();
|
|
7038
|
-
sha256 = (0,
|
|
7039
|
-
counts = sarifCounts(bytes.toString("utf8"));
|
|
7671
|
+
sha256 = (0, import_crypto3.createHash)("sha256").update(bytes).digest("hex");
|
|
7672
|
+
if (wantCounts) counts = sarifCounts(bytes.toString("utf8"));
|
|
7673
|
+
if (wantBlame) sarifBytes = bytes;
|
|
7040
7674
|
} else {
|
|
7041
7675
|
sha256 = await sha256Handle(opened.handle);
|
|
7042
7676
|
}
|
|
@@ -7049,13 +7683,29 @@ async function resolveAttestationFiles(attestations, workspace) {
|
|
|
7049
7683
|
if (a.kind === "scan" && scanFormat) data.format = scanFormat;
|
|
7050
7684
|
if (counts) Object.assign(data, counts);
|
|
7051
7685
|
else if (wantCounts) warnings.push(`${file} is too large to compute SARIF counts from (over ${MAX_SARIF_BYTES} bytes); skipping`);
|
|
7686
|
+
if (wantBlame && opened.size > MAX_SARIF_BYTES && !warnedTooLargeForBlame) {
|
|
7687
|
+
warnedTooLargeForBlame = true;
|
|
7688
|
+
warnings.push(blameTooLargeWarning(file));
|
|
7689
|
+
}
|
|
7690
|
+
if (blame && sarifBytes) {
|
|
7691
|
+
const sidecar = await blame.add(sarifBytes, path, workspace);
|
|
7692
|
+
if (typeof sidecar === "string") warnings.push(`${file}: ${sidecar}`);
|
|
7693
|
+
else if (sidecar && files.some((f) => f.path === sidecar.path)) warnings.push(`${file}: blame data was not added (${sidecar.path} is already in this report)`);
|
|
7694
|
+
else if (sidecar) {
|
|
7695
|
+
files.push(sidecar);
|
|
7696
|
+
data.blame = sidecar.path;
|
|
7697
|
+
}
|
|
7698
|
+
}
|
|
7052
7699
|
out.push({ kind: a.kind, name: a.name, status: a.status, data });
|
|
7053
7700
|
}
|
|
7054
|
-
|
|
7701
|
+
const notes = blame?.budget.gitMissing ? [BLAME_NO_GIT_NOTE] : [];
|
|
7702
|
+
return { attestations: out, files, warnings, notes, cleanup: async () => {
|
|
7703
|
+
await blame?.cleanup();
|
|
7704
|
+
} };
|
|
7055
7705
|
}
|
|
7056
7706
|
|
|
7057
7707
|
// src/core/artifacts.ts
|
|
7058
|
-
var
|
|
7708
|
+
var import_path4 = require("path");
|
|
7059
7709
|
var MAX_ARTIFACT_FILES = 50;
|
|
7060
7710
|
var TYPES = {
|
|
7061
7711
|
".zip": "application/zip",
|
|
@@ -7077,7 +7727,7 @@ var TYPES = {
|
|
|
7077
7727
|
".jpeg": "image/jpeg",
|
|
7078
7728
|
".svg": "image/svg+xml"
|
|
7079
7729
|
};
|
|
7080
|
-
var contentTypeFor = (path) => TYPES[(0,
|
|
7730
|
+
var contentTypeFor = (path) => TYPES[(0, import_path4.extname)(path).toLowerCase()] ?? "application/octet-stream";
|
|
7081
7731
|
async function collectArtifacts(patterns, workspace, deps) {
|
|
7082
7732
|
const warnings = [];
|
|
7083
7733
|
if (!patterns.length) return { files: [], warnings };
|
|
@@ -7089,6 +7739,10 @@ async function collectArtifacts(patterns, workspace, deps) {
|
|
|
7089
7739
|
if (checked.reason !== "missing") warnings.push(`${checked.message} and was not uploaded`);
|
|
7090
7740
|
continue;
|
|
7091
7741
|
}
|
|
7742
|
+
if (checked.file.path.toLowerCase().endsWith(BLAME_SIDECAR_SUFFIX)) {
|
|
7743
|
+
warnings.push(`${checked.file.path} ends in ${BLAME_SIDECAR_SUFFIX}, a name reserved for blame data, and was not uploaded`);
|
|
7744
|
+
continue;
|
|
7745
|
+
}
|
|
7092
7746
|
inside.push({ absolute: checked.file.absolute, path: checked.file.path });
|
|
7093
7747
|
}
|
|
7094
7748
|
inside.sort((a, b) => a.path.localeCompare(b.path));
|
|
@@ -7180,7 +7834,7 @@ var http = __toESM(require("http"));
|
|
|
7180
7834
|
var https = __toESM(require("https"));
|
|
7181
7835
|
var refused = (message) => Object.assign(new Error(message), { retryable: false });
|
|
7182
7836
|
async function openForUpload(file) {
|
|
7183
|
-
const opened = await openWorkspaceFile(file.workspace, file.path);
|
|
7837
|
+
const opened = await openWorkspaceFile(file.workspace, file.source ?? file.path, { display: file.path });
|
|
7184
7838
|
if (!opened.ok) throw refused(`${opened.message} and was not uploaded`);
|
|
7185
7839
|
if (opened.size !== file.size) {
|
|
7186
7840
|
await opened.handle.close();
|
|
@@ -7191,7 +7845,7 @@ async function openForUpload(file) {
|
|
|
7191
7845
|
async function putOnce(slot, file) {
|
|
7192
7846
|
const handle = await openForUpload(file);
|
|
7193
7847
|
const body = handle.createReadStream({ start: 0 });
|
|
7194
|
-
return new Promise((
|
|
7848
|
+
return new Promise((resolve4, reject) => {
|
|
7195
7849
|
const url = new URL(slot.url);
|
|
7196
7850
|
const mod = url.protocol === "http:" ? http : https;
|
|
7197
7851
|
const headers = Object.fromEntries(Object.entries(slot.headers).filter(([k]) => !/^(proxy-)?authorization$|^cookie$/i.test(k)));
|
|
@@ -7202,7 +7856,7 @@ async function putOnce(slot, file) {
|
|
|
7202
7856
|
if (body2.length < 4e3) body2 += c;
|
|
7203
7857
|
});
|
|
7204
7858
|
res.on("end", () => {
|
|
7205
|
-
if (res.statusCode && res.statusCode < 300) return
|
|
7859
|
+
if (res.statusCode && res.statusCode < 300) return resolve4();
|
|
7206
7860
|
const code = /<Code>([^<]+)<\/Code>/.exec(body2)?.[1] ?? `HTTP ${res.statusCode}`;
|
|
7207
7861
|
const err = new Error(`${file.path}: upload refused (${code})`);
|
|
7208
7862
|
err.retryable = (res.statusCode ?? 0) >= 500;
|
|
@@ -7338,7 +7992,9 @@ var OPTION_NAMES = {
|
|
|
7338
7992
|
failOnError: { flag: "--fail-on-error", env: "PRODGATOR_FAIL_ON_ERROR", pipe: "FAIL_ON_ERROR" },
|
|
7339
7993
|
tokenVariable: { flag: "--token-variable", env: "PRODGATOR_TOKEN_VARIABLE", pipe: null },
|
|
7340
7994
|
gate: { flag: "--gate", env: "PRODGATOR_GATE", pipe: "GATE" },
|
|
7341
|
-
gateTimeoutMinutes: { flag: "--gate-timeout", env: "PRODGATOR_GATE_TIMEOUT", pipe: "GATE_TIMEOUT" }
|
|
7995
|
+
gateTimeoutMinutes: { flag: "--gate-timeout", env: "PRODGATOR_GATE_TIMEOUT", pipe: "GATE_TIMEOUT" },
|
|
7996
|
+
// No pipe name: a Bitbucket pipe step sets PRODGATOR_BLAME: 'false' among its variables.
|
|
7997
|
+
blame: { flag: "--blame", env: "PRODGATOR_BLAME", pipe: null }
|
|
7342
7998
|
};
|
|
7343
7999
|
var DEFAULT_GATE_TIMEOUT_MINUTES = 60;
|
|
7344
8000
|
var MAX_GATE_TIMEOUT_MINUTES = 720;
|
|
@@ -7370,7 +8026,11 @@ function parseOptions(argv, env) {
|
|
|
7370
8026
|
pipe = true;
|
|
7371
8027
|
continue;
|
|
7372
8028
|
}
|
|
7373
|
-
if (
|
|
8029
|
+
if (a === "--no-blame") {
|
|
8030
|
+
flags.set("--blame", "false");
|
|
8031
|
+
continue;
|
|
8032
|
+
}
|
|
8033
|
+
if ((a === "--gate" || a === "--allow-custom-url" || a === "--blame") && (argv[i + 1] === void 0 || argv[i + 1].startsWith("--"))) {
|
|
7374
8034
|
flags.set(a, "true");
|
|
7375
8035
|
continue;
|
|
7376
8036
|
}
|
|
@@ -7418,7 +8078,8 @@ function parseOptions(argv, env) {
|
|
|
7418
8078
|
tokenVariable: get("tokenVariable")?.trim() ?? null,
|
|
7419
8079
|
pipe,
|
|
7420
8080
|
gate: (get("gate") ?? "false").trim().toLowerCase() === "true",
|
|
7421
|
-
gateTimeoutMinutes: gateTimeout(get("gateTimeoutMinutes"))
|
|
8081
|
+
gateTimeoutMinutes: gateTimeout(get("gateTimeoutMinutes")),
|
|
8082
|
+
blame: (get("blame") ?? "true").trim().toLowerCase() !== "false"
|
|
7422
8083
|
};
|
|
7423
8084
|
}
|
|
7424
8085
|
|
|
@@ -7515,7 +8176,7 @@ function redactDeep(value, values) {
|
|
|
7515
8176
|
}
|
|
7516
8177
|
|
|
7517
8178
|
// src/cli/glob.ts
|
|
7518
|
-
var
|
|
8179
|
+
var import_path5 = require("path");
|
|
7519
8180
|
var { glob } = require_dist2();
|
|
7520
8181
|
function workspaceGlob(workspace, warn) {
|
|
7521
8182
|
return async (patterns) => {
|
|
@@ -7524,9 +8185,9 @@ function workspaceGlob(workspace, warn) {
|
|
|
7524
8185
|
for (const raw of patterns) {
|
|
7525
8186
|
const neg = raw.startsWith("!");
|
|
7526
8187
|
let p = neg ? raw.slice(1) : raw;
|
|
7527
|
-
if ((0,
|
|
7528
|
-
const rel = (0,
|
|
7529
|
-
if (!rel || rel.startsWith("..") || (0,
|
|
8188
|
+
if ((0, import_path5.isAbsolute)(p)) {
|
|
8189
|
+
const rel = (0, import_path5.relative)(workspace, p);
|
|
8190
|
+
if (!rel || rel.startsWith("..") || (0, import_path5.isAbsolute)(rel)) {
|
|
7530
8191
|
warn(`${raw} is outside the workspace and was skipped`);
|
|
7531
8192
|
continue;
|
|
7532
8193
|
}
|
|
@@ -7536,7 +8197,7 @@ function workspaceGlob(workspace, warn) {
|
|
|
7536
8197
|
}
|
|
7537
8198
|
if (!include.length) return [];
|
|
7538
8199
|
const found = await glob(include, { cwd: workspace, ignore, absolute: true, onlyFiles: true, dot: true, followSymbolicLinks: true });
|
|
7539
|
-
return found.map((f) => (0,
|
|
8200
|
+
return found.map((f) => (0, import_path5.resolve)(f)).sort();
|
|
7540
8201
|
};
|
|
7541
8202
|
}
|
|
7542
8203
|
|
|
@@ -7623,10 +8284,11 @@ async function run(deps = {}) {
|
|
|
7623
8284
|
const argv = deps.argv ?? process.argv.slice(2);
|
|
7624
8285
|
const log = deps.log ?? { info: (m) => console.log(m), warn: (m) => console.log(`Warning: ${m}`), error: (m) => console.error(m) };
|
|
7625
8286
|
if (argv.includes("--version")) {
|
|
7626
|
-
log.info("3.
|
|
8287
|
+
log.info("3.25.0");
|
|
7627
8288
|
return 0;
|
|
7628
8289
|
}
|
|
7629
8290
|
let failOnError = false;
|
|
8291
|
+
let cleanup = null;
|
|
7630
8292
|
const truthy = (v) => v?.trim().toLowerCase() === "true";
|
|
7631
8293
|
let gate = argv.some((a) => a === "--gate" || a === "--gate=true") || truthy(env.PRODGATOR_GATE) || argv.includes("--pipe") && truthy(env.GATE);
|
|
7632
8294
|
try {
|
|
@@ -7641,7 +8303,7 @@ async function run(deps = {}) {
|
|
|
7641
8303
|
const problem = tokenProblem(token, opts.audience, ci.provider, variable, (deps.nowMs ?? Date.now)());
|
|
7642
8304
|
if (problem) throw new CliError(problem);
|
|
7643
8305
|
if (opts.gate) {
|
|
7644
|
-
const client2 = { getToken: async () => token, fetch: deps.fetch, sleep: deps.sleep, fixedToken: true, userAgent: `prodgator-report/${"3.
|
|
8306
|
+
const client2 = { getToken: async () => token, fetch: deps.fetch, sleep: deps.sleep, fixedToken: true, userAgent: `prodgator-report/${"3.25.0"} (${ci.provider}; gate)` };
|
|
7645
8307
|
return await runGate(ci, { prodgatorUrl: opts.prodgatorUrl, audience: opts.audience, timeoutMinutes: opts.gateTimeoutMinutes }, client2, {
|
|
7646
8308
|
sleep: deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms))),
|
|
7647
8309
|
nowMs: deps.nowMs ?? Date.now,
|
|
@@ -7652,7 +8314,9 @@ async function run(deps = {}) {
|
|
|
7652
8314
|
const glob2 = workspaceGlob(ci.workspace, (w) => log.warn(w));
|
|
7653
8315
|
const rawSummary = await readSummary(ci.workspace, opts.summary, opts.summaryFile, log);
|
|
7654
8316
|
const summary = rawSummary ? truncateUtf8(redactText(rawSummary, secrets), MAX_SUMMARY_BYTES).text : null;
|
|
7655
|
-
const scanned = await resolveAttestationFiles(await parseAttestationsInput(opts.attestations, ci.workspace), ci.workspace);
|
|
8317
|
+
const scanned = await resolveAttestationFiles(await parseAttestationsInput(opts.attestations, ci.workspace), ci.workspace, { blame: opts.blame });
|
|
8318
|
+
cleanup = scanned.cleanup;
|
|
8319
|
+
scanned.notes.forEach((n) => log.info(n));
|
|
7656
8320
|
const attestations = redactDeep(scanned.attestations, secrets);
|
|
7657
8321
|
const junit = await junitAttestation(opts.junit, { glob: glob2, workspace: ci.workspace, warn: (w) => log.warn(w) });
|
|
7658
8322
|
if (junit) attestations.push(junit);
|
|
@@ -7664,7 +8328,7 @@ async function run(deps = {}) {
|
|
|
7664
8328
|
fetch: deps.fetch,
|
|
7665
8329
|
sleep: deps.sleep,
|
|
7666
8330
|
fixedToken: true,
|
|
7667
|
-
userAgent: `prodgator-report/${"3.
|
|
8331
|
+
userAgent: `prodgator-report/${"3.25.0"} (${ci.provider})`
|
|
7668
8332
|
};
|
|
7669
8333
|
const request = buildIngestRequest({ job: { name: opts.jobName ?? ci.jobName, matrix: opts.matrix, runner: ci.runner }, name: opts.name, summary, attestations, files });
|
|
7670
8334
|
const res = await sendReport(opts.prodgatorUrl, ci.provider, request, opts.audience, client, ci.azure);
|
|
@@ -7702,6 +8366,8 @@ async function run(deps = {}) {
|
|
|
7702
8366
|
log.warn(`Prodgator report was not sent: ${msg2}`);
|
|
7703
8367
|
deps.onOutcome?.("not_sent");
|
|
7704
8368
|
return 0;
|
|
8369
|
+
} finally {
|
|
8370
|
+
await cleanup?.();
|
|
7705
8371
|
}
|
|
7706
8372
|
}
|
|
7707
8373
|
|