sentinello 3.7.2 → 3.7.4
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 +110 -31
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -13885,7 +13885,7 @@ function asError(err) {
|
|
|
13885
13885
|
}
|
|
13886
13886
|
|
|
13887
13887
|
// ../../packages/core/src/advisory-rows.ts
|
|
13888
|
-
var OSV_NORMALIZER_VERSION =
|
|
13888
|
+
var OSV_NORMALIZER_VERSION = 7;
|
|
13889
13889
|
var GEMNASIUM_NORMALIZER_VERSION = 7;
|
|
13890
13890
|
|
|
13891
13891
|
// ../../packages/core/src/advisory-export.ts
|
|
@@ -34133,6 +34133,10 @@ function createGemnasiumScanner(deps) {
|
|
|
34133
34133
|
durationMs: Date.now() - startedAt
|
|
34134
34134
|
};
|
|
34135
34135
|
}
|
|
34136
|
+
const notReady = evidenceNotReady(graph.packages, deps);
|
|
34137
|
+
if (notReady !== null) {
|
|
34138
|
+
return unauditable4("osv_db_not_seeded", "OSV withdrawal data for " + notReady + " is not complete yet", startedAt);
|
|
34139
|
+
}
|
|
34136
34140
|
let findings;
|
|
34137
34141
|
try {
|
|
34138
34142
|
findings = matchPackages2(graph.packages, deps.lookup, deps.isEnabled);
|
|
@@ -34176,6 +34180,13 @@ function matchPackages2(packages, lookup, isEnabled) {
|
|
|
34176
34180
|
}
|
|
34177
34181
|
return findings;
|
|
34178
34182
|
}
|
|
34183
|
+
function evidenceNotReady(packages, deps) {
|
|
34184
|
+
for (const ecosystem of groupByEcosystem2(packages).keys()) {
|
|
34185
|
+
if (!deps.isEnabled(ecosystem) || !comparatorForEcosystem(ecosystem)) continue;
|
|
34186
|
+
if (!deps.withdrawalEvidenceReady(ecosystem)) return ecosystem;
|
|
34187
|
+
}
|
|
34188
|
+
return null;
|
|
34189
|
+
}
|
|
34179
34190
|
function toCanonicalAdvisory2(packageName, ecosystem, advisory) {
|
|
34180
34191
|
return {
|
|
34181
34192
|
id: advisory.advisoryId,
|
|
@@ -34191,7 +34202,7 @@ function toCanonicalAdvisory2(packageName, ecosystem, advisory) {
|
|
|
34191
34202
|
severity: advisory.severity,
|
|
34192
34203
|
summary: advisory.summary,
|
|
34193
34204
|
url: advisory.url,
|
|
34194
|
-
withdrawn:
|
|
34205
|
+
withdrawn: advisory.withdrawn
|
|
34195
34206
|
};
|
|
34196
34207
|
}
|
|
34197
34208
|
function groupByEcosystem2(packages) {
|
|
@@ -34219,6 +34230,37 @@ function unauditable4(reasonCode, message, startedAt) {
|
|
|
34219
34230
|
};
|
|
34220
34231
|
}
|
|
34221
34232
|
|
|
34233
|
+
// ../../packages/scanners/src/withdrawn.ts
|
|
34234
|
+
function stampWithdrawn(advisories, links) {
|
|
34235
|
+
return advisories.map(function stamp(advisory) {
|
|
34236
|
+
const withdrawn = withdrawnAt(advisory, links);
|
|
34237
|
+
return withdrawn === null ? advisory : { ...advisory, withdrawn };
|
|
34238
|
+
});
|
|
34239
|
+
}
|
|
34240
|
+
function stampWithdrawnByPackage(byPackage, linksByPackage) {
|
|
34241
|
+
const out = /* @__PURE__ */ new Map();
|
|
34242
|
+
for (const [name, advisories] of byPackage.entries()) {
|
|
34243
|
+
out.set(name, stampWithdrawn(advisories, linksByPackage.get(name) ?? []));
|
|
34244
|
+
}
|
|
34245
|
+
return out;
|
|
34246
|
+
}
|
|
34247
|
+
function withdrawnAt(advisory, links) {
|
|
34248
|
+
const identity = /* @__PURE__ */ new Set([advisory.advisoryId, ...advisory.aliases]);
|
|
34249
|
+
let latest = null;
|
|
34250
|
+
for (const link2 of links) {
|
|
34251
|
+
if (!sharesIdentifier(identity, link2)) continue;
|
|
34252
|
+
if (link2.withdrawn === null) return null;
|
|
34253
|
+
if (latest === null || link2.withdrawn > latest) latest = link2.withdrawn;
|
|
34254
|
+
}
|
|
34255
|
+
return latest;
|
|
34256
|
+
}
|
|
34257
|
+
function sharesIdentifier(identity, link2) {
|
|
34258
|
+
if (identity.has(link2.advisoryId)) return true;
|
|
34259
|
+
return link2.aliases.some(function inIdentity(alias) {
|
|
34260
|
+
return identity.has(alias);
|
|
34261
|
+
});
|
|
34262
|
+
}
|
|
34263
|
+
|
|
34222
34264
|
// ../../packages/scanners/src/engine/reconcile.ts
|
|
34223
34265
|
function findingIdentityKeys(finding) {
|
|
34224
34266
|
const keys = [finding.advisoryId.toLowerCase()];
|
|
@@ -34504,7 +34546,7 @@ function normalizeOsvRecord(record2, ecosystem) {
|
|
|
34504
34546
|
});
|
|
34505
34547
|
}
|
|
34506
34548
|
for (const row of byPackage.values()) {
|
|
34507
|
-
if (row.ranges.length === 0 && row.versions.length === 0 && !malicious) continue;
|
|
34549
|
+
if (row.ranges.length === 0 && row.versions.length === 0 && !malicious && row.withdrawn === null) continue;
|
|
34508
34550
|
rows.push(row);
|
|
34509
34551
|
}
|
|
34510
34552
|
return rows;
|
|
@@ -34670,10 +34712,7 @@ async function fetchOsvChangedIds(ecosystem, cursorMs, etag, options) {
|
|
|
34670
34712
|
async function fetchOsvAdvisoryRows(id, ecosystem, options) {
|
|
34671
34713
|
const record2 = await getJsonOrNull(osvAdvisoryUrl(id, ecosystem), options);
|
|
34672
34714
|
if (record2 === null) return [];
|
|
34673
|
-
|
|
34674
|
-
return rows.filter(function notWithdrawn(row) {
|
|
34675
|
-
return row.withdrawn === null;
|
|
34676
|
-
});
|
|
34715
|
+
return normalizeOsvRecord(record2, ecosystem);
|
|
34677
34716
|
}
|
|
34678
34717
|
function selectChangedIds(csv, cursorMs) {
|
|
34679
34718
|
const ids = [];
|
|
@@ -48995,23 +49034,32 @@ function safeParse3(json3) {
|
|
|
48995
49034
|
}
|
|
48996
49035
|
|
|
48997
49036
|
// src/cache/lookup.ts
|
|
48998
|
-
async function loadCacheForPackages(cacheDir, ecosystem, packageNames, sources) {
|
|
49037
|
+
async function loadCacheForPackages(cacheDir, ecosystem, packageNames, sources, meta3) {
|
|
48999
49038
|
const wanted = new Set(packageNames);
|
|
49000
49039
|
const osv = /* @__PURE__ */ new Map();
|
|
49001
49040
|
const gemnasium = /* @__PURE__ */ new Map();
|
|
49041
|
+
const withGemnasium = sources.includes("gemnasium");
|
|
49042
|
+
const evidenceComplete = withGemnasium && isSeeded(meta3, "osv", ecosystem, OSV_NORMALIZER_VERSION);
|
|
49043
|
+
let osvRows = /* @__PURE__ */ new Map();
|
|
49044
|
+
if (sources.includes("osv") || evidenceComplete) {
|
|
49045
|
+
osvRows = await readRowsForPackages(advisoryFilePath(cacheDir, "osv", ecosystem), wanted);
|
|
49046
|
+
}
|
|
49002
49047
|
if (sources.includes("osv")) {
|
|
49003
|
-
const
|
|
49004
|
-
for (const [name, list] of rows.entries()) {
|
|
49048
|
+
for (const [name, list] of osvRows.entries()) {
|
|
49005
49049
|
osv.set(name, list.map(toOsvAdvisory));
|
|
49006
49050
|
}
|
|
49007
49051
|
}
|
|
49008
|
-
if (
|
|
49052
|
+
if (withGemnasium) {
|
|
49009
49053
|
const rows = await readRowsForPackages(advisoryFilePath(cacheDir, "gemnasium", ecosystem), wanted);
|
|
49010
49054
|
for (const [name, list] of rows.entries()) {
|
|
49011
49055
|
gemnasium.set(name, list.map(toGemnasiumAdvisory));
|
|
49012
49056
|
}
|
|
49013
49057
|
}
|
|
49014
|
-
return {
|
|
49058
|
+
return {
|
|
49059
|
+
osv,
|
|
49060
|
+
gemnasium: evidenceComplete ? stampWithdrawnByPackage(gemnasium, osvRows) : gemnasium,
|
|
49061
|
+
withdrawalsChecked: evidenceComplete
|
|
49062
|
+
};
|
|
49015
49063
|
}
|
|
49016
49064
|
function toOsvAdvisory(row) {
|
|
49017
49065
|
return {
|
|
@@ -49034,7 +49082,8 @@ function toGemnasiumAdvisory(row) {
|
|
|
49034
49082
|
versions: row.versions,
|
|
49035
49083
|
severity: row.severity,
|
|
49036
49084
|
summary: row.summary,
|
|
49037
|
-
url: row.url
|
|
49085
|
+
url: row.url,
|
|
49086
|
+
withdrawn: null
|
|
49038
49087
|
};
|
|
49039
49088
|
}
|
|
49040
49089
|
function registryFilePath(cacheDir) {
|
|
@@ -49151,17 +49200,26 @@ async function registryCacheSummary(cacheDir) {
|
|
|
49151
49200
|
}
|
|
49152
49201
|
|
|
49153
49202
|
// src/cache/sync.ts
|
|
49203
|
+
function syncTargets(sources) {
|
|
49204
|
+
const targets = sources.map(function forFindings(source) {
|
|
49205
|
+
return { source, purpose: "findings" };
|
|
49206
|
+
});
|
|
49207
|
+
if (sources.includes("gemnasium") && !sources.includes("osv")) {
|
|
49208
|
+
targets.push({ source: "osv", purpose: "withdrawal-evidence" });
|
|
49209
|
+
}
|
|
49210
|
+
return targets;
|
|
49211
|
+
}
|
|
49154
49212
|
async function planSync(options) {
|
|
49155
49213
|
const meta3 = await readCacheMeta(options.cacheDir);
|
|
49156
49214
|
const items = [];
|
|
49157
49215
|
let seedBytes = 0;
|
|
49158
|
-
for (const source of options.sources) {
|
|
49216
|
+
for (const { source, purpose } of syncTargets(options.sources)) {
|
|
49159
49217
|
if (source === "osv" && osvFeedDisabled()) continue;
|
|
49160
49218
|
if (source === "gemnasium" && gemnasiumFeedDisabled()) continue;
|
|
49161
49219
|
const normalizerVersion = source === "osv" ? OSV_NORMALIZER_VERSION : GEMNASIUM_NORMALIZER_VERSION;
|
|
49162
49220
|
const seeded = isSeeded(meta3, source, options.ecosystem, normalizerVersion);
|
|
49163
49221
|
if (seeded) {
|
|
49164
|
-
items.push({ source, ecosystem: options.ecosystem, kind: "refresh", downloadBytes: null, downloadBytesEstimated: false });
|
|
49222
|
+
items.push({ source, ecosystem: options.ecosystem, purpose, kind: "refresh", downloadBytes: null, downloadBytesEstimated: false });
|
|
49165
49223
|
continue;
|
|
49166
49224
|
}
|
|
49167
49225
|
const estimate = await estimateSeedBytes(source, options.ecosystem, options.abortSignal);
|
|
@@ -49169,6 +49227,7 @@ async function planSync(options) {
|
|
|
49169
49227
|
items.push({
|
|
49170
49228
|
source,
|
|
49171
49229
|
ecosystem: options.ecosystem,
|
|
49230
|
+
purpose,
|
|
49172
49231
|
kind: "seed",
|
|
49173
49232
|
downloadBytes: estimate.bytes,
|
|
49174
49233
|
downloadBytesEstimated: estimate.estimated
|
|
@@ -49307,32 +49366,38 @@ async function refreshOsv(options, meta3, item) {
|
|
|
49307
49366
|
return { source: "osv", ecosystem: item.ecosystem, status: "unchanged", rowCount: state?.recordCount ?? 0, message: null };
|
|
49308
49367
|
}
|
|
49309
49368
|
const append = [];
|
|
49310
|
-
|
|
49311
|
-
let skipped = 0;
|
|
49369
|
+
let failure = null;
|
|
49312
49370
|
for (const id of changed.ids) {
|
|
49313
49371
|
if (options.abortSignal && options.abortSignal.aborted) {
|
|
49314
|
-
|
|
49372
|
+
failure = "the refresh was cancelled before every changed advisory was re-read";
|
|
49315
49373
|
break;
|
|
49316
49374
|
}
|
|
49317
49375
|
try {
|
|
49318
49376
|
const rows = await fetchOsvAdvisoryRows(id, item.ecosystem, fetchOptionsFor(options, item));
|
|
49319
49377
|
for (const row of rows) append.push(row);
|
|
49320
|
-
|
|
49321
|
-
|
|
49322
|
-
|
|
49323
|
-
continue;
|
|
49378
|
+
} catch (err) {
|
|
49379
|
+
failure = id + " could not be re-read (" + errText(err) + ")";
|
|
49380
|
+
break;
|
|
49324
49381
|
}
|
|
49325
49382
|
}
|
|
49383
|
+
if (failure !== null) {
|
|
49384
|
+
if (state) state.etag = null;
|
|
49385
|
+
return {
|
|
49386
|
+
source: "osv",
|
|
49387
|
+
ecosystem: item.ecosystem,
|
|
49388
|
+
status: "error",
|
|
49389
|
+
rowCount: state?.recordCount ?? 0,
|
|
49390
|
+
message: failure + "; the previous OSV data is kept until a refresh re-reads all " + changed.ids.length + " changed advisories"
|
|
49391
|
+
};
|
|
49392
|
+
}
|
|
49326
49393
|
const path = advisoryFilePath(options.cacheDir, "osv", item.ecosystem);
|
|
49327
|
-
const rowCount = await rewriteRows(path, { dropAdvisoryIds:
|
|
49328
|
-
const complete = skipped === 0;
|
|
49329
|
-
const previousCursor = state?.cursorIso ?? null;
|
|
49394
|
+
const rowCount = await rewriteRows(path, { dropAdvisoryIds: new Set(changed.ids), append });
|
|
49330
49395
|
setSourceState(meta3, "osv", item.ecosystem, {
|
|
49331
49396
|
normalizerVersion: OSV_NORMALIZER_VERSION,
|
|
49332
49397
|
recordCount: rowCount,
|
|
49333
49398
|
refreshedAt: Date.now(),
|
|
49334
|
-
cursorIso:
|
|
49335
|
-
etag:
|
|
49399
|
+
cursorIso: changed.newestIso ?? state?.cursorIso ?? null,
|
|
49400
|
+
etag: changed.etag
|
|
49336
49401
|
});
|
|
49337
49402
|
return {
|
|
49338
49403
|
source: "osv",
|
|
@@ -49893,6 +49958,12 @@ function buildScanners(setup, cache) {
|
|
|
49893
49958
|
},
|
|
49894
49959
|
isEnabled: function isEnabled(ecosystem) {
|
|
49895
49960
|
return ecosystem === setup.ecosystem;
|
|
49961
|
+
},
|
|
49962
|
+
// Always ready: the cache was stamped (or not) before the scan, and a one-shot report with no
|
|
49963
|
+
// complete OSV evidence reports gemnasium unstamped under a warning instead of refusing it.
|
|
49964
|
+
// Only the portal, whose findings persist across scans, has to wait for the evidence.
|
|
49965
|
+
withdrawalEvidenceReady: function withdrawalEvidenceReady() {
|
|
49966
|
+
return true;
|
|
49896
49967
|
}
|
|
49897
49968
|
}));
|
|
49898
49969
|
}
|
|
@@ -50100,6 +50171,9 @@ function createUi(options) {
|
|
|
50100
50171
|
write("");
|
|
50101
50172
|
for (const item of seeds) {
|
|
50102
50173
|
write(" " + sourceLabel(item.source) + c.dim + " " + formatBytes(item.downloadBytes, item.downloadBytesEstimated) + c.reset);
|
|
50174
|
+
if (item.purpose === "withdrawal-evidence") {
|
|
50175
|
+
write(" " + c.dim + "used only to drop advisories GitHub has withdrawn" + c.reset);
|
|
50176
|
+
}
|
|
50103
50177
|
}
|
|
50104
50178
|
write("");
|
|
50105
50179
|
write(" " + c.dim + "Downloaded once from the upstream feeds, then kept fresh incrementally \u2014" + c.reset);
|
|
@@ -50208,6 +50282,9 @@ function createUi(options) {
|
|
|
50208
50282
|
write(" " + c.green + "\u2713" + c.reset + " " + sourceLabel(outcome.source) + c.dim + " " + verb + " (" + outcome.rowCount.toLocaleString() + " advisories)" + c.reset);
|
|
50209
50283
|
}
|
|
50210
50284
|
}
|
|
50285
|
+
function withdrawalsUnchecked() {
|
|
50286
|
+
write(" " + c.yellow + "!" + c.reset + c.dim + " OSV withdrawal data is not downloaded \u2014 GitLab gemnasium may list advisories GitHub has withdrawn" + c.reset);
|
|
50287
|
+
}
|
|
50211
50288
|
function sourceLabel(source) {
|
|
50212
50289
|
if (source === "osv") return "OSV";
|
|
50213
50290
|
if (source === "gemnasium") return "GitLab gemnasium";
|
|
@@ -50366,6 +50443,7 @@ function createUi(options) {
|
|
|
50366
50443
|
syncProgress,
|
|
50367
50444
|
syncRetry,
|
|
50368
50445
|
syncDone,
|
|
50446
|
+
withdrawalsUnchecked,
|
|
50369
50447
|
scanStart,
|
|
50370
50448
|
scanProject: scanProject2,
|
|
50371
50449
|
scanProjectDone,
|
|
@@ -50377,8 +50455,8 @@ function createUi(options) {
|
|
|
50377
50455
|
|
|
50378
50456
|
// src/help.ts
|
|
50379
50457
|
var CLI_VERSION = (function resolveVersion() {
|
|
50380
|
-
if ("3.7.
|
|
50381
|
-
return "3.7.
|
|
50458
|
+
if ("3.7.4".length > 0) {
|
|
50459
|
+
return "3.7.4";
|
|
50382
50460
|
}
|
|
50383
50461
|
const fromEnv = process.env.SENTINELLO_VERSION;
|
|
50384
50462
|
if (fromEnv && fromEnv.trim().length > 0) return fromEnv.trim();
|
|
@@ -50642,10 +50720,11 @@ async function runScan(options, cacheDir, ui) {
|
|
|
50642
50720
|
} else if (options.offline) {
|
|
50643
50721
|
ui.offlineNotice(options.includeNpmAudit);
|
|
50644
50722
|
}
|
|
50723
|
+
const meta3 = await readCacheMeta(cacheDir);
|
|
50645
50724
|
const resolved2 = await resolveProjects(projects);
|
|
50646
50725
|
const packageNames = collectPackageNames(resolved2);
|
|
50647
|
-
const cache = await loadCacheForPackages(cacheDir, DEFAULT_ECOSYSTEM, packageNames, sources);
|
|
50648
|
-
|
|
50726
|
+
const cache = await loadCacheForPackages(cacheDir, DEFAULT_ECOSYSTEM, packageNames, sources, meta3);
|
|
50727
|
+
if (sources.includes("gemnasium") && !cache.withdrawalsChecked) ui.withdrawalsUnchecked();
|
|
50649
50728
|
const registryStore = options.offline ? null : await loadRegistryStore(cacheDir);
|
|
50650
50729
|
const setup = {
|
|
50651
50730
|
cacheDir,
|