verimu 0.0.2 → 0.0.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/README.md +30 -10
- package/dist/cli.mjs +1616 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.cjs +779 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +422 -4
- package/dist/index.d.ts +422 -4
- package/dist/index.mjs +768 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -31,20 +31,28 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
ApiKeyRequiredError: () => ApiKeyRequiredError,
|
|
34
|
+
CargoScanner: () => CargoScanner,
|
|
34
35
|
ConsoleReporter: () => ConsoleReporter,
|
|
35
36
|
CveAggregator: () => CveAggregator,
|
|
36
37
|
CveSourceError: () => CveSourceError,
|
|
37
38
|
CycloneDxGenerator: () => CycloneDxGenerator,
|
|
39
|
+
GoScanner: () => GoScanner,
|
|
38
40
|
LockfileParseError: () => LockfileParseError,
|
|
41
|
+
MavenScanner: () => MavenScanner,
|
|
39
42
|
NoLockfileError: () => NoLockfileError,
|
|
40
43
|
NpmScanner: () => NpmScanner,
|
|
44
|
+
NugetScanner: () => NugetScanner,
|
|
41
45
|
OsvSource: () => OsvSource,
|
|
46
|
+
PipScanner: () => PipScanner,
|
|
47
|
+
RubyScanner: () => RubyScanner,
|
|
42
48
|
ScannerRegistry: () => ScannerRegistry,
|
|
49
|
+
VerimuApiClient: () => VerimuApiClient,
|
|
43
50
|
VerimuError: () => VerimuError,
|
|
44
51
|
generateSbom: () => generateSbom,
|
|
45
52
|
printReport: () => printReport,
|
|
46
53
|
scan: () => scan,
|
|
47
|
-
shouldFailCi: () => shouldFailCi
|
|
54
|
+
shouldFailCi: () => shouldFailCi,
|
|
55
|
+
uploadToVerimu: () => uploadToVerimu
|
|
48
56
|
});
|
|
49
57
|
module.exports = __toCommonJS(index_exports);
|
|
50
58
|
|
|
@@ -125,7 +133,8 @@ var PURL_TYPE_MAP = {
|
|
|
125
133
|
cargo: "cargo",
|
|
126
134
|
maven: "maven",
|
|
127
135
|
pip: "pypi",
|
|
128
|
-
go: "golang"
|
|
136
|
+
go: "golang",
|
|
137
|
+
ruby: "gem"
|
|
129
138
|
};
|
|
130
139
|
function buildPurl(name, version, ecosystem) {
|
|
131
140
|
const type = PURL_TYPE_MAP[ecosystem] || ecosystem;
|
|
@@ -142,7 +151,8 @@ function deriveSupplierName(packageName) {
|
|
|
142
151
|
}
|
|
143
152
|
|
|
144
153
|
// src/scan.ts
|
|
145
|
-
var
|
|
154
|
+
var import_promises8 = require("fs/promises");
|
|
155
|
+
var import_path8 = require("path");
|
|
146
156
|
|
|
147
157
|
// src/scanners/npm/npm-scanner.ts
|
|
148
158
|
var import_promises = require("fs/promises");
|
|
@@ -160,7 +170,7 @@ var VerimuError = class extends Error {
|
|
|
160
170
|
var NoLockfileError = class extends VerimuError {
|
|
161
171
|
constructor(projectPath) {
|
|
162
172
|
super(
|
|
163
|
-
`No supported lockfile found in ${projectPath}. Supported: package-lock.json (npm), packages.lock.json (NuGet), Cargo.lock (Rust)`,
|
|
173
|
+
`No supported lockfile found in ${projectPath}. Supported: package-lock.json (npm), packages.lock.json (NuGet), Cargo.lock (Rust), requirements.txt / Pipfile.lock (Python), pom.xml (Maven), go.sum (Go), Gemfile.lock (Ruby)`,
|
|
164
174
|
"NO_LOCKFILE"
|
|
165
175
|
);
|
|
166
176
|
this.name = "NoLockfileError";
|
|
@@ -296,26 +306,670 @@ var NpmScanner = class {
|
|
|
296
306
|
};
|
|
297
307
|
|
|
298
308
|
// src/scanners/nuget/nuget-scanner.ts
|
|
309
|
+
var import_promises2 = require("fs/promises");
|
|
310
|
+
var import_fs2 = require("fs");
|
|
311
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
299
312
|
var NugetScanner = class {
|
|
300
313
|
ecosystem = "nuget";
|
|
301
314
|
lockfileNames = ["packages.lock.json"];
|
|
302
|
-
async detect(
|
|
303
|
-
|
|
315
|
+
async detect(projectPath) {
|
|
316
|
+
const lockfilePath = import_path2.default.join(projectPath, "packages.lock.json");
|
|
317
|
+
return (0, import_fs2.existsSync)(lockfilePath) ? lockfilePath : null;
|
|
318
|
+
}
|
|
319
|
+
async scan(projectPath, lockfilePath) {
|
|
320
|
+
const lockfileRaw = await (0, import_promises2.readFile)(lockfilePath, "utf-8");
|
|
321
|
+
let lockfile;
|
|
322
|
+
try {
|
|
323
|
+
lockfile = JSON.parse(lockfileRaw);
|
|
324
|
+
} catch {
|
|
325
|
+
throw new LockfileParseError(lockfilePath, "Invalid JSON");
|
|
326
|
+
}
|
|
327
|
+
if (!lockfile.dependencies) {
|
|
328
|
+
throw new LockfileParseError(lockfilePath, 'Missing "dependencies" field');
|
|
329
|
+
}
|
|
330
|
+
const dependencies = this.parseLockfile(lockfile);
|
|
331
|
+
return {
|
|
332
|
+
projectPath,
|
|
333
|
+
ecosystem: "nuget",
|
|
334
|
+
dependencies,
|
|
335
|
+
lockfilePath,
|
|
336
|
+
scannedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Parses packages.lock.json and extracts dependencies across all
|
|
341
|
+
* target frameworks. Deduplicates by package name (keeps highest version
|
|
342
|
+
* if the same package appears under multiple frameworks).
|
|
343
|
+
*/
|
|
344
|
+
parseLockfile(lockfile) {
|
|
345
|
+
const depMap = /* @__PURE__ */ new Map();
|
|
346
|
+
for (const [_framework, packages] of Object.entries(lockfile.dependencies)) {
|
|
347
|
+
for (const [name, info] of Object.entries(packages)) {
|
|
348
|
+
if (!info.resolved) continue;
|
|
349
|
+
const isDirect = info.type === "Direct";
|
|
350
|
+
const existing = depMap.get(name);
|
|
351
|
+
if (!existing) {
|
|
352
|
+
depMap.set(name, {
|
|
353
|
+
name,
|
|
354
|
+
version: info.resolved,
|
|
355
|
+
direct: isDirect,
|
|
356
|
+
ecosystem: "nuget",
|
|
357
|
+
purl: this.buildPurl(name, info.resolved)
|
|
358
|
+
});
|
|
359
|
+
} else if (isDirect && !existing.direct) {
|
|
360
|
+
existing.direct = true;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return Array.from(depMap.values());
|
|
304
365
|
}
|
|
305
|
-
|
|
306
|
-
|
|
366
|
+
/**
|
|
367
|
+
* Builds a purl for a NuGet package.
|
|
368
|
+
* NuGet purls are straightforward: pkg:nuget/Name@Version
|
|
369
|
+
*/
|
|
370
|
+
buildPurl(name, version) {
|
|
371
|
+
return `pkg:nuget/${name}@${version}`;
|
|
307
372
|
}
|
|
308
373
|
};
|
|
309
374
|
|
|
310
375
|
// src/scanners/cargo/cargo-scanner.ts
|
|
376
|
+
var import_promises3 = require("fs/promises");
|
|
377
|
+
var import_fs3 = require("fs");
|
|
378
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
311
379
|
var CargoScanner = class {
|
|
312
380
|
ecosystem = "cargo";
|
|
313
381
|
lockfileNames = ["Cargo.lock"];
|
|
314
|
-
async detect(
|
|
382
|
+
async detect(projectPath) {
|
|
383
|
+
const lockfilePath = import_path3.default.join(projectPath, "Cargo.lock");
|
|
384
|
+
return (0, import_fs3.existsSync)(lockfilePath) ? lockfilePath : null;
|
|
385
|
+
}
|
|
386
|
+
async scan(projectPath, lockfilePath) {
|
|
387
|
+
const [lockfileRaw, cargoTomlRaw] = await Promise.all([
|
|
388
|
+
(0, import_promises3.readFile)(lockfilePath, "utf-8"),
|
|
389
|
+
(0, import_promises3.readFile)(import_path3.default.join(projectPath, "Cargo.toml"), "utf-8").catch(() => null)
|
|
390
|
+
]);
|
|
391
|
+
const packages = this.parseLockfile(lockfileRaw, lockfilePath);
|
|
392
|
+
const directNames = cargoTomlRaw ? this.parseCargoToml(cargoTomlRaw) : /* @__PURE__ */ new Set();
|
|
393
|
+
const rootName = packages.length > 0 ? packages[0].name : null;
|
|
394
|
+
const dependencies = [];
|
|
395
|
+
for (const pkg of packages) {
|
|
396
|
+
if (pkg.name === rootName && pkg.source === void 0) continue;
|
|
397
|
+
dependencies.push({
|
|
398
|
+
name: pkg.name,
|
|
399
|
+
version: pkg.version,
|
|
400
|
+
direct: directNames.has(pkg.name),
|
|
401
|
+
ecosystem: "cargo",
|
|
402
|
+
purl: this.buildPurl(pkg.name, pkg.version)
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
projectPath,
|
|
407
|
+
ecosystem: "cargo",
|
|
408
|
+
dependencies,
|
|
409
|
+
lockfilePath,
|
|
410
|
+
scannedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Parses Cargo.lock by splitting on [[package]] blocks.
|
|
415
|
+
* This is a lightweight parser that handles the regular structure
|
|
416
|
+
* of Cargo.lock without needing a full TOML parser.
|
|
417
|
+
*/
|
|
418
|
+
parseLockfile(content, lockfilePath) {
|
|
419
|
+
const packages = [];
|
|
420
|
+
const blocks = content.split(/^\[\[package\]\]$/m);
|
|
421
|
+
for (const block of blocks) {
|
|
422
|
+
if (!block.trim()) continue;
|
|
423
|
+
const name = this.extractField(block, "name");
|
|
424
|
+
const version = this.extractField(block, "version");
|
|
425
|
+
const source = this.extractField(block, "source");
|
|
426
|
+
if (name && version) {
|
|
427
|
+
packages.push({ name, version, source: source || void 0 });
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
if (packages.length === 0 && content.includes("[[package]]")) {
|
|
431
|
+
throw new LockfileParseError(lockfilePath, "Failed to parse any packages from Cargo.lock");
|
|
432
|
+
}
|
|
433
|
+
return packages;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Extracts a string field value from a TOML block.
|
|
437
|
+
* Handles: `name = "value"` format.
|
|
438
|
+
*/
|
|
439
|
+
extractField(block, fieldName) {
|
|
440
|
+
const regex = new RegExp(`^${fieldName}\\s*=\\s*"([^"]*)"`, "m");
|
|
441
|
+
const match = block.match(regex);
|
|
442
|
+
return match ? match[1] : null;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Parses Cargo.toml to extract direct dependency names.
|
|
446
|
+
* Looks for [dependencies] and [dev-dependencies] sections.
|
|
447
|
+
*/
|
|
448
|
+
parseCargoToml(content) {
|
|
449
|
+
const directNames = /* @__PURE__ */ new Set();
|
|
450
|
+
let inDepsSection = false;
|
|
451
|
+
for (const rawLine of content.split("\n")) {
|
|
452
|
+
const line = rawLine.trim();
|
|
453
|
+
if (line.startsWith("[")) {
|
|
454
|
+
inDepsSection = line === "[dependencies]" || line === "[dev-dependencies]" || line === "[build-dependencies]";
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
if (inDepsSection && line && !line.startsWith("#")) {
|
|
458
|
+
const match = line.match(/^([a-zA-Z0-9_-]+)\s*=/);
|
|
459
|
+
if (match) {
|
|
460
|
+
directNames.add(match[1]);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return directNames;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Builds a purl for a Cargo (crates.io) package.
|
|
468
|
+
*/
|
|
469
|
+
buildPurl(name, version) {
|
|
470
|
+
return `pkg:cargo/${name}@${version}`;
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
// src/scanners/pip/pip-scanner.ts
|
|
475
|
+
var import_promises4 = require("fs/promises");
|
|
476
|
+
var import_fs4 = require("fs");
|
|
477
|
+
var import_path4 = __toESM(require("path"), 1);
|
|
478
|
+
var PipScanner = class {
|
|
479
|
+
ecosystem = "pip";
|
|
480
|
+
lockfileNames = ["requirements.txt", "Pipfile.lock"];
|
|
481
|
+
async detect(projectPath) {
|
|
482
|
+
for (const lockfile of this.lockfileNames) {
|
|
483
|
+
const fullPath = import_path4.default.join(projectPath, lockfile);
|
|
484
|
+
if ((0, import_fs4.existsSync)(fullPath)) return fullPath;
|
|
485
|
+
}
|
|
315
486
|
return null;
|
|
316
487
|
}
|
|
317
|
-
async scan(
|
|
318
|
-
|
|
488
|
+
async scan(projectPath, lockfilePath) {
|
|
489
|
+
const raw = await (0, import_promises4.readFile)(lockfilePath, "utf-8");
|
|
490
|
+
const filename = import_path4.default.basename(lockfilePath);
|
|
491
|
+
let dependencies;
|
|
492
|
+
if (filename === "Pipfile.lock") {
|
|
493
|
+
dependencies = this.parsePipfileLock(raw, lockfilePath);
|
|
494
|
+
} else {
|
|
495
|
+
dependencies = this.parseRequirementsTxt(raw, lockfilePath);
|
|
496
|
+
}
|
|
497
|
+
return {
|
|
498
|
+
projectPath,
|
|
499
|
+
ecosystem: "pip",
|
|
500
|
+
dependencies,
|
|
501
|
+
lockfilePath,
|
|
502
|
+
scannedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Parses `requirements.txt` format.
|
|
507
|
+
*
|
|
508
|
+
* Supports:
|
|
509
|
+
* - `package==1.2.3` (pinned)
|
|
510
|
+
* - `package>=1.2.0` (minimum — uses the specified version)
|
|
511
|
+
* - `package~=1.2.0` (compatible release)
|
|
512
|
+
* - Comments (`#`) and blank lines are skipped
|
|
513
|
+
* - `-r other-file.txt` (include directive) — skipped for now
|
|
514
|
+
* - `--index-url` and other pip flags — skipped
|
|
515
|
+
*/
|
|
516
|
+
parseRequirementsTxt(content, lockfilePath) {
|
|
517
|
+
const deps = [];
|
|
518
|
+
for (const rawLine of content.split("\n")) {
|
|
519
|
+
const line = rawLine.trim();
|
|
520
|
+
if (!line || line.startsWith("#") || line.startsWith("-") || line.startsWith("--")) {
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
const match = line.match(/^([a-zA-Z0-9_][a-zA-Z0-9._-]*)\s*(?:[~=!<>]=?)\s*(.+)$/);
|
|
524
|
+
if (match) {
|
|
525
|
+
const [, name, versionSpec] = match;
|
|
526
|
+
const version = this.extractVersion(versionSpec);
|
|
527
|
+
if (name && version) {
|
|
528
|
+
deps.push({
|
|
529
|
+
name: this.normalizePipName(name),
|
|
530
|
+
version,
|
|
531
|
+
direct: true,
|
|
532
|
+
// requirements.txt doesn't distinguish
|
|
533
|
+
ecosystem: "pip",
|
|
534
|
+
purl: this.buildPurl(name, version)
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return deps;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Parses `Pipfile.lock` (JSON format from Pipenv).
|
|
543
|
+
*
|
|
544
|
+
* Structure:
|
|
545
|
+
* ```json
|
|
546
|
+
* {
|
|
547
|
+
* "_meta": { ... },
|
|
548
|
+
* "default": {
|
|
549
|
+
* "requests": { "version": "==2.31.0", ... }
|
|
550
|
+
* },
|
|
551
|
+
* "develop": {
|
|
552
|
+
* "pytest": { "version": "==7.4.0", ... }
|
|
553
|
+
* }
|
|
554
|
+
* }
|
|
555
|
+
* ```
|
|
556
|
+
*/
|
|
557
|
+
parsePipfileLock(content, lockfilePath) {
|
|
558
|
+
let lockfile;
|
|
559
|
+
try {
|
|
560
|
+
lockfile = JSON.parse(content);
|
|
561
|
+
} catch {
|
|
562
|
+
throw new LockfileParseError(lockfilePath, "Invalid JSON in Pipfile.lock");
|
|
563
|
+
}
|
|
564
|
+
const deps = [];
|
|
565
|
+
if (lockfile.default) {
|
|
566
|
+
for (const [name, info] of Object.entries(lockfile.default)) {
|
|
567
|
+
const version = info.version?.replace(/^==/, "") ?? "";
|
|
568
|
+
if (version) {
|
|
569
|
+
deps.push({
|
|
570
|
+
name: this.normalizePipName(name),
|
|
571
|
+
version,
|
|
572
|
+
direct: true,
|
|
573
|
+
ecosystem: "pip",
|
|
574
|
+
purl: this.buildPurl(name, version)
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
if (lockfile.develop) {
|
|
580
|
+
for (const [name, info] of Object.entries(lockfile.develop)) {
|
|
581
|
+
const version = info.version?.replace(/^==/, "") ?? "";
|
|
582
|
+
if (version) {
|
|
583
|
+
deps.push({
|
|
584
|
+
name: this.normalizePipName(name),
|
|
585
|
+
version,
|
|
586
|
+
direct: true,
|
|
587
|
+
ecosystem: "pip",
|
|
588
|
+
purl: this.buildPurl(name, version)
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return deps;
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Extracts the version number from a pip version specifier.
|
|
597
|
+
* "1.2.3" → "1.2.3"
|
|
598
|
+
* "1.2.3,<2.0" → "1.2.3"
|
|
599
|
+
*/
|
|
600
|
+
extractVersion(spec) {
|
|
601
|
+
const cleaned = spec.split(",")[0].trim();
|
|
602
|
+
return cleaned;
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Normalizes a pip package name per PEP 503.
|
|
606
|
+
* Converts to lowercase and replaces any run of [-_.] with a single hyphen.
|
|
607
|
+
*/
|
|
608
|
+
normalizePipName(name) {
|
|
609
|
+
return name.toLowerCase().replace(/[-_.]+/g, "-");
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Builds a purl for a PyPI package.
|
|
613
|
+
* Per purl spec, the type is "pypi" (not "pip").
|
|
614
|
+
*/
|
|
615
|
+
buildPurl(name, version) {
|
|
616
|
+
return `pkg:pypi/${this.normalizePipName(name)}@${version}`;
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
// src/scanners/maven/maven-scanner.ts
|
|
621
|
+
var import_promises5 = require("fs/promises");
|
|
622
|
+
var import_fs5 = require("fs");
|
|
623
|
+
var import_child_process = require("child_process");
|
|
624
|
+
var import_path5 = __toESM(require("path"), 1);
|
|
625
|
+
var MavenScanner = class {
|
|
626
|
+
ecosystem = "maven";
|
|
627
|
+
lockfileNames = ["pom.xml"];
|
|
628
|
+
/** Allow injection for testing */
|
|
629
|
+
execSyncFn;
|
|
630
|
+
constructor(execSyncImpl) {
|
|
631
|
+
this.execSyncFn = execSyncImpl ?? import_child_process.execSync;
|
|
632
|
+
}
|
|
633
|
+
async detect(projectPath) {
|
|
634
|
+
const pomPath = import_path5.default.join(projectPath, "pom.xml");
|
|
635
|
+
return (0, import_fs5.existsSync)(pomPath) ? pomPath : null;
|
|
636
|
+
}
|
|
637
|
+
async scan(projectPath, _lockfilePath) {
|
|
638
|
+
const depTreePath = import_path5.default.join(projectPath, "dependency-tree.txt");
|
|
639
|
+
if ((0, import_fs5.existsSync)(depTreePath)) {
|
|
640
|
+
const content = await (0, import_promises5.readFile)(depTreePath, "utf-8");
|
|
641
|
+
const dependencies = this.parseDependencyList(content, depTreePath);
|
|
642
|
+
return this.buildResult(projectPath, depTreePath, dependencies);
|
|
643
|
+
}
|
|
644
|
+
if (this.isMavenAvailable()) {
|
|
645
|
+
const output = this.runMavenDependencyList(projectPath);
|
|
646
|
+
const dependencies = this.parseDependencyList(output, "mvn dependency:list");
|
|
647
|
+
return this.buildResult(projectPath, import_path5.default.join(projectPath, "pom.xml"), dependencies);
|
|
648
|
+
}
|
|
649
|
+
throw new LockfileParseError(
|
|
650
|
+
import_path5.default.join(projectPath, "pom.xml"),
|
|
651
|
+
"Maven project detected (pom.xml found) but could not resolve dependencies. Either install Maven (`mvn` must be on $PATH) or pre-generate a dependency list:\n mvn dependency:list -DoutputFile=dependency-tree.txt -DappendOutput=true"
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Parses Maven `dependency:list` output.
|
|
656
|
+
*
|
|
657
|
+
* Each dependency line has the format:
|
|
658
|
+
* groupId:artifactId:type:version:scope
|
|
659
|
+
* groupId:artifactId:type:classifier:version:scope
|
|
660
|
+
*
|
|
661
|
+
* Lines are typically indented with leading whitespace.
|
|
662
|
+
*/
|
|
663
|
+
parseDependencyList(content, source) {
|
|
664
|
+
const deps = [];
|
|
665
|
+
const depPattern = /^\s*([a-zA-Z0-9._-]+):([a-zA-Z0-9._-]+):([a-z]+):(?:([a-zA-Z0-9._-]+):)?([a-zA-Z0-9._-]+):([a-z]+)/;
|
|
666
|
+
for (const rawLine of content.split("\n")) {
|
|
667
|
+
const line = rawLine.trim();
|
|
668
|
+
if (!line) continue;
|
|
669
|
+
const match = line.match(depPattern);
|
|
670
|
+
if (match) {
|
|
671
|
+
const groupId = match[1];
|
|
672
|
+
const artifactId = match[2];
|
|
673
|
+
const version = match[4] && match[5] ? match[5] : match[4] ?? match[5];
|
|
674
|
+
const scope = match[4] && match[5] ? match[6] : match[5] && match[6] ? match[6] : match[5];
|
|
675
|
+
const parts = line.split(":");
|
|
676
|
+
if (parts.length >= 5) {
|
|
677
|
+
const gId = parts[0].trim();
|
|
678
|
+
const aId = parts[1];
|
|
679
|
+
const ver = parts.length === 6 ? parts[4] : parts[3];
|
|
680
|
+
const scp = parts.length === 6 ? parts[5] : parts[4];
|
|
681
|
+
if (gId && aId && ver) {
|
|
682
|
+
const name = `${gId}:${aId}`;
|
|
683
|
+
deps.push({
|
|
684
|
+
name,
|
|
685
|
+
version: ver,
|
|
686
|
+
direct: scp === "compile" || scp === "runtime" || scp === "provided",
|
|
687
|
+
ecosystem: "maven",
|
|
688
|
+
purl: this.buildPurl(gId, aId, ver)
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
return deps;
|
|
695
|
+
}
|
|
696
|
+
/** Checks if `mvn` is available on PATH */
|
|
697
|
+
isMavenAvailable() {
|
|
698
|
+
try {
|
|
699
|
+
this.execSyncFn("mvn --version", { stdio: "pipe", timeout: 1e4 });
|
|
700
|
+
return true;
|
|
701
|
+
} catch {
|
|
702
|
+
return false;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Runs `mvn dependency:list` and returns the output.
|
|
707
|
+
*/
|
|
708
|
+
runMavenDependencyList(projectPath) {
|
|
709
|
+
try {
|
|
710
|
+
const output = this.execSyncFn(
|
|
711
|
+
"mvn dependency:list -DoutputType=text -DincludeScope=compile",
|
|
712
|
+
{
|
|
713
|
+
cwd: projectPath,
|
|
714
|
+
stdio: "pipe",
|
|
715
|
+
timeout: 12e4,
|
|
716
|
+
// 2 minute timeout
|
|
717
|
+
encoding: "utf-8"
|
|
718
|
+
}
|
|
719
|
+
);
|
|
720
|
+
return output.toString();
|
|
721
|
+
} catch (err) {
|
|
722
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
723
|
+
throw new LockfileParseError(
|
|
724
|
+
import_path5.default.join(projectPath, "pom.xml"),
|
|
725
|
+
`Failed to run 'mvn dependency:list': ${message}`
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Builds a purl for a Maven package.
|
|
731
|
+
* Format: pkg:maven/groupId/artifactId@version
|
|
732
|
+
*/
|
|
733
|
+
buildPurl(groupId, artifactId, version) {
|
|
734
|
+
return `pkg:maven/${groupId}/${artifactId}@${version}`;
|
|
735
|
+
}
|
|
736
|
+
buildResult(projectPath, lockfilePath, dependencies) {
|
|
737
|
+
return {
|
|
738
|
+
projectPath,
|
|
739
|
+
ecosystem: "maven",
|
|
740
|
+
dependencies,
|
|
741
|
+
lockfilePath,
|
|
742
|
+
scannedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
// src/scanners/go/go-scanner.ts
|
|
748
|
+
var import_promises6 = require("fs/promises");
|
|
749
|
+
var import_fs6 = require("fs");
|
|
750
|
+
var import_path6 = __toESM(require("path"), 1);
|
|
751
|
+
var GoScanner = class {
|
|
752
|
+
ecosystem = "go";
|
|
753
|
+
lockfileNames = ["go.sum"];
|
|
754
|
+
async detect(projectPath) {
|
|
755
|
+
const goSumPath = import_path6.default.join(projectPath, "go.sum");
|
|
756
|
+
return (0, import_fs6.existsSync)(goSumPath) ? goSumPath : null;
|
|
757
|
+
}
|
|
758
|
+
async scan(projectPath, lockfilePath) {
|
|
759
|
+
const [goSumRaw, goModRaw] = await Promise.all([
|
|
760
|
+
(0, import_promises6.readFile)(lockfilePath, "utf-8"),
|
|
761
|
+
(0, import_promises6.readFile)(import_path6.default.join(projectPath, "go.mod"), "utf-8").catch(() => null)
|
|
762
|
+
]);
|
|
763
|
+
const { directNames, indirectNames } = goModRaw ? this.parseGoMod(goModRaw) : { directNames: /* @__PURE__ */ new Set(), indirectNames: /* @__PURE__ */ new Set() };
|
|
764
|
+
const dependencies = this.parseGoSum(goSumRaw, lockfilePath, directNames, indirectNames);
|
|
765
|
+
return {
|
|
766
|
+
projectPath,
|
|
767
|
+
ecosystem: "go",
|
|
768
|
+
dependencies,
|
|
769
|
+
lockfilePath,
|
|
770
|
+
scannedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Parses go.sum and extracts unique module dependencies.
|
|
775
|
+
*
|
|
776
|
+
* Each module may appear twice in go.sum (once for the source archive,
|
|
777
|
+
* once for go.mod). We deduplicate by module path + version, keeping
|
|
778
|
+
* only the `h1:` entry (not the `/go.mod` entry).
|
|
779
|
+
*/
|
|
780
|
+
parseGoSum(content, lockfilePath, directNames, indirectNames) {
|
|
781
|
+
const depMap = /* @__PURE__ */ new Map();
|
|
782
|
+
for (const rawLine of content.split("\n")) {
|
|
783
|
+
const line = rawLine.trim();
|
|
784
|
+
if (!line) continue;
|
|
785
|
+
const parts = line.split(/\s+/);
|
|
786
|
+
if (parts.length < 3) continue;
|
|
787
|
+
const modulePath = parts[0];
|
|
788
|
+
let version = parts[1];
|
|
789
|
+
if (version.endsWith("/go.mod")) continue;
|
|
790
|
+
version = version.replace(/\+incompatible$/, "");
|
|
791
|
+
const key = `${modulePath}@${version}`;
|
|
792
|
+
if (depMap.has(key)) continue;
|
|
793
|
+
const isDirect = directNames.size > 0 || indirectNames.size > 0 ? directNames.has(modulePath) || (!indirectNames.has(modulePath) && !directNames.has(modulePath) ? false : directNames.has(modulePath)) : true;
|
|
794
|
+
depMap.set(key, {
|
|
795
|
+
name: modulePath,
|
|
796
|
+
version,
|
|
797
|
+
direct: isDirect,
|
|
798
|
+
ecosystem: "go",
|
|
799
|
+
purl: this.buildPurl(modulePath, version)
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
return Array.from(depMap.values());
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Parses go.mod to extract direct and indirect dependency names.
|
|
806
|
+
*
|
|
807
|
+
* Handles both single-line and block `require` directives:
|
|
808
|
+
* ```
|
|
809
|
+
* require github.com/pkg/errors v0.9.1
|
|
810
|
+
*
|
|
811
|
+
* require (
|
|
812
|
+
* github.com/gin-gonic/gin v1.9.1
|
|
813
|
+
* golang.org/x/text v0.14.0 // indirect
|
|
814
|
+
* )
|
|
815
|
+
* ```
|
|
816
|
+
*/
|
|
817
|
+
parseGoMod(content) {
|
|
818
|
+
const directNames = /* @__PURE__ */ new Set();
|
|
819
|
+
const indirectNames = /* @__PURE__ */ new Set();
|
|
820
|
+
let inRequireBlock = false;
|
|
821
|
+
for (const rawLine of content.split("\n")) {
|
|
822
|
+
const line = rawLine.trim();
|
|
823
|
+
if (line.startsWith("require ") && !line.includes("(")) {
|
|
824
|
+
const match = line.match(/^require\s+(\S+)\s+\S+(.*)$/);
|
|
825
|
+
if (match) {
|
|
826
|
+
const modulePath = match[1];
|
|
827
|
+
const rest = match[2];
|
|
828
|
+
if (rest.includes("// indirect")) {
|
|
829
|
+
indirectNames.add(modulePath);
|
|
830
|
+
} else {
|
|
831
|
+
directNames.add(modulePath);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
if (line === "require (" || line.startsWith("require (")) {
|
|
837
|
+
inRequireBlock = true;
|
|
838
|
+
continue;
|
|
839
|
+
}
|
|
840
|
+
if (inRequireBlock && line === ")") {
|
|
841
|
+
inRequireBlock = false;
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
if (inRequireBlock && line && !line.startsWith("//")) {
|
|
845
|
+
const match = line.match(/^(\S+)\s+\S+(.*)$/);
|
|
846
|
+
if (match) {
|
|
847
|
+
const modulePath = match[1];
|
|
848
|
+
const rest = match[2];
|
|
849
|
+
if (rest.includes("// indirect")) {
|
|
850
|
+
indirectNames.add(modulePath);
|
|
851
|
+
} else {
|
|
852
|
+
directNames.add(modulePath);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
return { directNames, indirectNames };
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Builds a purl for a Go module.
|
|
861
|
+
*
|
|
862
|
+
* Per purl spec, the type is "golang" and the module path
|
|
863
|
+
* uses `/` separators (no encoding needed for path segments).
|
|
864
|
+
*
|
|
865
|
+
* Example: `pkg:golang/github.com/gin-gonic/gin@v1.9.1`
|
|
866
|
+
*/
|
|
867
|
+
buildPurl(modulePath, version) {
|
|
868
|
+
return `pkg:golang/${modulePath}@${version}`;
|
|
869
|
+
}
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
// src/scanners/ruby/ruby-scanner.ts
|
|
873
|
+
var import_promises7 = require("fs/promises");
|
|
874
|
+
var import_fs7 = require("fs");
|
|
875
|
+
var import_path7 = __toESM(require("path"), 1);
|
|
876
|
+
var RubyScanner = class {
|
|
877
|
+
ecosystem = "ruby";
|
|
878
|
+
lockfileNames = ["Gemfile.lock"];
|
|
879
|
+
async detect(projectPath) {
|
|
880
|
+
const lockfilePath = import_path7.default.join(projectPath, "Gemfile.lock");
|
|
881
|
+
return (0, import_fs7.existsSync)(lockfilePath) ? lockfilePath : null;
|
|
882
|
+
}
|
|
883
|
+
async scan(projectPath, lockfilePath) {
|
|
884
|
+
const content = await (0, import_promises7.readFile)(lockfilePath, "utf-8");
|
|
885
|
+
const specs = this.parseSpecs(content, lockfilePath);
|
|
886
|
+
const directNames = this.parseDependencies(content);
|
|
887
|
+
const dependencies = specs.map(({ name, version }) => ({
|
|
888
|
+
name,
|
|
889
|
+
version,
|
|
890
|
+
direct: directNames.has(name),
|
|
891
|
+
ecosystem: "ruby",
|
|
892
|
+
purl: `pkg:gem/${name}@${version}`
|
|
893
|
+
}));
|
|
894
|
+
return {
|
|
895
|
+
projectPath,
|
|
896
|
+
ecosystem: "ruby",
|
|
897
|
+
dependencies,
|
|
898
|
+
lockfilePath,
|
|
899
|
+
scannedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Parses the GEM > specs section to extract all resolved gems.
|
|
904
|
+
*
|
|
905
|
+
* Gems at the top level of the specs section (indented 4 spaces) are
|
|
906
|
+
* resolved packages. Their sub-dependencies (indented 6+ spaces) are
|
|
907
|
+
* constraints, not separate entries — those sub-deps appear as their
|
|
908
|
+
* own top-level spec entries elsewhere.
|
|
909
|
+
*
|
|
910
|
+
* Format: ` gem-name (1.2.3)`
|
|
911
|
+
*/
|
|
912
|
+
parseSpecs(content, lockfilePath) {
|
|
913
|
+
const gems = [];
|
|
914
|
+
let inGemSection = false;
|
|
915
|
+
let inSpecs = false;
|
|
916
|
+
for (const rawLine of content.split("\n")) {
|
|
917
|
+
const line = rawLine;
|
|
918
|
+
if (line.length > 0 && line[0] !== " ") {
|
|
919
|
+
if (line.startsWith("GEM")) {
|
|
920
|
+
inGemSection = true;
|
|
921
|
+
inSpecs = false;
|
|
922
|
+
continue;
|
|
923
|
+
}
|
|
924
|
+
inGemSection = false;
|
|
925
|
+
inSpecs = false;
|
|
926
|
+
continue;
|
|
927
|
+
}
|
|
928
|
+
if (inGemSection && line.trimStart().startsWith("specs:")) {
|
|
929
|
+
inSpecs = true;
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
if (!inSpecs) continue;
|
|
933
|
+
const match = line.match(/^ {4}(\S+)\s+\(([^)]+)\)$/);
|
|
934
|
+
if (match) {
|
|
935
|
+
const [, name, version] = match;
|
|
936
|
+
gems.push({ name, version });
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
if (gems.length === 0) {
|
|
940
|
+
throw new LockfileParseError(
|
|
941
|
+
lockfilePath,
|
|
942
|
+
"No gems found in GEM specs section"
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
return gems;
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Parses the DEPENDENCIES section to get direct dependency names.
|
|
949
|
+
*
|
|
950
|
+
* Format: ` gem-name (>= 1.0)` or ` gem-name`
|
|
951
|
+
* The version constraint is optional and we only need the name.
|
|
952
|
+
*/
|
|
953
|
+
parseDependencies(content) {
|
|
954
|
+
const directNames = /* @__PURE__ */ new Set();
|
|
955
|
+
let inDependencies = false;
|
|
956
|
+
for (const rawLine of content.split("\n")) {
|
|
957
|
+
const line = rawLine;
|
|
958
|
+
if (line.length > 0 && line[0] !== " ") {
|
|
959
|
+
if (line.startsWith("DEPENDENCIES")) {
|
|
960
|
+
inDependencies = true;
|
|
961
|
+
continue;
|
|
962
|
+
}
|
|
963
|
+
if (inDependencies) break;
|
|
964
|
+
continue;
|
|
965
|
+
}
|
|
966
|
+
if (!inDependencies) continue;
|
|
967
|
+
const match = line.match(/^ {2}(\S+?)!?\s*(?:\(|$)/);
|
|
968
|
+
if (match) {
|
|
969
|
+
directNames.add(match[1]);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
return directNames;
|
|
319
973
|
}
|
|
320
974
|
};
|
|
321
975
|
|
|
@@ -326,8 +980,11 @@ var ScannerRegistry = class {
|
|
|
326
980
|
this.scanners = [
|
|
327
981
|
new NpmScanner(),
|
|
328
982
|
new NugetScanner(),
|
|
329
|
-
new CargoScanner()
|
|
330
|
-
|
|
983
|
+
new CargoScanner(),
|
|
984
|
+
new PipScanner(),
|
|
985
|
+
new MavenScanner(),
|
|
986
|
+
new GoScanner(),
|
|
987
|
+
new RubyScanner()
|
|
331
988
|
];
|
|
332
989
|
}
|
|
333
990
|
/**
|
|
@@ -622,7 +1279,8 @@ var OsvSource = class {
|
|
|
622
1279
|
cargo: "crates.io",
|
|
623
1280
|
maven: "Maven",
|
|
624
1281
|
pip: "PyPI",
|
|
625
|
-
go: "Go"
|
|
1282
|
+
go: "Go",
|
|
1283
|
+
ruby: "RubyGems"
|
|
626
1284
|
};
|
|
627
1285
|
return map[ecosystem] ?? ecosystem;
|
|
628
1286
|
}
|
|
@@ -787,6 +1445,76 @@ function severityBadge(s) {
|
|
|
787
1445
|
return badges[s] ?? "[???] ";
|
|
788
1446
|
}
|
|
789
1447
|
|
|
1448
|
+
// src/api/client.ts
|
|
1449
|
+
var DEFAULT_API_BASE = "https://api.verimu.com";
|
|
1450
|
+
var VerimuApiClient = class {
|
|
1451
|
+
baseUrl;
|
|
1452
|
+
apiKey;
|
|
1453
|
+
constructor(apiKey, baseUrl) {
|
|
1454
|
+
this.apiKey = apiKey;
|
|
1455
|
+
this.baseUrl = (baseUrl ?? DEFAULT_API_BASE).replace(/\/+$/, "");
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Upsert a project — finds by name or creates it.
|
|
1459
|
+
* Used so `npx verimu` auto-registers projects without manual dashboard setup.
|
|
1460
|
+
*/
|
|
1461
|
+
async upsertProject(opts) {
|
|
1462
|
+
const res = await fetch(`${this.baseUrl}/api/projects/upsert`, {
|
|
1463
|
+
method: "POST",
|
|
1464
|
+
headers: this.headers(),
|
|
1465
|
+
body: JSON.stringify({
|
|
1466
|
+
name: opts.name,
|
|
1467
|
+
ecosystem: this.mapEcosystem(opts.ecosystem),
|
|
1468
|
+
repository_url: opts.repositoryUrl ?? null,
|
|
1469
|
+
platform: opts.platform ?? null
|
|
1470
|
+
})
|
|
1471
|
+
});
|
|
1472
|
+
if (!res.ok) {
|
|
1473
|
+
const body = await res.text();
|
|
1474
|
+
throw new Error(`Verimu API: upsert project failed (${res.status}): ${body}`);
|
|
1475
|
+
}
|
|
1476
|
+
return res.json();
|
|
1477
|
+
}
|
|
1478
|
+
/**
|
|
1479
|
+
* Upload a CycloneDX SBOM to a project and trigger CVE scanning.
|
|
1480
|
+
*/
|
|
1481
|
+
async uploadSbom(projectId, sbomContent) {
|
|
1482
|
+
const sbomJson = JSON.parse(sbomContent);
|
|
1483
|
+
const res = await fetch(`${this.baseUrl}/api/projects/${projectId}/scan`, {
|
|
1484
|
+
method: "POST",
|
|
1485
|
+
headers: this.headers(),
|
|
1486
|
+
body: JSON.stringify(sbomJson)
|
|
1487
|
+
});
|
|
1488
|
+
if (!res.ok) {
|
|
1489
|
+
const body = await res.text();
|
|
1490
|
+
throw new Error(`Verimu API: upload SBOM failed (${res.status}): ${body}`);
|
|
1491
|
+
}
|
|
1492
|
+
return res.json();
|
|
1493
|
+
}
|
|
1494
|
+
headers() {
|
|
1495
|
+
return {
|
|
1496
|
+
"Content-Type": "application/json",
|
|
1497
|
+
"X-API-Key": this.apiKey
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
/**
|
|
1501
|
+
* Maps internal ecosystem names to what the backend expects.
|
|
1502
|
+
* Currently 1:1, but keeps the mapping explicit.
|
|
1503
|
+
*/
|
|
1504
|
+
mapEcosystem(eco) {
|
|
1505
|
+
const map = {
|
|
1506
|
+
npm: "npm",
|
|
1507
|
+
pip: "pip",
|
|
1508
|
+
maven: "maven",
|
|
1509
|
+
nuget: "nuget",
|
|
1510
|
+
go: "gomod",
|
|
1511
|
+
cargo: "cargo",
|
|
1512
|
+
ruby: "bundler"
|
|
1513
|
+
};
|
|
1514
|
+
return map[eco] ?? eco;
|
|
1515
|
+
}
|
|
1516
|
+
};
|
|
1517
|
+
|
|
790
1518
|
// src/scan.ts
|
|
791
1519
|
async function scan(config) {
|
|
792
1520
|
const {
|
|
@@ -798,7 +1526,7 @@ async function scan(config) {
|
|
|
798
1526
|
const scanResult = await registry.detectAndScan(projectPath);
|
|
799
1527
|
const sbomGenerator = new CycloneDxGenerator();
|
|
800
1528
|
const sbom = sbomGenerator.generate(scanResult);
|
|
801
|
-
await (0,
|
|
1529
|
+
await (0, import_promises8.writeFile)(sbomOutput, sbom.content, "utf-8");
|
|
802
1530
|
let cveCheck;
|
|
803
1531
|
if (skipCveCheck) {
|
|
804
1532
|
cveCheck = {
|
|
@@ -831,8 +1559,35 @@ async function scan(config) {
|
|
|
831
1559
|
summary,
|
|
832
1560
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
833
1561
|
};
|
|
1562
|
+
if (config.apiKey) {
|
|
1563
|
+
try {
|
|
1564
|
+
const uploadResult = await uploadToVerimu(report, config);
|
|
1565
|
+
report.upload = uploadResult;
|
|
1566
|
+
} catch {
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
834
1569
|
return report;
|
|
835
1570
|
}
|
|
1571
|
+
async function uploadToVerimu(report, config) {
|
|
1572
|
+
if (!config.apiKey) {
|
|
1573
|
+
throw new Error("API key required for upload");
|
|
1574
|
+
}
|
|
1575
|
+
const client = new VerimuApiClient(config.apiKey, config.apiBaseUrl);
|
|
1576
|
+
const projectName = (0, import_path8.basename)(config.projectPath);
|
|
1577
|
+
const upsertRes = await client.upsertProject({
|
|
1578
|
+
name: projectName,
|
|
1579
|
+
ecosystem: report.project.ecosystem
|
|
1580
|
+
});
|
|
1581
|
+
const projectId = upsertRes.project.id;
|
|
1582
|
+
const scanRes = await client.uploadSbom(projectId, report.sbom.content);
|
|
1583
|
+
return {
|
|
1584
|
+
projectId,
|
|
1585
|
+
projectCreated: upsertRes.created,
|
|
1586
|
+
totalDependencies: scanRes.summary.total_dependencies,
|
|
1587
|
+
vulnerableDependencies: scanRes.summary.vulnerable_dependencies,
|
|
1588
|
+
dashboardUrl: `https://app.verimu.com/dashboard/projects/${projectId}`
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
836
1591
|
function shouldFailCi(report, threshold) {
|
|
837
1592
|
const severityOrder2 = {
|
|
838
1593
|
CRITICAL: 0,
|
|
@@ -853,19 +1608,27 @@ function printReport(report) {
|
|
|
853
1608
|
// Annotate the CommonJS export names for ESM import in node:
|
|
854
1609
|
0 && (module.exports = {
|
|
855
1610
|
ApiKeyRequiredError,
|
|
1611
|
+
CargoScanner,
|
|
856
1612
|
ConsoleReporter,
|
|
857
1613
|
CveAggregator,
|
|
858
1614
|
CveSourceError,
|
|
859
1615
|
CycloneDxGenerator,
|
|
1616
|
+
GoScanner,
|
|
860
1617
|
LockfileParseError,
|
|
1618
|
+
MavenScanner,
|
|
861
1619
|
NoLockfileError,
|
|
862
1620
|
NpmScanner,
|
|
1621
|
+
NugetScanner,
|
|
863
1622
|
OsvSource,
|
|
1623
|
+
PipScanner,
|
|
1624
|
+
RubyScanner,
|
|
864
1625
|
ScannerRegistry,
|
|
1626
|
+
VerimuApiClient,
|
|
865
1627
|
VerimuError,
|
|
866
1628
|
generateSbom,
|
|
867
1629
|
printReport,
|
|
868
1630
|
scan,
|
|
869
|
-
shouldFailCi
|
|
1631
|
+
shouldFailCi,
|
|
1632
|
+
uploadToVerimu
|
|
870
1633
|
});
|
|
871
1634
|
//# sourceMappingURL=index.cjs.map
|