@prodgator/report 3.19.0 → 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 +988 -226
- 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
|
}
|
|
@@ -634,7 +634,7 @@ var require_scan = __commonJS({
|
|
|
634
634
|
var require_parse = __commonJS({
|
|
635
635
|
"node_modules/picomatch/lib/parse.js"(exports2, module2) {
|
|
636
636
|
"use strict";
|
|
637
|
-
var
|
|
637
|
+
var constants2 = require_constants();
|
|
638
638
|
var utils = require_utils();
|
|
639
639
|
var {
|
|
640
640
|
MAX_LENGTH,
|
|
@@ -642,7 +642,7 @@ var require_parse = __commonJS({
|
|
|
642
642
|
REGEX_NON_SPECIAL_CHARS,
|
|
643
643
|
REGEX_SPECIAL_CHARS_BACKREF,
|
|
644
644
|
REPLACEMENTS
|
|
645
|
-
} =
|
|
645
|
+
} = constants2;
|
|
646
646
|
var expandRange = (args, options) => {
|
|
647
647
|
if (typeof options.expandRange === "function") {
|
|
648
648
|
return options.expandRange(...args, options);
|
|
@@ -851,7 +851,7 @@ var require_parse = __commonJS({
|
|
|
851
851
|
if (options.maxExtglobRecursion === false) {
|
|
852
852
|
return { risky: false };
|
|
853
853
|
}
|
|
854
|
-
const max = typeof options.maxExtglobRecursion === "number" ? options.maxExtglobRecursion :
|
|
854
|
+
const max = typeof options.maxExtglobRecursion === "number" ? options.maxExtglobRecursion : constants2.DEFAULT_MAX_EXTGLOB_RECURSION;
|
|
855
855
|
const branches = splitTopLevel(body).map((branch) => branch.trim());
|
|
856
856
|
if (branches.length > 1) {
|
|
857
857
|
if (branches.some((branch) => branch === "") || branches.some((branch) => /^[*?]+$/.test(branch)) || hasRepeatedCharPrefixOverlap(branches)) {
|
|
@@ -897,8 +897,8 @@ var require_parse = __commonJS({
|
|
|
897
897
|
const bos = { type: "bos", value: "", output: opts.prepend || "" };
|
|
898
898
|
const tokens = [bos];
|
|
899
899
|
const capture = opts.capture ? "" : "?:";
|
|
900
|
-
const PLATFORM_CHARS =
|
|
901
|
-
const EXTGLOB_CHARS =
|
|
900
|
+
const PLATFORM_CHARS = constants2.globChars(opts.windows);
|
|
901
|
+
const EXTGLOB_CHARS = constants2.extglobChars(PLATFORM_CHARS);
|
|
902
902
|
const {
|
|
903
903
|
DOT_LITERAL,
|
|
904
904
|
PLUS_LITERAL,
|
|
@@ -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);
|
|
@@ -1594,7 +1594,7 @@ var require_parse = __commonJS({
|
|
|
1594
1594
|
NO_DOTS_SLASH,
|
|
1595
1595
|
STAR,
|
|
1596
1596
|
START_ANCHOR
|
|
1597
|
-
} =
|
|
1597
|
+
} = constants2.globChars(opts.windows);
|
|
1598
1598
|
const nodot = opts.dot ? NO_DOTS : NO_DOT;
|
|
1599
1599
|
const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT;
|
|
1600
1600
|
const capture = opts.capture ? "" : "?:";
|
|
@@ -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;
|
|
@@ -1652,14 +1652,14 @@ var require_picomatch = __commonJS({
|
|
|
1652
1652
|
var scan = require_scan();
|
|
1653
1653
|
var parse = require_parse();
|
|
1654
1654
|
var utils = require_utils();
|
|
1655
|
-
var
|
|
1655
|
+
var constants2 = require_constants();
|
|
1656
1656
|
var isObject = (val) => val && typeof val === "object" && !Array.isArray(val);
|
|
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 });
|
|
@@ -1780,7 +1780,7 @@ var require_picomatch = __commonJS({
|
|
|
1780
1780
|
return /$^/;
|
|
1781
1781
|
}
|
|
1782
1782
|
};
|
|
1783
|
-
picomatch.constants =
|
|
1783
|
+
picomatch.constants = constants2;
|
|
1784
1784
|
module2.exports = picomatch;
|
|
1785
1785
|
}
|
|
1786
1786
|
});
|
|
@@ -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;
|
|
@@ -2641,10 +2641,6 @@ var require_dist2 = __commonJS({
|
|
|
2641
2641
|
}
|
|
2642
2642
|
});
|
|
2643
2643
|
|
|
2644
|
-
// src/cli/main.ts
|
|
2645
|
-
var import_fs5 = require("fs");
|
|
2646
|
-
var import_path4 = require("path");
|
|
2647
|
-
|
|
2648
2644
|
// src/core/text.ts
|
|
2649
2645
|
var MAX_SUMMARY_BYTES = 1024 * 1024;
|
|
2650
2646
|
var MARKER = "\n\n\u2026(truncated by prodgator-report)";
|
|
@@ -2656,9 +2652,6 @@ function truncateUtf8(text, maxBytes) {
|
|
|
2656
2652
|
return { text: bytes.subarray(0, end).toString("utf8") + MARKER, truncated: true };
|
|
2657
2653
|
}
|
|
2658
2654
|
|
|
2659
|
-
// src/core/junit.ts
|
|
2660
|
-
var import_fs = require("fs");
|
|
2661
|
-
|
|
2662
2655
|
// node_modules/fast-xml-parser/src/util.js
|
|
2663
2656
|
var nameStartChar = ":A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
|
|
2664
2657
|
var nameChar = nameStartChar + "\\-.\\d\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
|
|
@@ -3348,29 +3341,29 @@ var EntityDecoder = class {
|
|
|
3348
3341
|
* @param {string} str
|
|
3349
3342
|
* @returns {string}
|
|
3350
3343
|
*/
|
|
3351
|
-
decode(
|
|
3352
|
-
if (typeof
|
|
3353
|
-
if (
|
|
3354
|
-
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;
|
|
3355
3348
|
const chunks = [];
|
|
3356
|
-
const len =
|
|
3349
|
+
const len = str2.length;
|
|
3357
3350
|
let last = 0;
|
|
3358
3351
|
let i = 0;
|
|
3359
3352
|
const limitExpansions = this._maxTotalExpansions > 0;
|
|
3360
3353
|
const limitLength = this._maxExpandedLength > 0;
|
|
3361
3354
|
const checkLimits = limitExpansions || limitLength;
|
|
3362
3355
|
while (i < len) {
|
|
3363
|
-
if (
|
|
3356
|
+
if (str2.charCodeAt(i) !== 38) {
|
|
3364
3357
|
i++;
|
|
3365
3358
|
continue;
|
|
3366
3359
|
}
|
|
3367
3360
|
let j = i + 1;
|
|
3368
|
-
while (j < len &&
|
|
3369
|
-
if (j >= len ||
|
|
3361
|
+
while (j < len && str2.charCodeAt(j) !== 59 && j - i <= 32) j++;
|
|
3362
|
+
if (j >= len || str2.charCodeAt(j) !== 59) {
|
|
3370
3363
|
i++;
|
|
3371
3364
|
continue;
|
|
3372
3365
|
}
|
|
3373
|
-
const token =
|
|
3366
|
+
const token = str2.slice(i + 1, j);
|
|
3374
3367
|
if (token.length === 0) {
|
|
3375
3368
|
i++;
|
|
3376
3369
|
continue;
|
|
@@ -3402,7 +3395,7 @@ var EntityDecoder = class {
|
|
|
3402
3395
|
i++;
|
|
3403
3396
|
continue;
|
|
3404
3397
|
}
|
|
3405
|
-
if (i > last) chunks.push(
|
|
3398
|
+
if (i > last) chunks.push(str2.slice(last, i));
|
|
3406
3399
|
chunks.push(replacement);
|
|
3407
3400
|
last = j + 1;
|
|
3408
3401
|
i = last;
|
|
@@ -3428,8 +3421,8 @@ var EntityDecoder = class {
|
|
|
3428
3421
|
}
|
|
3429
3422
|
}
|
|
3430
3423
|
}
|
|
3431
|
-
if (last < len) chunks.push(
|
|
3432
|
-
const result = chunks.length === 0 ?
|
|
3424
|
+
if (last < len) chunks.push(str2.slice(last));
|
|
3425
|
+
const result = chunks.length === 0 ? str2 : chunks.join("");
|
|
3433
3426
|
return this._postCheck(result, original);
|
|
3434
3427
|
}
|
|
3435
3428
|
// -------------------------------------------------------------------------
|
|
@@ -3748,7 +3741,7 @@ var getRegexes = (xmlVersion = "1.0", asciiOnly = false) => {
|
|
|
3748
3741
|
if (asciiOnly) return regexesAscii;
|
|
3749
3742
|
return xmlVersion === "1.1" ? regexes11 : regexes10;
|
|
3750
3743
|
};
|
|
3751
|
-
var qName = (
|
|
3744
|
+
var qName = (str2, { xmlVersion = "1.0", asciiOnly = false } = {}) => getRegexes(xmlVersion, asciiOnly).qName.test(str2);
|
|
3752
3745
|
|
|
3753
3746
|
// node_modules/fast-xml-parser/src/xmlparser/DocTypeReader.js
|
|
3754
3747
|
var DocTypeReader = class {
|
|
@@ -4196,13 +4189,13 @@ var CHAR_0 = 48;
|
|
|
4196
4189
|
var CHAR_9 = 57;
|
|
4197
4190
|
var CHAR_MINUS = 45;
|
|
4198
4191
|
var MINUS_SET = /* @__PURE__ */ new Set([8722, 65293, 65123]);
|
|
4199
|
-
function anynum(
|
|
4200
|
-
if (typeof
|
|
4201
|
-
const len =
|
|
4202
|
-
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;
|
|
4203
4196
|
let firstHit = -1;
|
|
4204
4197
|
for (let i = 0; i < len; i++) {
|
|
4205
|
-
const cc =
|
|
4198
|
+
const cc = str2.charCodeAt(i);
|
|
4206
4199
|
if (cc >= CHAR_0 && cc <= CHAR_9 || cc === CHAR_MINUS) continue;
|
|
4207
4200
|
if (cc < TABLE_OFFSET) {
|
|
4208
4201
|
if (MINUS_SET.has(cc)) {
|
|
@@ -4213,7 +4206,7 @@ function anynum(str) {
|
|
|
4213
4206
|
}
|
|
4214
4207
|
if (cc >= 55296 && cc <= 56319) {
|
|
4215
4208
|
if (i + 1 < len) {
|
|
4216
|
-
const low =
|
|
4209
|
+
const low = str2.charCodeAt(i + 1);
|
|
4217
4210
|
if (low >= 56320 && low <= 57343) {
|
|
4218
4211
|
const cp = 65536 + (cc - 55296 << 10) + (low - 56320);
|
|
4219
4212
|
if (HIGH_MAP.has(cp)) {
|
|
@@ -4229,22 +4222,22 @@ function anynum(str) {
|
|
|
4229
4222
|
break;
|
|
4230
4223
|
}
|
|
4231
4224
|
}
|
|
4232
|
-
if (firstHit === -1) return
|
|
4225
|
+
if (firstHit === -1) return str2;
|
|
4233
4226
|
const chars = [];
|
|
4234
|
-
if (firstHit > 0) chars.push(
|
|
4227
|
+
if (firstHit > 0) chars.push(str2.slice(0, firstHit));
|
|
4235
4228
|
for (let i = firstHit; i < len; i++) {
|
|
4236
|
-
const cc =
|
|
4229
|
+
const cc = str2.charCodeAt(i);
|
|
4237
4230
|
if (cc >= CHAR_0 && cc <= CHAR_9 || cc === CHAR_MINUS) {
|
|
4238
|
-
chars.push(
|
|
4231
|
+
chars.push(str2[i]);
|
|
4239
4232
|
continue;
|
|
4240
4233
|
}
|
|
4241
4234
|
if (cc < TABLE_OFFSET) {
|
|
4242
|
-
chars.push(MINUS_SET.has(cc) ? "-" :
|
|
4235
|
+
chars.push(MINUS_SET.has(cc) ? "-" : str2[i]);
|
|
4243
4236
|
continue;
|
|
4244
4237
|
}
|
|
4245
4238
|
if (cc >= 55296 && cc <= 56319) {
|
|
4246
4239
|
if (i + 1 < len) {
|
|
4247
|
-
const low =
|
|
4240
|
+
const low = str2.charCodeAt(i + 1);
|
|
4248
4241
|
if (low >= 56320 && low <= 57343) {
|
|
4249
4242
|
const cp = 65536 + (cc - 55296 << 10) + (low - 56320);
|
|
4250
4243
|
const d2 = HIGH_MAP.get(cp);
|
|
@@ -4255,7 +4248,7 @@ function anynum(str) {
|
|
|
4255
4248
|
}
|
|
4256
4249
|
}
|
|
4257
4250
|
}
|
|
4258
|
-
chars.push(
|
|
4251
|
+
chars.push(str2[i]);
|
|
4259
4252
|
continue;
|
|
4260
4253
|
}
|
|
4261
4254
|
if (MINUS_SET.has(cc)) {
|
|
@@ -4263,7 +4256,7 @@ function anynum(str) {
|
|
|
4263
4256
|
continue;
|
|
4264
4257
|
}
|
|
4265
4258
|
const d = TABLE[cc - TABLE_OFFSET];
|
|
4266
|
-
chars.push(d !== NOT_DIGIT ? String.fromCharCode(d + 48) :
|
|
4259
|
+
chars.push(d !== NOT_DIGIT ? String.fromCharCode(d + 48) : str2[i]);
|
|
4267
4260
|
}
|
|
4268
4261
|
return chars.join("");
|
|
4269
4262
|
}
|
|
@@ -4286,12 +4279,12 @@ var consider = {
|
|
|
4286
4279
|
// "null", "infinity" (Infinity type), "string" ("Infinity" (the string literal))
|
|
4287
4280
|
unicode: false
|
|
4288
4281
|
};
|
|
4289
|
-
function toNumber(
|
|
4282
|
+
function toNumber(str2, options = {}) {
|
|
4290
4283
|
options = Object.assign({}, consider, options);
|
|
4291
|
-
if (!
|
|
4292
|
-
let trimmedStr =
|
|
4293
|
-
if (trimmedStr.length === 0) return
|
|
4294
|
-
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;
|
|
4295
4288
|
else if (trimmedStr === "0") return 0;
|
|
4296
4289
|
if (options.unicode) {
|
|
4297
4290
|
trimmedStr = anynum_default(trimmedStr);
|
|
@@ -4304,9 +4297,9 @@ function toNumber(str, options = {}) {
|
|
|
4304
4297
|
} else if (options.octal && octRegex.test(trimmedStr)) {
|
|
4305
4298
|
return parse_int(trimmedStr, 8);
|
|
4306
4299
|
} else if (!isFinite(trimmedStr)) {
|
|
4307
|
-
return handleInfinity(
|
|
4300
|
+
return handleInfinity(str2, Number(trimmedStr), options);
|
|
4308
4301
|
} else if (trimmedStr.includes("e") || trimmedStr.includes("E")) {
|
|
4309
|
-
return resolveEnotation(
|
|
4302
|
+
return resolveEnotation(str2, trimmedStr, options);
|
|
4310
4303
|
} else {
|
|
4311
4304
|
const match = numRegex.exec(trimmedStr);
|
|
4312
4305
|
if (match) {
|
|
@@ -4315,38 +4308,38 @@ function toNumber(str, options = {}) {
|
|
|
4315
4308
|
let numTrimmedByZeros = trimZeros(match[3]);
|
|
4316
4309
|
const decimalAdjacentToLeadingZeros = sign ? (
|
|
4317
4310
|
// 0., -00., 000.
|
|
4318
|
-
|
|
4319
|
-
) :
|
|
4311
|
+
str2[leadingZeros.length + 1] === "."
|
|
4312
|
+
) : str2[leadingZeros.length] === ".";
|
|
4320
4313
|
if (!options.leadingZeros && (leadingZeros.length > 1 || leadingZeros.length === 1 && !decimalAdjacentToLeadingZeros)) {
|
|
4321
|
-
return
|
|
4314
|
+
return str2;
|
|
4322
4315
|
} else {
|
|
4323
4316
|
const num = Number(trimmedStr);
|
|
4324
4317
|
const parsedStr = String(num);
|
|
4325
4318
|
if (num === 0) return num;
|
|
4326
4319
|
if (parsedStr.search(/[eE]/) !== -1) {
|
|
4327
4320
|
if (options.eNotation) return num;
|
|
4328
|
-
else return
|
|
4321
|
+
else return str2;
|
|
4329
4322
|
} else if (trimmedStr.indexOf(".") !== -1) {
|
|
4330
4323
|
if (parsedStr === "0") return num;
|
|
4331
4324
|
else if (parsedStr === numTrimmedByZeros) return num;
|
|
4332
4325
|
else if (parsedStr === `${sign}${numTrimmedByZeros}`) return num;
|
|
4333
|
-
else return
|
|
4326
|
+
else return str2;
|
|
4334
4327
|
}
|
|
4335
4328
|
let n = leadingZeros ? numTrimmedByZeros : trimmedStr;
|
|
4336
4329
|
if (leadingZeros) {
|
|
4337
|
-
return n === parsedStr || sign + n === parsedStr ? num :
|
|
4330
|
+
return n === parsedStr || sign + n === parsedStr ? num : str2;
|
|
4338
4331
|
} else {
|
|
4339
|
-
return n === parsedStr || n === sign + parsedStr ? num :
|
|
4332
|
+
return n === parsedStr || n === sign + parsedStr ? num : str2;
|
|
4340
4333
|
}
|
|
4341
4334
|
}
|
|
4342
4335
|
} else {
|
|
4343
|
-
return
|
|
4336
|
+
return str2;
|
|
4344
4337
|
}
|
|
4345
4338
|
}
|
|
4346
4339
|
}
|
|
4347
4340
|
var eNotationRegx = /^([-+])?(0*)(\d*(\.\d*)?[eE][-\+]?\d+)$/;
|
|
4348
|
-
function resolveEnotation(
|
|
4349
|
-
if (!options.eNotation) return
|
|
4341
|
+
function resolveEnotation(str2, trimmedStr, options) {
|
|
4342
|
+
if (!options.eNotation) return str2;
|
|
4350
4343
|
const notation = trimmedStr.match(eNotationRegx);
|
|
4351
4344
|
if (notation) {
|
|
4352
4345
|
let sign = notation[1] || "";
|
|
@@ -4354,21 +4347,21 @@ function resolveEnotation(str, trimmedStr, options) {
|
|
|
4354
4347
|
const leadingZeros = notation[2];
|
|
4355
4348
|
const eAdjacentToLeadingZeros = sign ? (
|
|
4356
4349
|
// 0E.
|
|
4357
|
-
|
|
4358
|
-
) :
|
|
4359
|
-
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;
|
|
4360
4353
|
else if (leadingZeros.length === 1 && (notation[3].startsWith(`.${eChar}`) || notation[3][0] === eChar)) {
|
|
4361
4354
|
return Number(trimmedStr);
|
|
4362
4355
|
} else if (leadingZeros.length > 0) {
|
|
4363
4356
|
if (options.leadingZeros && !eAdjacentToLeadingZeros) {
|
|
4364
4357
|
trimmedStr = (notation[1] || "") + notation[3];
|
|
4365
4358
|
return Number(trimmedStr);
|
|
4366
|
-
} else return
|
|
4359
|
+
} else return str2;
|
|
4367
4360
|
} else {
|
|
4368
4361
|
return Number(trimmedStr);
|
|
4369
4362
|
}
|
|
4370
4363
|
} else {
|
|
4371
|
-
return
|
|
4364
|
+
return str2;
|
|
4372
4365
|
}
|
|
4373
4366
|
}
|
|
4374
4367
|
function trimZeros(numStr) {
|
|
@@ -4384,14 +4377,14 @@ function trimZeros(numStr) {
|
|
|
4384
4377
|
return numStr;
|
|
4385
4378
|
}
|
|
4386
4379
|
function parse_int(numStr, base) {
|
|
4387
|
-
const
|
|
4388
|
-
if (base === 2 || base === 8) numStr =
|
|
4380
|
+
const str2 = numStr.trim();
|
|
4381
|
+
if (base === 2 || base === 8) numStr = str2.substring(2);
|
|
4389
4382
|
if (parseInt) return parseInt(numStr, base);
|
|
4390
4383
|
else if (Number.parseInt) return Number.parseInt(numStr, base);
|
|
4391
4384
|
else if (window && window.parseInt) return window.parseInt(numStr, base);
|
|
4392
4385
|
else throw new Error("parseInt, Number.parseInt, window.parseInt are not supported");
|
|
4393
4386
|
}
|
|
4394
|
-
function handleInfinity(
|
|
4387
|
+
function handleInfinity(str2, num, options) {
|
|
4395
4388
|
const isPositive = num === Infinity;
|
|
4396
4389
|
switch (options.infinity.toLowerCase()) {
|
|
4397
4390
|
case "null":
|
|
@@ -4403,7 +4396,7 @@ function handleInfinity(str, num, options) {
|
|
|
4403
4396
|
return isPositive ? "Infinity" : "-Infinity";
|
|
4404
4397
|
case "original":
|
|
4405
4398
|
default:
|
|
4406
|
-
return
|
|
4399
|
+
return str2;
|
|
4407
4400
|
}
|
|
4408
4401
|
}
|
|
4409
4402
|
|
|
@@ -6485,12 +6478,12 @@ function tagExpWithClosingIndex(xmlData, i, closingChar = ">") {
|
|
|
6485
6478
|
}
|
|
6486
6479
|
}
|
|
6487
6480
|
}
|
|
6488
|
-
function findClosingIndex(xmlData,
|
|
6489
|
-
const closingIndex = xmlData.indexOf(
|
|
6481
|
+
function findClosingIndex(xmlData, str2, i, errMsg) {
|
|
6482
|
+
const closingIndex = xmlData.indexOf(str2, i);
|
|
6490
6483
|
if (closingIndex === -1) {
|
|
6491
6484
|
throw new Error(errMsg);
|
|
6492
6485
|
} else {
|
|
6493
|
-
return closingIndex +
|
|
6486
|
+
return closingIndex + str2.length - 1;
|
|
6494
6487
|
}
|
|
6495
6488
|
}
|
|
6496
6489
|
function findClosingChar(xmlData, char, i, errMsg) {
|
|
@@ -6621,11 +6614,11 @@ function stripAttributePrefix(attrs, prefix) {
|
|
|
6621
6614
|
function prettify(node, options, matcher, readonlyMatcher) {
|
|
6622
6615
|
return compress(node, options, matcher, readonlyMatcher);
|
|
6623
6616
|
}
|
|
6624
|
-
function compress(
|
|
6617
|
+
function compress(arr2, options, matcher, readonlyMatcher) {
|
|
6625
6618
|
let text;
|
|
6626
6619
|
const compressedObj = {};
|
|
6627
|
-
for (let i = 0; i <
|
|
6628
|
-
const tagObj =
|
|
6620
|
+
for (let i = 0; i < arr2.length; i++) {
|
|
6621
|
+
const tagObj = arr2[i];
|
|
6629
6622
|
const property = propName(tagObj);
|
|
6630
6623
|
if (property !== void 0 && property !== options.textNodeName) {
|
|
6631
6624
|
const rawAttrs = stripAttributePrefix(
|
|
@@ -6778,6 +6771,103 @@ var XMLParser = class {
|
|
|
6778
6771
|
}
|
|
6779
6772
|
};
|
|
6780
6773
|
|
|
6774
|
+
// src/core/safe-path.ts
|
|
6775
|
+
var import_crypto = require("crypto");
|
|
6776
|
+
var import_fs = require("fs");
|
|
6777
|
+
var import_path = require("path");
|
|
6778
|
+
var O_NOFOLLOW = import_fs.constants.O_NOFOLLOW ?? 0;
|
|
6779
|
+
var outside = (rel) => !rel || rel === ".." || rel.startsWith(`..${import_path.sep}`) || rel.startsWith("../") || (0, import_path.isAbsolute)(rel) || /^[A-Za-z]:/.test(rel);
|
|
6780
|
+
var underGit = (rel) => rel.split(/[\\/]/).includes(".git");
|
|
6781
|
+
var refuse = (reason, message) => ({ ok: false, reason, message });
|
|
6782
|
+
function displayName(root, file) {
|
|
6783
|
+
const rel = (0, import_path.relative)((0, import_path.resolve)(root), (0, import_path.resolve)(root, file));
|
|
6784
|
+
return outside(rel) ? file : rel.split(import_path.sep).join("/");
|
|
6785
|
+
}
|
|
6786
|
+
async function checkWorkspacePath(root, name, opts = {}) {
|
|
6787
|
+
const display = opts.display ?? name;
|
|
6788
|
+
const absolute = (0, import_path.resolve)(root, name);
|
|
6789
|
+
const rel = (0, import_path.relative)((0, import_path.resolve)(root), absolute);
|
|
6790
|
+
if (outside(rel)) return refuse("outside", `${display} is outside the workspace`);
|
|
6791
|
+
const path = rel.split(import_path.sep).join("/");
|
|
6792
|
+
if (underGit(path)) return refuse("git", `${display} is inside a .git directory`);
|
|
6793
|
+
let real;
|
|
6794
|
+
let realRoot;
|
|
6795
|
+
try {
|
|
6796
|
+
[real, realRoot] = await Promise.all([import_fs.promises.realpath(absolute), import_fs.promises.realpath(root)]);
|
|
6797
|
+
} catch {
|
|
6798
|
+
return refuse("missing", `${display} was not found`);
|
|
6799
|
+
}
|
|
6800
|
+
const realRel = (0, import_path.relative)(realRoot, real);
|
|
6801
|
+
if (outside(realRel)) return refuse("symlink", `${display} resolves outside the workspace (symlink)`);
|
|
6802
|
+
if (underGit(realRel)) return refuse("git", `${display} resolves into a .git directory (symlink)`);
|
|
6803
|
+
if (opts.followLinks === false && realRel !== rel) return refuse("symlink", `${display} is a symlink`);
|
|
6804
|
+
return { ok: true, file: { absolute, path, real } };
|
|
6805
|
+
}
|
|
6806
|
+
async function openWorkspaceFile(root, name, opts = {}) {
|
|
6807
|
+
const display = opts.display ?? name;
|
|
6808
|
+
const checked = await checkWorkspacePath(root, name, opts);
|
|
6809
|
+
if (!checked.ok) return checked;
|
|
6810
|
+
const { file } = checked;
|
|
6811
|
+
let handle;
|
|
6812
|
+
try {
|
|
6813
|
+
handle = await import_fs.promises.open(file.real, import_fs.constants.O_RDONLY | O_NOFOLLOW);
|
|
6814
|
+
} catch (e) {
|
|
6815
|
+
const code = e.code;
|
|
6816
|
+
return code === "ENOENT" ? refuse("missing", `${display} was not found`) : refuse("changed", `${display} could not be opened safely`);
|
|
6817
|
+
}
|
|
6818
|
+
try {
|
|
6819
|
+
const st = await handle.stat();
|
|
6820
|
+
if (!st.isFile()) {
|
|
6821
|
+
await handle.close();
|
|
6822
|
+
return refuse("not-file", `${display} is not a file`);
|
|
6823
|
+
}
|
|
6824
|
+
if (!await sameFile(handle, st, file.real, await import_fs.promises.realpath(root))) {
|
|
6825
|
+
await handle.close();
|
|
6826
|
+
return refuse("changed", `${display} changed before it could be read (symlink)`);
|
|
6827
|
+
}
|
|
6828
|
+
return { ok: true, file, size: st.size, handle };
|
|
6829
|
+
} catch {
|
|
6830
|
+
await handle.close().catch(() => {
|
|
6831
|
+
});
|
|
6832
|
+
return refuse("changed", `${display} changed before it could be read (symlink)`);
|
|
6833
|
+
}
|
|
6834
|
+
}
|
|
6835
|
+
async function sameFile(handle, st, real, realRoot) {
|
|
6836
|
+
const viaProc = await import_fs.promises.realpath(`/proc/self/fd/${handle.fd}`).catch(() => null);
|
|
6837
|
+
if (viaProc !== null) {
|
|
6838
|
+
const rel = (0, import_path.relative)(realRoot, viaProc);
|
|
6839
|
+
return viaProc === real && !outside(rel) && !underGit(rel);
|
|
6840
|
+
}
|
|
6841
|
+
const again = await import_fs.promises.realpath(real).catch(() => null);
|
|
6842
|
+
if (again !== real) return false;
|
|
6843
|
+
const now = await import_fs.promises.stat(real).catch(() => null);
|
|
6844
|
+
return !!now && now.dev === st.dev && now.ino === st.ino;
|
|
6845
|
+
}
|
|
6846
|
+
async function readWorkspaceText(root, name, opts = {}) {
|
|
6847
|
+
const opened = await openWorkspaceFile(root, name, opts);
|
|
6848
|
+
if (!opened.ok) return opened;
|
|
6849
|
+
try {
|
|
6850
|
+
if (opts.maxBytes !== void 0 && opened.size > opts.maxBytes) return { ok: true, file: opened.file, size: opened.size, text: null };
|
|
6851
|
+
return { ok: true, file: opened.file, size: opened.size, text: await opened.handle.readFile("utf8") };
|
|
6852
|
+
} finally {
|
|
6853
|
+
await opened.handle.close();
|
|
6854
|
+
}
|
|
6855
|
+
}
|
|
6856
|
+
async function sha256Handle(handle) {
|
|
6857
|
+
const hash = (0, import_crypto.createHash)("sha256");
|
|
6858
|
+
for await (const chunk of handle.createReadStream({ autoClose: false, start: 0 })) hash.update(chunk);
|
|
6859
|
+
return hash.digest("hex");
|
|
6860
|
+
}
|
|
6861
|
+
async function hashWorkspaceFile(root, name, opts = {}) {
|
|
6862
|
+
const opened = await openWorkspaceFile(root, name, opts);
|
|
6863
|
+
if (!opened.ok) return opened;
|
|
6864
|
+
try {
|
|
6865
|
+
return { ok: true, file: opened.file, size: opened.size, sha256: await sha256Handle(opened.handle) };
|
|
6866
|
+
} finally {
|
|
6867
|
+
await opened.handle.close();
|
|
6868
|
+
}
|
|
6869
|
+
}
|
|
6870
|
+
|
|
6781
6871
|
// src/core/junit.ts
|
|
6782
6872
|
var MAX_FILE_BYTES = 50 * 1024 * 1024;
|
|
6783
6873
|
var MAX_FILES = 500;
|
|
@@ -6822,12 +6912,17 @@ async function junitAttestation(patterns, opts) {
|
|
|
6822
6912
|
if (!patterns.length) return null;
|
|
6823
6913
|
const find = opts.glob;
|
|
6824
6914
|
const files = (await find(patterns)).slice(0, MAX_FILES);
|
|
6825
|
-
if (!files.length) return null;
|
|
6826
6915
|
const total = empty();
|
|
6916
|
+
let read = 0;
|
|
6827
6917
|
for (const f of files) {
|
|
6828
|
-
const
|
|
6829
|
-
if (
|
|
6830
|
-
|
|
6918
|
+
const r = await readWorkspaceText(opts.workspace, f, { maxBytes: MAX_FILE_BYTES, display: displayName(opts.workspace, f) });
|
|
6919
|
+
if (!r.ok) {
|
|
6920
|
+
if (r.reason !== "missing") opts.warn(`${r.message} and was not read`);
|
|
6921
|
+
continue;
|
|
6922
|
+
}
|
|
6923
|
+
read++;
|
|
6924
|
+
if (r.text === null) continue;
|
|
6925
|
+
const t = parseJUnitXml(r.text);
|
|
6831
6926
|
total.passed += t.passed;
|
|
6832
6927
|
total.failed += t.failed;
|
|
6833
6928
|
total.skipped += t.skipped;
|
|
@@ -6835,87 +6930,582 @@ async function junitAttestation(patterns, opts) {
|
|
|
6835
6930
|
total.durationSeconds = (total.durationSeconds ?? 0) + (t.durationSeconds ?? 0);
|
|
6836
6931
|
total.failures.push(...t.failures.slice(0, 50 - total.failures.length));
|
|
6837
6932
|
}
|
|
6933
|
+
if (!read) return null;
|
|
6838
6934
|
return { kind: "test-results", name: opts.name ?? "junit", data: { ...total } };
|
|
6839
6935
|
}
|
|
6840
6936
|
|
|
6841
6937
|
// src/core/attestations.ts
|
|
6938
|
+
var import_crypto3 = require("crypto");
|
|
6842
6939
|
var import_fs3 = require("fs");
|
|
6843
|
-
var
|
|
6940
|
+
var import_os = require("os");
|
|
6941
|
+
var import_path3 = require("path");
|
|
6844
6942
|
|
|
6845
|
-
// src/core/
|
|
6846
|
-
var
|
|
6943
|
+
// src/core/blame.ts
|
|
6944
|
+
var import_child_process = require("child_process");
|
|
6945
|
+
var import_crypto2 = require("crypto");
|
|
6847
6946
|
var import_fs2 = require("fs");
|
|
6848
|
-
var
|
|
6849
|
-
var
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
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
|
+
}
|
|
6869
6972
|
};
|
|
6870
|
-
var
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
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));
|
|
6875
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 };
|
|
6876
7024
|
}
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
if (path.split("/").includes(".git")) return `${path} is inside a .git directory and was not uploaded`;
|
|
6880
|
-
const [real, root] = await Promise.all([import_fs2.promises.realpath(absolute), import_fs2.promises.realpath(workspace)]);
|
|
6881
|
-
if (outside((0, import_path.relative)(root, real))) return `${path} resolves outside the workspace (symlink) and was not uploaded`;
|
|
6882
|
-
return null;
|
|
7025
|
+
function findGit(workspace, o = {}) {
|
|
7026
|
+
return searchGit(workspace, o).git;
|
|
6883
7027
|
}
|
|
6884
|
-
|
|
6885
|
-
const
|
|
6886
|
-
|
|
6887
|
-
const
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
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 });
|
|
6893
7296
|
continue;
|
|
6894
7297
|
}
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
7298
|
+
if (!current) continue;
|
|
7299
|
+
if (raw.startsWith(" ")) {
|
|
7300
|
+
lines.push(raw.slice(1));
|
|
7301
|
+
order.push(current);
|
|
6899
7302
|
continue;
|
|
6900
7303
|
}
|
|
6901
|
-
|
|
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;
|
|
6902
7307
|
}
|
|
6903
|
-
|
|
6904
|
-
if (
|
|
6905
|
-
|
|
6906
|
-
for (const
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
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;
|
|
6910
7336
|
}
|
|
6911
|
-
|
|
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
|
+
};
|
|
6912
7497
|
}
|
|
6913
7498
|
|
|
6914
7499
|
// src/core/attestations.ts
|
|
6915
7500
|
var KINDS = ["test-results", "coverage", "sbom", "scan", "custom", "provenance"];
|
|
6916
|
-
function parseAttestationsInput(value, workspace) {
|
|
7501
|
+
async function parseAttestationsInput(value, workspace) {
|
|
6917
7502
|
if (!value) return [];
|
|
6918
|
-
|
|
7503
|
+
let text = value;
|
|
7504
|
+
if (!/^\s*[[{]/.test(value)) {
|
|
7505
|
+
const read = await readWorkspaceText(workspace, value.trim());
|
|
7506
|
+
if (!read.ok) throw new Error(`attestations file ${read.message}; it was not read`);
|
|
7507
|
+
text = read.text ?? "";
|
|
7508
|
+
}
|
|
6919
7509
|
let parsed;
|
|
6920
7510
|
try {
|
|
6921
7511
|
parsed = JSON.parse(text);
|
|
@@ -6937,7 +7527,7 @@ function contentTypeForScanFile(format, path) {
|
|
|
6937
7527
|
if (format === "sarif") return "application/sarif+json";
|
|
6938
7528
|
if (format === "cyclonedx") return "application/vnd.cyclonedx+json";
|
|
6939
7529
|
if (format === "spdx") return "application/spdx+json";
|
|
6940
|
-
return (0,
|
|
7530
|
+
return (0, import_path3.extname)(path).toLowerCase() === ".json" ? "application/json" : "application/octet-stream";
|
|
6941
7531
|
}
|
|
6942
7532
|
function sarifCounts(json) {
|
|
6943
7533
|
const counts = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
@@ -6968,13 +7558,77 @@ function sarifCounts(json) {
|
|
|
6968
7558
|
}
|
|
6969
7559
|
return counts;
|
|
6970
7560
|
}
|
|
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
|
+
};
|
|
6971
7611
|
async function resolveAttestationFiles(attestations, workspace, deps = {}) {
|
|
6972
|
-
const
|
|
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) {
|
|
6973
7621
|
const files = [];
|
|
6974
7622
|
const warnings = [];
|
|
6975
7623
|
const out = [];
|
|
7624
|
+
let warnedTooLargeForBlame = false;
|
|
6976
7625
|
for (const input of attestations) {
|
|
6977
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
|
+
}
|
|
6978
7632
|
if (a.kind === "scan" && "mode" in a.data && a.data.mode !== "full" && a.data.mode !== "diff") {
|
|
6979
7633
|
const { mode: _ignored, ...rest } = a.data;
|
|
6980
7634
|
a = { ...a, data: rest };
|
|
@@ -6987,39 +7641,123 @@ async function resolveAttestationFiles(attestations, workspace, deps = {}) {
|
|
|
6987
7641
|
}
|
|
6988
7642
|
const format = a.format;
|
|
6989
7643
|
const scanFormat = typeof format === "string" && SCAN_FILE_FORMATS.includes(format) ? format : void 0;
|
|
6990
|
-
const
|
|
6991
|
-
const
|
|
6992
|
-
|
|
6993
|
-
|
|
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;
|
|
7645
|
+
const notAttached = `and was not attached to "${a.name}"`;
|
|
7646
|
+
const wantBlame = !!blame && a.kind === "scan" && scanFormat === "sarif";
|
|
7647
|
+
const opened = await openWorkspaceFile(workspace, file);
|
|
7648
|
+
if (!opened.ok) {
|
|
7649
|
+
const missing = opened.reason === "missing" || opened.reason === "not-file";
|
|
7650
|
+
warnings.push(missing ? `${file} was not found (or is empty) ${notAttached}` : `${opened.message} ${notAttached}`);
|
|
6994
7651
|
out.push(a);
|
|
6995
7652
|
continue;
|
|
6996
7653
|
}
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
|
|
7000
|
-
warnings.push(`${file} was not found (or is empty) and was not attached to "${a.name}"`);
|
|
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}`);
|
|
7001
7657
|
out.push(a);
|
|
7002
7658
|
continue;
|
|
7003
7659
|
}
|
|
7004
|
-
|
|
7005
|
-
|
|
7006
|
-
|
|
7007
|
-
|
|
7008
|
-
|
|
7660
|
+
let sha256;
|
|
7661
|
+
let counts = null;
|
|
7662
|
+
let sarifBytes = null;
|
|
7663
|
+
try {
|
|
7664
|
+
if (opened.size === 0) {
|
|
7665
|
+
warnings.push(`${file} was not found (or is empty) ${notAttached}`);
|
|
7666
|
+
out.push(a);
|
|
7667
|
+
continue;
|
|
7668
|
+
}
|
|
7669
|
+
if ((wantCounts || wantBlame) && opened.size <= MAX_SARIF_BYTES) {
|
|
7670
|
+
const bytes = await opened.handle.readFile();
|
|
7671
|
+
sha256 = (0, import_crypto3.createHash)("sha256").update(bytes).digest("hex");
|
|
7672
|
+
if (wantCounts) counts = sarifCounts(bytes.toString("utf8"));
|
|
7673
|
+
if (wantBlame) sarifBytes = bytes;
|
|
7674
|
+
} else {
|
|
7675
|
+
sha256 = await sha256Handle(opened.handle);
|
|
7676
|
+
}
|
|
7677
|
+
} finally {
|
|
7678
|
+
await opened.handle.close();
|
|
7009
7679
|
}
|
|
7010
|
-
|
|
7680
|
+
const { path, absolute } = opened.file;
|
|
7681
|
+
files.push({ path, absolute, workspace, size: opened.size, sha256, contentType: contentTypeForScanFile(scanFormat, path) });
|
|
7011
7682
|
const data = { ...a.data, artifact: path };
|
|
7012
7683
|
if (a.kind === "scan" && scanFormat) data.format = scanFormat;
|
|
7013
|
-
if (
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7684
|
+
if (counts) Object.assign(data, counts);
|
|
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;
|
|
7018
7697
|
}
|
|
7019
7698
|
}
|
|
7020
7699
|
out.push({ kind: a.kind, name: a.name, status: a.status, data });
|
|
7021
7700
|
}
|
|
7022
|
-
|
|
7701
|
+
const notes = blame?.budget.gitMissing ? [BLAME_NO_GIT_NOTE] : [];
|
|
7702
|
+
return { attestations: out, files, warnings, notes, cleanup: async () => {
|
|
7703
|
+
await blame?.cleanup();
|
|
7704
|
+
} };
|
|
7705
|
+
}
|
|
7706
|
+
|
|
7707
|
+
// src/core/artifacts.ts
|
|
7708
|
+
var import_path4 = require("path");
|
|
7709
|
+
var MAX_ARTIFACT_FILES = 50;
|
|
7710
|
+
var TYPES = {
|
|
7711
|
+
".zip": "application/zip",
|
|
7712
|
+
".gz": "application/gzip",
|
|
7713
|
+
".tgz": "application/gzip",
|
|
7714
|
+
".tar": "application/x-tar",
|
|
7715
|
+
".json": "application/json",
|
|
7716
|
+
".xml": "application/xml",
|
|
7717
|
+
".pdf": "application/pdf",
|
|
7718
|
+
".txt": "text/plain",
|
|
7719
|
+
".log": "text/plain",
|
|
7720
|
+
".info": "text/plain",
|
|
7721
|
+
".md": "text/markdown",
|
|
7722
|
+
".csv": "text/csv",
|
|
7723
|
+
".html": "text/html",
|
|
7724
|
+
".htm": "text/html",
|
|
7725
|
+
".png": "image/png",
|
|
7726
|
+
".jpg": "image/jpeg",
|
|
7727
|
+
".jpeg": "image/jpeg",
|
|
7728
|
+
".svg": "image/svg+xml"
|
|
7729
|
+
};
|
|
7730
|
+
var contentTypeFor = (path) => TYPES[(0, import_path4.extname)(path).toLowerCase()] ?? "application/octet-stream";
|
|
7731
|
+
async function collectArtifacts(patterns, workspace, deps) {
|
|
7732
|
+
const warnings = [];
|
|
7733
|
+
if (!patterns.length) return { files: [], warnings };
|
|
7734
|
+
const find = deps.glob;
|
|
7735
|
+
const inside = [];
|
|
7736
|
+
for (const absolute of [...new Set(await find(patterns))]) {
|
|
7737
|
+
const checked = await checkWorkspacePath(workspace, absolute, { display: displayName(workspace, absolute) });
|
|
7738
|
+
if (!checked.ok) {
|
|
7739
|
+
if (checked.reason !== "missing") warnings.push(`${checked.message} and was not uploaded`);
|
|
7740
|
+
continue;
|
|
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
|
+
}
|
|
7746
|
+
inside.push({ absolute: checked.file.absolute, path: checked.file.path });
|
|
7747
|
+
}
|
|
7748
|
+
inside.sort((a, b) => a.path.localeCompare(b.path));
|
|
7749
|
+
if (inside.length > MAX_ARTIFACT_FILES) warnings.push(`${inside.length} files matched; only the first ${MAX_ARTIFACT_FILES} are uploaded`);
|
|
7750
|
+
const files = [];
|
|
7751
|
+
for (const f of inside.slice(0, MAX_ARTIFACT_FILES)) {
|
|
7752
|
+
const hashed = await hashWorkspaceFile(workspace, f.path);
|
|
7753
|
+
if (!hashed.ok) {
|
|
7754
|
+
if (hashed.reason !== "missing" && hashed.reason !== "not-file") warnings.push(`${hashed.message} and was not uploaded`);
|
|
7755
|
+
continue;
|
|
7756
|
+
}
|
|
7757
|
+
if (hashed.size === 0) continue;
|
|
7758
|
+
files.push({ path: f.path, absolute: f.absolute, workspace, size: hashed.size, sha256: hashed.sha256, contentType: contentTypeFor(f.path) });
|
|
7759
|
+
}
|
|
7760
|
+
return { files, warnings };
|
|
7023
7761
|
}
|
|
7024
7762
|
|
|
7025
7763
|
// src/core/request.ts
|
|
@@ -7092,23 +7830,34 @@ var sendReport = (baseUrl, provider, req, audience, deps, azure) => postJson(`${
|
|
|
7092
7830
|
var completeReport = (baseUrl, provider, reportId, artifactIds, audience, deps, azure) => postJson(`${baseUrl}/api/ingest/${provider}/reports/${encodeURIComponent(reportId)}/complete`, azure ? { artifactIds, azure } : { artifactIds }, audience, deps);
|
|
7093
7831
|
|
|
7094
7832
|
// src/core/upload.ts
|
|
7095
|
-
var import_fs4 = require("fs");
|
|
7096
7833
|
var http = __toESM(require("http"));
|
|
7097
7834
|
var https = __toESM(require("https"));
|
|
7098
|
-
|
|
7099
|
-
|
|
7835
|
+
var refused = (message) => Object.assign(new Error(message), { retryable: false });
|
|
7836
|
+
async function openForUpload(file) {
|
|
7837
|
+
const opened = await openWorkspaceFile(file.workspace, file.source ?? file.path, { display: file.path });
|
|
7838
|
+
if (!opened.ok) throw refused(`${opened.message} and was not uploaded`);
|
|
7839
|
+
if (opened.size !== file.size) {
|
|
7840
|
+
await opened.handle.close();
|
|
7841
|
+
throw refused(`${file.path} changed since it was hashed and was not uploaded`);
|
|
7842
|
+
}
|
|
7843
|
+
return opened.handle;
|
|
7844
|
+
}
|
|
7845
|
+
async function putOnce(slot, file) {
|
|
7846
|
+
const handle = await openForUpload(file);
|
|
7847
|
+
const body = handle.createReadStream({ start: 0 });
|
|
7848
|
+
return new Promise((resolve4, reject) => {
|
|
7100
7849
|
const url = new URL(slot.url);
|
|
7101
7850
|
const mod = url.protocol === "http:" ? http : https;
|
|
7102
7851
|
const headers = Object.fromEntries(Object.entries(slot.headers).filter(([k]) => !/^(proxy-)?authorization$|^cookie$/i.test(k)));
|
|
7103
7852
|
const req = mod.request(url, { method: "PUT", headers, timeout: 10 * 6e4 }, (res) => {
|
|
7104
|
-
let
|
|
7853
|
+
let body2 = "";
|
|
7105
7854
|
res.setEncoding("utf8");
|
|
7106
7855
|
res.on("data", (c) => {
|
|
7107
|
-
if (
|
|
7856
|
+
if (body2.length < 4e3) body2 += c;
|
|
7108
7857
|
});
|
|
7109
7858
|
res.on("end", () => {
|
|
7110
|
-
if (res.statusCode && res.statusCode < 300) return
|
|
7111
|
-
const code = /<Code>([^<]+)<\/Code>/.exec(
|
|
7859
|
+
if (res.statusCode && res.statusCode < 300) return resolve4();
|
|
7860
|
+
const code = /<Code>([^<]+)<\/Code>/.exec(body2)?.[1] ?? `HTTP ${res.statusCode}`;
|
|
7112
7861
|
const err = new Error(`${file.path}: upload refused (${code})`);
|
|
7113
7862
|
err.retryable = (res.statusCode ?? 0) >= 500;
|
|
7114
7863
|
reject(err);
|
|
@@ -7123,8 +7872,8 @@ function putOnce(slot, file) {
|
|
|
7123
7872
|
if (e.retryable === void 0) e.retryable = true;
|
|
7124
7873
|
reject(e);
|
|
7125
7874
|
});
|
|
7126
|
-
|
|
7127
|
-
});
|
|
7875
|
+
body.on("error", reject).pipe(req);
|
|
7876
|
+
}).finally(() => body.destroy());
|
|
7128
7877
|
}
|
|
7129
7878
|
async function uploadFile(slot, file, attempts = 2) {
|
|
7130
7879
|
for (let i = 1; ; i++) {
|
|
@@ -7243,7 +7992,9 @@ var OPTION_NAMES = {
|
|
|
7243
7992
|
failOnError: { flag: "--fail-on-error", env: "PRODGATOR_FAIL_ON_ERROR", pipe: "FAIL_ON_ERROR" },
|
|
7244
7993
|
tokenVariable: { flag: "--token-variable", env: "PRODGATOR_TOKEN_VARIABLE", pipe: null },
|
|
7245
7994
|
gate: { flag: "--gate", env: "PRODGATOR_GATE", pipe: "GATE" },
|
|
7246
|
-
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 }
|
|
7247
7998
|
};
|
|
7248
7999
|
var DEFAULT_GATE_TIMEOUT_MINUTES = 60;
|
|
7249
8000
|
var MAX_GATE_TIMEOUT_MINUTES = 720;
|
|
@@ -7275,7 +8026,11 @@ function parseOptions(argv, env) {
|
|
|
7275
8026
|
pipe = true;
|
|
7276
8027
|
continue;
|
|
7277
8028
|
}
|
|
7278
|
-
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("--"))) {
|
|
7279
8034
|
flags.set(a, "true");
|
|
7280
8035
|
continue;
|
|
7281
8036
|
}
|
|
@@ -7323,7 +8078,8 @@ function parseOptions(argv, env) {
|
|
|
7323
8078
|
tokenVariable: get("tokenVariable")?.trim() ?? null,
|
|
7324
8079
|
pipe,
|
|
7325
8080
|
gate: (get("gate") ?? "false").trim().toLowerCase() === "true",
|
|
7326
|
-
gateTimeoutMinutes: gateTimeout(get("gateTimeoutMinutes"))
|
|
8081
|
+
gateTimeoutMinutes: gateTimeout(get("gateTimeoutMinutes")),
|
|
8082
|
+
blame: (get("blame") ?? "true").trim().toLowerCase() !== "false"
|
|
7327
8083
|
};
|
|
7328
8084
|
}
|
|
7329
8085
|
|
|
@@ -7420,7 +8176,7 @@ function redactDeep(value, values) {
|
|
|
7420
8176
|
}
|
|
7421
8177
|
|
|
7422
8178
|
// src/cli/glob.ts
|
|
7423
|
-
var
|
|
8179
|
+
var import_path5 = require("path");
|
|
7424
8180
|
var { glob } = require_dist2();
|
|
7425
8181
|
function workspaceGlob(workspace, warn) {
|
|
7426
8182
|
return async (patterns) => {
|
|
@@ -7429,9 +8185,9 @@ function workspaceGlob(workspace, warn) {
|
|
|
7429
8185
|
for (const raw of patterns) {
|
|
7430
8186
|
const neg = raw.startsWith("!");
|
|
7431
8187
|
let p = neg ? raw.slice(1) : raw;
|
|
7432
|
-
if ((0,
|
|
7433
|
-
const rel = (0,
|
|
7434
|
-
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)) {
|
|
7435
8191
|
warn(`${raw} is outside the workspace and was skipped`);
|
|
7436
8192
|
continue;
|
|
7437
8193
|
}
|
|
@@ -7441,7 +8197,7 @@ function workspaceGlob(workspace, warn) {
|
|
|
7441
8197
|
}
|
|
7442
8198
|
if (!include.length) return [];
|
|
7443
8199
|
const found = await glob(include, { cwd: workspace, ignore, absolute: true, onlyFiles: true, dot: true, followSymbolicLinks: true });
|
|
7444
|
-
return found.map((f) => (0,
|
|
8200
|
+
return found.map((f) => (0, import_path5.resolve)(f)).sort();
|
|
7445
8201
|
};
|
|
7446
8202
|
}
|
|
7447
8203
|
|
|
@@ -7504,16 +8260,17 @@ var SKIPPED = {
|
|
|
7504
8260
|
monthly_quota: "your organization's monthly upload allowance is used up"
|
|
7505
8261
|
};
|
|
7506
8262
|
var DEFAULT_SUMMARY_FILE = "prodgator-summary.md";
|
|
7507
|
-
async function readSummary(workspace, summary, file,
|
|
8263
|
+
async function readSummary(workspace, summary, file, log) {
|
|
7508
8264
|
if (summary) return summary;
|
|
7509
8265
|
const path = file ?? DEFAULT_SUMMARY_FILE;
|
|
7510
|
-
const
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
8266
|
+
const read = await readWorkspaceText(workspace, path);
|
|
8267
|
+
if (read.ok) return read.text;
|
|
8268
|
+
if (read.reason === "missing") {
|
|
8269
|
+
if (file) log.info(`No summary file at ${path}; sending the report without a summary.`);
|
|
8270
|
+
} else {
|
|
8271
|
+
log.warn(`${read.message}; sending the report without a summary.`);
|
|
7516
8272
|
}
|
|
8273
|
+
return null;
|
|
7517
8274
|
}
|
|
7518
8275
|
function mergeFiles(first, rest, warn) {
|
|
7519
8276
|
const byPath = /* @__PURE__ */ new Map();
|
|
@@ -7527,10 +8284,11 @@ async function run(deps = {}) {
|
|
|
7527
8284
|
const argv = deps.argv ?? process.argv.slice(2);
|
|
7528
8285
|
const log = deps.log ?? { info: (m) => console.log(m), warn: (m) => console.log(`Warning: ${m}`), error: (m) => console.error(m) };
|
|
7529
8286
|
if (argv.includes("--version")) {
|
|
7530
|
-
log.info("3.
|
|
8287
|
+
log.info("3.25.0");
|
|
7531
8288
|
return 0;
|
|
7532
8289
|
}
|
|
7533
8290
|
let failOnError = false;
|
|
8291
|
+
let cleanup = null;
|
|
7534
8292
|
const truthy = (v) => v?.trim().toLowerCase() === "true";
|
|
7535
8293
|
let gate = argv.some((a) => a === "--gate" || a === "--gate=true") || truthy(env.PRODGATOR_GATE) || argv.includes("--pipe") && truthy(env.GATE);
|
|
7536
8294
|
try {
|
|
@@ -7545,7 +8303,7 @@ async function run(deps = {}) {
|
|
|
7545
8303
|
const problem = tokenProblem(token, opts.audience, ci.provider, variable, (deps.nowMs ?? Date.now)());
|
|
7546
8304
|
if (problem) throw new CliError(problem);
|
|
7547
8305
|
if (opts.gate) {
|
|
7548
|
-
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)` };
|
|
7549
8307
|
return await runGate(ci, { prodgatorUrl: opts.prodgatorUrl, audience: opts.audience, timeoutMinutes: opts.gateTimeoutMinutes }, client2, {
|
|
7550
8308
|
sleep: deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms))),
|
|
7551
8309
|
nowMs: deps.nowMs ?? Date.now,
|
|
@@ -7554,11 +8312,13 @@ async function run(deps = {}) {
|
|
|
7554
8312
|
}
|
|
7555
8313
|
const secrets = secretValues(env, [variable, ...opts.redactVariables]);
|
|
7556
8314
|
const glob2 = workspaceGlob(ci.workspace, (w) => log.warn(w));
|
|
7557
|
-
const rawSummary = await readSummary(ci.workspace, opts.summary, opts.summaryFile,
|
|
8315
|
+
const rawSummary = await readSummary(ci.workspace, opts.summary, opts.summaryFile, log);
|
|
7558
8316
|
const summary = rawSummary ? truncateUtf8(redactText(rawSummary, secrets), MAX_SUMMARY_BYTES).text : null;
|
|
7559
|
-
const scanned = await resolveAttestationFiles(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));
|
|
7560
8320
|
const attestations = redactDeep(scanned.attestations, secrets);
|
|
7561
|
-
const junit = await junitAttestation(opts.junit, { glob: glob2 });
|
|
8321
|
+
const junit = await junitAttestation(opts.junit, { glob: glob2, workspace: ci.workspace, warn: (w) => log.warn(w) });
|
|
7562
8322
|
if (junit) attestations.push(junit);
|
|
7563
8323
|
const globbed = await collectArtifacts(opts.artifacts, ci.workspace, { glob: glob2 });
|
|
7564
8324
|
[...scanned.warnings, ...globbed.warnings].forEach((w) => log.warn(w));
|
|
@@ -7568,7 +8328,7 @@ async function run(deps = {}) {
|
|
|
7568
8328
|
fetch: deps.fetch,
|
|
7569
8329
|
sleep: deps.sleep,
|
|
7570
8330
|
fixedToken: true,
|
|
7571
|
-
userAgent: `prodgator-report/${"3.
|
|
8331
|
+
userAgent: `prodgator-report/${"3.25.0"} (${ci.provider})`
|
|
7572
8332
|
};
|
|
7573
8333
|
const request = buildIngestRequest({ job: { name: opts.jobName ?? ci.jobName, matrix: opts.matrix, runner: ci.runner }, name: opts.name, summary, attestations, files });
|
|
7574
8334
|
const res = await sendReport(opts.prodgatorUrl, ci.provider, request, opts.audience, client, ci.azure);
|
|
@@ -7606,6 +8366,8 @@ async function run(deps = {}) {
|
|
|
7606
8366
|
log.warn(`Prodgator report was not sent: ${msg2}`);
|
|
7607
8367
|
deps.onOutcome?.("not_sent");
|
|
7608
8368
|
return 0;
|
|
8369
|
+
} finally {
|
|
8370
|
+
await cleanup?.();
|
|
7609
8371
|
}
|
|
7610
8372
|
}
|
|
7611
8373
|
|