sentinello 3.1.1 → 3.3.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.
Files changed (2) hide show
  1. package/dist/cli.cjs +441 -332
  2. package/package.json +1 -2
package/dist/cli.cjs CHANGED
@@ -526,11 +526,11 @@ var require_parse = __commonJS({
526
526
  var require_valid = __commonJS({
527
527
  "../../node_modules/.pnpm/semver@7.7.4/node_modules/semver/functions/valid.js"(exports2, module2) {
528
528
  var parse3 = require_parse();
529
- var valid4 = (version2, options) => {
529
+ var valid3 = (version2, options) => {
530
530
  const v = parse3(version2, options);
531
531
  return v ? v.version : null;
532
532
  };
533
- module2.exports = valid4;
533
+ module2.exports = valid3;
534
534
  }
535
535
  });
536
536
 
@@ -772,7 +772,7 @@ var require_cmp = __commonJS({
772
772
  var gte2 = require_gte();
773
773
  var lt = require_lt();
774
774
  var lte = require_lte();
775
- var cmp = (a, op, b, loose) => {
775
+ var cmp2 = (a, op, b, loose) => {
776
776
  switch (op) {
777
777
  case "===":
778
778
  if (typeof a === "object") {
@@ -808,7 +808,7 @@ var require_cmp = __commonJS({
808
808
  throw new TypeError(`Invalid operator: ${op}`);
809
809
  }
810
810
  };
811
- module2.exports = cmp;
811
+ module2.exports = cmp2;
812
812
  }
813
813
  });
814
814
 
@@ -818,7 +818,7 @@ var require_coerce = __commonJS({
818
818
  var SemVer = require_semver();
819
819
  var parse3 = require_parse();
820
820
  var { safeRe: re, t } = require_re();
821
- var coerce4 = (version2, options) => {
821
+ var coerce3 = (version2, options) => {
822
822
  if (version2 instanceof SemVer) {
823
823
  return version2;
824
824
  }
@@ -853,7 +853,7 @@ var require_coerce = __commonJS({
853
853
  const build = options.includePrerelease && match[6] ? `+${match[6]}` : "";
854
854
  return parse3(`${major}.${minor}.${patch}${prerelease}${build}`, options);
855
855
  };
856
- module2.exports = coerce4;
856
+ module2.exports = coerce3;
857
857
  }
858
858
  });
859
859
 
@@ -1330,7 +1330,7 @@ var require_comparator = __commonJS({
1330
1330
  return false;
1331
1331
  }
1332
1332
  }
1333
- return cmp(version2, this.operator, this.semver, this.options);
1333
+ return cmp2(version2, this.operator, this.semver, this.options);
1334
1334
  }
1335
1335
  intersects(comp, options) {
1336
1336
  if (!(comp instanceof _Comparator)) {
@@ -1363,10 +1363,10 @@ var require_comparator = __commonJS({
1363
1363
  if (this.semver.version === comp.semver.version && this.operator.includes("=") && comp.operator.includes("=")) {
1364
1364
  return true;
1365
1365
  }
1366
- if (cmp(this.semver, "<", comp.semver, options) && this.operator.startsWith(">") && comp.operator.startsWith("<")) {
1366
+ if (cmp2(this.semver, "<", comp.semver, options) && this.operator.startsWith(">") && comp.operator.startsWith("<")) {
1367
1367
  return true;
1368
1368
  }
1369
- if (cmp(this.semver, ">", comp.semver, options) && this.operator.startsWith("<") && comp.operator.startsWith(">")) {
1369
+ if (cmp2(this.semver, ">", comp.semver, options) && this.operator.startsWith("<") && comp.operator.startsWith(">")) {
1370
1370
  return true;
1371
1371
  }
1372
1372
  return false;
@@ -1375,7 +1375,7 @@ var require_comparator = __commonJS({
1375
1375
  module2.exports = Comparator;
1376
1376
  var parseOptions = require_parse_options();
1377
1377
  var { safeRe: re, t } = require_re();
1378
- var cmp = require_cmp();
1378
+ var cmp2 = require_cmp();
1379
1379
  var debug = require_debug();
1380
1380
  var SemVer = require_semver();
1381
1381
  var Range2 = require_range();
@@ -1853,7 +1853,7 @@ var require_semver2 = __commonJS({
1853
1853
  var SemVer = require_semver();
1854
1854
  var identifiers = require_identifiers();
1855
1855
  var parse3 = require_parse();
1856
- var valid4 = require_valid();
1856
+ var valid3 = require_valid();
1857
1857
  var clean = require_clean();
1858
1858
  var inc = require_inc();
1859
1859
  var diff = require_diff();
@@ -1873,8 +1873,8 @@ var require_semver2 = __commonJS({
1873
1873
  var neq = require_neq();
1874
1874
  var gte2 = require_gte();
1875
1875
  var lte = require_lte();
1876
- var cmp = require_cmp();
1877
- var coerce4 = require_coerce();
1876
+ var cmp2 = require_cmp();
1877
+ var coerce3 = require_coerce();
1878
1878
  var Comparator = require_comparator();
1879
1879
  var Range2 = require_range();
1880
1880
  var satisfies3 = require_satisfies();
@@ -1891,7 +1891,7 @@ var require_semver2 = __commonJS({
1891
1891
  var subset = require_subset();
1892
1892
  module2.exports = {
1893
1893
  parse: parse3,
1894
- valid: valid4,
1894
+ valid: valid3,
1895
1895
  clean,
1896
1896
  inc,
1897
1897
  diff,
@@ -1911,8 +1911,8 @@ var require_semver2 = __commonJS({
1911
1911
  neq,
1912
1912
  gte: gte2,
1913
1913
  lte,
1914
- cmp,
1915
- coerce: coerce4,
1914
+ cmp: cmp2,
1915
+ coerce: coerce3,
1916
1916
  Comparator,
1917
1917
  Range: Range2,
1918
1918
  satisfies: satisfies3,
@@ -3411,7 +3411,7 @@ var require_stream_writable = __commonJS({
3411
3411
  pna.nextTick(cb, er);
3412
3412
  }
3413
3413
  function validChunk(stream, state, chunk, cb) {
3414
- var valid4 = true;
3414
+ var valid3 = true;
3415
3415
  var er = false;
3416
3416
  if (chunk === null) {
3417
3417
  er = new TypeError("May not write null values to stream");
@@ -3421,9 +3421,9 @@ var require_stream_writable = __commonJS({
3421
3421
  if (er) {
3422
3422
  stream.emit("error", er);
3423
3423
  pna.nextTick(cb, er);
3424
- valid4 = false;
3424
+ valid3 = false;
3425
3425
  }
3426
- return valid4;
3426
+ return valid3;
3427
3427
  }
3428
3428
  Writable.prototype.write = function(chunk, encoding, cb) {
3429
3429
  var state = this._writableState;
@@ -13123,6 +13123,25 @@ function compareSeverity(a, b) {
13123
13123
  function meetsSeverityFloor(severity, floor) {
13124
13124
  return severityWeight(severity) >= severityWeight(floor);
13125
13125
  }
13126
+ function maxSeverity(severities) {
13127
+ let best = "info";
13128
+ let bestWeight = 0;
13129
+ for (const s of severities) {
13130
+ const normalized = s.trim().toLowerCase();
13131
+ const known = SEVERITY_WEIGHT[normalized];
13132
+ if (known !== void 0 && known > bestWeight) {
13133
+ bestWeight = known;
13134
+ best = normalized;
13135
+ }
13136
+ }
13137
+ return best;
13138
+ }
13139
+ function escalatedSeverity(own, corroborations) {
13140
+ if (corroborations.length === 0) return own;
13141
+ return maxSeverity([own, ...corroborations.map(function grade(c) {
13142
+ return c.severity;
13143
+ })]);
13144
+ }
13126
13145
  var SOURCE_UNAVAILABLE_REASON_CODES = [
13127
13146
  "osv_db_not_seeded",
13128
13147
  "osv_db_unavailable",
@@ -13135,7 +13154,7 @@ function isSourceUnavailableReason(code) {
13135
13154
 
13136
13155
  // ../../packages/core/src/advisory-rows.ts
13137
13156
  var OSV_NORMALIZER_VERSION = 4;
13138
- var GEMNASIUM_NORMALIZER_VERSION = 3;
13157
+ var GEMNASIUM_NORMALIZER_VERSION = 5;
13139
13158
 
13140
13159
  // ../../packages/core/src/advisory-export.ts
13141
13160
  var DEFAULT_EXPORT_PROMPT = `You are helping a development team triage and fix the vulnerabilities listed at the bottom of this document. Treat this as a remediation work list, not a checklist to rubber-stamp. Work in a planning posture from the start: if your tooling has a read-only planning mode (Claude Code's plan mode, for example), enter it now and stay in it until the human has approved the triage below. Nothing in this document authorises you to edit a file before then.
@@ -13352,7 +13371,10 @@ var ECOSYSTEMS = [
13352
13371
  {
13353
13372
  id: "npm",
13354
13373
  language: "javascript",
13355
- displayName: "JavaScript",
13374
+ // The registry ecosystem is npm and the toolchain is Node — 'JavaScript' overclaimed, naming a
13375
+ // language rather than the package ecosystem actually scanned.
13376
+ displayName: "Node.js",
13377
+ status: "stable",
13356
13378
  osvEcosystem: "npm",
13357
13379
  gemnasiumPackageType: "npm",
13358
13380
  resolverKinds: ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"],
@@ -13362,6 +13384,7 @@ var ECOSYSTEMS = [
13362
13384
  id: "PyPI",
13363
13385
  language: "python",
13364
13386
  displayName: "Python",
13387
+ status: "preview",
13365
13388
  osvEcosystem: "PyPI",
13366
13389
  gemnasiumPackageType: "pypi",
13367
13390
  resolverKinds: ["poetry.lock", "Pipfile.lock", "requirements.txt", "uv.lock"],
@@ -13371,6 +13394,7 @@ var ECOSYSTEMS = [
13371
13394
  id: "Go",
13372
13395
  language: "go",
13373
13396
  displayName: "Go",
13397
+ status: "preview",
13374
13398
  osvEcosystem: "Go",
13375
13399
  gemnasiumPackageType: "go",
13376
13400
  resolverKinds: ["go.mod", "go.sum"],
@@ -13380,6 +13404,7 @@ var ECOSYSTEMS = [
13380
13404
  id: "crates.io",
13381
13405
  language: "rust",
13382
13406
  displayName: "Rust",
13407
+ status: "preview",
13383
13408
  osvEcosystem: "crates.io",
13384
13409
  gemnasiumPackageType: "cargo",
13385
13410
  resolverKinds: ["Cargo.lock"],
@@ -13387,6 +13412,9 @@ var ECOSYSTEMS = [
13387
13412
  }
13388
13413
  ];
13389
13414
  var DEFAULT_ECOSYSTEM = "npm";
13415
+ var STABLE_ECOSYSTEMS = ECOSYSTEMS.filter(function stable(eco) {
13416
+ return eco.status === "stable";
13417
+ });
13390
13418
  function getEcosystem(id) {
13391
13419
  for (const eco of ECOSYSTEMS) {
13392
13420
  if (eco.id === id) return eco;
@@ -13396,7 +13424,241 @@ function getEcosystem(id) {
13396
13424
  var GEMNASIUM_SEED_DOWNLOAD_BYTES = 52 * 1024 * 1024;
13397
13425
 
13398
13426
  // ../../packages/scanners/src/lockfile-cross-check.ts
13427
+ var import_semver5 = __toESM(require_semver2());
13428
+
13429
+ // ../../packages/versions/src/range.ts
13430
+ function isZeroVersion(version2) {
13431
+ return /^0(\.0)*$/.test(version2.trim());
13432
+ }
13433
+ function formatRange(range, options) {
13434
+ const lo = range.introduced === "0" ? options.zero : range.introduced;
13435
+ const lower = (range.introducedExclusive === true ? ">" : ">=") + lo;
13436
+ if (range.fixed !== null && range.fixed !== void 0) return lower + " <" + range.fixed;
13437
+ const lastAffected = range.lastAffected;
13438
+ if (lastAffected !== null && lastAffected !== void 0) return lower + " <=" + lastAffected;
13439
+ return lower;
13440
+ }
13441
+ function formatRanges(ranges, options) {
13442
+ return ranges.map(function one(range) {
13443
+ return formatRange(range, options);
13444
+ }).join(" || ");
13445
+ }
13446
+
13447
+ // ../../packages/versions/src/match.ts
13448
+ function versionInRange(version2, range, comparator) {
13449
+ const installed = comparator.normalize(version2);
13450
+ if (installed === null) return false;
13451
+ if (!aboveLowerBound(installed, range, comparator)) return false;
13452
+ if (range.fixed !== null && range.fixed !== void 0) {
13453
+ const fixed = comparator.normalize(range.fixed);
13454
+ return fixed !== null && comparator.lt(installed, fixed);
13455
+ }
13456
+ const lastAffectedRaw = range.lastAffected;
13457
+ if (lastAffectedRaw !== null && lastAffectedRaw !== void 0) {
13458
+ const lastAffected = comparator.normalize(lastAffectedRaw);
13459
+ return lastAffected !== null && comparator.lte(installed, lastAffected);
13460
+ }
13461
+ return true;
13462
+ }
13463
+ function aboveLowerBound(installed, range, comparator) {
13464
+ if (range.introducedExclusive !== true && isZeroVersion(range.introduced)) return true;
13465
+ const introduced = comparator.normalize(range.introduced);
13466
+ if (introduced === null) return false;
13467
+ return range.introducedExclusive === true ? comparator.gt(installed, introduced) : comparator.gte(installed, introduced);
13468
+ }
13469
+
13470
+ // ../../packages/versions/src/compare.ts
13471
+ __toESM(require_semver2());
13472
+
13473
+ // ../../packages/versions/src/comparators/semver.ts
13399
13474
  var import_semver = __toESM(require_semver2());
13475
+ var MEANING_CHANGING_RANGE_SYNTAX = /[<>^~|*]|\s-\s|(^|\.)[xX](\.|$)/;
13476
+ function normalizeSemver(raw) {
13477
+ const strict = (0, import_semver.valid)(raw);
13478
+ if (strict !== null) return strict;
13479
+ if (MEANING_CHANGING_RANGE_SYNTAX.test(raw)) return null;
13480
+ const coerced = (0, import_semver.coerce)(raw);
13481
+ return coerced === null ? null : coerced.version;
13482
+ }
13483
+ var semverComparator = {
13484
+ normalize(raw) {
13485
+ return normalizeSemver(raw);
13486
+ },
13487
+ gt(a, b) {
13488
+ return (0, import_semver.gt)(a, b);
13489
+ },
13490
+ gte(a, b) {
13491
+ return (0, import_semver.gte)(a, b);
13492
+ },
13493
+ lt(a, b) {
13494
+ return (0, import_semver.lt)(a, b);
13495
+ },
13496
+ lte(a, b) {
13497
+ return (0, import_semver.lte)(a, b);
13498
+ }
13499
+ };
13500
+
13501
+ // ../../packages/versions/src/comparators/pep440.ts
13502
+ var PRE_RANK = { a: 0, b: 1, rc: 2 };
13503
+ var PEP440_RE = new RegExp(
13504
+ "^\\s*v?(?:(?<epoch>[0-9]+)!)?(?<release>[0-9]+(?:\\.[0-9]+)*)(?:[-_.]?(?<preL>a|b|c|rc|alpha|beta|pre|preview)[-_.]?(?<preN>[0-9]+)?)?(?:(?:-(?<postN1>[0-9]+))|(?:[-_.]?(?<postL>post|rev|r)[-_.]?(?<postN2>[0-9]+)?))?(?:[-_.]?(?<devL>dev)[-_.]?(?<devN>[0-9]+)?)?(?:\\+(?<local>[a-z0-9]+(?:[-_.][a-z0-9]+)*))?\\s*$",
13505
+ "i"
13506
+ );
13507
+ function foldPreLetter(raw) {
13508
+ const l = raw.toLowerCase();
13509
+ if (l === "alpha") return "a";
13510
+ if (l === "beta") return "b";
13511
+ if (l === "c" || l === "pre" || l === "preview" || l === "rc") return "rc";
13512
+ return l;
13513
+ }
13514
+ function parsePep440(raw) {
13515
+ const m = PEP440_RE.exec(raw.trim());
13516
+ if (!m || !m.groups) return null;
13517
+ const g = m.groups;
13518
+ if (!g.release) return null;
13519
+ const release = g.release.split(".").map(function toInt(s) {
13520
+ return parseInt(s, 10);
13521
+ });
13522
+ if (release.some(function bad(n) {
13523
+ return Number.isNaN(n);
13524
+ })) return null;
13525
+ let pre = null;
13526
+ if (g.preL) {
13527
+ pre = { letter: foldPreLetter(g.preL), n: g.preN ? parseInt(g.preN, 10) : 0 };
13528
+ }
13529
+ let post = null;
13530
+ if (g.postN1 !== void 0) {
13531
+ post = parseInt(g.postN1, 10);
13532
+ } else if (g.postL) {
13533
+ post = g.postN2 ? parseInt(g.postN2, 10) : 0;
13534
+ }
13535
+ const dev = g.devL ? g.devN ? parseInt(g.devN, 10) : 0 : null;
13536
+ let local = null;
13537
+ if (g.local) {
13538
+ local = g.local.toLowerCase().split(/[-_.]/).map(function part(seg) {
13539
+ return /^[0-9]+$/.test(seg) ? parseInt(seg, 10) : seg;
13540
+ });
13541
+ }
13542
+ return { epoch: g.epoch ? parseInt(g.epoch, 10) : 0, release, pre, post, dev, local };
13543
+ }
13544
+ function stripTrailingZeros(release) {
13545
+ let end = release.length;
13546
+ while (end > 1 && release[end - 1] === 0) end--;
13547
+ return release.slice(0, end);
13548
+ }
13549
+ function canonicalPep440(v) {
13550
+ const parts = [];
13551
+ if (v.epoch > 0) parts.push(v.epoch.toString() + "!");
13552
+ parts.push(stripTrailingZeros(v.release).join("."));
13553
+ if (v.pre) parts.push(v.pre.letter + v.pre.n.toString());
13554
+ if (v.post !== null) parts.push(".post" + v.post.toString());
13555
+ if (v.dev !== null) parts.push(".dev" + v.dev.toString());
13556
+ if (v.local) parts.push("+" + v.local.join("."));
13557
+ return parts.join("");
13558
+ }
13559
+ function cmpRelease(a, b) {
13560
+ const ra = stripTrailingZeros(a);
13561
+ const rb = stripTrailingZeros(b);
13562
+ const len = Math.max(ra.length, rb.length);
13563
+ for (let i = 0; i < len; i++) {
13564
+ const x = ra[i] ?? 0;
13565
+ const y = rb[i] ?? 0;
13566
+ if (x !== y) return x < y ? -1 : 1;
13567
+ }
13568
+ return 0;
13569
+ }
13570
+ function preKind(v) {
13571
+ if (v.pre !== null) return 0;
13572
+ if (v.post === null && v.dev !== null) return -1;
13573
+ return 1;
13574
+ }
13575
+ function cmpPre(a, b) {
13576
+ const ka = preKind(a);
13577
+ const kb = preKind(b);
13578
+ if (ka !== kb) return ka < kb ? -1 : 1;
13579
+ if (ka !== 0 || !a.pre || !b.pre) return 0;
13580
+ const la = PRE_RANK[a.pre.letter] ?? 0;
13581
+ const lb = PRE_RANK[b.pre.letter] ?? 0;
13582
+ if (la !== lb) return la < lb ? -1 : 1;
13583
+ if (a.pre.n !== b.pre.n) return a.pre.n < b.pre.n ? -1 : 1;
13584
+ return 0;
13585
+ }
13586
+ function cmpPost(a, b) {
13587
+ if (a === null && b === null) return 0;
13588
+ if (a === null) return -1;
13589
+ if (b === null) return 1;
13590
+ return a < b ? -1 : a > b ? 1 : 0;
13591
+ }
13592
+ function cmpDev(a, b) {
13593
+ if (a === null && b === null) return 0;
13594
+ if (a === null) return 1;
13595
+ if (b === null) return -1;
13596
+ return a < b ? -1 : a > b ? 1 : 0;
13597
+ }
13598
+ function cmpLocal(a, b) {
13599
+ if (a === null && b === null) return 0;
13600
+ if (a === null) return -1;
13601
+ if (b === null) return 1;
13602
+ const len = Math.max(a.length, b.length);
13603
+ for (let i = 0; i < len; i++) {
13604
+ if (i >= a.length) return -1;
13605
+ if (i >= b.length) return 1;
13606
+ const x = a[i];
13607
+ const y = b[i];
13608
+ const xNum = typeof x === "number";
13609
+ const yNum = typeof y === "number";
13610
+ if (xNum && yNum) {
13611
+ if (x !== y) return x < y ? -1 : 1;
13612
+ } else if (xNum !== yNum) {
13613
+ return xNum ? 1 : -1;
13614
+ } else if (x !== y) {
13615
+ return x < y ? -1 : 1;
13616
+ }
13617
+ }
13618
+ return 0;
13619
+ }
13620
+ function comparePep440(a, b) {
13621
+ if (a.epoch !== b.epoch) return a.epoch < b.epoch ? -1 : 1;
13622
+ const r = cmpRelease(a.release, b.release);
13623
+ if (r !== 0) return r;
13624
+ const p = cmpPre(a, b);
13625
+ if (p !== 0) return p;
13626
+ const po = cmpPost(a.post, b.post);
13627
+ if (po !== 0) return po;
13628
+ const d = cmpDev(a.dev, b.dev);
13629
+ if (d !== 0) return d;
13630
+ return cmpLocal(a.local, b.local);
13631
+ }
13632
+ function cmp(a, b) {
13633
+ const pa = parsePep440(a);
13634
+ const pb = parsePep440(b);
13635
+ if (!pa || !pb) return null;
13636
+ return comparePep440(pa, pb);
13637
+ }
13638
+ var pep440Comparator = {
13639
+ normalize(raw) {
13640
+ const parsed = parsePep440(raw);
13641
+ return parsed === null ? null : canonicalPep440(parsed);
13642
+ },
13643
+ gt(a, b) {
13644
+ const r = cmp(a, b);
13645
+ return r !== null && r > 0;
13646
+ },
13647
+ gte(a, b) {
13648
+ const r = cmp(a, b);
13649
+ return r !== null && r >= 0;
13650
+ },
13651
+ lt(a, b) {
13652
+ const r = cmp(a, b);
13653
+ return r !== null && r < 0;
13654
+ },
13655
+ lte(a, b) {
13656
+ const r = cmp(a, b);
13657
+ return r !== null && r <= 0;
13658
+ }
13659
+ };
13660
+
13661
+ // ../../packages/scanners/src/lockfile-cross-check.ts
13400
13662
  function filterFindingsByLockfileResolution(findings) {
13401
13663
  const kept = [];
13402
13664
  const droppedAdvisoryIds = [];
@@ -13413,7 +13675,7 @@ function shouldKeep(finding) {
13413
13675
  const installed = finding.installedVersion.trim();
13414
13676
  const range = finding.vulnerableRange.trim();
13415
13677
  if (!installed || !range) return true;
13416
- if ((0, import_semver.validRange)(range) === null) return true;
13678
+ if ((0, import_semver5.validRange)(range) === null) return true;
13417
13679
  const candidates = installed.split(/,\s*/).map(function trim(s) {
13418
13680
  return s.trim();
13419
13681
  }).filter(Boolean);
@@ -13421,19 +13683,11 @@ function shouldKeep(finding) {
13421
13683
  for (const candidate of candidates) {
13422
13684
  const cleaned = normalizeVersion(candidate);
13423
13685
  if (cleaned === null) return true;
13424
- if ((0, import_semver.satisfies)(cleaned, range, { includePrerelease: true })) return true;
13686
+ if ((0, import_semver5.satisfies)(cleaned, range, { includePrerelease: true })) return true;
13425
13687
  }
13426
13688
  return false;
13427
13689
  }
13428
- var RANGE_SYNTAX = /[<>=^~|*]|\s-\s|(^|\.)[xX](\.|$)/;
13429
- function normalizeVersion(raw) {
13430
- const strict = (0, import_semver.valid)(raw);
13431
- if (strict !== null) return strict;
13432
- if (RANGE_SYNTAX.test(raw)) return null;
13433
- const coerced = (0, import_semver.coerce)(raw);
13434
- if (coerced === null) return null;
13435
- return coerced.version;
13436
- }
13690
+ var normalizeVersion = normalizeSemver;
13437
13691
 
13438
13692
  // ../../node_modules/.pnpm/zod@4.4.1/node_modules/zod/v4/classic/external.js
13439
13693
  var external_exports = {};
@@ -27918,7 +28172,7 @@ function date4(params) {
27918
28172
  config(en_default());
27919
28173
 
27920
28174
  // ../../packages/scanners/src/version-fix.ts
27921
- var import_semver2 = __toESM(require_semver2());
28175
+ var import_semver6 = __toESM(require_semver2());
27922
28176
  var VERSION_LITERAL_RE = /\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?/g;
27923
28177
  function parseRangeSafely(input) {
27924
28178
  if (!input) return null;
@@ -27926,7 +28180,7 @@ function parseRangeSafely(input) {
27926
28180
  if (!trimmed) return null;
27927
28181
  if (trimmed === "<0.0.0") return null;
27928
28182
  try {
27929
- return new import_semver2.Range(trimmed, { includePrerelease: false });
28183
+ return new import_semver6.Range(trimmed, { includePrerelease: false });
27930
28184
  } catch {
27931
28185
  return null;
27932
28186
  }
@@ -27937,12 +28191,12 @@ function extractLiteralCandidates(input) {
27937
28191
  if (!matches) return [];
27938
28192
  const out = [];
27939
28193
  for (const m of matches) {
27940
- if ((0, import_semver2.valid)(m)) out.push(m);
28194
+ if ((0, import_semver6.valid)(m)) out.push(m);
27941
28195
  }
27942
28196
  return out;
27943
28197
  }
27944
28198
  function bumpPatch(v) {
27945
- const sv = (0, import_semver2.coerce)(v);
28199
+ const sv = (0, import_semver6.coerce)(v);
27946
28200
  if (!sv) return null;
27947
28201
  return sv.major + "." + sv.minor + "." + (sv.patch + 1);
27948
28202
  }
@@ -27953,13 +28207,13 @@ function extractRangeLowerBounds(range) {
27953
28207
  for (const c of conjuncts) {
27954
28208
  if (!c.semver || !c.semver.version) continue;
27955
28209
  const v = c.semver.version;
27956
- if (!(0, import_semver2.valid)(v)) continue;
28210
+ if (!(0, import_semver6.valid)(v)) continue;
27957
28211
  const op = c.operator;
27958
28212
  if (op === ">=" || op === "=" || op === "") {
27959
- if (!candidate || (0, import_semver2.gt)(v, candidate)) candidate = v;
28213
+ if (!candidate || (0, import_semver6.gt)(v, candidate)) candidate = v;
27960
28214
  } else if (op === ">") {
27961
28215
  const inc = bumpPatch(v);
27962
- if (inc && (!candidate || (0, import_semver2.gt)(inc, candidate))) candidate = inc;
28216
+ if (inc && (!candidate || (0, import_semver6.gt)(inc, candidate))) candidate = inc;
27963
28217
  }
27964
28218
  }
27965
28219
  if (candidate) out.push(candidate);
@@ -27972,7 +28226,7 @@ function extractRangeUpperBoundsBeyond(range) {
27972
28226
  for (const c of conjuncts) {
27973
28227
  if (!c.semver || !c.semver.version) continue;
27974
28228
  const v = c.semver.version;
27975
- if (!(0, import_semver2.valid)(v)) continue;
28229
+ if (!(0, import_semver6.valid)(v)) continue;
27976
28230
  const op = c.operator;
27977
28231
  if (op === "<") {
27978
28232
  out.push(v);
@@ -27991,8 +28245,8 @@ function pickHighestInstalled(installed) {
27991
28245
  for (const raw of parts) {
27992
28246
  const part = raw.trim();
27993
28247
  if (!part) continue;
27994
- if (!(0, import_semver2.valid)(part)) continue;
27995
- if (!highest || (0, import_semver2.gt)(part, highest)) highest = part;
28248
+ if (!(0, import_semver6.valid)(part)) continue;
28249
+ if (!highest || (0, import_semver6.gt)(part, highest)) highest = part;
27996
28250
  }
27997
28251
  return highest;
27998
28252
  }
@@ -28014,17 +28268,17 @@ function pickSafeFixVersion(args) {
28014
28268
  if (candidates.size === 0) return null;
28015
28269
  let best = null;
28016
28270
  for (const v of candidates) {
28017
- if (patchedRange && !(0, import_semver2.satisfies)(v, patchedRange)) continue;
28018
- if (vulnRange && (0, import_semver2.satisfies)(v, vulnRange)) continue;
28019
- if (installedFloor && !(0, import_semver2.gte)(v, installedFloor)) continue;
28020
- if (!best || (0, import_semver2.gt)(best, v)) best = v;
28271
+ if (patchedRange && !(0, import_semver6.satisfies)(v, patchedRange)) continue;
28272
+ if (vulnRange && (0, import_semver6.satisfies)(v, vulnRange)) continue;
28273
+ if (installedFloor && !(0, import_semver6.gte)(v, installedFloor)) continue;
28274
+ if (!best || (0, import_semver6.gt)(best, v)) best = v;
28021
28275
  }
28022
28276
  return best;
28023
28277
  }
28024
28278
 
28025
28279
  // ../../packages/scanners/src/npm-audit-parse.ts
28026
28280
  var SEVERITY_VALUES = ["critical", "high", "moderate", "low", "info"];
28027
- var severitySchema = external_exports.enum(SEVERITY_VALUES);
28281
+ var severitySchema = external_exports.enum(SEVERITY_VALUES).catch("moderate");
28028
28282
  var viaObjectSchema = external_exports.object({
28029
28283
  source: external_exports.number().int().optional(),
28030
28284
  name: external_exports.string().optional(),
@@ -28201,10 +28455,17 @@ function pickAdvisoryId(via) {
28201
28455
  if (via.source == null && !via.url && !via.title) return null;
28202
28456
  return fallbackAdvisoryHash(via);
28203
28457
  }
28458
+ function pickAdvisoryAliases(advisoryId, url2, githubAdvisoryId) {
28459
+ const explicit = typeof githubAdvisoryId === "string" && githubAdvisoryId.length > 0 && githubAdvisoryId;
28460
+ const ghsa = explicit || pickGhsaIdFromUrl(url2);
28461
+ if (!ghsa) return [];
28462
+ if (ghsa.toLowerCase() === advisoryId.toLowerCase()) return [];
28463
+ return [ghsa];
28464
+ }
28204
28465
  function pickSeverity(via, vuln) {
28205
28466
  if (via.severity) return via.severity;
28206
28467
  if (vuln.severity) return vuln.severity;
28207
- return "info";
28468
+ return "moderate";
28208
28469
  }
28209
28470
  function pickFixAvailability(fix) {
28210
28471
  if (fix === void 0) return { fixAvailable: false, fixVersion: null };
@@ -28262,6 +28523,7 @@ function normalizeOneVulnerability(vuln, packageName, installedVersions, classif
28262
28523
  const cls = classifier.classify(packageName, installedVersion);
28263
28524
  const finding = {
28264
28525
  advisoryId,
28526
+ aliases: pickAdvisoryAliases(advisoryId, via.url),
28265
28527
  advisoryTitle: via.title || null,
28266
28528
  advisoryUrl: via.url || null,
28267
28529
  packageName,
@@ -28318,12 +28580,13 @@ function normalizePnpmAuditOutput(parsed, classifier) {
28318
28580
  const adv = advisories[idKey];
28319
28581
  if (!adv) continue;
28320
28582
  const advisoryId = pickPnpmAdvisoryId(adv, idKey);
28321
- const severity = adv.severity || "info";
28583
+ const severity = adv.severity || "moderate";
28322
28584
  const patched = adv.patched_versions || null;
28323
28585
  const recommendation = adv.recommendation || null;
28324
28586
  const vulnRange = adv.vulnerable_versions || "";
28325
28587
  const advisoryTitle = adv.title || null;
28326
28588
  const advisoryUrl = adv.url || null;
28589
+ const aliases = pickAdvisoryAliases(advisoryId, adv.url ?? void 0, adv.github_advisory_id);
28327
28590
  const packageName = adv.module_name;
28328
28591
  const findings = adv.findings || [];
28329
28592
  if (findings.length === 0) {
@@ -28331,6 +28594,7 @@ function normalizePnpmAuditOutput(parsed, classifier) {
28331
28594
  const cls = classifier.classify(packageName, null);
28332
28595
  out.push({
28333
28596
  advisoryId,
28597
+ aliases,
28334
28598
  advisoryTitle,
28335
28599
  advisoryUrl,
28336
28600
  packageName,
@@ -28354,6 +28618,7 @@ function normalizePnpmAuditOutput(parsed, classifier) {
28354
28618
  const cls = classifier.classify(packageName, f.version || null);
28355
28619
  out.push({
28356
28620
  advisoryId,
28621
+ aliases,
28357
28622
  advisoryTitle,
28358
28623
  advisoryUrl,
28359
28624
  packageName,
@@ -28373,6 +28638,7 @@ function normalizePnpmAuditOutput(parsed, classifier) {
28373
28638
  const cls = classifier.classify(packageName, f.version || null);
28374
28639
  out.push({
28375
28640
  advisoryId,
28641
+ aliases,
28376
28642
  advisoryTitle,
28377
28643
  advisoryUrl,
28378
28644
  packageName,
@@ -28956,15 +29222,11 @@ async function parseNpmLock(projectPath, absolutePath) {
28956
29222
  if (!entry || !entry.version) continue;
28957
29223
  const name = entry.name || nameFromNodePath(nodePath);
28958
29224
  if (!name) continue;
28959
- let isDev = entry.dev === true || entry.devOptional === true;
28960
- let isProd = !isDev;
28961
- if (direct.prod.has(name)) {
28962
- isProd = true;
28963
- }
28964
- if (direct.dev.has(name) && !direct.prod.has(name)) {
28965
- isDev = true;
28966
- isProd = false;
28967
- }
29225
+ const lockSaysDevOnly = entry.dev === true || entry.devOptional === true;
29226
+ let isProd = !lockSaysDevOnly;
29227
+ let isDev = lockSaysDevOnly;
29228
+ if (direct.prod.has(name)) isProd = true;
29229
+ if (direct.dev.has(name)) isDev = true;
28968
29230
  out.push({
28969
29231
  ecosystem: NPM_ECOSYSTEM,
28970
29232
  name,
@@ -32422,7 +32684,7 @@ function stripComment3(line) {
32422
32684
  // ../../packages/scanners/src/resolver/index.ts
32423
32685
  async function detectManifests(projectPath) {
32424
32686
  const out = [];
32425
- for (const eco of ECOSYSTEMS) {
32687
+ for (const eco of STABLE_ECOSYSTEMS) {
32426
32688
  for (const kind of eco.resolverKinds) {
32427
32689
  const absolutePath = path.join(projectPath, kind);
32428
32690
  if (await fileExists2(absolutePath)) {
@@ -32600,7 +32862,7 @@ function detectProject(rootPath, dir) {
32600
32862
  }
32601
32863
  function detectEcosystems(dir) {
32602
32864
  const out = [];
32603
- for (const eco of ECOSYSTEMS) {
32865
+ for (const eco of STABLE_ECOSYSTEMS) {
32604
32866
  if (eco.id === "npm") {
32605
32867
  if (existsFile(path.join(dir, "package.json"))) out.push(eco.id);
32606
32868
  continue;
@@ -32686,162 +32948,6 @@ function isFile(path) {
32686
32948
  }
32687
32949
  }
32688
32950
 
32689
- // ../../packages/scanners/src/engine/comparators/pep440.ts
32690
- var PRE_RANK = { a: 0, b: 1, rc: 2 };
32691
- var PEP440_RE = new RegExp(
32692
- "^\\s*v?(?:(?<epoch>[0-9]+)!)?(?<release>[0-9]+(?:\\.[0-9]+)*)(?:[-_.]?(?<preL>a|b|c|rc|alpha|beta|pre|preview)[-_.]?(?<preN>[0-9]+)?)?(?:(?:-(?<postN1>[0-9]+))|(?:[-_.]?(?<postL>post|rev|r)[-_.]?(?<postN2>[0-9]+)?))?(?:[-_.]?(?<devL>dev)[-_.]?(?<devN>[0-9]+)?)?(?:\\+(?<local>[a-z0-9]+(?:[-_.][a-z0-9]+)*))?\\s*$",
32693
- "i"
32694
- );
32695
- function foldPreLetter(raw) {
32696
- const l = raw.toLowerCase();
32697
- if (l === "alpha") return "a";
32698
- if (l === "beta") return "b";
32699
- if (l === "c" || l === "pre" || l === "preview" || l === "rc") return "rc";
32700
- return l;
32701
- }
32702
- function parsePep440(raw) {
32703
- const m = PEP440_RE.exec(raw.trim());
32704
- if (!m || !m.groups) return null;
32705
- const g = m.groups;
32706
- if (!g.release) return null;
32707
- const release = g.release.split(".").map(function toInt(s) {
32708
- return parseInt(s, 10);
32709
- });
32710
- if (release.some(function bad(n) {
32711
- return Number.isNaN(n);
32712
- })) return null;
32713
- let pre = null;
32714
- if (g.preL) {
32715
- pre = { letter: foldPreLetter(g.preL), n: g.preN ? parseInt(g.preN, 10) : 0 };
32716
- }
32717
- let post = null;
32718
- if (g.postN1 !== void 0) {
32719
- post = parseInt(g.postN1, 10);
32720
- } else if (g.postL) {
32721
- post = g.postN2 ? parseInt(g.postN2, 10) : 0;
32722
- }
32723
- const dev = g.devL ? g.devN ? parseInt(g.devN, 10) : 0 : null;
32724
- let local = null;
32725
- if (g.local) {
32726
- local = g.local.toLowerCase().split(/[-_.]/).map(function part(seg) {
32727
- return /^[0-9]+$/.test(seg) ? parseInt(seg, 10) : seg;
32728
- });
32729
- }
32730
- return { epoch: g.epoch ? parseInt(g.epoch, 10) : 0, release, pre, post, dev, local };
32731
- }
32732
- function stripTrailingZeros(release) {
32733
- let end = release.length;
32734
- while (end > 1 && release[end - 1] === 0) end--;
32735
- return release.slice(0, end);
32736
- }
32737
- function cmpRelease(a, b) {
32738
- const ra = stripTrailingZeros(a);
32739
- const rb = stripTrailingZeros(b);
32740
- const len = Math.max(ra.length, rb.length);
32741
- for (let i = 0; i < len; i++) {
32742
- const x = ra[i] ?? 0;
32743
- const y = rb[i] ?? 0;
32744
- if (x !== y) return x < y ? -1 : 1;
32745
- }
32746
- return 0;
32747
- }
32748
- function preKind(v) {
32749
- if (v.pre !== null) return 0;
32750
- if (v.post === null && v.dev !== null) return -1;
32751
- return 1;
32752
- }
32753
- function cmpPre(a, b) {
32754
- const ka = preKind(a);
32755
- const kb = preKind(b);
32756
- if (ka !== kb) return ka < kb ? -1 : 1;
32757
- if (ka !== 0 || !a.pre || !b.pre) return 0;
32758
- const la = PRE_RANK[a.pre.letter] ?? 0;
32759
- const lb = PRE_RANK[b.pre.letter] ?? 0;
32760
- if (la !== lb) return la < lb ? -1 : 1;
32761
- if (a.pre.n !== b.pre.n) return a.pre.n < b.pre.n ? -1 : 1;
32762
- return 0;
32763
- }
32764
- function cmpPost(a, b) {
32765
- if (a === null && b === null) return 0;
32766
- if (a === null) return -1;
32767
- if (b === null) return 1;
32768
- return a < b ? -1 : a > b ? 1 : 0;
32769
- }
32770
- function cmpDev(a, b) {
32771
- if (a === null && b === null) return 0;
32772
- if (a === null) return 1;
32773
- if (b === null) return -1;
32774
- return a < b ? -1 : a > b ? 1 : 0;
32775
- }
32776
- function cmpLocal(a, b) {
32777
- if (a === null && b === null) return 0;
32778
- if (a === null) return -1;
32779
- if (b === null) return 1;
32780
- const len = Math.max(a.length, b.length);
32781
- for (let i = 0; i < len; i++) {
32782
- if (i >= a.length) return -1;
32783
- if (i >= b.length) return 1;
32784
- const x = a[i];
32785
- const y = b[i];
32786
- const xNum = typeof x === "number";
32787
- const yNum = typeof y === "number";
32788
- if (xNum && yNum) {
32789
- if (x !== y) return x < y ? -1 : 1;
32790
- } else if (xNum !== yNum) {
32791
- return xNum ? 1 : -1;
32792
- } else if (x !== y) {
32793
- return x < y ? -1 : 1;
32794
- }
32795
- }
32796
- return 0;
32797
- }
32798
- function comparePep440(a, b) {
32799
- if (a.epoch !== b.epoch) return a.epoch < b.epoch ? -1 : 1;
32800
- const r = cmpRelease(a.release, b.release);
32801
- if (r !== 0) return r;
32802
- const p = cmpPre(a, b);
32803
- if (p !== 0) return p;
32804
- const po = cmpPost(a.post, b.post);
32805
- if (po !== 0) return po;
32806
- const d = cmpDev(a.dev, b.dev);
32807
- if (d !== 0) return d;
32808
- return cmpLocal(a.local, b.local);
32809
- }
32810
- var pep440Comparator = {
32811
- normalize(raw) {
32812
- return parsePep440(raw) === null ? null : raw.trim();
32813
- },
32814
- gte(a, b) {
32815
- const pa = parsePep440(a);
32816
- const pb = parsePep440(b);
32817
- if (!pa || !pb) return false;
32818
- return comparePep440(pa, pb) >= 0;
32819
- },
32820
- lt(a, b) {
32821
- const pa = parsePep440(a);
32822
- const pb = parsePep440(b);
32823
- if (!pa || !pb) return false;
32824
- return comparePep440(pa, pb) < 0;
32825
- }
32826
- };
32827
-
32828
- // ../../packages/scanners/src/engine/comparators/semver.ts
32829
- var import_semver3 = __toESM(require_semver2());
32830
- var semverComparator = {
32831
- normalize(raw) {
32832
- const strict = (0, import_semver3.valid)(raw);
32833
- if (strict !== null) return strict;
32834
- const coerced = (0, import_semver3.coerce)(raw);
32835
- return coerced === null ? null : coerced.version;
32836
- },
32837
- gte(a, b) {
32838
- return (0, import_semver3.gte)(a, b);
32839
- },
32840
- lt(a, b) {
32841
- return (0, import_semver3.lt)(a, b);
32842
- }
32843
- };
32844
-
32845
32951
  // ../../packages/scanners/src/engine/comparators/index.ts
32846
32952
  var COMPARATORS = {
32847
32953
  semver: semverComparator,
@@ -32863,6 +32969,8 @@ function acceptedRangeTypesForEcosystem(ecosystem) {
32863
32969
  }
32864
32970
 
32865
32971
  // ../../packages/scanners/src/engine/matcher.ts
32972
+ var DISPLAY_FORMAT = { zero: "0" };
32973
+ var SEMVER_FORMAT = { zero: "0.0.0" };
32866
32974
  function matchAdvisories(packages, byPackage, comparator, acceptedRangeTypes) {
32867
32975
  const findings = [];
32868
32976
  for (const pkg2 of packages) {
@@ -32887,6 +32995,7 @@ function applicableRanges(ranges, acceptedRangeTypes) {
32887
32995
  });
32888
32996
  }
32889
32997
  function matchOne(pkg2, advisory, comparator, acceptedRangeTypes) {
32998
+ if (advisory.withdrawn !== null) return null;
32890
32999
  const ranges = applicableRanges(advisory.affected.ranges, acceptedRangeTypes);
32891
33000
  const exactVersions = advisory.affected.exactVersions;
32892
33001
  const hasVersionData = ranges.length > 0 || exactVersions.length > 0;
@@ -32916,25 +33025,12 @@ function isAffected(installedRaw, ranges, exactVersions, comparator) {
32916
33025
  let affected = false;
32917
33026
  let firstFixed = null;
32918
33027
  for (const range of ranges) {
32919
- const introduced = range.introduced === "0" ? "0.0.0" : comparator.normalize(range.introduced);
32920
- if (introduced === null) continue;
32921
- if (!comparator.gte(installed, introduced)) continue;
33028
+ if (!versionInRange(installedRaw, range, comparator)) continue;
33029
+ affected = true;
32922
33030
  const fixed = range.fixed ? comparator.normalize(range.fixed) : null;
32923
- if (fixed !== null) {
32924
- if (comparator.lt(installed, fixed)) {
32925
- affected = true;
32926
- if (firstFixed === null || comparator.lt(fixed, firstFixed)) {
32927
- firstFixed = fixed;
32928
- }
32929
- }
32930
- continue;
33031
+ if (fixed !== null && (firstFixed === null || comparator.lt(fixed, firstFixed))) {
33032
+ firstFixed = fixed;
32931
33033
  }
32932
- const lastAffected = range.lastAffected ? comparator.normalize(range.lastAffected) : null;
32933
- if (lastAffected !== null) {
32934
- if (comparator.gte(lastAffected, installed)) affected = true;
32935
- continue;
32936
- }
32937
- affected = true;
32938
33034
  }
32939
33035
  return { affected, firstFixed };
32940
33036
  }
@@ -32960,29 +33056,11 @@ function buildFinding(pkg2, advisory, severity, fixVersion, vulnerableRange) {
32960
33056
  function rangesToDisplay(ranges, exactVersions) {
32961
33057
  const parts = [];
32962
33058
  for (const v of exactVersions) parts.push("=" + v);
32963
- for (const range of ranges) {
32964
- const lo = range.introduced === "0" ? "0" : range.introduced;
32965
- if (range.fixed) {
32966
- parts.push(">=" + lo + " <" + range.fixed);
32967
- } else if (range.lastAffected) {
32968
- parts.push(">=" + lo + " <=" + range.lastAffected);
32969
- } else {
32970
- parts.push(">=" + lo);
32971
- }
32972
- }
33059
+ for (const range of ranges) parts.push(formatRange(range, DISPLAY_FORMAT));
32973
33060
  return parts.length > 0 ? parts.join(" || ") : "*";
32974
33061
  }
32975
33062
  function vulnerableRangeForFix(ranges) {
32976
- const parts = [];
32977
- for (const range of ranges) {
32978
- const lo = range.introduced === "0" ? "0.0.0" : range.introduced;
32979
- if (range.fixed) {
32980
- parts.push(">=" + lo + " <" + range.fixed);
32981
- } else {
32982
- parts.push(">=" + lo);
32983
- }
32984
- }
32985
- return parts.join(" || ");
33063
+ return formatRanges(ranges, SEMVER_FORMAT);
32986
33064
  }
32987
33065
  function mapSeverity(severity) {
32988
33066
  if (!severity) return "moderate";
@@ -33058,7 +33136,7 @@ function toCanonicalAdvisory(packageName, ecosystem, advisory) {
33058
33136
  severity: advisory.severity,
33059
33137
  summary: advisory.summary,
33060
33138
  url: advisory.url,
33061
- withdrawn: null
33139
+ withdrawn: advisory.withdrawn
33062
33140
  };
33063
33141
  }
33064
33142
  function groupByEcosystem(packages) {
@@ -33204,22 +33282,45 @@ function findingIdentityKeys(finding) {
33204
33282
  function findingPackageIdentity(finding) {
33205
33283
  return (finding.ecosystem ?? DEFAULT_ECOSYSTEM) + "|" + finding.packageName;
33206
33284
  }
33207
- function reconcileAgainstReported(findings, reportedByPackage) {
33285
+ function reconcileAgainstReported(findings, reportedByPackage, source) {
33208
33286
  const kept = [];
33287
+ const corroborations = [];
33209
33288
  for (const finding of findings) {
33210
33289
  const packageKey = findingPackageIdentity(finding);
33211
- const existing = reportedByPackage.get(packageKey);
33290
+ const existing = reportedByPackage.get(packageKey) || /* @__PURE__ */ new Map();
33212
33291
  const keys = findingIdentityKeys(finding);
33213
- const isDup = existing ? keys.some(function seen(k) {
33214
- return existing.has(k);
33215
- }) : false;
33216
- if (isDup) continue;
33292
+ const target = firstReported(existing, keys);
33293
+ if (target) {
33294
+ const by = { source, advisoryId: finding.advisoryId, severity: finding.severity };
33295
+ corroborations.push({ target, by });
33296
+ const attached = target.finding.corroborations || [];
33297
+ if (!attached.some(function already(c) {
33298
+ return c.source === by.source;
33299
+ })) attached.push(by);
33300
+ target.finding.corroborations = attached;
33301
+ target.finding.severity = escalatedSeverity(target.severity, attached);
33302
+ continue;
33303
+ }
33217
33304
  kept.push(finding);
33218
- const set3 = existing || /* @__PURE__ */ new Set();
33219
- for (const k of keys) set3.add(k);
33220
- reportedByPackage.set(packageKey, set3);
33305
+ const reported = {
33306
+ source,
33307
+ advisoryId: finding.advisoryId,
33308
+ packageName: finding.packageName,
33309
+ ecosystem: finding.ecosystem ?? DEFAULT_ECOSYSTEM,
33310
+ severity: finding.severity,
33311
+ finding
33312
+ };
33313
+ for (const key of keys) existing.set(key, reported);
33314
+ reportedByPackage.set(packageKey, existing);
33315
+ }
33316
+ return { kept, corroborations };
33317
+ }
33318
+ function firstReported(existing, keys) {
33319
+ for (const key of keys) {
33320
+ const found = existing.get(key);
33321
+ if (found) return found;
33221
33322
  }
33222
- return kept;
33323
+ return null;
33223
33324
  }
33224
33325
 
33225
33326
  // ../../packages/scanners/src/index.ts
@@ -33704,7 +33805,7 @@ function roundUpV3(value) {
33704
33805
  return (Math.floor(intInput / 1e4) + 1) / 10;
33705
33806
  }
33706
33807
  function bucketV3(score) {
33707
- if (score === 0) return "none";
33808
+ if (score === 0) return "info";
33708
33809
  if (score < 4) return "low";
33709
33810
  if (score < 7) return "moderate";
33710
33811
  if (score < 9) return "high";
@@ -33739,16 +33840,14 @@ function normalizeGemnasiumRecord(record2, ecosystem, slugPrefix) {
33739
33840
  if (!slug || !slug.startsWith(slugPrefix)) return [];
33740
33841
  const rawName = slug.slice(slugPrefix.length);
33741
33842
  if (rawName.length === 0) return [];
33843
+ if (isRetractedUpstream(r)) return [];
33742
33844
  const packageName = ecosystem === "PyPI" ? normalizePyName2(rawName) : rawName;
33743
33845
  const identifiers = stringArray(r.identifiers);
33744
33846
  const advisoryId = typeof r.identifier === "string" && r.identifier.length > 0 ? r.identifier : identifiers[0] ?? null;
33745
33847
  if (!advisoryId) return [];
33746
33848
  const fixedVersions = stringArray(r.fixed_versions);
33747
- const parsed = parseAffectedRange(
33748
- typeof r.affected_range === "string" ? r.affected_range : "",
33749
- fixedVersions,
33750
- typeof r.affected_versions === "string" ? r.affected_versions : ""
33751
- );
33849
+ const parsed = parseAffectedRange(typeof r.affected_range === "string" ? r.affected_range : "", fixedVersions);
33850
+ if (parsed.ranges.length === 0 && parsed.versions.length === 0) return [];
33752
33851
  const severity = severityFromCvss(
33753
33852
  typeof r.cvss_v3 === "string" ? r.cvss_v3 : null,
33754
33853
  typeof r.cvss_v2 === "string" ? r.cvss_v2 : null
@@ -33770,11 +33869,29 @@ function normalizeGemnasiumRecord(record2, ecosystem, slugPrefix) {
33770
33869
  summary,
33771
33870
  url: url2,
33772
33871
  malicious: false,
33773
- withdrawn: null,
33774
- rangeSource: parsed.source
33872
+ withdrawn: null
33775
33873
  }];
33776
33874
  }
33777
- function parseAffectedRange(affectedRange, fixedVersions, affectedVersions = "") {
33875
+ var RETRACTION_PHRASES = [
33876
+ "marked as false positive",
33877
+ "marked as a false positive",
33878
+ "this is a false positive",
33879
+ "this advisory has been withdrawn",
33880
+ "has been invalidated",
33881
+ "cve being rejected",
33882
+ "cve has been rejected"
33883
+ ];
33884
+ function isRetractedUpstream(record2) {
33885
+ const title = typeof record2.title === "string" ? record2.title.trim().toLowerCase() : "";
33886
+ if (title === "false positive") return true;
33887
+ if (title.startsWith("withdrawn advisory:")) return true;
33888
+ if (title.startsWith("duplicate advisory:")) return true;
33889
+ const description = typeof record2.description === "string" ? record2.description.replace(/\s+/g, " ").toLowerCase() : "";
33890
+ return RETRACTION_PHRASES.some(function stated(phrase) {
33891
+ return description.includes(phrase);
33892
+ });
33893
+ }
33894
+ function parseAffectedRange(affectedRange, fixedVersions) {
33778
33895
  const ranges = [];
33779
33896
  const versions = [];
33780
33897
  const trimmed = affectedRange.trim();
@@ -33785,79 +33902,55 @@ function parseAffectedRange(affectedRange, fixedVersions, affectedVersions = "")
33785
33902
  if (interval.exact !== null) {
33786
33903
  versions.push(interval.exact);
33787
33904
  } else {
33788
- ranges.push({ introduced: interval.introduced, fixed: interval.fixed });
33905
+ ranges.push(toRange(interval.introduced, interval.introducedExclusive, interval.fixed, interval.lastAffected));
33789
33906
  }
33790
33907
  }
33791
- if (versions.length === 0 && ranges.every(isEmptyInterval)) {
33792
- const prose = parseAffectedVersionsProse(affectedVersions);
33793
- if (prose) return { ranges: prose, versions, source: "prose" };
33794
- return { ranges: [], versions: [], source: "unresolved" };
33795
- }
33908
+ if (versions.length === 0 && ranges.every(isEmptyInterval)) return { ranges: [], versions: [] };
33796
33909
  const only = ranges[0];
33797
33910
  const authoritativeFixed = fixedVersions[0];
33798
33911
  if (ranges.length === 1 && versions.length === 0 && only && fixedVersions.length === 1 && authoritativeFixed !== void 0) {
33799
- ranges[0] = { introduced: only.introduced, fixed: authoritativeFixed };
33912
+ ranges[0] = toRange(only.introduced, only.introducedExclusive === true, authoritativeFixed, null);
33800
33913
  }
33801
- return { ranges, versions, source: "range" };
33914
+ return { ranges, versions };
33915
+ }
33916
+ function toRange(introduced, introducedExclusive, fixed, lastAffected) {
33917
+ const range = { introduced, fixed, lastAffected };
33918
+ if (introducedExclusive) range.introducedExclusive = true;
33919
+ return range;
33802
33920
  }
33803
33921
  function isEmptyInterval(range) {
33922
+ if (range.lastAffected !== null) {
33923
+ return range.introducedExclusive === true && range.lastAffected === range.introduced;
33924
+ }
33804
33925
  if (range.fixed === null) return false;
33805
33926
  if (range.fixed === range.introduced) return true;
33806
33927
  return isZeroVersion(range.introduced) && isZeroVersion(range.fixed);
33807
33928
  }
33808
- function isZeroVersion(version2) {
33809
- return /^0(\.0)*$/.test(version2);
33810
- }
33811
- function parseAffectedVersionsProse(text) {
33812
- const sentence = text.replace(/\s+/g, " ").trim();
33813
- if (sentence.length === 0) return null;
33814
- const lower = sentence.toLowerCase();
33815
- if (lower === "none" || lower.startsWith("no version") || lower.startsWith("this advisory has been withdrawn")) return null;
33816
- const out = [];
33817
- for (const clause of sentence.split(",")) {
33818
- const c = clause.trim();
33819
- const fromBefore = /^all versions starting from (\S+) before (\S+)$/i.exec(c);
33820
- if (fromBefore && fromBefore[1] && fromBefore[2]) {
33821
- out.push({ introduced: stripV(fromBefore[1]), fixed: stripV(fromBefore[2]) });
33822
- continue;
33823
- }
33824
- const before = /^all versions before (\S+)$/i.exec(c);
33825
- if (before && before[1]) {
33826
- out.push({ introduced: "0", fixed: stripV(before[1]) });
33827
- continue;
33828
- }
33829
- const from = /^all versions starting from (\S+)$/i.exec(c);
33830
- if (from && from[1]) {
33831
- out.push({ introduced: stripV(from[1]), fixed: null });
33832
- continue;
33833
- }
33834
- if (/^all versions$/i.test(c)) {
33835
- out.push({ introduced: "0", fixed: null });
33836
- continue;
33837
- }
33838
- return null;
33839
- }
33840
- return out;
33841
- }
33842
33929
  function parseDisjunct(disjunct) {
33843
33930
  const first = disjunct[0];
33844
33931
  if (first === "(" || first === "[") return parseIntervalNotation(disjunct);
33845
33932
  return parseComparatorForm(disjunct);
33846
33933
  }
33847
33934
  function parseIntervalNotation(disjunct) {
33935
+ const open = disjunct[0];
33848
33936
  const close = disjunct[disjunct.length - 1];
33849
33937
  if (close !== ")" && close !== "]") return null;
33850
33938
  const inner = disjunct.slice(1, -1);
33851
33939
  const comma = inner.indexOf(",");
33852
33940
  if (comma < 0) {
33853
33941
  const exact = inner.trim();
33854
- return exact.length > 0 ? { introduced: "0", fixed: null, exact } : null;
33942
+ return exact.length > 0 ? { introduced: "0", introducedExclusive: false, fixed: null, lastAffected: null, exact } : null;
33855
33943
  }
33856
33944
  const lo = inner.slice(0, comma).trim();
33857
33945
  const hi = inner.slice(comma + 1).trim();
33946
+ const hasUpper = hi.length > 0;
33858
33947
  return {
33859
33948
  introduced: lo.length > 0 ? lo : "0",
33860
- fixed: hi.length > 0 ? hi : null,
33949
+ // An open "(" excludes the lower bound — but only when one was actually given; "(,2.0.0)" has no
33950
+ // lower bound at all and starts from the bottom of the version space inclusively.
33951
+ introducedExclusive: open === "(" && lo.length > 0,
33952
+ fixed: hasUpper && close === ")" ? hi : null,
33953
+ lastAffected: hasUpper && close === "]" ? hi : null,
33861
33954
  exact: null
33862
33955
  };
33863
33956
  }
@@ -33865,23 +33958,29 @@ function parseComparatorForm(disjunct) {
33865
33958
  const tokens = disjunct.split(/\s+/).filter(nonEmpty);
33866
33959
  if (tokens.length === 0) return null;
33867
33960
  let introduced = "0";
33961
+ let introducedExclusive = false;
33868
33962
  let fixed = null;
33963
+ let lastAffected = null;
33869
33964
  for (const token of tokens) {
33870
33965
  const op = readOperator(token);
33871
- if (!op) continue;
33966
+ if (!op) return null;
33872
33967
  if (op.operator === "=") {
33873
- return { introduced: "0", fixed: null, exact: op.version };
33968
+ return { introduced: "0", introducedExclusive: false, fixed: null, lastAffected: null, exact: op.version };
33874
33969
  }
33875
33970
  if (op.operator === ">=" || op.operator === ">") {
33876
33971
  introduced = op.version;
33972
+ introducedExclusive = op.operator === ">";
33877
33973
  } else if (op.operator === "<") {
33878
33974
  fixed = op.version;
33879
- } else if (op.operator === "<=") {
33880
- fixed = op.version;
33975
+ lastAffected = null;
33976
+ } else {
33977
+ lastAffected = op.version;
33978
+ fixed = null;
33881
33979
  }
33882
33980
  }
33883
- return { introduced, fixed, exact: null };
33981
+ return { introduced, introducedExclusive, fixed, lastAffected, exact: null };
33884
33982
  }
33983
+ var BARE_VERSION = /^[0-9]+(\.[0-9]+)*([-+][0-9A-Za-z.-]*)?$/;
33885
33984
  function readOperator(token) {
33886
33985
  if (token.startsWith(">=")) return { operator: ">=", version: stripV(token.slice(2)) };
33887
33986
  if (token.startsWith("<=")) return { operator: "<=", version: stripV(token.slice(2)) };
@@ -33889,7 +33988,7 @@ function readOperator(token) {
33889
33988
  if (token.startsWith("<")) return { operator: "<", version: stripV(token.slice(1)) };
33890
33989
  if (token.startsWith("=")) return { operator: "=", version: stripV(token.slice(1)) };
33891
33990
  const bare = stripV(token);
33892
- return bare.length > 0 ? { operator: "=", version: bare } : null;
33991
+ return BARE_VERSION.test(bare) ? { operator: "=", version: bare } : null;
33893
33992
  }
33894
33993
  function stripV(raw) {
33895
33994
  const t = raw.trim();
@@ -34317,7 +34416,8 @@ function toOsvAdvisory(row) {
34317
34416
  severity: row.severity,
34318
34417
  summary: row.summary,
34319
34418
  url: row.url,
34320
- malicious: row.malicious
34419
+ malicious: row.malicious,
34420
+ withdrawn: row.withdrawn
34321
34421
  };
34322
34422
  }
34323
34423
  function toGemnasiumAdvisory(row) {
@@ -34489,23 +34589,32 @@ async function refreshOsv(options, meta3, item) {
34489
34589
  return { source: "osv", ecosystem: item.ecosystem, status: "unchanged", rowCount: state?.recordCount ?? 0, message: null };
34490
34590
  }
34491
34591
  const append = [];
34592
+ const replaced = /* @__PURE__ */ new Set();
34593
+ let skipped = 0;
34492
34594
  for (const id of changed.ids) {
34493
- if (options.abortSignal && options.abortSignal.aborted) break;
34595
+ if (options.abortSignal && options.abortSignal.aborted) {
34596
+ skipped += 1;
34597
+ break;
34598
+ }
34494
34599
  try {
34495
34600
  const rows = await fetchOsvAdvisoryRows(id, item.ecosystem, fetchOptionsFor(options, item));
34496
34601
  for (const row of rows) append.push(row);
34602
+ replaced.add(id);
34497
34603
  } catch {
34604
+ skipped += 1;
34498
34605
  continue;
34499
34606
  }
34500
34607
  }
34501
34608
  const path = advisoryFilePath(options.cacheDir, "osv", item.ecosystem);
34502
- const rowCount = await rewriteRows(path, { dropAdvisoryIds: new Set(changed.ids), append });
34609
+ const rowCount = await rewriteRows(path, { dropAdvisoryIds: replaced, append });
34610
+ const complete = skipped === 0;
34611
+ const previousCursor = state?.cursorIso ?? null;
34503
34612
  setSourceState(meta3, "osv", item.ecosystem, {
34504
34613
  normalizerVersion: OSV_NORMALIZER_VERSION,
34505
34614
  recordCount: rowCount,
34506
34615
  refreshedAt: Date.now(),
34507
- cursorIso: changed.newestIso ?? state?.cursorIso ?? null,
34508
- etag: changed.etag
34616
+ cursorIso: complete ? changed.newestIso ?? previousCursor : previousCursor,
34617
+ etag: complete ? changed.etag : null
34509
34618
  });
34510
34619
  return {
34511
34620
  source: "osv",
@@ -34887,7 +34996,7 @@ function emptyCounts() {
34887
34996
  function matchesDepType(finding, depType) {
34888
34997
  if (depType === "all") return true;
34889
34998
  if (depType === "prod") return finding.isProd;
34890
- return finding.isDev;
34999
+ return finding.isDev && !finding.isProd;
34891
35000
  }
34892
35001
  function summarize(results, options) {
34893
35002
  const projects = [];
@@ -35101,7 +35210,7 @@ async function scanProject(setup, resolved, scanners) {
35101
35210
  durationMs: result.durationMs
35102
35211
  });
35103
35212
  if (result.status !== "ok") continue;
35104
- for (const finding of reconcileAgainstReported(result.findings, reportedByPackage)) {
35213
+ for (const finding of reconcileAgainstReported(result.findings, reportedByPackage, scanner.name).kept) {
35105
35214
  findings.push(finding);
35106
35215
  }
35107
35216
  }
@@ -35426,8 +35535,8 @@ function createUi(options) {
35426
35535
 
35427
35536
  // src/help.ts
35428
35537
  var CLI_VERSION = (function resolveVersion() {
35429
- if ("3.1.1".length > 0) {
35430
- return "3.1.1";
35538
+ if ("3.3.0".length > 0) {
35539
+ return "3.3.0";
35431
35540
  }
35432
35541
  const fromEnv = process.env.SENTINELLO_VERSION;
35433
35542
  if (fromEnv && fromEnv.trim().length > 0) return fromEnv.trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sentinello",
3
- "version": "3.1.1",
3
+ "version": "3.3.0",
4
4
  "license": "MIT",
5
5
  "description": "Scan the projects in a folder for vulnerable dependencies and write an advisory an agent can act on.",
6
6
  "keywords": [
@@ -41,7 +41,6 @@
41
41
  },
42
42
  "scripts": {
43
43
  "build": "tsup",
44
- "dev": "tsx src/cli.ts",
45
44
  "lint": "eslint . --max-warnings 0",
46
45
  "typecheck": "tsc -p tsconfig.json --noEmit"
47
46
  },