namespace StellaOps.Interop.Tests.Analysis; /// /// Analyzes and categorizes differences between tool findings. /// public sealed class FindingsParityAnalyzer { /// /// Categorizes differences between tools. /// public ParityAnalysisReport Analyze( IReadOnlyList stellaFindings, IReadOnlyList grypeFindings) { var differences = new List(); // Category 1: Version matching differences // (e.g., semver vs non-semver interpretation) var versionDiffs = AnalyzeVersionMatchingDifferences(stellaFindings, grypeFindings); differences.AddRange(versionDiffs); // Category 2: Feed coverage differences // (e.g., Stella has feed X, Grype doesn't) var feedDiffs = AnalyzeFeedCoverageDifferences(stellaFindings, grypeFindings); differences.AddRange(feedDiffs); // Category 3: Package identification differences // (e.g., different PURL generation) var purlDiffs = AnalyzePurlDifferences(stellaFindings, grypeFindings); differences.AddRange(purlDiffs); // Category 4: VEX application differences // (e.g., Stella applies VEX, Grype doesn't) var vexDiffs = AnalyzeVexDifferences(stellaFindings, grypeFindings); differences.AddRange(vexDiffs); return new ParityAnalysisReport { TotalDifferences = differences.Count, VersionMatchingDifferences = versionDiffs, FeedCoverageDifferences = feedDiffs, PurlDifferences = purlDiffs, VexDifferences = vexDiffs, AcceptableDifferences = differences.Count(d => d.IsAcceptable), RequiresInvestigation = differences.Count(d => !d.IsAcceptable) }; } private static List AnalyzeVersionMatchingDifferences( IReadOnlyList stellaFindings, IReadOnlyList grypeFindings) { var differences = new List(); // TODO: Implement version matching analysis // Compare how Stella and Grype interpret version ranges // e.g., >=1.0.0 vs ^1.0.0 return differences; } private static List AnalyzeFeedCoverageDifferences( IReadOnlyList stellaFindings, IReadOnlyList grypeFindings) { var differences = new List(); // TODO: Implement feed coverage analysis // Identify which vulnerabilities come from feeds only one tool has // e.g., Stella has GHSA, Grype doesn't, or vice versa return differences; } private static List AnalyzePurlDifferences( IReadOnlyList stellaFindings, IReadOnlyList grypeFindings) { var differences = new List(); // TODO: Implement PURL difference analysis // Compare how packages are identified // e.g., pkg:npm/package vs pkg:npm/package@version return differences; } private static List AnalyzeVexDifferences( IReadOnlyList stellaFindings, IReadOnlyList grypeFindings) { var differences = new List(); // TODO: Implement VEX application analysis // Stella applies VEX documents, Grype may not // This is an acceptable difference return differences; } } public sealed class ParityAnalysisReport { public int TotalDifferences { get; init; } public IReadOnlyList VersionMatchingDifferences { get; init; } = []; public IReadOnlyList FeedCoverageDifferences { get; init; } = []; public IReadOnlyList PurlDifferences { get; init; } = []; public IReadOnlyList VexDifferences { get; init; } = []; public int AcceptableDifferences { get; init; } public int RequiresInvestigation { get; init; } } public sealed record FindingDifference( string Category, string Description, bool IsAcceptable, string? Reason = null);