sprints work
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace StellaOps.Scanner.Triage.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an attestation for a triage finding.
|
||||
/// </summary>
|
||||
[Table("triage_attestation")]
|
||||
public sealed class TriageAttestation
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique identifier.
|
||||
/// </summary>
|
||||
[Key]
|
||||
[Column("id")]
|
||||
public Guid Id { get; init; } = Guid.NewGuid();
|
||||
|
||||
/// <summary>
|
||||
/// The finding this attestation applies to.
|
||||
/// </summary>
|
||||
[Column("finding_id")]
|
||||
public Guid FindingId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of attestation (vex, sbom, reachability, etc.).
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Column("type")]
|
||||
public required string Type { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Issuer of the attestation.
|
||||
/// </summary>
|
||||
[Column("issuer")]
|
||||
public string? Issuer { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Hash of the DSSE envelope.
|
||||
/// </summary>
|
||||
[Column("envelope_hash")]
|
||||
public string? EnvelopeHash { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the attestation content (CAS URI).
|
||||
/// </summary>
|
||||
[Column("content_ref")]
|
||||
public string? ContentRef { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Reference to ledger/Rekor entry for signature verification.
|
||||
/// </summary>
|
||||
[Column("ledger_ref")]
|
||||
public string? LedgerRef { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When this attestation was collected.
|
||||
/// </summary>
|
||||
[Column("collected_at")]
|
||||
public DateTimeOffset CollectedAt { get; init; } = DateTimeOffset.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Navigation property back to the finding.
|
||||
/// </summary>
|
||||
[ForeignKey(nameof(FindingId))]
|
||||
public TriageFinding? Finding { get; init; }
|
||||
}
|
||||
@@ -68,6 +68,72 @@ public sealed class TriageFinding
|
||||
[Column("last_seen_at")]
|
||||
public DateTimeOffset LastSeenAt { get; set; } = DateTimeOffset.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// When this finding was last updated.
|
||||
/// </summary>
|
||||
[Column("updated_at")]
|
||||
public DateTimeOffset UpdatedAt { get; set; } = DateTimeOffset.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Current status of the finding (e.g., "open", "resolved", "muted").
|
||||
/// </summary>
|
||||
[Column("status")]
|
||||
public string? Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Artifact digest for replay command generation.
|
||||
/// </summary>
|
||||
[Column("artifact_digest")]
|
||||
public string? ArtifactDigest { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The scan that detected this finding.
|
||||
/// </summary>
|
||||
[Column("scan_id")]
|
||||
public Guid? ScanId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this finding has been muted by a user decision.
|
||||
/// </summary>
|
||||
[Column("is_muted")]
|
||||
public bool IsMuted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this finding is fixed via distro backport.
|
||||
/// </summary>
|
||||
[Column("is_backport_fixed")]
|
||||
public bool IsBackportFixed { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Version in which this vulnerability is fixed (for backport detection).
|
||||
/// </summary>
|
||||
[Column("fixed_in_version")]
|
||||
public string? FixedInVersion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// CVE identifier that supersedes this finding's CVE.
|
||||
/// </summary>
|
||||
[Column("superseded_by")]
|
||||
public string? SupersededBy { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Package URL identifying the affected component (alias for Purl for compatibility).
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
public string? ComponentPurl => Purl;
|
||||
|
||||
/// <summary>
|
||||
/// ID of the delta comparison showing what changed for this finding.
|
||||
/// </summary>
|
||||
[Column("delta_comparison_id")]
|
||||
public Guid? DeltaComparisonId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Knowledge snapshot ID used during analysis.
|
||||
/// </summary>
|
||||
[Column("knowledge_snapshot_id")]
|
||||
public string? KnowledgeSnapshotId { get; init; }
|
||||
|
||||
// Navigation properties
|
||||
public ICollection<TriageEffectiveVex> EffectiveVexRecords { get; init; } = new List<TriageEffectiveVex>();
|
||||
public ICollection<TriageReachabilityResult> ReachabilityResults { get; init; } = new List<TriageReachabilityResult>();
|
||||
@@ -75,4 +141,20 @@ public sealed class TriageFinding
|
||||
public ICollection<TriageDecision> Decisions { get; init; } = new List<TriageDecision>();
|
||||
public ICollection<TriageEvidenceArtifact> EvidenceArtifacts { get; init; } = new List<TriageEvidenceArtifact>();
|
||||
public ICollection<TriageSnapshot> Snapshots { get; init; } = new List<TriageSnapshot>();
|
||||
|
||||
/// <summary>
|
||||
/// Policy decisions associated with this finding.
|
||||
/// </summary>
|
||||
public ICollection<TriagePolicyDecision> PolicyDecisions { get; init; } = new List<TriagePolicyDecision>();
|
||||
|
||||
/// <summary>
|
||||
/// Attestations for this finding.
|
||||
/// </summary>
|
||||
public ICollection<TriageAttestation> Attestations { get; init; } = new List<TriageAttestation>();
|
||||
|
||||
/// <summary>
|
||||
/// Navigation property back to the scan.
|
||||
/// </summary>
|
||||
[ForeignKey(nameof(ScanId))]
|
||||
public TriageScan? Scan { get; init; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace StellaOps.Scanner.Triage.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a policy decision applied to a triage finding.
|
||||
/// </summary>
|
||||
[Table("triage_policy_decision")]
|
||||
public sealed class TriagePolicyDecision
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique identifier.
|
||||
/// </summary>
|
||||
[Key]
|
||||
[Column("id")]
|
||||
public Guid Id { get; init; } = Guid.NewGuid();
|
||||
|
||||
/// <summary>
|
||||
/// The finding this decision applies to.
|
||||
/// </summary>
|
||||
[Column("finding_id")]
|
||||
public Guid FindingId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Policy identifier that made this decision.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Column("policy_id")]
|
||||
public required string PolicyId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Action taken (dismiss, waive, tolerate, block).
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Column("action")]
|
||||
public required string Action { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Reason for the decision.
|
||||
/// </summary>
|
||||
[Column("reason")]
|
||||
public string? Reason { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When this decision was applied.
|
||||
/// </summary>
|
||||
[Column("applied_at")]
|
||||
public DateTimeOffset AppliedAt { get; init; } = DateTimeOffset.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Navigation property back to the finding.
|
||||
/// </summary>
|
||||
[ForeignKey(nameof(FindingId))]
|
||||
public TriageFinding? Finding { get; init; }
|
||||
}
|
||||
@@ -60,6 +60,12 @@ public sealed class TriageReachabilityResult
|
||||
[Column("computed_at")]
|
||||
public DateTimeOffset ComputedAt { get; init; } = DateTimeOffset.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Content-addressed ID of the reachability subgraph for this finding.
|
||||
/// </summary>
|
||||
[Column("subgraph_id")]
|
||||
public string? SubgraphId { get; init; }
|
||||
|
||||
// Navigation property
|
||||
[ForeignKey(nameof(FindingId))]
|
||||
public TriageFinding? Finding { get; init; }
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace StellaOps.Scanner.Triage.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a scan that produced triage findings.
|
||||
/// </summary>
|
||||
[Table("triage_scan")]
|
||||
public sealed class TriageScan
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique identifier for the scan.
|
||||
/// </summary>
|
||||
[Key]
|
||||
[Column("id")]
|
||||
public Guid Id { get; init; } = Guid.NewGuid();
|
||||
|
||||
/// <summary>
|
||||
/// Image reference that was scanned.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Column("image_reference")]
|
||||
public required string ImageReference { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Image digest (sha256:...).
|
||||
/// </summary>
|
||||
[Column("image_digest")]
|
||||
public string? ImageDigest { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Target digest for replay command generation.
|
||||
/// </summary>
|
||||
[Column("target_digest")]
|
||||
public string? TargetDigest { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Target reference for replay command generation.
|
||||
/// </summary>
|
||||
[Column("target_reference")]
|
||||
public string? TargetReference { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Knowledge snapshot ID used for this scan.
|
||||
/// </summary>
|
||||
[Column("knowledge_snapshot_id")]
|
||||
public string? KnowledgeSnapshotId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the scan started.
|
||||
/// </summary>
|
||||
[Column("started_at")]
|
||||
public DateTimeOffset StartedAt { get; init; } = DateTimeOffset.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// When the scan completed.
|
||||
/// </summary>
|
||||
[Column("completed_at")]
|
||||
public DateTimeOffset? CompletedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Scan status (running, completed, failed).
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Column("status")]
|
||||
public required string Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Policy file hash used during the scan.
|
||||
/// </summary>
|
||||
[Column("policy_hash")]
|
||||
public string? PolicyHash { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Feed snapshot hash for deterministic replay.
|
||||
/// </summary>
|
||||
[Column("feed_snapshot_hash")]
|
||||
public string? FeedSnapshotHash { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the knowledge snapshot was created.
|
||||
/// </summary>
|
||||
[Column("snapshot_created_at")]
|
||||
public DateTimeOffset? SnapshotCreatedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Feed versions used in this scan (JSON dictionary).
|
||||
/// </summary>
|
||||
[Column("feed_versions", TypeName = "jsonb")]
|
||||
public Dictionary<string, string>? FeedVersions { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Content hash of the snapshot for verification.
|
||||
/// </summary>
|
||||
[Column("snapshot_content_hash")]
|
||||
public string? SnapshotContentHash { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Final digest of the scan result for verification.
|
||||
/// </summary>
|
||||
[Column("final_digest")]
|
||||
public string? FinalDigest { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Feed snapshot timestamp.
|
||||
/// </summary>
|
||||
[Column("feed_snapshot_at")]
|
||||
public DateTimeOffset? FeedSnapshotAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Offline kit bundle ID if scan was done with offline kit.
|
||||
/// </summary>
|
||||
[Column("offline_bundle_id")]
|
||||
public string? OfflineBundleId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Navigation property to findings.
|
||||
/// </summary>
|
||||
public ICollection<TriageFinding> Findings { get; init; } = new List<TriageFinding>();
|
||||
}
|
||||
@@ -51,6 +51,21 @@ public sealed class TriageDbContext : DbContext
|
||||
/// </summary>
|
||||
public DbSet<TriageSnapshot> Snapshots => Set<TriageSnapshot>();
|
||||
|
||||
/// <summary>
|
||||
/// Scans that produced findings.
|
||||
/// </summary>
|
||||
public DbSet<TriageScan> Scans => Set<TriageScan>();
|
||||
|
||||
/// <summary>
|
||||
/// Policy decisions.
|
||||
/// </summary>
|
||||
public DbSet<TriagePolicyDecision> PolicyDecisions => Set<TriagePolicyDecision>();
|
||||
|
||||
/// <summary>
|
||||
/// Attestations.
|
||||
/// </summary>
|
||||
public DbSet<TriageAttestation> Attestations => Set<TriageAttestation>();
|
||||
|
||||
/// <summary>
|
||||
/// Current case view (read-only).
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user