280 lines
8.0 KiB
C#
280 lines
8.0 KiB
C#
// <copyright file="IPedigreeDataProvider.cs" company="StellaOps">
|
|
// Copyright (c) StellaOps. Licensed under the BUSL-1.1.
|
|
// </copyright>
|
|
|
|
using System.Collections.Immutable;
|
|
|
|
namespace StellaOps.Scanner.Emit.Pedigree;
|
|
|
|
/// <summary>
|
|
/// Provider interface for retrieving component pedigree data from Feedser.
|
|
/// Sprint: SPRINT_20260107_005_002 Task PD-001
|
|
/// </summary>
|
|
public interface IPedigreeDataProvider
|
|
{
|
|
/// <summary>
|
|
/// Retrieves pedigree data for a component identified by its PURL.
|
|
/// </summary>
|
|
/// <param name="purl">Package URL identifying the component.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>Pedigree data if available, null otherwise.</returns>
|
|
Task<PedigreeData?> GetPedigreeAsync(
|
|
string purl,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Retrieves pedigree data for multiple components.
|
|
/// </summary>
|
|
/// <param name="purls">Package URLs identifying the components.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>Dictionary of PURL to pedigree data (missing components not included).</returns>
|
|
Task<IReadOnlyDictionary<string, PedigreeData>> GetPedigreesBatchAsync(
|
|
IEnumerable<string> purls,
|
|
CancellationToken cancellationToken = default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Aggregate of pedigree information for a component.
|
|
/// </summary>
|
|
public sealed record PedigreeData
|
|
{
|
|
/// <summary>
|
|
/// Gets the ancestor components (upstream sources).
|
|
/// </summary>
|
|
public ImmutableArray<AncestorComponent> Ancestors { get; init; } = ImmutableArray<AncestorComponent>.Empty;
|
|
|
|
/// <summary>
|
|
/// Gets the variant components (distro-specific packages derived from same source).
|
|
/// </summary>
|
|
public ImmutableArray<VariantComponent> Variants { get; init; } = ImmutableArray<VariantComponent>.Empty;
|
|
|
|
/// <summary>
|
|
/// Gets the relevant commits (security fixes, backports).
|
|
/// </summary>
|
|
public ImmutableArray<CommitInfo> Commits { get; init; } = ImmutableArray<CommitInfo>.Empty;
|
|
|
|
/// <summary>
|
|
/// Gets the patches applied to the component.
|
|
/// </summary>
|
|
public ImmutableArray<PatchInfo> Patches { get; init; } = ImmutableArray<PatchInfo>.Empty;
|
|
|
|
/// <summary>
|
|
/// Gets optional notes about the pedigree (e.g., backport explanation).
|
|
/// </summary>
|
|
public string? Notes { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets whether any pedigree data is present.
|
|
/// </summary>
|
|
public bool HasData =>
|
|
!Ancestors.IsDefaultOrEmpty ||
|
|
!Variants.IsDefaultOrEmpty ||
|
|
!Commits.IsDefaultOrEmpty ||
|
|
!Patches.IsDefaultOrEmpty ||
|
|
Notes is not null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents an upstream ancestor component.
|
|
/// </summary>
|
|
public sealed record AncestorComponent
|
|
{
|
|
/// <summary>
|
|
/// Gets the component type (e.g., "library", "application").
|
|
/// </summary>
|
|
public string Type { get; init; } = "library";
|
|
|
|
/// <summary>
|
|
/// Gets the component name.
|
|
/// </summary>
|
|
public required string Name { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the upstream version.
|
|
/// </summary>
|
|
public required string Version { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the Package URL for the ancestor.
|
|
/// </summary>
|
|
public string? Purl { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the URL to the upstream project.
|
|
/// </summary>
|
|
public string? ProjectUrl { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the relationship level (1 = direct parent, 2 = grandparent, etc.).
|
|
/// </summary>
|
|
public int Level { get; init; } = 1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a variant component (distro-specific package).
|
|
/// </summary>
|
|
public sealed record VariantComponent
|
|
{
|
|
/// <summary>
|
|
/// Gets the component type.
|
|
/// </summary>
|
|
public string Type { get; init; } = "library";
|
|
|
|
/// <summary>
|
|
/// Gets the package name in the distribution.
|
|
/// </summary>
|
|
public required string Name { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the distribution-specific version.
|
|
/// </summary>
|
|
public required string Version { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the Package URL for the variant.
|
|
/// </summary>
|
|
public required string Purl { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the distribution name (e.g., "debian", "rhel", "alpine").
|
|
/// </summary>
|
|
public string? Distribution { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the distribution release (e.g., "bookworm", "9.3").
|
|
/// </summary>
|
|
public string? Release { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents commit information for a security fix or backport.
|
|
/// </summary>
|
|
public sealed record CommitInfo
|
|
{
|
|
/// <summary>
|
|
/// Gets the commit SHA (full or abbreviated).
|
|
/// </summary>
|
|
public required string Uid { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the URL to view the commit.
|
|
/// </summary>
|
|
public string? Url { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the commit message (may be truncated).
|
|
/// </summary>
|
|
public string? Message { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the author information.
|
|
/// </summary>
|
|
public CommitActor? Author { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the committer information.
|
|
/// </summary>
|
|
public CommitActor? Committer { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the CVE IDs resolved by this commit, if known.
|
|
/// </summary>
|
|
public ImmutableArray<string> ResolvesCves { get; init; } = ImmutableArray<string>.Empty;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents an actor (author or committer) in a commit.
|
|
/// </summary>
|
|
public sealed record CommitActor
|
|
{
|
|
/// <summary>
|
|
/// Gets the actor's name.
|
|
/// </summary>
|
|
public string? Name { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the actor's email.
|
|
/// </summary>
|
|
public string? Email { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the timestamp of the action.
|
|
/// </summary>
|
|
public DateTimeOffset? Timestamp { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a patch applied to the component.
|
|
/// </summary>
|
|
public sealed record PatchInfo
|
|
{
|
|
/// <summary>
|
|
/// Gets the patch type.
|
|
/// </summary>
|
|
public PatchType Type { get; init; } = PatchType.Backport;
|
|
|
|
/// <summary>
|
|
/// Gets the URL to the patch file.
|
|
/// </summary>
|
|
public string? DiffUrl { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the patch diff content (optional, may be truncated).
|
|
/// </summary>
|
|
public string? DiffText { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the CVE IDs resolved by this patch.
|
|
/// </summary>
|
|
public ImmutableArray<PatchResolution> Resolves { get; init; } = ImmutableArray<PatchResolution>.Empty;
|
|
|
|
/// <summary>
|
|
/// Gets the functions affected by this patch.
|
|
/// </summary>
|
|
public ImmutableArray<string> AffectedFunctions { get; init; } = ImmutableArray<string>.Empty;
|
|
|
|
/// <summary>
|
|
/// Gets the source of the patch (e.g., "debian-security").
|
|
/// </summary>
|
|
public string? Source { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Patch type enumeration per CycloneDX 1.7 specification.
|
|
/// </summary>
|
|
public enum PatchType
|
|
{
|
|
/// <summary>Informal patch not associated with upstream.</summary>
|
|
Unofficial,
|
|
|
|
/// <summary>A patch that is a bugfix or security fix that does not change feature.</summary>
|
|
Monkey,
|
|
|
|
/// <summary>A patch that is a backport of a fix from a later version.</summary>
|
|
Backport,
|
|
|
|
/// <summary>A cherry-picked commit from upstream.</summary>
|
|
CherryPick
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a vulnerability resolved by a patch.
|
|
/// </summary>
|
|
public sealed record PatchResolution
|
|
{
|
|
/// <summary>
|
|
/// Gets the vulnerability ID (e.g., "CVE-2024-1234").
|
|
/// </summary>
|
|
public required string Id { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the source of the vulnerability reference.
|
|
/// </summary>
|
|
public string? SourceName { get; init; }
|
|
|
|
/// <summary>
|
|
/// Gets the URL to the vulnerability reference.
|
|
/// </summary>
|
|
public string? SourceUrl { get; init; }
|
|
}
|