@cyclonedx/cdxgen 12.3.0 → 12.3.2

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 (121) hide show
  1. package/README.md +15 -5
  2. package/bin/audit.js +7 -0
  3. package/bin/cdxgen.js +241 -81
  4. package/bin/repl.js +138 -0
  5. package/data/rules/ai-agent-governance.yaml +249 -0
  6. package/data/rules/dependency-sources.yaml +41 -0
  7. package/data/rules/mcp-servers.yaml +304 -0
  8. package/data/rules/package-integrity.yaml +123 -0
  9. package/lib/audit/index.js +353 -29
  10. package/lib/audit/index.poku.js +247 -7
  11. package/lib/audit/reporters.js +26 -0
  12. package/lib/audit/scoring.js +262 -13
  13. package/lib/audit/scoring.poku.js +179 -0
  14. package/lib/audit/targets.js +391 -2
  15. package/lib/audit/targets.poku.js +416 -3
  16. package/lib/cli/index.js +588 -45
  17. package/lib/cli/index.poku.js +735 -1
  18. package/lib/evinser/evinser.js +8 -5
  19. package/lib/helpers/agentFormulationParser.js +318 -0
  20. package/lib/helpers/aiInventory.js +262 -0
  21. package/lib/helpers/aiInventory.poku.js +111 -0
  22. package/lib/helpers/analyzer.js +1769 -0
  23. package/lib/helpers/analyzer.poku.js +284 -3
  24. package/lib/helpers/auditCategories.js +76 -0
  25. package/lib/helpers/ciParsers/githubActions.js +140 -16
  26. package/lib/helpers/ciParsers/githubActions.poku.js +110 -0
  27. package/lib/helpers/communityAiConfigParser.js +672 -0
  28. package/lib/helpers/communityAiConfigParser.poku.js +63 -0
  29. package/lib/helpers/depsUtils.js +108 -0
  30. package/lib/helpers/depsUtils.poku.js +72 -1
  31. package/lib/helpers/display.js +325 -3
  32. package/lib/helpers/display.poku.js +301 -0
  33. package/lib/helpers/formulationParsers.js +28 -0
  34. package/lib/helpers/formulationParsers.poku.js +504 -1
  35. package/lib/helpers/jsonLike.js +102 -0
  36. package/lib/helpers/jsonLike.poku.js +34 -0
  37. package/lib/helpers/mcp.js +248 -0
  38. package/lib/helpers/mcp.poku.js +101 -0
  39. package/lib/helpers/mcpConfigParser.js +656 -0
  40. package/lib/helpers/mcpConfigParser.poku.js +126 -0
  41. package/lib/helpers/mcpDiscovery.js +84 -0
  42. package/lib/helpers/mcpDiscovery.poku.js +21 -0
  43. package/lib/helpers/protobom.js +3 -3
  44. package/lib/helpers/provenanceUtils.js +29 -4
  45. package/lib/helpers/provenanceUtils.poku.js +29 -3
  46. package/lib/helpers/registryProvenance.js +210 -0
  47. package/lib/helpers/registryProvenance.poku.js +144 -0
  48. package/lib/helpers/rustFormulationParser.js +330 -0
  49. package/lib/helpers/source.js +21 -2
  50. package/lib/helpers/source.poku.js +38 -0
  51. package/lib/helpers/utils.js +1331 -83
  52. package/lib/helpers/utils.poku.js +599 -188
  53. package/lib/helpers/vsixutils.js +12 -4
  54. package/lib/helpers/vsixutils.poku.js +34 -0
  55. package/lib/managers/binary.js +36 -12
  56. package/lib/managers/binary.poku.js +68 -0
  57. package/lib/managers/docker.js +59 -9
  58. package/lib/managers/docker.poku.js +61 -0
  59. package/lib/managers/piptree.js +12 -7
  60. package/lib/managers/piptree.poku.js +44 -0
  61. package/lib/stages/postgen/annotator.js +2 -1
  62. package/lib/stages/postgen/annotator.poku.js +15 -0
  63. package/lib/stages/postgen/auditBom.js +20 -6
  64. package/lib/stages/postgen/auditBom.poku.js +694 -1
  65. package/lib/stages/postgen/postgen.js +262 -11
  66. package/lib/stages/postgen/postgen.poku.js +306 -2
  67. package/lib/stages/postgen/ruleEngine.js +49 -1
  68. package/lib/stages/postgen/spdxConverter.poku.js +70 -0
  69. package/lib/stages/pregen/pregen.js +6 -4
  70. package/package.json +1 -1
  71. package/types/bin/repl.d.ts.map +1 -1
  72. package/types/lib/audit/index.d.ts.map +1 -1
  73. package/types/lib/audit/reporters.d.ts.map +1 -1
  74. package/types/lib/audit/scoring.d.ts.map +1 -1
  75. package/types/lib/audit/targets.d.ts +12 -0
  76. package/types/lib/audit/targets.d.ts.map +1 -1
  77. package/types/lib/cli/index.d.ts +2 -8
  78. package/types/lib/cli/index.d.ts.map +1 -1
  79. package/types/lib/evinser/evinser.d.ts.map +1 -1
  80. package/types/lib/helpers/agentFormulationParser.d.ts +19 -0
  81. package/types/lib/helpers/agentFormulationParser.d.ts.map +1 -0
  82. package/types/lib/helpers/aiInventory.d.ts +23 -0
  83. package/types/lib/helpers/aiInventory.d.ts.map +1 -0
  84. package/types/lib/helpers/analyzer.d.ts +10 -0
  85. package/types/lib/helpers/analyzer.d.ts.map +1 -1
  86. package/types/lib/helpers/auditCategories.d.ts +12 -0
  87. package/types/lib/helpers/auditCategories.d.ts.map +1 -0
  88. package/types/lib/helpers/ciParsers/githubActions.d.ts.map +1 -1
  89. package/types/lib/helpers/communityAiConfigParser.d.ts +29 -0
  90. package/types/lib/helpers/communityAiConfigParser.d.ts.map +1 -0
  91. package/types/lib/helpers/depsUtils.d.ts +8 -0
  92. package/types/lib/helpers/depsUtils.d.ts.map +1 -1
  93. package/types/lib/helpers/display.d.ts +17 -1
  94. package/types/lib/helpers/display.d.ts.map +1 -1
  95. package/types/lib/helpers/formulationParsers.d.ts.map +1 -1
  96. package/types/lib/helpers/jsonLike.d.ts +4 -0
  97. package/types/lib/helpers/jsonLike.d.ts.map +1 -0
  98. package/types/lib/helpers/mcp.d.ts +29 -0
  99. package/types/lib/helpers/mcp.d.ts.map +1 -0
  100. package/types/lib/helpers/mcpConfigParser.d.ts +30 -0
  101. package/types/lib/helpers/mcpConfigParser.d.ts.map +1 -0
  102. package/types/lib/helpers/mcpDiscovery.d.ts +5 -0
  103. package/types/lib/helpers/mcpDiscovery.d.ts.map +1 -0
  104. package/types/lib/helpers/provenanceUtils.d.ts +5 -3
  105. package/types/lib/helpers/provenanceUtils.d.ts.map +1 -1
  106. package/types/lib/helpers/registryProvenance.d.ts +9 -0
  107. package/types/lib/helpers/registryProvenance.d.ts.map +1 -1
  108. package/types/lib/helpers/rustFormulationParser.d.ts +17 -0
  109. package/types/lib/helpers/rustFormulationParser.d.ts.map +1 -0
  110. package/types/lib/helpers/source.d.ts.map +1 -1
  111. package/types/lib/helpers/utils.d.ts +31 -1
  112. package/types/lib/helpers/utils.d.ts.map +1 -1
  113. package/types/lib/helpers/vsixutils.d.ts.map +1 -1
  114. package/types/lib/managers/binary.d.ts.map +1 -1
  115. package/types/lib/managers/docker.d.ts.map +1 -1
  116. package/types/lib/managers/piptree.d.ts.map +1 -1
  117. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  118. package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
  119. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  120. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  121. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
