@mitre/hdf-diff 3.1.0-rc.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/README.md +1 -1
- package/dist/index.d.ts +55 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +411 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -104,7 +104,7 @@ hdf diff --mode baseline golden.json current.json
|
|
|
104
104
|
The diff engine is also available as a Go module:
|
|
105
105
|
|
|
106
106
|
```go
|
|
107
|
-
import diff "github.com/mitre/hdf-libs/hdf-diff/go"
|
|
107
|
+
import diff "github.com/mitre/hdf-libs/hdf-diff/go/v3"
|
|
108
108
|
```
|
|
109
109
|
|
|
110
110
|
See the [hdf-diff/go](https://github.com/mitre/hdf-libs/tree/main/hdf-diff/go) directory for the Go API.
|
package/dist/index.d.ts
CHANGED
|
@@ -377,6 +377,8 @@ interface MatchPair {
|
|
|
377
377
|
strategy: string;
|
|
378
378
|
/** Confidence score for the match (0.0 - 1.0) */
|
|
379
379
|
confidence: number;
|
|
380
|
+
/** Relationship type for delta matching: 'primary' (code source) or 'related' (informational) */
|
|
381
|
+
relationship?: 'primary' | 'related';
|
|
380
382
|
}
|
|
381
383
|
/**
|
|
382
384
|
* A pluggable strategy for matching requirements between evaluations.
|
|
@@ -450,6 +452,58 @@ declare function jaccardSimilarity(a: Set<string>, b: Set<string>): number;
|
|
|
450
452
|
*/
|
|
451
453
|
declare function createFuzzyTitleStrategy(minConfidence?: number): MatchStrategy;
|
|
452
454
|
//#endregion
|
|
455
|
+
//#region src/matching/srg-deterministic.d.ts
|
|
456
|
+
/**
|
|
457
|
+
* Create a deterministic SRG matching strategy (Tier 1 of the delta pipeline).
|
|
458
|
+
*
|
|
459
|
+
* Matches requirements by exact `tags.gtitle` (SRG-OS requirement ID).
|
|
460
|
+
*
|
|
461
|
+
* For each gtitle shared between old and new:
|
|
462
|
+
* - 1 old + 1 new → single MatchPair (confidence 1.0, relationship "primary")
|
|
463
|
+
* - 1 new + N old → N MatchPairs (first old "primary", rest "related")
|
|
464
|
+
* - N new + 1 old → N MatchPairs (first new "primary", rest "related")
|
|
465
|
+
* - N:M (both > 1) → skip, leave for fallback strategies
|
|
466
|
+
*
|
|
467
|
+
* Requirements without a gtitle pass through as unmatched.
|
|
468
|
+
*/
|
|
469
|
+
declare function createSrgDeterministicStrategy(): MatchStrategy;
|
|
470
|
+
//#endregion
|
|
471
|
+
//#region src/matching/srg-cci-tiebreak.d.ts
|
|
472
|
+
/**
|
|
473
|
+
* Create an SRG CCI Tiebreak matching strategy (Tier 2 of the delta pipeline).
|
|
474
|
+
*
|
|
475
|
+
* Handles ambiguous SRG matches where multiple old or new requirements share
|
|
476
|
+
* the same tags.gtitle. Uses a composite score of CCI Jaccard (70%) and
|
|
477
|
+
* token Jaccard on titles (30%) to pick the best match.
|
|
478
|
+
*
|
|
479
|
+
* Only activates for gtitle groups with multiple candidates (>1 old or >1 new).
|
|
480
|
+
* 1:1 matches are left for srgDeterministic (Tier 1).
|
|
481
|
+
*/
|
|
482
|
+
declare function createSrgCciTiebreakStrategy(): MatchStrategy;
|
|
483
|
+
//#endregion
|
|
484
|
+
//#region src/matching/vendor-fuzzy-title.d.ts
|
|
485
|
+
/**
|
|
486
|
+
* Compute the Levenshtein edit distance between two strings.
|
|
487
|
+
*/
|
|
488
|
+
declare function levenshteinDistance(a: string, b: string): number;
|
|
489
|
+
/**
|
|
490
|
+
* Normalized Levenshtein distance (0.0 = identical, 1.0 = completely different).
|
|
491
|
+
*/
|
|
492
|
+
declare function normalizedLevenshtein(a: string, b: string): number;
|
|
493
|
+
/**
|
|
494
|
+
* Create a vendor fuzzy title matching strategy (Tier 3 of the delta pipeline).
|
|
495
|
+
*
|
|
496
|
+
* Cross-vendor matching with auto-detected vendor prefix stripping.
|
|
497
|
+
* Uses normalized Levenshtein distance to compare titles after removing
|
|
498
|
+
* the dominant vendor prefix (e.g., "RHEL 9" or "Amazon Linux 2023").
|
|
499
|
+
*
|
|
500
|
+
* Accepts matches below threshold 0.45 (confidence = 1.0 - distance).
|
|
501
|
+
* Greedy best-match.
|
|
502
|
+
*
|
|
503
|
+
* @param acceptThreshold Max normalized Levenshtein distance to accept (default: 0.45)
|
|
504
|
+
*/
|
|
505
|
+
declare function createVendorFuzzyTitleStrategy(acceptThreshold?: number): MatchStrategy;
|
|
506
|
+
//#endregion
|
|
453
507
|
//#region src/matching/index.d.ts
|
|
454
508
|
/**
|
|
455
509
|
* Options for configuring requirement matching.
|
|
@@ -619,5 +673,5 @@ declare function renderCsv(comparison: HdfComparison, options?: RenderOptions):
|
|
|
619
673
|
*/
|
|
620
674
|
declare function render(comparison: HdfComparison, format: 'json' | 'markdown' | 'terminal' | 'csv', options?: RenderOptions): string;
|
|
621
675
|
//#endregion
|
|
622
|
-
export { type Annotation, type BaselineDiff, type ChangeReason, type ComparisonSummary, type ComponentDiff, type DetailLevel, type DiffOptions, type DiffStatus, type DiffSummary, 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, type FieldChange, type HdfComparison, type HdfDiff, type MatchOptions, type MatchPair, type MatchResult, type MatchStrategy, type MatchingConfig, type PackageDiff, type RenderOptions, type RequirementDiff, type RequirementState, type SbomDiffResult, type Source, type ValidationResult, classifyChangeReasons, classifyDiffStatus, computeDetailedExitCode, computeEffectiveStatus, computeExitCode, computeSummary, createCciMatchStrategy, createExactIdStrategy, createFuzzyTitleStrategy, createMappedIdStrategy, diffBaselines, diffHdf, diffSboms, diffSystems, isV1Format, jaccardSimilarity, matchRequirements, normalizeToV2, render, renderCsv, renderJson, renderMarkdown, renderTerminal, tokenize, validateComparison };
|
|
676
|
+
export { type Annotation, type BaselineDiff, type ChangeReason, type ComparisonSummary, type ComponentDiff, type DetailLevel, type DiffOptions, type DiffStatus, type DiffSummary, 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, type FieldChange, type HdfComparison, type HdfDiff, type MatchOptions, type MatchPair, type MatchResult, type MatchStrategy, type MatchingConfig, type PackageDiff, type RenderOptions, type RequirementDiff, type RequirementState, type SbomDiffResult, type Source, type ValidationResult, 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 };
|
|
623
677
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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/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":";;AAcA
|
|
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
|
@@ -398,7 +398,7 @@ function createMappedIdStrategy(mapping) {
|
|
|
398
398
|
* Extract CCI identifiers from a requirement's tags.
|
|
399
399
|
* Looks for `tags.cci` as an array of strings.
|
|
400
400
|
*/
|
|
401
|
-
function extractCcis(req) {
|
|
401
|
+
function extractCcis$1(req) {
|
|
402
402
|
const tags = req["tags"];
|
|
403
403
|
if (!tags) return [];
|
|
404
404
|
const cci = tags["cci"];
|
|
@@ -426,13 +426,13 @@ function createCciMatchStrategy() {
|
|
|
426
426
|
unmatchedNew: []
|
|
427
427
|
};
|
|
428
428
|
const oldCciMap = /* @__PURE__ */ new Map();
|
|
429
|
-
for (let i = 0; i < oldReqs.length; i++) for (const cci of extractCcis(oldReqs[i])) {
|
|
429
|
+
for (let i = 0; i < oldReqs.length; i++) for (const cci of extractCcis$1(oldReqs[i])) {
|
|
430
430
|
const list = oldCciMap.get(cci);
|
|
431
431
|
if (list) list.push(i);
|
|
432
432
|
else oldCciMap.set(cci, [i]);
|
|
433
433
|
}
|
|
434
434
|
const newCciMap = /* @__PURE__ */ new Map();
|
|
435
|
-
for (let i = 0; i < newReqs.length; i++) for (const cci of extractCcis(newReqs[i])) {
|
|
435
|
+
for (let i = 0; i < newReqs.length; i++) for (const cci of extractCcis$1(newReqs[i])) {
|
|
436
436
|
const list = newCciMap.get(cci);
|
|
437
437
|
if (list) list.push(i);
|
|
438
438
|
else newCciMap.set(cci, [i]);
|
|
@@ -648,6 +648,410 @@ function createFuzzyTitleStrategy(minConfidence) {
|
|
|
648
648
|
};
|
|
649
649
|
}
|
|
650
650
|
//#endregion
|
|
651
|
+
//#region src/matching/srg-deterministic.ts
|
|
652
|
+
/**
|
|
653
|
+
* Extract the SRG-OS ID from a requirement's tags.gtitle field.
|
|
654
|
+
* Returns null if missing or not a string.
|
|
655
|
+
*/
|
|
656
|
+
function extractGtitle$1(req) {
|
|
657
|
+
const tags = req["tags"];
|
|
658
|
+
if (!tags) return null;
|
|
659
|
+
const gtitle = tags["gtitle"];
|
|
660
|
+
if (typeof gtitle !== "string" || gtitle === "") return null;
|
|
661
|
+
return gtitle;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Create a deterministic SRG matching strategy (Tier 1 of the delta pipeline).
|
|
665
|
+
*
|
|
666
|
+
* Matches requirements by exact `tags.gtitle` (SRG-OS requirement ID).
|
|
667
|
+
*
|
|
668
|
+
* For each gtitle shared between old and new:
|
|
669
|
+
* - 1 old + 1 new → single MatchPair (confidence 1.0, relationship "primary")
|
|
670
|
+
* - 1 new + N old → N MatchPairs (first old "primary", rest "related")
|
|
671
|
+
* - N new + 1 old → N MatchPairs (first new "primary", rest "related")
|
|
672
|
+
* - N:M (both > 1) → skip, leave for fallback strategies
|
|
673
|
+
*
|
|
674
|
+
* Requirements without a gtitle pass through as unmatched.
|
|
675
|
+
*/
|
|
676
|
+
function createSrgDeterministicStrategy() {
|
|
677
|
+
return {
|
|
678
|
+
name: "srgDeterministic",
|
|
679
|
+
match(oldReqs, newReqs) {
|
|
680
|
+
const oldGtitleMap = /* @__PURE__ */ new Map();
|
|
681
|
+
for (let i = 0; i < oldReqs.length; i++) {
|
|
682
|
+
const g = extractGtitle$1(oldReqs[i]);
|
|
683
|
+
if (g) {
|
|
684
|
+
const list = oldGtitleMap.get(g);
|
|
685
|
+
if (list) list.push(i);
|
|
686
|
+
else oldGtitleMap.set(g, [i]);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
const newGtitleMap = /* @__PURE__ */ new Map();
|
|
690
|
+
for (let i = 0; i < newReqs.length; i++) {
|
|
691
|
+
const g = extractGtitle$1(newReqs[i]);
|
|
692
|
+
if (g) {
|
|
693
|
+
const list = newGtitleMap.get(g);
|
|
694
|
+
if (list) list.push(i);
|
|
695
|
+
else newGtitleMap.set(g, [i]);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
const matched = [];
|
|
699
|
+
const matchedOldIndices = /* @__PURE__ */ new Set();
|
|
700
|
+
const matchedNewIndices = /* @__PURE__ */ new Set();
|
|
701
|
+
const claimedOldIndices = /* @__PURE__ */ new Set();
|
|
702
|
+
for (const [gtitle, newIdxList] of newGtitleMap) {
|
|
703
|
+
const oldIdxList = oldGtitleMap.get(gtitle);
|
|
704
|
+
if (!oldIdxList || oldIdxList.length === 0) continue;
|
|
705
|
+
const nc = newIdxList.length;
|
|
706
|
+
const oc = oldIdxList.length;
|
|
707
|
+
if (nc > 1 && oc > 1) continue;
|
|
708
|
+
for (const ni of newIdxList) {
|
|
709
|
+
let primaryOldSet = false;
|
|
710
|
+
for (const oi of oldIdxList) {
|
|
711
|
+
let relationship;
|
|
712
|
+
if (oc === 1) {
|
|
713
|
+
relationship = claimedOldIndices.has(oi) ? "related" : "primary";
|
|
714
|
+
if (relationship === "primary") claimedOldIndices.add(oi);
|
|
715
|
+
} else {
|
|
716
|
+
relationship = primaryOldSet ? "related" : "primary";
|
|
717
|
+
primaryOldSet = true;
|
|
718
|
+
}
|
|
719
|
+
matched.push({
|
|
720
|
+
oldReq: oldReqs[oi],
|
|
721
|
+
newReq: newReqs[ni],
|
|
722
|
+
strategy: "srgDeterministic",
|
|
723
|
+
confidence: 1,
|
|
724
|
+
relationship
|
|
725
|
+
});
|
|
726
|
+
matchedOldIndices.add(oi);
|
|
727
|
+
}
|
|
728
|
+
matchedNewIndices.add(ni);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
const unmatchedOld = [];
|
|
732
|
+
for (let i = 0; i < oldReqs.length; i++) if (!matchedOldIndices.has(i)) unmatchedOld.push(oldReqs[i]);
|
|
733
|
+
const unmatchedNew = [];
|
|
734
|
+
for (let i = 0; i < newReqs.length; i++) if (!matchedNewIndices.has(i)) unmatchedNew.push(newReqs[i]);
|
|
735
|
+
return {
|
|
736
|
+
matched,
|
|
737
|
+
unmatchedOld,
|
|
738
|
+
unmatchedNew
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
//#endregion
|
|
744
|
+
//#region src/matching/srg-cci-tiebreak.ts
|
|
745
|
+
/** Composite score weights matching SAF CLI's Tier 2. */
|
|
746
|
+
const CCI_WEIGHT = .7;
|
|
747
|
+
const TITLE_WEIGHT = .3;
|
|
748
|
+
/**
|
|
749
|
+
* Extract CCI identifiers from a requirement's tags.cci.
|
|
750
|
+
*/
|
|
751
|
+
function extractCcis(req) {
|
|
752
|
+
const tags = req["tags"];
|
|
753
|
+
if (!tags) return /* @__PURE__ */ new Set();
|
|
754
|
+
const cci = tags["cci"];
|
|
755
|
+
if (!Array.isArray(cci)) return /* @__PURE__ */ new Set();
|
|
756
|
+
return new Set(cci.filter((c) => typeof c === "string"));
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Extract tags.gtitle from a requirement.
|
|
760
|
+
*/
|
|
761
|
+
function extractGtitle(req) {
|
|
762
|
+
const tags = req["tags"];
|
|
763
|
+
if (!tags) return null;
|
|
764
|
+
const gtitle = tags["gtitle"];
|
|
765
|
+
if (typeof gtitle !== "string" || gtitle === "") return null;
|
|
766
|
+
return gtitle;
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Extract title from a requirement.
|
|
770
|
+
*/
|
|
771
|
+
function safeTitle$1(req) {
|
|
772
|
+
const title = req["title"];
|
|
773
|
+
return typeof title === "string" ? title : "";
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Jaccard similarity between two sets.
|
|
777
|
+
*/
|
|
778
|
+
function cciJaccard(a, b) {
|
|
779
|
+
if (a.size === 0 && b.size === 0) return 0;
|
|
780
|
+
let intersectionSize = 0;
|
|
781
|
+
for (const x of a) if (b.has(x)) intersectionSize++;
|
|
782
|
+
const unionSize = a.size + b.size - intersectionSize;
|
|
783
|
+
return intersectionSize / unionSize;
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Simple whitespace-split token Jaccard similarity.
|
|
787
|
+
*/
|
|
788
|
+
function tokenJaccard(a, b) {
|
|
789
|
+
const ta = new Set(a.toLowerCase().split(/\s+/).filter((t) => t.length > 0));
|
|
790
|
+
const tb = new Set(b.toLowerCase().split(/\s+/).filter((t) => t.length > 0));
|
|
791
|
+
if (ta.size === 0 || tb.size === 0) return 0;
|
|
792
|
+
let intersectionSize = 0;
|
|
793
|
+
for (const x of ta) if (tb.has(x)) intersectionSize++;
|
|
794
|
+
const unionSize = ta.size + tb.size - intersectionSize;
|
|
795
|
+
return intersectionSize / unionSize;
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Create an SRG CCI Tiebreak matching strategy (Tier 2 of the delta pipeline).
|
|
799
|
+
*
|
|
800
|
+
* Handles ambiguous SRG matches where multiple old or new requirements share
|
|
801
|
+
* the same tags.gtitle. Uses a composite score of CCI Jaccard (70%) and
|
|
802
|
+
* token Jaccard on titles (30%) to pick the best match.
|
|
803
|
+
*
|
|
804
|
+
* Only activates for gtitle groups with multiple candidates (>1 old or >1 new).
|
|
805
|
+
* 1:1 matches are left for srgDeterministic (Tier 1).
|
|
806
|
+
*/
|
|
807
|
+
function createSrgCciTiebreakStrategy() {
|
|
808
|
+
return {
|
|
809
|
+
name: "srgCciTiebreak",
|
|
810
|
+
match(oldReqs, newReqs) {
|
|
811
|
+
const oldGtitleMap = /* @__PURE__ */ new Map();
|
|
812
|
+
for (let i = 0; i < oldReqs.length; i++) {
|
|
813
|
+
const g = extractGtitle(oldReqs[i]);
|
|
814
|
+
if (g) {
|
|
815
|
+
const list = oldGtitleMap.get(g);
|
|
816
|
+
if (list) list.push(i);
|
|
817
|
+
else oldGtitleMap.set(g, [i]);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
const newGtitleMap = /* @__PURE__ */ new Map();
|
|
821
|
+
for (let i = 0; i < newReqs.length; i++) {
|
|
822
|
+
const g = extractGtitle(newReqs[i]);
|
|
823
|
+
if (g) {
|
|
824
|
+
const list = newGtitleMap.get(g);
|
|
825
|
+
if (list) list.push(i);
|
|
826
|
+
else newGtitleMap.set(g, [i]);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
const matched = [];
|
|
830
|
+
const matchedOldIndices = /* @__PURE__ */ new Set();
|
|
831
|
+
const matchedNewIndices = /* @__PURE__ */ new Set();
|
|
832
|
+
for (const [gtitle, newIdxList] of newGtitleMap) {
|
|
833
|
+
const oldIdxList = oldGtitleMap.get(gtitle);
|
|
834
|
+
if (!oldIdxList || oldIdxList.length === 0) continue;
|
|
835
|
+
const nc = newIdxList.length;
|
|
836
|
+
const oc = oldIdxList.length;
|
|
837
|
+
if (nc === 1 && oc === 1) continue;
|
|
838
|
+
for (const ni of newIdxList) {
|
|
839
|
+
if (matchedNewIndices.has(ni)) continue;
|
|
840
|
+
const newCcis = extractCcis(newReqs[ni]);
|
|
841
|
+
const newTitle = safeTitle$1(newReqs[ni]);
|
|
842
|
+
let bestIdx = -1;
|
|
843
|
+
let bestComposite = -1;
|
|
844
|
+
let bestCci = 0;
|
|
845
|
+
let bestIsUnclaimed = false;
|
|
846
|
+
for (const oi of oldIdxList) {
|
|
847
|
+
const oldCcis = extractCcis(oldReqs[oi]);
|
|
848
|
+
const oldTitle = safeTitle$1(oldReqs[oi]);
|
|
849
|
+
const cci = cciJaccard(newCcis, oldCcis);
|
|
850
|
+
const title = tokenJaccard(newTitle, oldTitle);
|
|
851
|
+
const composite = CCI_WEIGHT * cci + TITLE_WEIGHT * title;
|
|
852
|
+
const isUnclaimed = !matchedOldIndices.has(oi);
|
|
853
|
+
if (bestIdx === -1 || isUnclaimed && !bestIsUnclaimed || isUnclaimed === bestIsUnclaimed && composite > bestComposite) {
|
|
854
|
+
bestIdx = oi;
|
|
855
|
+
bestComposite = composite;
|
|
856
|
+
bestCci = cci;
|
|
857
|
+
bestIsUnclaimed = isUnclaimed;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
if (bestIdx >= 0) {
|
|
861
|
+
const relationship = matchedOldIndices.has(bestIdx) ? "related" : "primary";
|
|
862
|
+
matched.push({
|
|
863
|
+
oldReq: oldReqs[bestIdx],
|
|
864
|
+
newReq: newReqs[ni],
|
|
865
|
+
strategy: "srgCciTiebreak",
|
|
866
|
+
confidence: Math.max(bestCci, 0),
|
|
867
|
+
relationship
|
|
868
|
+
});
|
|
869
|
+
matchedOldIndices.add(bestIdx);
|
|
870
|
+
matchedNewIndices.add(ni);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
const unmatchedOld = [];
|
|
875
|
+
for (let i = 0; i < oldReqs.length; i++) if (!matchedOldIndices.has(i)) unmatchedOld.push(oldReqs[i]);
|
|
876
|
+
const unmatchedNew = [];
|
|
877
|
+
for (let i = 0; i < newReqs.length; i++) if (!matchedNewIndices.has(i)) unmatchedNew.push(newReqs[i]);
|
|
878
|
+
return {
|
|
879
|
+
matched,
|
|
880
|
+
unmatchedOld,
|
|
881
|
+
unmatchedNew
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
//#endregion
|
|
887
|
+
//#region src/matching/vendor-fuzzy-title.ts
|
|
888
|
+
/** Default threshold for accepting a Levenshtein match. */
|
|
889
|
+
const DEFAULT_ACCEPT_THRESHOLD = .45;
|
|
890
|
+
/** Modal verbs that mark the start of compliance statements. */
|
|
891
|
+
const COMPLIANCE_MODALS = new Set([
|
|
892
|
+
"must",
|
|
893
|
+
"will",
|
|
894
|
+
"shall",
|
|
895
|
+
"should",
|
|
896
|
+
"may",
|
|
897
|
+
"needs"
|
|
898
|
+
]);
|
|
899
|
+
/**
|
|
900
|
+
* Compute the Levenshtein edit distance between two strings.
|
|
901
|
+
*/
|
|
902
|
+
function levenshteinDistance(a, b) {
|
|
903
|
+
const m = a.length;
|
|
904
|
+
const n = b.length;
|
|
905
|
+
if (m === 0) return n;
|
|
906
|
+
if (n === 0) return m;
|
|
907
|
+
let prev = new Array(n + 1);
|
|
908
|
+
let curr = new Array(n + 1);
|
|
909
|
+
for (let j = 0; j <= n; j++) prev[j] = j;
|
|
910
|
+
for (let i = 1; i <= m; i++) {
|
|
911
|
+
curr[0] = i;
|
|
912
|
+
for (let j = 1; j <= n; j++) {
|
|
913
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
914
|
+
curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
|
|
915
|
+
}
|
|
916
|
+
[prev, curr] = [curr, prev];
|
|
917
|
+
}
|
|
918
|
+
return prev[n];
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Normalized Levenshtein distance (0.0 = identical, 1.0 = completely different).
|
|
922
|
+
*/
|
|
923
|
+
function normalizedLevenshtein(a, b) {
|
|
924
|
+
const maxLen = Math.max(a.length, b.length);
|
|
925
|
+
if (maxLen === 0) return 0;
|
|
926
|
+
return levenshteinDistance(a, b) / maxLen;
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Extract tokens before the first modal verb in a title.
|
|
930
|
+
*/
|
|
931
|
+
function tokensBeforeModal(title) {
|
|
932
|
+
const tokens = title.split(/\s+/).filter((t) => t.length > 0);
|
|
933
|
+
const modalIdx = tokens.findIndex((t) => COMPLIANCE_MODALS.has(t.toLowerCase()));
|
|
934
|
+
return modalIdx === -1 ? tokens : tokens.slice(0, modalIdx);
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Auto-detect the dominant vendor prefix in a corpus of titles.
|
|
938
|
+
*
|
|
939
|
+
* Tries progressively shorter leading-token prefixes. Returns the prefix
|
|
940
|
+
* that appears in > 50% of titles, stopping before modal verbs.
|
|
941
|
+
* Returns '' when no prefix reaches the threshold.
|
|
942
|
+
*/
|
|
943
|
+
function autoDetectPrefix(titles, threshold = .5) {
|
|
944
|
+
if (titles.length === 0) return "";
|
|
945
|
+
const leading = titles.map((t) => tokensBeforeModal(t));
|
|
946
|
+
const maxLen = Math.max(...leading.map((l) => l.length));
|
|
947
|
+
for (let n = maxLen; n > 0; n--) {
|
|
948
|
+
const counts = /* @__PURE__ */ new Map();
|
|
949
|
+
for (const l of leading) if (l.length >= n) {
|
|
950
|
+
const key = l.slice(0, n).join(" ");
|
|
951
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
952
|
+
}
|
|
953
|
+
let bestKey = "";
|
|
954
|
+
let bestCount = 0;
|
|
955
|
+
for (const [key, count] of counts) if (count > bestCount) {
|
|
956
|
+
bestKey = key;
|
|
957
|
+
bestCount = count;
|
|
958
|
+
}
|
|
959
|
+
if (bestCount / titles.length > threshold) return bestKey;
|
|
960
|
+
}
|
|
961
|
+
return "";
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Strip a detected vendor prefix from a title.
|
|
965
|
+
*/
|
|
966
|
+
function normalizeTitle(title, prefix) {
|
|
967
|
+
if (!prefix) return title;
|
|
968
|
+
if (title === prefix) return "";
|
|
969
|
+
if (title.startsWith(prefix + " ")) return title.slice(prefix.length + 1);
|
|
970
|
+
return title;
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Extract title string from a requirement.
|
|
974
|
+
*/
|
|
975
|
+
function safeTitle(req) {
|
|
976
|
+
const title = req["title"];
|
|
977
|
+
return typeof title === "string" ? title : "";
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Create a vendor fuzzy title matching strategy (Tier 3 of the delta pipeline).
|
|
981
|
+
*
|
|
982
|
+
* Cross-vendor matching with auto-detected vendor prefix stripping.
|
|
983
|
+
* Uses normalized Levenshtein distance to compare titles after removing
|
|
984
|
+
* the dominant vendor prefix (e.g., "RHEL 9" or "Amazon Linux 2023").
|
|
985
|
+
*
|
|
986
|
+
* Accepts matches below threshold 0.45 (confidence = 1.0 - distance).
|
|
987
|
+
* Greedy best-match.
|
|
988
|
+
*
|
|
989
|
+
* @param acceptThreshold Max normalized Levenshtein distance to accept (default: 0.45)
|
|
990
|
+
*/
|
|
991
|
+
function createVendorFuzzyTitleStrategy(acceptThreshold) {
|
|
992
|
+
const threshold = acceptThreshold ?? DEFAULT_ACCEPT_THRESHOLD;
|
|
993
|
+
return {
|
|
994
|
+
name: "vendorFuzzyTitle",
|
|
995
|
+
match(oldReqs, newReqs) {
|
|
996
|
+
const oldTitles = oldReqs.map(safeTitle).filter((t) => t.length > 0);
|
|
997
|
+
const newTitles = newReqs.map(safeTitle).filter((t) => t.length > 0);
|
|
998
|
+
const oldPrefix = autoDetectPrefix(oldTitles);
|
|
999
|
+
const newPrefix = autoDetectPrefix(newTitles);
|
|
1000
|
+
const oldNorm = [];
|
|
1001
|
+
for (let i = 0; i < oldReqs.length; i++) {
|
|
1002
|
+
const t = safeTitle(oldReqs[i]);
|
|
1003
|
+
if (t) oldNorm.push({
|
|
1004
|
+
idx: i,
|
|
1005
|
+
normalized: normalizeTitle(t, oldPrefix)
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
const newNorm = [];
|
|
1009
|
+
for (let i = 0; i < newReqs.length; i++) {
|
|
1010
|
+
const t = safeTitle(newReqs[i]);
|
|
1011
|
+
if (t) newNorm.push({
|
|
1012
|
+
idx: i,
|
|
1013
|
+
normalized: normalizeTitle(t, newPrefix)
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
const candidates = [];
|
|
1017
|
+
for (const old of oldNorm) for (const nw of newNorm) {
|
|
1018
|
+
if (!old.normalized || !nw.normalized) continue;
|
|
1019
|
+
const dist = normalizedLevenshtein(old.normalized, nw.normalized);
|
|
1020
|
+
if (dist < threshold) candidates.push({
|
|
1021
|
+
oldIdx: old.idx,
|
|
1022
|
+
newIdx: nw.idx,
|
|
1023
|
+
distance: dist
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
candidates.sort((a, b) => a.distance - b.distance);
|
|
1027
|
+
const matched = [];
|
|
1028
|
+
const matchedOldIndices = /* @__PURE__ */ new Set();
|
|
1029
|
+
const matchedNewIndices = /* @__PURE__ */ new Set();
|
|
1030
|
+
for (const c of candidates) {
|
|
1031
|
+
if (matchedOldIndices.has(c.oldIdx) || matchedNewIndices.has(c.newIdx)) continue;
|
|
1032
|
+
matched.push({
|
|
1033
|
+
oldReq: oldReqs[c.oldIdx],
|
|
1034
|
+
newReq: newReqs[c.newIdx],
|
|
1035
|
+
strategy: "vendorFuzzyTitle",
|
|
1036
|
+
confidence: 1 - c.distance,
|
|
1037
|
+
relationship: "primary"
|
|
1038
|
+
});
|
|
1039
|
+
matchedOldIndices.add(c.oldIdx);
|
|
1040
|
+
matchedNewIndices.add(c.newIdx);
|
|
1041
|
+
}
|
|
1042
|
+
const unmatchedOld = [];
|
|
1043
|
+
for (let i = 0; i < oldReqs.length; i++) if (!matchedOldIndices.has(i)) unmatchedOld.push(oldReqs[i]);
|
|
1044
|
+
const unmatchedNew = [];
|
|
1045
|
+
for (let i = 0; i < newReqs.length; i++) if (!matchedNewIndices.has(i)) unmatchedNew.push(newReqs[i]);
|
|
1046
|
+
return {
|
|
1047
|
+
matched,
|
|
1048
|
+
unmatchedOld,
|
|
1049
|
+
unmatchedNew
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
//#endregion
|
|
651
1055
|
//#region src/matching/index.ts
|
|
652
1056
|
/**
|
|
653
1057
|
* Create a strategy instance by name, using the provided options for
|
|
@@ -659,6 +1063,9 @@ function createStrategy(name, options) {
|
|
|
659
1063
|
case "mappedId": return createMappedIdStrategy(options.mappingTable ?? {});
|
|
660
1064
|
case "cciMatch": return createCciMatchStrategy();
|
|
661
1065
|
case "fuzzyTitle": return createFuzzyTitleStrategy(options.minConfidence);
|
|
1066
|
+
case "srgDeterministic": return createSrgDeterministicStrategy();
|
|
1067
|
+
case "srgCciTiebreak": return createSrgCciTiebreakStrategy();
|
|
1068
|
+
case "vendorFuzzyTitle": return createVendorFuzzyTitleStrategy(options.minConfidence !== void 0 && options.minConfidence > 0 ? 1 - options.minConfidence : void 0);
|
|
662
1069
|
default: throw new Error(`Unknown matching strategy: '${name}'`);
|
|
663
1070
|
}
|
|
664
1071
|
}
|
|
@@ -2033,6 +2440,6 @@ function render(comparison, format, options) {
|
|
|
2033
2440
|
}
|
|
2034
2441
|
}
|
|
2035
2442
|
//#endregion
|
|
2036
|
-
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, diffBaselines, diffHdf, diffSboms, diffSystems, isV1Format, jaccardSimilarity, matchRequirements, normalizeToV2, render, renderCsv, renderJson, renderMarkdown, renderTerminal, tokenize, validateComparison };
|
|
2443
|
+
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 };
|
|
2037
2444
|
|
|
2038
2445
|
//# sourceMappingURL=index.js.map
|