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);