@@ -170,6 +170,7 @@ describe("runAuditFromBoms()", () => {
170
170
  {
171
171
  maxTargets: 50,
172
172
  onProgress: (event) => progressEvents.push(event),
173
+ prioritizeDirectRuntime: true,
173
174
  scope: "required",
174
175
  trustedSelectionHelp:
175
176
  "Use --include-trusted to include them or --only-trusted to audit just those packages.",
@@ -179,6 +180,7 @@ describe("runAuditFromBoms()", () => {
179
180
  assert.strictEqual(report.summary.totalTargets, 1);
180
181
  assert.deepStrictEqual(collectAuditTargetsStub.firstCall.args[1], {
181
182
  maxTargets: 50,
183
+ prioritizeDirectRuntime: true,
182
184
  scope: "required",
183
185
  trusted: undefined,
184
186
  });
@@ -318,6 +320,43 @@ describe("finalizeAuditReport()", () => {
318
320
  assert.match(finalized.output, /@npmcli/);
319
321
  });
320
322
 
323
+ it("does not treat target analysis errors as fail-threshold hits on their own", () => {
324
+ const finalized = finalizeAuditReport(
325
+ {
326
+ results: [
327
+ {
328
+ assessment: {
329
+ severity: "critical",
330
+ },
331
+ error: "Unable to clone repository.",
332
+ findings: [],
333
+ status: "error",
334
+ target: {
335
+ name: "left-pad",
336
+ type: "npm",
337
+ },
338
+ },
339
+ ],
340
+ summary: {
341
+ analysisErrorCounts: { clone: 1 },
342
+ erroredTargets: 1,
343
+ inputBomCount: 1,
344
+ scannedTargets: 0,
345
+ skippedTargets: 0,
346
+ totalTargets: 1,
347
+ },
348
+ },
349
+ {
350
+ failSeverity: "high",
351
+ minSeverity: "low",
352
+ report: "console",
353
+ },
354
+ );
355
+
356
+ assert.strictEqual(finalized.exitCode, 0);
357
+ assert.match(finalized.output, /analysis error types: clone: 1/i);
358
+ });
359
+
321
360
  it("renders grouped predictive findings as SARIF 2.1.0 output", () => {
322
361
  const finalized = finalizeAuditReport(
323
362
  {
@@ -363,7 +402,7 @@ describe("finalizeAuditReport()", () => {
363
402
  },
364
403
  tool: {
365
404
  name: "cdx-audit",
366
- version: "12.3.0",
405
+ version: "12.3.1",
367
406
  },
368
407
  },
369
408
  {
@@ -377,7 +416,7 @@ describe("finalizeAuditReport()", () => {
377
416
  assert.strictEqual(finalized.exitCode, 0);
378
417
  assert.strictEqual(parsed.version, "2.1.0");
379
418
  assert.strictEqual(parsed.runs[0].tool.driver.name, "cdx-audit");
380
- assert.strictEqual(parsed.runs[0].tool.driver.version, "12.3.0");
419
+ assert.strictEqual(parsed.runs[0].tool.driver.version, "12.3.1");
381
420
  assert.strictEqual(parsed.runs[0].results.length, 1);
382
421
  assert.strictEqual(parsed.runs[0].results[0].ruleId, "PROV-001");
383
422
  assert.strictEqual(
@@ -421,7 +460,7 @@ describe("finalizeAuditReport()", () => {
421
460
  },
422
461
  tool: {
423
462
  name: "cdx-audit",
424
- version: "12.3.0",
463
+ version: "12.3.1",
425
464
  },
426
465
  },
427
466
  {
@@ -483,7 +522,7 @@ describe("finalizeAuditReport()", () => {
483
522
  },
484
523
  tool: {
485
524
  name: "cdx-audit",
486
- version: "12.3.0",
525
+ version: "12.3.1",
487
526
  },
488
527
  },
489
528
  {
@@ -750,6 +789,165 @@ describe("groupAuditResults()", () => {
750
789
  assert.strictEqual(groupedResults[0].grouping?.memberCount, 2);
751
790
  assert.strictEqual(groupedResults[1].target.name, "string-locale-compare");
752
791
  });
792
+
793
+ it("consolidates shared-repository CI findings across multiple packages", () => {
794
+ const groupedResults = groupAuditResults([
795
+ {
796
+ assessment: {
797
+ categoryCounts: {
798
+ "ci-permission": 2,
799
+ },
800
+ confidenceLabel: "high",
801
+ reasons: ["CI hygiene signals were observed."],
802
+ score: 42,
803
+ severity: "medium",
804
+ },
805
+ findings: [
806
+ {
807
+ category: "ci-permission",
808
+ location: {
809
+ file: ".github/workflows/release.yml",
810
+ },
811
+ message: "Unpinned privileged action",
812
+ ruleId: "CI-001",
813
+ },
814
+ ],
815
+ repoUrl: "https://github.com/example/mono.git",
816
+ status: "audited",
817
+ target: {
818
+ bomRefs: ["pkg:npm/pkg-a@1.0.0"],
819
+ name: "pkg-a",
820
+ namespace: "@acme",
821
+ purl: "pkg:npm/%40acme/pkg-a@1.0.0",
822
+ type: "npm",
823
+ version: "1.0.0",
824
+ },
825
+ },
826
+ {
827
+ assessment: {
828
+ categoryCounts: {
829
+ "ci-permission": 2,
830
+ },
831
+ confidenceLabel: "high",
832
+ reasons: ["CI hygiene signals were observed."],
833
+ score: 42,
834
+ severity: "medium",
835
+ },
836
+ findings: [
837
+ {
838
+ category: "ci-permission",
839
+ location: {
840
+ file: ".github/workflows/release.yml",
841
+ },
842
+ message: "Unpinned privileged action",
843
+ ruleId: "CI-001",
844
+ },
845
+ ],
846
+ repoUrl: "https://github.com/example/mono",
847
+ status: "audited",
848
+ target: {
849
+ bomRefs: ["pkg:npm/pkg-b@1.0.0"],
850
+ name: "pkg-b",
851
+ namespace: "@acme",
852
+ purl: "pkg:npm/%40acme/pkg-b@1.0.0",
853
+ type: "npm",
854
+ version: "1.0.0",
855
+ },
856
+ },
857
+ ]);
858
+
859
+ assert.strictEqual(groupedResults.length, 1);
860
+ assert.strictEqual(groupedResults[0].grouping?.kind, "shared-repo-ci");
861
+ assert.strictEqual(groupedResults[0].grouping?.memberCount, 2);
862
+ assert.strictEqual(groupedResults[0].findings.length, 1);
863
+ assert.match(
864
+ groupedResults[0].assessment.reasons.join(" "),
865
+ /same repository/i,
866
+ );
867
+ });
868
+
869
+ it("consolidates Cargo repository findings with the same predictive pattern", () => {
870
+ const groupedResults = groupAuditResults([
871
+ {
872
+ assessment: {
873
+ categoryCounts: {
874
+ "dependency-source": 1,
875
+ "package-integrity": 1,
876
+ },
877
+ confidenceLabel: "high",
878
+ reasons: ["Cargo build-surface signals increased review priority."],
879
+ score: 61,
880
+ severity: "high",
881
+ },
882
+ findings: [
883
+ {
884
+ category: "dependency-source",
885
+ message: "Mutable source for workspace build dependency",
886
+ ruleId: "PKG-001",
887
+ },
888
+ {
889
+ category: "package-integrity",
890
+ message: "Crate was yanked from the registry",
891
+ ruleId: "PROV-015",
892
+ },
893
+ ],
894
+ repoUrl: "https://github.com/example/rust-mono.git",
895
+ status: "audited",
896
+ target: {
897
+ bomRefs: ["pkg:cargo/core-crate@1.2.3"],
898
+ name: "core-crate",
899
+ purl: "pkg:cargo/core-crate@1.2.3",
900
+ type: "cargo",
901
+ version: "1.2.3",
902
+ },
903
+ },
904
+ {
905
+ assessment: {
906
+ categoryCounts: {
907
+ "dependency-source": 1,
908
+ "package-integrity": 1,
909
+ },
910
+ confidenceLabel: "high",
911
+ reasons: ["Cargo build-surface signals increased review priority."],
912
+ score: 59,
913
+ severity: "high",
914
+ },
915
+ findings: [
916
+ {
917
+ category: "dependency-source",
918
+ message: "Mutable source for workspace build dependency",
919
+ ruleId: "PKG-001",
920
+ },
921
+ {
922
+ category: "package-integrity",
923
+ message: "Crate was yanked from the registry",
924
+ ruleId: "PROV-015",
925
+ },
926
+ ],
927
+ repoUrl: "https://github.com/example/rust-mono",
928
+ status: "audited",
929
+ target: {
930
+ bomRefs: ["pkg:cargo/cli-crate@1.2.3"],
931
+ name: "cli-crate",
932
+ purl: "pkg:cargo/cli-crate@1.2.3",
933
+ type: "cargo",
934
+ version: "1.2.3",
935
+ },
936
+ },
937
+ ]);
938
+
939
+ assert.strictEqual(groupedResults.length, 1);
940
+ assert.strictEqual(groupedResults[0].grouping?.kind, "cargo-repository");
941
+ assert.strictEqual(groupedResults[0].grouping?.memberCount, 2);
942
+ assert.match(
943
+ groupedResults[0].grouping?.label,
944
+ /^cargo:https:\/\/github.com/,
945
+ );
946
+ assert.match(
947
+ groupedResults[0].assessment.reasons.join(" "),
948
+ /Cargo packages resolved to the same repository/i,
949
+ );
950
+ });
753
951
  });
754
952
 
755
953
  describe("buildTargetContextFindings()", () => {
@@ -1083,6 +1281,48 @@ describe("buildTargetContextFindings()", () => {
1083
1281
  assert.ok(findings.some((finding) => finding.ruleId === "PROV-014"));
1084
1282
  assert.ok(!findings.some((finding) => finding.ruleId === "PROV-009"));
1085
1283
  });
1284
+
1285
+ it("creates yanked and provenance-aware drift detectors for Cargo packages", () => {
1286
+ const recentTimestamp = new Date(
1287
+ Date.now() - 1000 * 60 * 60 * 12,
1288
+ ).toISOString();
1289
+ const oldTimestamp = new Date(
1290
+ Date.now() - 1000 * 60 * 60 * 24 * 120,
1291
+ ).toISOString();
1292
+ const findings = buildTargetContextFindings({
1293
+ bomRefs: ["pkg:cargo/serde@1.0.217"],
1294
+ name: "serde",
1295
+ purl: "pkg:cargo/serde@1.0.217",
1296
+ properties: [
1297
+ {
1298
+ name: "cdx:cargo:yanked",
1299
+ value: "true",
1300
+ },
1301
+ {
1302
+ name: "cdx:cargo:publishTime",
1303
+ value: recentTimestamp,
1304
+ },
1305
+ {
1306
+ name: "cdx:cargo:packageCreatedTime",
1307
+ value: oldTimestamp,
1308
+ },
1309
+ {
1310
+ name: "cdx:cargo:versionCount",
1311
+ value: "10",
1312
+ },
1313
+ {
1314
+ name: "cdx:cargo:publisherDrift",
1315
+ value: "true",
1316
+ },
1317
+ ],
1318
+ type: "cargo",
1319
+ version: "1.0.217",
1320
+ });
1321
+
1322
+ assert.ok(findings.some((finding) => finding.ruleId === "PROV-015"));
1323
+ assert.ok(findings.some((finding) => finding.ruleId === "PROV-016"));
1324
+ assert.ok(findings.some((finding) => finding.ruleId === "PROV-017"));
1325
+ });
1086
1326
  });
1087
1327
 
1088
1328
  describe("buildPythonSourceHeuristicFindings()", () => {
@@ -1242,7 +1482,7 @@ describe("formatPredictiveAnnotations()", () => {
1242
1482
  {
1243
1483
  name: "cdxgen",
1244
1484
  type: "application",
1245
- version: "12.3.0",
1485
+ version: "12.3.1",
1246
1486
  },
1247
1487
  ],
1248
1488
  },
@@ -1306,7 +1546,7 @@ describe("formatPredictiveAnnotations()", () => {
1306
1546
  {
1307
1547
  name: "cdxgen",
1308
1548
  type: "application",
1309
- version: "12.3.0",
1549
+ version: "12.3.1",
1310
1550
  },
1311
1551
  ],
1312
1552
  },
@@ -1380,7 +1620,7 @@ describe("audit reporters", () => {
1380
1620
  },
1381
1621
  tool: {
1382
1622
  name: "cdx-audit",
1383
- version: "12.3.0",
1623
+ version: "12.3.1",
1384
1624
  },
1385
1625
  };
1386
1626
 
@@ -27,6 +27,17 @@ function effectiveResults(report) {
27
27
  : report.results || [];
28
28
  }
29
29
 
30
+ function formatAnalysisErrorCounts(summary) {
31
+ const entries = Object.entries(summary?.analysisErrorCounts || {});
32
+ if (!entries.length) {
33
+ return undefined;
34
+ }
35
+ return entries
36
+ .sort(([left], [right]) => left.localeCompare(right))
37
+ .map(([errorType, count]) => `${errorType}: ${count}`)
38
+ .join(", ");
39
+ }
40
+
30
41
  function severityToSarifLevel(severity) {
31
42
  switch (severity) {
32
43
  case "critical":
@@ -488,6 +499,10 @@ export function renderConsoleReport(report, options = {}) {
488
499
  lines.push(`Scanned targets: ${report.summary.scannedTargets}`);
489
500
  lines.push(`Errored targets: ${report.summary.erroredTargets}`);
490
501
  lines.push(`Skipped targets: ${report.summary.skippedTargets}`);
502
+ const analysisErrorSummary = formatAnalysisErrorCounts(report.summary);
503
+ if (analysisErrorSummary) {
504
+ lines.push(`Analysis error types: ${analysisErrorSummary}`);
505
+ }
491
506
  if (report.summary.groupedResultCount) {
492
507
  lines.push(
493
508
  `Consolidated alert groups: ${report.summary.groupedResultCount}`,
@@ -499,12 +514,23 @@ export function renderConsoleReport(report, options = {}) {
499
514
  lines.push(
500
515
  `No predictive findings met or exceeded the configured severity threshold ('${minSeverity}').`,
501
516
  );
517
+ if (report.summary.erroredTargets > 0) {
518
+ lines.push(
519
+ "Some targets could not be fully analyzed, so review the recorded analysis errors before treating this rollup as complete.",
520
+ );
521
+ }
502
522
  return `${lines.join("\n")}\n`;
503
523
  }
504
524
  lines.push("Dependencies requiring your attention:");
505
525
  lines.push("");
506
526
  lines.push(renderActionTable(visibleResults));
507
527
  lines.push("");
528
+ if (report.summary.erroredTargets > 0) {
529
+ lines.push(
530
+ "Note: one or more targets could not be fully analyzed, so the final rollup may be incomplete until those analysis errors are resolved.",
531
+ );
532
+ lines.push("");
533
+ }
508
534
  lines.push(
509
535
  "Next step: review the file, repository, or package listed in 'What to do next'. If you maintain it, make the remediation directly; otherwise, open an upstream issue or discussion with the relevant maintainers, then re-run cdx-audit or cdxgen --bom-audit.",
510
536
  );