@mitre/hdf-diff 3.3.2 → 3.4.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/README.md CHANGED
@@ -89,16 +89,32 @@ hdf diff old-results.json new-results.json
89
89
  hdf diff old-results.json new-results.json --format markdown
90
90
  hdf diff old-results.json new-results.json --json
91
91
 
92
- # System drift detection
92
+ # System drift detection (auto-detected from document type)
93
93
  hdf diff old-system.json new-system.json
94
94
 
95
95
  # SBOM comparison
96
- hdf diff --sbom old-sbom.json new-sbom.json
96
+ hdf diff --sbom old.cdx.json new.cdx.json
97
97
 
98
- # Baseline mode (golden baseline vs current scan)
99
- hdf diff --mode baseline golden.json current.json
98
+ # Encode the security outcome in the exit code (10=fixes, 11=regressions, 12=mixed, 13=baseline change)
99
+ hdf diff old-results.json new-results.json --detailed-exitcode
100
100
  ```
101
101
 
102
+ Example output — a scan where one requirement regressed and one was added:
103
+
104
+ ```console
105
+ $ hdf diff old-results.json new-results.json
106
+ HDF Comparison: old-results.json → new-results.json
107
+
108
+ ID Title Old Status New Status State
109
+ ------- ----------------------------- ---------- ---------- ---------
110
+ REQ-001 Test Requirement passed failed regressed
111
+ REQ-002 Audit logging must be enabled - passed new
112
+
113
+ Summary: 0 fixed, 1 regressed, 1 new, 0 absent, 0 unchanged, 0 updated (2 total)
114
+ ```
115
+
116
+ The same comparison with `--format markdown` renders a GitHub-friendly table; `--json` emits an HDF Comparison document (`formatVersion`, `summary`, `requirementDiffs[]`) suitable for CI gating.
117
+
102
118
  ## Go usage
103
119
 
104
120
  The diff engine is also available as a Go module:
package/dist/index.d.ts CHANGED
@@ -235,9 +235,13 @@ interface HDFComparison {
235
235
  baselineDiffs: BaselineDiff[];
236
236
  /** Per-requirement diffs, sorted by id */
237
237
  requirementDiffs: RequirementDiff[];
238
- /** Per-component diffs (systemDrift mode only) */
238
+ /** Per-component diffs (systemDrift mode only). A component's BOM changes
239
+ * surface as a field change on its boms[]. */
239
240
  componentDiffs?: ComponentDiff[];
240
- /** Per-package diffs from embedded SBOM comparison (systemDrift mode only) */
241
+ /** RESERVED not emitted by the current systemDrift comparison (component
242
+ * BOM changes surface via componentDiffs boms[] field changes; standalone SBOM
243
+ * package diffing is a separate `hdf diff <sbom> <sbom>` output). Retained for
244
+ * a future normalized package-level diff. */
241
245
  packageDiffs?: PackageDiff[];
242
246
  /** URI identifying the system being compared (systemDrift mode only) */
243
247
  systemRef?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/sbom.ts","../src/types.ts","../src/diff.ts","../src/status.ts","../src/summary.ts","../src/normalize.ts","../src/matching/types.ts","../src/matching/exact-id.ts","../src/matching/mapped-id.ts","../src/matching/cci-match.ts","../src/matching/fuzzy-match.ts","../src/matching/srg-deterministic.ts","../src/matching/srg-cci-tiebreak.ts","../src/matching/vendor-fuzzy-title.ts","../src/matching/index.ts","../src/validate.ts","../src/exit-codes.ts","../src/renderers/types.ts","../src/renderers/json.ts","../src/renderers/markdown.ts","../src/renderers/terminal.ts","../src/renderers/csv.ts","../src/renderers/index.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/sbom.ts","../src/types.ts","../src/diff.ts","../src/status.ts","../src/summary.ts","../src/normalize.ts","../src/matching/types.ts","../src/matching/exact-id.ts","../src/matching/mapped-id.ts","../src/matching/cci-match.ts","../src/matching/fuzzy-match.ts","../src/matching/srg-deterministic.ts","../src/matching/srg-cci-tiebreak.ts","../src/matching/vendor-fuzzy-title.ts","../src/matching/index.ts","../src/validate.ts","../src/exit-codes.ts","../src/renderers/types.ts","../src/renderers/json.ts","../src/renderers/markdown.ts","../src/renderers/terminal.ts","../src/renderers/csv.ts","../src/renderers/index.ts"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1203,165 +1203,6 @@ function validateComparison(doc) {
1203
1203
  };
1204
1204
  }
1205
1205
  //#endregion
1206
- //#region src/sbom.ts
1207
- /**
1208
- * Compare two SBOM documents (CycloneDX or SPDX JSON strings) and return
1209
- * package-level diffs.
1210
- *
1211
- * @param oldJson - JSON string of the old SBOM document
1212
- * @param newJson - JSON string of the new SBOM document
1213
- * @returns Structured diff result with per-package state and aggregate counts
1214
- * @throws Error if either input is not valid JSON or not a recognized SBOM format
1215
- */
1216
- function diffSboms(oldJson, newJson) {
1217
- const oldParsed = JSON.parse(oldJson);
1218
- const newParsed = JSON.parse(newJson);
1219
- const oldPackages = extractPackages(oldParsed);
1220
- const newPackages = extractPackages(newParsed);
1221
- const oldMap = buildPackageMap(oldPackages);
1222
- const newMap = buildPackageMap(newPackages);
1223
- const diffs = [];
1224
- const seen = /* @__PURE__ */ new Set();
1225
- for (const [key, oldPkg] of oldMap) {
1226
- seen.add(key);
1227
- const newPkg = newMap.get(key);
1228
- if (newPkg) if (oldPkg.version !== newPkg.version) diffs.push({
1229
- purl: newPkg.purl || newPkg.name,
1230
- name: newPkg.name,
1231
- state: "updated",
1232
- oldVersion: oldPkg.version,
1233
- newVersion: newPkg.version,
1234
- licenses: newPkg.licenses.length > 0 ? newPkg.licenses : void 0
1235
- });
1236
- else diffs.push({
1237
- purl: oldPkg.purl || oldPkg.name,
1238
- name: oldPkg.name,
1239
- state: "unchanged"
1240
- });
1241
- else diffs.push({
1242
- purl: oldPkg.purl || oldPkg.name,
1243
- name: oldPkg.name,
1244
- state: "removed",
1245
- oldVersion: oldPkg.version
1246
- });
1247
- }
1248
- for (const [key, newPkg] of newMap) if (!seen.has(key)) diffs.push({
1249
- purl: newPkg.purl || newPkg.name,
1250
- name: newPkg.name,
1251
- state: "added",
1252
- newVersion: newPkg.version,
1253
- licenses: newPkg.licenses.length > 0 ? newPkg.licenses : void 0
1254
- });
1255
- diffs.sort((a, b) => a.name.localeCompare(b.name));
1256
- let added = 0;
1257
- let removed = 0;
1258
- let updated = 0;
1259
- let unchanged = 0;
1260
- for (const d of diffs) switch (d.state) {
1261
- case "added":
1262
- added++;
1263
- break;
1264
- case "removed":
1265
- removed++;
1266
- break;
1267
- case "updated":
1268
- updated++;
1269
- break;
1270
- case "unchanged":
1271
- unchanged++;
1272
- break;
1273
- }
1274
- return {
1275
- packageDiffs: diffs,
1276
- added,
1277
- removed,
1278
- updated,
1279
- unchanged
1280
- };
1281
- }
1282
- /**
1283
- * Extract packages from an SBOM document, auto-detecting the format.
1284
- */
1285
- function extractPackages(doc) {
1286
- if (doc["bomFormat"] === "CycloneDX") return extractCycloneDXPackages(doc);
1287
- if (typeof doc["spdxVersion"] === "string") return extractSPDXPackages(doc);
1288
- throw new Error("Unrecognized SBOM format: expected CycloneDX (bomFormat) or SPDX (spdxVersion)");
1289
- }
1290
- /**
1291
- * Extract packages from a CycloneDX document.
1292
- * Reads components[] with purl, name, version fields.
1293
- */
1294
- function extractCycloneDXPackages(doc) {
1295
- const components = doc["components"];
1296
- if (!Array.isArray(components)) return [];
1297
- return components.map((comp) => {
1298
- const licenses = extractCycloneDXLicenses(comp);
1299
- return {
1300
- purl: comp["purl"] ?? "",
1301
- name: comp["name"] ?? "",
1302
- version: comp["version"] ?? "",
1303
- licenses
1304
- };
1305
- });
1306
- }
1307
- /**
1308
- * Extract license strings from a CycloneDX component.
1309
- * CycloneDX licenses can be in `licenses[].license.id` or `licenses[].license.name`.
1310
- */
1311
- function extractCycloneDXLicenses(comp) {
1312
- const licensesArr = comp["licenses"];
1313
- if (!Array.isArray(licensesArr)) return [];
1314
- const result = [];
1315
- for (const entry of licensesArr) {
1316
- const license = entry["license"];
1317
- if (license) {
1318
- const id = license["id"];
1319
- const name = license["name"];
1320
- if (id) result.push(id);
1321
- else if (name) result.push(name);
1322
- }
1323
- }
1324
- return result;
1325
- }
1326
- /**
1327
- * Extract packages from an SPDX document.
1328
- * Reads packages[] with name, versionInfo, and externalRefs (for PURL).
1329
- */
1330
- function extractSPDXPackages(doc) {
1331
- const packages = doc["packages"];
1332
- if (!Array.isArray(packages)) return [];
1333
- return packages.map((pkg) => {
1334
- const purl = extractSPDXPurl(pkg);
1335
- const license = pkg["licenseConcluded"] ?? pkg["licenseDeclared"] ?? "";
1336
- return {
1337
- purl,
1338
- name: pkg["name"] ?? "",
1339
- version: pkg["versionInfo"] ?? "",
1340
- licenses: license && license !== "NOASSERTION" ? [license] : []
1341
- };
1342
- });
1343
- }
1344
- /**
1345
- * Extract a PURL from an SPDX package's externalRefs array.
1346
- */
1347
- function extractSPDXPurl(pkg) {
1348
- const refs = pkg["externalRefs"];
1349
- if (!Array.isArray(refs)) return "";
1350
- for (const ref of refs) if (ref["referenceType"] === "purl") return ref["referenceLocator"] ?? "";
1351
- return "";
1352
- }
1353
- /**
1354
- * Build a lookup map of packages indexed by name (version-independent key).
1355
- */
1356
- function buildPackageMap(packages) {
1357
- const map = /* @__PURE__ */ new Map();
1358
- for (const pkg of packages) {
1359
- const key = pkg.name || pkg.purl;
1360
- if (key) map.set(key, pkg);
1361
- }
1362
- return map;
1363
- }
1364
- //#endregion
1365
1206
  //#region src/diff.ts
1366
1207
  const DEFAULT_TRACKED_FIELDS = [
1367
1208
  "impact",
@@ -2197,7 +2038,8 @@ const COMPONENT_TRACKED_FIELDS = [
2197
2038
  "description",
2198
2039
  "baselineRefs",
2199
2040
  "inputOverrides",
2200
- "sbomRef",
2041
+ "boms",
2042
+ "integrity",
2201
2043
  "targetSelector"
2202
2044
  ];
2203
2045
  /**
@@ -2279,8 +2121,6 @@ function diffSystems(oldSystem, newSystem, options) {
2279
2121
  const dataFlowChanges = diffDataFlows(oldSystem, newSystem);
2280
2122
  if (dataFlowChanges.length > 0) extensions["dataFlowChanges"] = dataFlowChanges;
2281
2123
  if (Object.keys(extensions).length > 0) comparison.extensions = extensions;
2282
- const allPackageDiffs = diffEmbeddedSboms(pairs);
2283
- if (allPackageDiffs.length > 0) comparison.packageDiffs = allPackageDiffs;
2284
2124
  if (options?.validateOutput) {
2285
2125
  const result = validateComparison(comparison);
2286
2126
  /* c8 ignore start */
@@ -2391,24 +2231,6 @@ function diffDataFlows(oldSystem, newSystem) {
2391
2231
  return changes;
2392
2232
  }
2393
2233
  /**
2394
- * Diff embedded SBOMs across matched component pairs.
2395
- */
2396
- function diffEmbeddedSboms(pairs) {
2397
- const allDiffs = [];
2398
- for (const { oldComp, newComp } of pairs) {
2399
- if (!oldComp || !newComp) continue;
2400
- const oldSbom = oldComp["sbom"];
2401
- const newSbom = newComp["sbom"];
2402
- if (!oldSbom || !newSbom) continue;
2403
- if (typeof oldSbom !== "object" || typeof newSbom !== "object") continue;
2404
- try {
2405
- const result = diffSboms(JSON.stringify(oldSbom), JSON.stringify(newSbom));
2406
- allDiffs.push(...result.packageDiffs);
2407
- } catch {}
2408
- }
2409
- return allDiffs;
2410
- }
2411
- /**
2412
2234
  * Compute field-level changes for component or system fields.
2413
2235
  * Uses JSON Patch-like op/path format.
2414
2236
  */
@@ -2894,6 +2716,165 @@ function render(comparison, format, options) {
2894
2716
  }
2895
2717
  }
2896
2718
  //#endregion
2719
+ //#region src/sbom.ts
2720
+ /**
2721
+ * Compare two SBOM documents (CycloneDX or SPDX JSON strings) and return
2722
+ * package-level diffs.
2723
+ *
2724
+ * @param oldJson - JSON string of the old SBOM document
2725
+ * @param newJson - JSON string of the new SBOM document
2726
+ * @returns Structured diff result with per-package state and aggregate counts
2727
+ * @throws Error if either input is not valid JSON or not a recognized SBOM format
2728
+ */
2729
+ function diffSboms(oldJson, newJson) {
2730
+ const oldParsed = JSON.parse(oldJson);
2731
+ const newParsed = JSON.parse(newJson);
2732
+ const oldPackages = extractPackages(oldParsed);
2733
+ const newPackages = extractPackages(newParsed);
2734
+ const oldMap = buildPackageMap(oldPackages);
2735
+ const newMap = buildPackageMap(newPackages);
2736
+ const diffs = [];
2737
+ const seen = /* @__PURE__ */ new Set();
2738
+ for (const [key, oldPkg] of oldMap) {
2739
+ seen.add(key);
2740
+ const newPkg = newMap.get(key);
2741
+ if (newPkg) if (oldPkg.version !== newPkg.version) diffs.push({
2742
+ purl: newPkg.purl || newPkg.name,
2743
+ name: newPkg.name,
2744
+ state: "updated",
2745
+ oldVersion: oldPkg.version,
2746
+ newVersion: newPkg.version,
2747
+ licenses: newPkg.licenses.length > 0 ? newPkg.licenses : void 0
2748
+ });
2749
+ else diffs.push({
2750
+ purl: oldPkg.purl || oldPkg.name,
2751
+ name: oldPkg.name,
2752
+ state: "unchanged"
2753
+ });
2754
+ else diffs.push({
2755
+ purl: oldPkg.purl || oldPkg.name,
2756
+ name: oldPkg.name,
2757
+ state: "removed",
2758
+ oldVersion: oldPkg.version
2759
+ });
2760
+ }
2761
+ for (const [key, newPkg] of newMap) if (!seen.has(key)) diffs.push({
2762
+ purl: newPkg.purl || newPkg.name,
2763
+ name: newPkg.name,
2764
+ state: "added",
2765
+ newVersion: newPkg.version,
2766
+ licenses: newPkg.licenses.length > 0 ? newPkg.licenses : void 0
2767
+ });
2768
+ diffs.sort((a, b) => a.name.localeCompare(b.name));
2769
+ let added = 0;
2770
+ let removed = 0;
2771
+ let updated = 0;
2772
+ let unchanged = 0;
2773
+ for (const d of diffs) switch (d.state) {
2774
+ case "added":
2775
+ added++;
2776
+ break;
2777
+ case "removed":
2778
+ removed++;
2779
+ break;
2780
+ case "updated":
2781
+ updated++;
2782
+ break;
2783
+ case "unchanged":
2784
+ unchanged++;
2785
+ break;
2786
+ }
2787
+ return {
2788
+ packageDiffs: diffs,
2789
+ added,
2790
+ removed,
2791
+ updated,
2792
+ unchanged
2793
+ };
2794
+ }
2795
+ /**
2796
+ * Extract packages from an SBOM document, auto-detecting the format.
2797
+ */
2798
+ function extractPackages(doc) {
2799
+ if (doc["bomFormat"] === "CycloneDX") return extractCycloneDXPackages(doc);
2800
+ if (typeof doc["spdxVersion"] === "string") return extractSPDXPackages(doc);
2801
+ throw new Error("Unrecognized SBOM format: expected CycloneDX (bomFormat) or SPDX (spdxVersion)");
2802
+ }
2803
+ /**
2804
+ * Extract packages from a CycloneDX document.
2805
+ * Reads components[] with purl, name, version fields.
2806
+ */
2807
+ function extractCycloneDXPackages(doc) {
2808
+ const components = doc["components"];
2809
+ if (!Array.isArray(components)) return [];
2810
+ return components.map((comp) => {
2811
+ const licenses = extractCycloneDXLicenses(comp);
2812
+ return {
2813
+ purl: comp["purl"] ?? "",
2814
+ name: comp["name"] ?? "",
2815
+ version: comp["version"] ?? "",
2816
+ licenses
2817
+ };
2818
+ });
2819
+ }
2820
+ /**
2821
+ * Extract license strings from a CycloneDX component.
2822
+ * CycloneDX licenses can be in `licenses[].license.id` or `licenses[].license.name`.
2823
+ */
2824
+ function extractCycloneDXLicenses(comp) {
2825
+ const licensesArr = comp["licenses"];
2826
+ if (!Array.isArray(licensesArr)) return [];
2827
+ const result = [];
2828
+ for (const entry of licensesArr) {
2829
+ const license = entry["license"];
2830
+ if (license) {
2831
+ const id = license["id"];
2832
+ const name = license["name"];
2833
+ if (id) result.push(id);
2834
+ else if (name) result.push(name);
2835
+ }
2836
+ }
2837
+ return result;
2838
+ }
2839
+ /**
2840
+ * Extract packages from an SPDX document.
2841
+ * Reads packages[] with name, versionInfo, and externalRefs (for PURL).
2842
+ */
2843
+ function extractSPDXPackages(doc) {
2844
+ const packages = doc["packages"];
2845
+ if (!Array.isArray(packages)) return [];
2846
+ return packages.map((pkg) => {
2847
+ const purl = extractSPDXPurl(pkg);
2848
+ const license = pkg["licenseConcluded"] ?? pkg["licenseDeclared"] ?? "";
2849
+ return {
2850
+ purl,
2851
+ name: pkg["name"] ?? "",
2852
+ version: pkg["versionInfo"] ?? "",
2853
+ licenses: license && license !== "NOASSERTION" ? [license] : []
2854
+ };
2855
+ });
2856
+ }
2857
+ /**
2858
+ * Extract a PURL from an SPDX package's externalRefs array.
2859
+ */
2860
+ function extractSPDXPurl(pkg) {
2861
+ const refs = pkg["externalRefs"];
2862
+ if (!Array.isArray(refs)) return "";
2863
+ for (const ref of refs) if (ref["referenceType"] === "purl") return ref["referenceLocator"] ?? "";
2864
+ return "";
2865
+ }
2866
+ /**
2867
+ * Build a lookup map of packages indexed by name (version-independent key).
2868
+ */
2869
+ function buildPackageMap(packages) {
2870
+ const map = /* @__PURE__ */ new Map();
2871
+ for (const pkg of packages) {
2872
+ const key = pkg.name || pkg.purl;
2873
+ if (key) map.set(key, pkg);
2874
+ }
2875
+ return map;
2876
+ }
2877
+ //#endregion
2897
2878
  export { EXIT_DETAILED_BASELINE_CHANGED, EXIT_DETAILED_DRIFT_ONLY, EXIT_DETAILED_ERROR, EXIT_DETAILED_FIXES_ONLY, EXIT_DETAILED_IDENTICAL, EXIT_DETAILED_MIXED, EXIT_DETAILED_REGRESSIONS_ONLY, EXIT_DIFFERENCES, EXIT_ERROR, EXIT_IDENTICAL, classifyChangeReasons, classifyDiffStatus, computeDetailedExitCode, computeEffectiveStatus, computeExitCode, computeSummary, createCciMatchStrategy, createExactIdStrategy, createFuzzyTitleStrategy, createMappedIdStrategy, createSrgCciTiebreakStrategy, createSrgDeterministicStrategy, createVendorFuzzyTitleStrategy, diffBaselines, diffHdf, diffSboms, diffSystems, isV1Format, jaccardSimilarity, levenshteinDistance, matchRequirements, normalizeToV2, normalizedLevenshtein, render, renderCsv, renderJson, renderMarkdown, renderTerminal, tokenize, validateComparison };
2898
2879
 
2899
2880
  //# sourceMappingURL=index.js.map