sentinello 3.1.1 → 3.2.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/dist/cli.cjs +82 -59
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -13123,6 +13123,19 @@ 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
|
+
}
|
|
13126
13139
|
var SOURCE_UNAVAILABLE_REASON_CODES = [
|
|
13127
13140
|
"osv_db_not_seeded",
|
|
13128
13141
|
"osv_db_unavailable",
|
|
@@ -13135,7 +13148,7 @@ function isSourceUnavailableReason(code) {
|
|
|
13135
13148
|
|
|
13136
13149
|
// ../../packages/core/src/advisory-rows.ts
|
|
13137
13150
|
var OSV_NORMALIZER_VERSION = 4;
|
|
13138
|
-
var GEMNASIUM_NORMALIZER_VERSION =
|
|
13151
|
+
var GEMNASIUM_NORMALIZER_VERSION = 4;
|
|
13139
13152
|
|
|
13140
13153
|
// ../../packages/core/src/advisory-export.ts
|
|
13141
13154
|
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.
|
|
@@ -33204,22 +33217,51 @@ function findingIdentityKeys(finding) {
|
|
|
33204
33217
|
function findingPackageIdentity(finding) {
|
|
33205
33218
|
return (finding.ecosystem ?? DEFAULT_ECOSYSTEM) + "|" + finding.packageName;
|
|
33206
33219
|
}
|
|
33207
|
-
function reconcileAgainstReported(findings, reportedByPackage) {
|
|
33220
|
+
function reconcileAgainstReported(findings, reportedByPackage, source) {
|
|
33208
33221
|
const kept = [];
|
|
33222
|
+
const corroborations = [];
|
|
33209
33223
|
for (const finding of findings) {
|
|
33210
33224
|
const packageKey = findingPackageIdentity(finding);
|
|
33211
|
-
const existing = reportedByPackage.get(packageKey);
|
|
33225
|
+
const existing = reportedByPackage.get(packageKey) || /* @__PURE__ */ new Map();
|
|
33212
33226
|
const keys = findingIdentityKeys(finding);
|
|
33213
|
-
const
|
|
33214
|
-
|
|
33215
|
-
|
|
33216
|
-
|
|
33227
|
+
const target = firstReported(existing, keys);
|
|
33228
|
+
if (target) {
|
|
33229
|
+
const by = { source, advisoryId: finding.advisoryId, severity: finding.severity };
|
|
33230
|
+
corroborations.push({ target, by });
|
|
33231
|
+
const attached = target.finding.corroborations || [];
|
|
33232
|
+
if (!attached.some(function already(c) {
|
|
33233
|
+
return c.source === by.source;
|
|
33234
|
+
})) attached.push(by);
|
|
33235
|
+
target.finding.corroborations = attached;
|
|
33236
|
+
target.finding.severity = escalatedSeverity(target.severity, attached);
|
|
33237
|
+
continue;
|
|
33238
|
+
}
|
|
33217
33239
|
kept.push(finding);
|
|
33218
|
-
const
|
|
33219
|
-
|
|
33220
|
-
|
|
33240
|
+
const reported = {
|
|
33241
|
+
source,
|
|
33242
|
+
advisoryId: finding.advisoryId,
|
|
33243
|
+
packageName: finding.packageName,
|
|
33244
|
+
ecosystem: finding.ecosystem ?? DEFAULT_ECOSYSTEM,
|
|
33245
|
+
severity: finding.severity,
|
|
33246
|
+
finding
|
|
33247
|
+
};
|
|
33248
|
+
for (const key of keys) existing.set(key, reported);
|
|
33249
|
+
reportedByPackage.set(packageKey, existing);
|
|
33221
33250
|
}
|
|
33222
|
-
return kept;
|
|
33251
|
+
return { kept, corroborations };
|
|
33252
|
+
}
|
|
33253
|
+
function firstReported(existing, keys) {
|
|
33254
|
+
for (const key of keys) {
|
|
33255
|
+
const found = existing.get(key);
|
|
33256
|
+
if (found) return found;
|
|
33257
|
+
}
|
|
33258
|
+
return null;
|
|
33259
|
+
}
|
|
33260
|
+
function escalatedSeverity(own, corroborations) {
|
|
33261
|
+
if (corroborations.length === 0) return own;
|
|
33262
|
+
return maxSeverity([own, ...corroborations.map(function grade(c) {
|
|
33263
|
+
return c.severity;
|
|
33264
|
+
})]);
|
|
33223
33265
|
}
|
|
33224
33266
|
|
|
33225
33267
|
// ../../packages/scanners/src/index.ts
|
|
@@ -33739,16 +33781,14 @@ function normalizeGemnasiumRecord(record2, ecosystem, slugPrefix) {
|
|
|
33739
33781
|
if (!slug || !slug.startsWith(slugPrefix)) return [];
|
|
33740
33782
|
const rawName = slug.slice(slugPrefix.length);
|
|
33741
33783
|
if (rawName.length === 0) return [];
|
|
33784
|
+
if (isRetractedUpstream(r)) return [];
|
|
33742
33785
|
const packageName = ecosystem === "PyPI" ? normalizePyName2(rawName) : rawName;
|
|
33743
33786
|
const identifiers = stringArray(r.identifiers);
|
|
33744
33787
|
const advisoryId = typeof r.identifier === "string" && r.identifier.length > 0 ? r.identifier : identifiers[0] ?? null;
|
|
33745
33788
|
if (!advisoryId) return [];
|
|
33746
33789
|
const fixedVersions = stringArray(r.fixed_versions);
|
|
33747
|
-
const parsed = parseAffectedRange(
|
|
33748
|
-
|
|
33749
|
-
fixedVersions,
|
|
33750
|
-
typeof r.affected_versions === "string" ? r.affected_versions : ""
|
|
33751
|
-
);
|
|
33790
|
+
const parsed = parseAffectedRange(typeof r.affected_range === "string" ? r.affected_range : "", fixedVersions);
|
|
33791
|
+
if (parsed.ranges.length === 0 && parsed.versions.length === 0) return [];
|
|
33752
33792
|
const severity = severityFromCvss(
|
|
33753
33793
|
typeof r.cvss_v3 === "string" ? r.cvss_v3 : null,
|
|
33754
33794
|
typeof r.cvss_v2 === "string" ? r.cvss_v2 : null
|
|
@@ -33770,11 +33810,29 @@ function normalizeGemnasiumRecord(record2, ecosystem, slugPrefix) {
|
|
|
33770
33810
|
summary,
|
|
33771
33811
|
url: url2,
|
|
33772
33812
|
malicious: false,
|
|
33773
|
-
withdrawn: null
|
|
33774
|
-
rangeSource: parsed.source
|
|
33813
|
+
withdrawn: null
|
|
33775
33814
|
}];
|
|
33776
33815
|
}
|
|
33777
|
-
|
|
33816
|
+
var RETRACTION_PHRASES = [
|
|
33817
|
+
"marked as false positive",
|
|
33818
|
+
"marked as a false positive",
|
|
33819
|
+
"this is a false positive",
|
|
33820
|
+
"this advisory has been withdrawn",
|
|
33821
|
+
"has been invalidated",
|
|
33822
|
+
"cve being rejected",
|
|
33823
|
+
"cve has been rejected"
|
|
33824
|
+
];
|
|
33825
|
+
function isRetractedUpstream(record2) {
|
|
33826
|
+
const title = typeof record2.title === "string" ? record2.title.trim().toLowerCase() : "";
|
|
33827
|
+
if (title === "false positive") return true;
|
|
33828
|
+
if (title.startsWith("withdrawn advisory:")) return true;
|
|
33829
|
+
if (title.startsWith("duplicate advisory:")) return true;
|
|
33830
|
+
const description = typeof record2.description === "string" ? record2.description.replace(/\s+/g, " ").toLowerCase() : "";
|
|
33831
|
+
return RETRACTION_PHRASES.some(function stated(phrase) {
|
|
33832
|
+
return description.includes(phrase);
|
|
33833
|
+
});
|
|
33834
|
+
}
|
|
33835
|
+
function parseAffectedRange(affectedRange, fixedVersions) {
|
|
33778
33836
|
const ranges = [];
|
|
33779
33837
|
const versions = [];
|
|
33780
33838
|
const trimmed = affectedRange.trim();
|
|
@@ -33788,17 +33846,13 @@ function parseAffectedRange(affectedRange, fixedVersions, affectedVersions = "")
|
|
|
33788
33846
|
ranges.push({ introduced: interval.introduced, fixed: interval.fixed });
|
|
33789
33847
|
}
|
|
33790
33848
|
}
|
|
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
|
-
}
|
|
33849
|
+
if (versions.length === 0 && ranges.every(isEmptyInterval)) return { ranges: [], versions: [] };
|
|
33796
33850
|
const only = ranges[0];
|
|
33797
33851
|
const authoritativeFixed = fixedVersions[0];
|
|
33798
33852
|
if (ranges.length === 1 && versions.length === 0 && only && fixedVersions.length === 1 && authoritativeFixed !== void 0) {
|
|
33799
33853
|
ranges[0] = { introduced: only.introduced, fixed: authoritativeFixed };
|
|
33800
33854
|
}
|
|
33801
|
-
return { ranges, versions
|
|
33855
|
+
return { ranges, versions };
|
|
33802
33856
|
}
|
|
33803
33857
|
function isEmptyInterval(range) {
|
|
33804
33858
|
if (range.fixed === null) return false;
|
|
@@ -33808,37 +33862,6 @@ function isEmptyInterval(range) {
|
|
|
33808
33862
|
function isZeroVersion(version2) {
|
|
33809
33863
|
return /^0(\.0)*$/.test(version2);
|
|
33810
33864
|
}
|
|
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
33865
|
function parseDisjunct(disjunct) {
|
|
33843
33866
|
const first = disjunct[0];
|
|
33844
33867
|
if (first === "(" || first === "[") return parseIntervalNotation(disjunct);
|
|
@@ -35101,7 +35124,7 @@ async function scanProject(setup, resolved, scanners) {
|
|
|
35101
35124
|
durationMs: result.durationMs
|
|
35102
35125
|
});
|
|
35103
35126
|
if (result.status !== "ok") continue;
|
|
35104
|
-
for (const finding of reconcileAgainstReported(result.findings, reportedByPackage)) {
|
|
35127
|
+
for (const finding of reconcileAgainstReported(result.findings, reportedByPackage, scanner.name).kept) {
|
|
35105
35128
|
findings.push(finding);
|
|
35106
35129
|
}
|
|
35107
35130
|
}
|
|
@@ -35426,8 +35449,8 @@ function createUi(options) {
|
|
|
35426
35449
|
|
|
35427
35450
|
// src/help.ts
|
|
35428
35451
|
var CLI_VERSION = (function resolveVersion() {
|
|
35429
|
-
if ("3.
|
|
35430
|
-
return "3.
|
|
35452
|
+
if ("3.2.0".length > 0) {
|
|
35453
|
+
return "3.2.0";
|
|
35431
35454
|
}
|
|
35432
35455
|
const fromEnv = process.env.SENTINELLO_VERSION;
|
|
35433
35456
|
if (fromEnv && fromEnv.trim().length > 0) return fromEnv.trim();
|