Files
git.stella-ops.org/src/BinaryIndex/__Libraries/StellaOps.BinaryIndex.Builders/FingerprintClaimModels.cs
StellaOps Bot 83c37243e0 save progress
2026-01-03 11:02:24 +02:00

305 lines
10 KiB
C#

using System.Text.Json.Serialization;
namespace StellaOps.BinaryIndex.Builders;
/// <summary>
/// A claim asserting a CVE verdict for a specific fingerprint.
/// Created when reproducible builds show a function was modified to fix a CVE.
/// </summary>
public sealed record FingerprintClaim
{
/// <summary>
/// Unique identifier for this claim.
/// </summary>
public Guid Id { get; init; }
/// <summary>
/// ID of the fingerprint this claim is about.
/// </summary>
public required Guid FingerprintId { get; init; }
/// <summary>
/// CVE identifier (e.g., "CVE-2023-12345").
/// </summary>
public required string CveId { get; init; }
/// <summary>
/// Verdict: whether this fingerprint is fixed, vulnerable, or unknown.
/// </summary>
public required ClaimVerdict Verdict { get; init; }
/// <summary>
/// Evidence supporting this claim.
/// </summary>
public required FingerprintClaimEvidence Evidence { get; init; }
/// <summary>
/// Hash of the DSSE attestation if signed.
/// </summary>
public string? AttestationDsseHash { get; init; }
/// <summary>
/// When this claim was created.
/// </summary>
public DateTimeOffset CreatedAt { get; init; } = DateTimeOffset.UtcNow;
/// <summary>
/// When this claim was last updated.
/// </summary>
public DateTimeOffset? UpdatedAt { get; init; }
/// <summary>
/// Source that generated this claim (e.g., "repro-builder-alpine").
/// </summary>
public string? Source { get; init; }
/// <summary>
/// Confidence in this claim (0.0-1.0).
/// </summary>
public decimal Confidence { get; init; } = 1.0m;
}
/// <summary>
/// Verdict for a fingerprint claim.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ClaimVerdict
{
/// <summary>
/// The fingerprint is from a binary that contains the CVE fix.
/// </summary>
Fixed,
/// <summary>
/// The fingerprint is from a binary that is vulnerable to the CVE.
/// </summary>
Vulnerable,
/// <summary>
/// Unable to determine fix status.
/// </summary>
Unknown
}
/// <summary>
/// Evidence supporting a fingerprint claim.
/// </summary>
public sealed record FingerprintClaimEvidence
{
/// <summary>
/// Git commit or patch reference that introduced the fix.
/// </summary>
public required string PatchCommit { get; init; }
/// <summary>
/// List of function names that changed between vulnerable and fixed versions.
/// </summary>
public required IReadOnlyList<string> ChangedFunctions { get; init; }
/// <summary>
/// Similarity scores for modified functions (function name -> score).
/// </summary>
public IReadOnlyDictionary<string, decimal>? FunctionSimilarities { get; init; }
/// <summary>
/// Reference to the vulnerable build artifacts.
/// </summary>
public string? VulnerableBuildRef { get; init; }
/// <summary>
/// Reference to the patched build artifacts.
/// </summary>
public string? PatchedBuildRef { get; init; }
/// <summary>
/// Source package name.
/// </summary>
public string? SourcePackage { get; init; }
/// <summary>
/// Vulnerable version string.
/// </summary>
public string? VulnerableVersion { get; init; }
/// <summary>
/// Patched version string.
/// </summary>
public string? PatchedVersion { get; init; }
/// <summary>
/// Distro and release this build was done for.
/// </summary>
public string? DistroRelease { get; init; }
/// <summary>
/// Builder image used for reproducible builds.
/// </summary>
public string? BuilderImage { get; init; }
/// <summary>
/// Timestamp of the vulnerable build.
/// </summary>
public DateTimeOffset? VulnerableBuildTimestamp { get; init; }
/// <summary>
/// Timestamp of the patched build.
/// </summary>
public DateTimeOffset? PatchedBuildTimestamp { get; init; }
/// <summary>
/// Diff statistics summary.
/// </summary>
public DiffStatistics? DiffStatistics { get; init; }
}
/// <summary>
/// Repository for managing fingerprint claims.
/// </summary>
public interface IFingerprintClaimRepository
{
/// <summary>
/// Creates a new fingerprint claim.
/// </summary>
/// <param name="claim">The claim to create.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>The created claim ID.</returns>
Task<Guid> CreateClaimAsync(FingerprintClaim claim, CancellationToken ct = default);
/// <summary>
/// Creates multiple claims in a batch.
/// </summary>
/// <param name="claims">Claims to create.</param>
/// <param name="ct">Cancellation token.</param>
Task CreateClaimsBatchAsync(IEnumerable<FingerprintClaim> claims, CancellationToken ct = default);
/// <summary>
/// Gets a claim by ID.
/// </summary>
/// <param name="id">Claim ID.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>The claim if found.</returns>
Task<FingerprintClaim?> GetClaimByIdAsync(Guid id, CancellationToken ct = default);
/// <summary>
/// Gets all claims for a specific fingerprint.
/// </summary>
/// <param name="fingerprintId">Fingerprint ID.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>List of claims for the fingerprint.</returns>
Task<IReadOnlyList<FingerprintClaim>> GetClaimsByFingerprintAsync(
Guid fingerprintId,
CancellationToken ct = default);
/// <summary>
/// Gets all claims for a specific fingerprint hash.
/// </summary>
/// <param name="fingerprintHash">Fingerprint hash (hex-encoded).</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>List of claims for the fingerprint.</returns>
Task<IReadOnlyList<FingerprintClaim>> GetClaimsByFingerprintHashAsync(
string fingerprintHash,
CancellationToken ct = default);
/// <summary>
/// Gets all claims for a specific CVE.
/// </summary>
/// <param name="cveId">CVE identifier.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>List of claims for the CVE.</returns>
Task<IReadOnlyList<FingerprintClaim>> GetClaimsByCveAsync(
string cveId,
CancellationToken ct = default);
/// <summary>
/// Gets claims with a specific verdict.
/// </summary>
/// <param name="verdict">Verdict to filter by.</param>
/// <param name="limit">Maximum results to return.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>List of claims with the verdict.</returns>
Task<IReadOnlyList<FingerprintClaim>> GetClaimsByVerdictAsync(
ClaimVerdict verdict,
int limit = 100,
CancellationToken ct = default);
/// <summary>
/// Updates an existing claim.
/// </summary>
/// <param name="claim">The updated claim.</param>
/// <param name="ct">Cancellation token.</param>
Task UpdateClaimAsync(FingerprintClaim claim, CancellationToken ct = default);
/// <summary>
/// Deletes a claim by ID.
/// </summary>
/// <param name="id">Claim ID.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>True if deleted, false if not found.</returns>
Task<bool> DeleteClaimAsync(Guid id, CancellationToken ct = default);
/// <summary>
/// Checks if a claim already exists for a fingerprint+CVE combination.
/// </summary>
/// <param name="fingerprintId">Fingerprint ID.</param>
/// <param name="cveId">CVE identifier.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>True if a claim exists.</returns>
Task<bool> ClaimExistsAsync(Guid fingerprintId, string cveId, CancellationToken ct = default);
}
/// <summary>
/// Repository for managing function fingerprints (per-binary breakdown).
/// </summary>
public interface IFunctionFingerprintRepository
{
/// <summary>
/// Stores function fingerprints for a binary.
/// </summary>
/// <param name="binaryFingerprintId">Parent binary fingerprint ID.</param>
/// <param name="functions">Function fingerprints to store.</param>
/// <param name="ct">Cancellation token.</param>
Task StoreFunctionsAsync(
Guid binaryFingerprintId,
IEnumerable<FunctionFingerprint> functions,
CancellationToken ct = default);
/// <summary>
/// Gets all function fingerprints for a binary.
/// </summary>
/// <param name="binaryFingerprintId">Parent binary fingerprint ID.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>List of function fingerprints.</returns>
Task<IReadOnlyList<FunctionFingerprint>> GetFunctionsByBinaryAsync(
Guid binaryFingerprintId,
CancellationToken ct = default);
/// <summary>
/// Searches for functions by name pattern.
/// </summary>
/// <param name="namePattern">Function name pattern (SQL LIKE).</param>
/// <param name="limit">Maximum results.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>Matching functions with their binary IDs.</returns>
Task<IReadOnlyList<(Guid BinaryId, FunctionFingerprint Function)>> SearchFunctionsByNameAsync(
string namePattern,
int limit = 100,
CancellationToken ct = default);
/// <summary>
/// Finds functions matching a specific basic block hash.
/// </summary>
/// <param name="basicBlockHash">Hash to search for.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>Matching functions with their binary IDs.</returns>
Task<IReadOnlyList<(Guid BinaryId, FunctionFingerprint Function)>> FindByBasicBlockHashAsync(
byte[] basicBlockHash,
CancellationToken ct = default);
/// <summary>
/// Deletes all function fingerprints for a binary.
/// </summary>
/// <param name="binaryFingerprintId">Parent binary fingerprint ID.</param>
/// <param name="ct">Cancellation token.</param>
Task DeleteFunctionsByBinaryAsync(Guid binaryFingerprintId, CancellationToken ct = default);
}