118 lines
4.2 KiB
C#
118 lines
4.2 KiB
C#
namespace StellaOps.Interop.Tests.Analysis;
|
|
|
|
/// <summary>
|
|
/// Analyzes and categorizes differences between tool findings.
|
|
/// </summary>
|
|
public sealed class FindingsParityAnalyzer
|
|
{
|
|
/// <summary>
|
|
/// Categorizes differences between tools.
|
|
/// </summary>
|
|
public ParityAnalysisReport Analyze(
|
|
IReadOnlyList<Finding> stellaFindings,
|
|
IReadOnlyList<GrypeFinding> grypeFindings)
|
|
{
|
|
var differences = new List<FindingDifference>();
|
|
|
|
// 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<FindingDifference> AnalyzeVersionMatchingDifferences(
|
|
IReadOnlyList<Finding> stellaFindings,
|
|
IReadOnlyList<GrypeFinding> grypeFindings)
|
|
{
|
|
var differences = new List<FindingDifference>();
|
|
|
|
// 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<FindingDifference> AnalyzeFeedCoverageDifferences(
|
|
IReadOnlyList<Finding> stellaFindings,
|
|
IReadOnlyList<GrypeFinding> grypeFindings)
|
|
{
|
|
var differences = new List<FindingDifference>();
|
|
|
|
// 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<FindingDifference> AnalyzePurlDifferences(
|
|
IReadOnlyList<Finding> stellaFindings,
|
|
IReadOnlyList<GrypeFinding> grypeFindings)
|
|
{
|
|
var differences = new List<FindingDifference>();
|
|
|
|
// 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<FindingDifference> AnalyzeVexDifferences(
|
|
IReadOnlyList<Finding> stellaFindings,
|
|
IReadOnlyList<GrypeFinding> grypeFindings)
|
|
{
|
|
var differences = new List<FindingDifference>();
|
|
|
|
// 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<FindingDifference> VersionMatchingDifferences { get; init; } = [];
|
|
public IReadOnlyList<FindingDifference> FeedCoverageDifferences { get; init; } = [];
|
|
public IReadOnlyList<FindingDifference> PurlDifferences { get; init; } = [];
|
|
public IReadOnlyList<FindingDifference> 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);
|