- Introduced `VexStatusChipComponent` to display VEX status with color coding and tooltips. - Implemented integration tests for reachability drift detection, covering various scenarios including drift detection, determinism, and error handling. - Enhanced `ScannerToSignalsReachabilityTests` with a null implementation of `ICallGraphSyncService` for better test isolation. - Updated project references to include the new Reachability Drift library.
207 lines
6.3 KiB
C#
207 lines
6.3 KiB
C#
// -----------------------------------------------------------------------------
|
|
// IHumanApprovalAttestationService.cs
|
|
// Sprint: SPRINT_3801_0001_0004_human_approval_attestation (APPROVE-001)
|
|
// Description: Interface for creating human approval attestations.
|
|
// -----------------------------------------------------------------------------
|
|
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using StellaOps.Scanner.WebService.Contracts;
|
|
using StellaOps.Scanner.WebService.Domain;
|
|
|
|
namespace StellaOps.Scanner.WebService.Services;
|
|
|
|
/// <summary>
|
|
/// Creates DSSE attestations for human approval decisions.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// Human approvals record decisions made by authorized personnel to
|
|
/// accept, defer, reject, suppress, or escalate security findings.
|
|
/// </para>
|
|
/// <para>
|
|
/// These attestations have a 30-day default TTL to force periodic
|
|
/// re-review of risk acceptance decisions.
|
|
/// </para>
|
|
/// </remarks>
|
|
public interface IHumanApprovalAttestationService
|
|
{
|
|
/// <summary>
|
|
/// Creates a human approval attestation.
|
|
/// </summary>
|
|
/// <param name="input">The approval input parameters.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>
|
|
/// A <see cref="HumanApprovalAttestationResult"/> containing the
|
|
/// attestation statement and content-addressed attestation ID.
|
|
/// </returns>
|
|
Task<HumanApprovalAttestationResult> CreateAttestationAsync(
|
|
HumanApprovalAttestationInput input,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Gets an existing approval attestation.
|
|
/// </summary>
|
|
/// <param name="scanId">The scan ID.</param>
|
|
/// <param name="findingId">The finding ID.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>The attestation result if found, null otherwise.</returns>
|
|
Task<HumanApprovalAttestationResult?> GetAttestationAsync(
|
|
ScanId scanId,
|
|
string findingId,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Gets all active approval attestations for a scan.
|
|
/// </summary>
|
|
/// <param name="scanId">The scan ID.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>List of active approval attestations.</returns>
|
|
Task<IReadOnlyList<HumanApprovalAttestationResult>> GetApprovalsByScanAsync(
|
|
ScanId scanId,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Revokes an existing approval attestation.
|
|
/// </summary>
|
|
/// <param name="scanId">The scan ID.</param>
|
|
/// <param name="findingId">The finding ID.</param>
|
|
/// <param name="revokedBy">Who revoked the approval.</param>
|
|
/// <param name="reason">Reason for revocation.</param>
|
|
/// <param name="cancellationToken">Cancellation token.</param>
|
|
/// <returns>True if revoked, false if not found.</returns>
|
|
Task<bool> RevokeApprovalAsync(
|
|
ScanId scanId,
|
|
string findingId,
|
|
string revokedBy,
|
|
string reason,
|
|
CancellationToken cancellationToken = default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Input for creating a human approval attestation.
|
|
/// </summary>
|
|
public sealed record HumanApprovalAttestationInput
|
|
{
|
|
/// <summary>
|
|
/// The scan ID.
|
|
/// </summary>
|
|
public required ScanId ScanId { get; init; }
|
|
|
|
/// <summary>
|
|
/// The finding ID (e.g., CVE identifier).
|
|
/// </summary>
|
|
public required string FindingId { get; init; }
|
|
|
|
/// <summary>
|
|
/// The approval decision.
|
|
/// </summary>
|
|
public required ApprovalDecision Decision { get; init; }
|
|
|
|
/// <summary>
|
|
/// The approver's user ID.
|
|
/// </summary>
|
|
public required string ApproverUserId { get; init; }
|
|
|
|
/// <summary>
|
|
/// The approver's display name.
|
|
/// </summary>
|
|
public string? ApproverDisplayName { get; init; }
|
|
|
|
/// <summary>
|
|
/// The approver's role.
|
|
/// </summary>
|
|
public string? ApproverRole { get; init; }
|
|
|
|
/// <summary>
|
|
/// Justification for the decision.
|
|
/// </summary>
|
|
public required string Justification { get; init; }
|
|
|
|
/// <summary>
|
|
/// Optional custom TTL for the approval.
|
|
/// </summary>
|
|
public TimeSpan? ApprovalTtl { get; init; }
|
|
|
|
/// <summary>
|
|
/// Reference to the policy decision attestation.
|
|
/// </summary>
|
|
public string? PolicyDecisionRef { get; init; }
|
|
|
|
/// <summary>
|
|
/// Optional restrictions on the approval scope.
|
|
/// </summary>
|
|
public ApprovalRestrictions? Restrictions { get; init; }
|
|
|
|
/// <summary>
|
|
/// Optional prior approval being superseded.
|
|
/// </summary>
|
|
public string? Supersedes { get; init; }
|
|
|
|
/// <summary>
|
|
/// Optional metadata.
|
|
/// </summary>
|
|
public IDictionary<string, string>? Metadata { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Result of creating a human approval attestation.
|
|
/// </summary>
|
|
public sealed record HumanApprovalAttestationResult
|
|
{
|
|
/// <summary>
|
|
/// Whether the attestation was created successfully.
|
|
/// </summary>
|
|
public required bool Success { get; init; }
|
|
|
|
/// <summary>
|
|
/// The human approval statement.
|
|
/// </summary>
|
|
public HumanApprovalStatement? Statement { get; init; }
|
|
|
|
/// <summary>
|
|
/// The content-addressed attestation ID.
|
|
/// </summary>
|
|
public string? AttestationId { get; init; }
|
|
|
|
/// <summary>
|
|
/// The DSSE envelope (if signing is enabled).
|
|
/// </summary>
|
|
public string? DsseEnvelope { get; init; }
|
|
|
|
/// <summary>
|
|
/// Error message if creation failed.
|
|
/// </summary>
|
|
public string? Error { get; init; }
|
|
|
|
/// <summary>
|
|
/// Whether the approval has been revoked.
|
|
/// </summary>
|
|
public bool IsRevoked { get; init; }
|
|
|
|
/// <summary>
|
|
/// Creates a successful result.
|
|
/// </summary>
|
|
public static HumanApprovalAttestationResult Succeeded(
|
|
HumanApprovalStatement statement,
|
|
string attestationId,
|
|
string? dsseEnvelope = null)
|
|
=> new()
|
|
{
|
|
Success = true,
|
|
Statement = statement,
|
|
AttestationId = attestationId,
|
|
DsseEnvelope = dsseEnvelope
|
|
};
|
|
|
|
/// <summary>
|
|
/// Creates a failed result.
|
|
/// </summary>
|
|
public static HumanApprovalAttestationResult Failed(string error)
|
|
=> new()
|
|
{
|
|
Success = false,
|
|
Error = error
|
|
};
|
|
}
|