save audit remarks applications progress
This commit is contained in:
@@ -151,6 +151,7 @@ public static class EpssEndpoints
|
||||
private static async Task<IResult> GetHistory(
|
||||
[FromRoute] string cveId,
|
||||
[FromServices] IEpssProvider epssProvider,
|
||||
[FromServices] TimeProvider timeProvider,
|
||||
[FromQuery] string? startDate = null,
|
||||
[FromQuery] string? endDate = null,
|
||||
[FromQuery] int days = 30,
|
||||
@@ -183,7 +184,7 @@ public static class EpssEndpoints
|
||||
else
|
||||
{
|
||||
// Default to last N days
|
||||
end = DateOnly.FromDateTime(DateTime.UtcNow);
|
||||
end = DateOnly.FromDateTime(timeProvider.GetUtcNow().UtcDateTime);
|
||||
start = end.AddDays(-days);
|
||||
}
|
||||
|
||||
@@ -213,6 +214,7 @@ public static class EpssEndpoints
|
||||
/// </summary>
|
||||
private static async Task<IResult> GetStatus(
|
||||
[FromServices] IEpssProvider epssProvider,
|
||||
[FromServices] TimeProvider timeProvider,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var isAvailable = await epssProvider.IsAvailableAsync(cancellationToken);
|
||||
@@ -222,7 +224,7 @@ public static class EpssEndpoints
|
||||
{
|
||||
Available = isAvailable,
|
||||
LatestModelDate = modelDate?.ToString("yyyy-MM-dd"),
|
||||
LastCheckedUtc = DateTimeOffset.UtcNow
|
||||
LastCheckedUtc = timeProvider.GetUtcNow()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ internal static class EvidenceEndpoints
|
||||
string scanId,
|
||||
string findingId,
|
||||
IEvidenceCompositionService evidenceService,
|
||||
TimeProvider timeProvider,
|
||||
HttpContext context,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -108,7 +109,7 @@ internal static class EvidenceEndpoints
|
||||
}
|
||||
else if (evidence.Freshness.ExpiresAt.HasValue)
|
||||
{
|
||||
var timeUntilExpiry = evidence.Freshness.ExpiresAt.Value - DateTimeOffset.UtcNow;
|
||||
var timeUntilExpiry = evidence.Freshness.ExpiresAt.Value - timeProvider.GetUtcNow();
|
||||
if (timeUntilExpiry <= TimeSpan.FromDays(1))
|
||||
{
|
||||
context.Response.Headers["X-Evidence-Warning"] = "near-expiry";
|
||||
|
||||
@@ -270,6 +270,7 @@ internal static class SmartDiffEndpoints
|
||||
string candidateId,
|
||||
ReviewRequest request,
|
||||
IVexCandidateStore store,
|
||||
TimeProvider timeProvider,
|
||||
HttpContext httpContext,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
@@ -282,7 +283,7 @@ internal static class SmartDiffEndpoints
|
||||
var review = new VexCandidateReview(
|
||||
Action: action,
|
||||
Reviewer: reviewer,
|
||||
ReviewedAt: DateTimeOffset.UtcNow,
|
||||
ReviewedAt: timeProvider.GetUtcNow(),
|
||||
Comment: request.Comment);
|
||||
|
||||
var success = await store.ReviewCandidateAsync(candidateId, review, ct);
|
||||
|
||||
@@ -41,6 +41,7 @@ internal static class ProofBundleEndpoints
|
||||
private static async Task<IResult> HandleGenerateProofBundleAsync(
|
||||
[FromBody] ProofBundleRequest request,
|
||||
[FromServices] IProofBundleGenerator bundleGenerator,
|
||||
[FromServices] TimeProvider timeProvider,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(bundleGenerator);
|
||||
@@ -67,7 +68,7 @@ internal static class ProofBundleEndpoints
|
||||
{
|
||||
PathId = request.PathId,
|
||||
Bundle = bundle,
|
||||
GeneratedAt = DateTimeOffset.UtcNow
|
||||
GeneratedAt = timeProvider.GetUtcNow()
|
||||
};
|
||||
|
||||
return Results.Ok(response);
|
||||
|
||||
@@ -50,6 +50,7 @@ internal static class TriageInboxEndpoints
|
||||
[FromQuery] string? filter,
|
||||
[FromServices] IExploitPathGroupingService groupingService,
|
||||
[FromServices] IFindingQueryService findingService,
|
||||
[FromServices] TimeProvider timeProvider,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(groupingService);
|
||||
@@ -77,7 +78,7 @@ internal static class TriageInboxEndpoints
|
||||
FilteredPaths = filteredPaths.Count,
|
||||
Filter = filter,
|
||||
Paths = filteredPaths,
|
||||
GeneratedAt = DateTimeOffset.UtcNow
|
||||
GeneratedAt = timeProvider.GetUtcNow()
|
||||
};
|
||||
|
||||
return Results.Ok(response);
|
||||
|
||||
@@ -55,6 +55,7 @@ internal static class UnknownsEndpoints
|
||||
[FromQuery] int? limit,
|
||||
IUnknownRepository repository,
|
||||
IUnknownRanker ranker,
|
||||
TimeProvider timeProvider,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// Validate and default pagination
|
||||
@@ -95,9 +96,10 @@ internal static class UnknownsEndpoints
|
||||
PageSize: pageSize);
|
||||
|
||||
var result = await repository.ListUnknownsAsync(query, cancellationToken);
|
||||
var now = timeProvider.GetUtcNow();
|
||||
|
||||
return Results.Ok(new UnknownsListResponse(
|
||||
Items: result.Items.Select(UnknownItemResponse.FromUnknownItem).ToList(),
|
||||
Items: result.Items.Select(item => UnknownItemResponse.FromUnknownItem(item, now)).ToList(),
|
||||
TotalCount: result.TotalCount,
|
||||
Page: pageNum,
|
||||
PageSize: pageSize,
|
||||
@@ -195,7 +197,7 @@ public sealed record UnknownItemResponse(
|
||||
ContainmentResponse? Containment,
|
||||
DateTimeOffset CreatedAt)
|
||||
{
|
||||
public static UnknownItemResponse FromUnknownItem(UnknownItem item) => new(
|
||||
public static UnknownItemResponse FromUnknownItem(UnknownItem item, DateTimeOffset now) => new(
|
||||
Id: Guid.TryParse(item.Id, out var id) ? id : Guid.Empty,
|
||||
SubjectRef: item.ArtifactPurl ?? item.ArtifactDigest,
|
||||
Kind: string.Join(",", item.Reasons),
|
||||
@@ -209,7 +211,7 @@ public sealed record UnknownItemResponse(
|
||||
Containment: item.Containment != null
|
||||
? new ContainmentResponse(item.Containment.Seccomp, item.Containment.Fs)
|
||||
: null,
|
||||
CreatedAt: DateTimeOffset.UtcNow); // Would come from Unknown.SysFrom
|
||||
CreatedAt: now); // Would come from Unknown.SysFrom
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -120,6 +120,7 @@ internal static class WitnessEndpoints
|
||||
private static async Task<IResult> HandleVerifyWitnessAsync(
|
||||
Guid witnessId,
|
||||
IWitnessRepository repository,
|
||||
TimeProvider timeProvider,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(repository);
|
||||
@@ -161,10 +162,11 @@ internal static class WitnessEndpoints
|
||||
}
|
||||
|
||||
// Record verification attempt
|
||||
var now = timeProvider.GetUtcNow();
|
||||
await repository.RecordVerificationAsync(new WitnessVerificationRecord
|
||||
{
|
||||
WitnessId = witnessId,
|
||||
VerifiedAt = DateTimeOffset.UtcNow,
|
||||
VerifiedAt = now,
|
||||
VerifiedBy = "api",
|
||||
VerificationStatus = verificationStatus,
|
||||
VerificationError = verificationError
|
||||
@@ -176,7 +178,7 @@ internal static class WitnessEndpoints
|
||||
WitnessHash = witness.WitnessHash,
|
||||
Status = verificationStatus,
|
||||
Error = verificationError,
|
||||
VerifiedAt = DateTimeOffset.UtcNow,
|
||||
VerifiedAt = now,
|
||||
IsSigned = !string.IsNullOrEmpty(witness.DsseEnvelope)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@ public sealed class EvidenceBundleExporter : IEvidenceBundleExporter
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static async Task PrepareEvidenceFilesAsync(
|
||||
private async Task PrepareEvidenceFilesAsync(
|
||||
UnifiedEvidenceResponseDto evidence,
|
||||
List<(string path, MemoryStream stream, string contentType)> streams,
|
||||
List<ArchiveFileEntry> entries,
|
||||
@@ -621,7 +621,7 @@ public sealed class EvidenceBundleExporter : IEvidenceBundleExporter
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task CreateTarGzArchiveAsync(
|
||||
private async Task CreateTarGzArchiveAsync(
|
||||
string findingId,
|
||||
List<(string path, MemoryStream stream, string contentType)> files,
|
||||
Stream outputStream,
|
||||
@@ -660,7 +660,7 @@ public sealed class EvidenceBundleExporter : IEvidenceBundleExporter
|
||||
await gzipStream.WriteAsync(endBlocks, ct).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static byte[] CreateTarHeader(string name, long size)
|
||||
private byte[] CreateTarHeader(string name, long size)
|
||||
{
|
||||
var header = new byte[512];
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ public sealed class GatingReasonService : IGatingReasonService
|
||||
/// <summary>
|
||||
/// Computes a composite trust score for a VEX record.
|
||||
/// </summary>
|
||||
private static double ComputeVexTrustScore(TriageEffectiveVex vex)
|
||||
private double ComputeVexTrustScore(TriageEffectiveVex vex)
|
||||
{
|
||||
// Weighted combination of trust factors
|
||||
const double IssuerWeight = 0.4;
|
||||
|
||||
@@ -29,6 +29,7 @@ public sealed class ReportSigner : IReportSigner
|
||||
private readonly ILogger<ReportSigner> logger;
|
||||
private readonly ICryptoProviderRegistry cryptoRegistry;
|
||||
private readonly ICryptoHmac cryptoHmac;
|
||||
private readonly TimeProvider timeProvider;
|
||||
private readonly ICryptoProvider? provider;
|
||||
private readonly CryptoKeyReference? keyReference;
|
||||
private readonly CryptoSignerResolution? signerResolution;
|
||||
@@ -38,11 +39,13 @@ public sealed class ReportSigner : IReportSigner
|
||||
IOptions<ScannerWebServiceOptions> options,
|
||||
ICryptoProviderRegistry cryptoRegistry,
|
||||
ICryptoHmac cryptoHmac,
|
||||
TimeProvider timeProvider,
|
||||
ILogger<ReportSigner> logger)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
this.cryptoRegistry = cryptoRegistry ?? throw new ArgumentNullException(nameof(cryptoRegistry));
|
||||
this.cryptoHmac = cryptoHmac ?? throw new ArgumentNullException(nameof(cryptoHmac));
|
||||
this.timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
var value = options.Value ?? new ScannerWebServiceOptions();
|
||||
@@ -79,7 +82,7 @@ public sealed class ReportSigner : IReportSigner
|
||||
reference,
|
||||
canonicalAlgorithm,
|
||||
privateKey,
|
||||
createdAt: DateTimeOffset.UtcNow);
|
||||
createdAt: timeProvider.GetUtcNow());
|
||||
|
||||
provider.UpsertSigningKey(signingKeyDescriptor);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ public sealed class ScoreReplayService : IScoreReplayService
|
||||
private readonly IProofBundleWriter _bundleWriter;
|
||||
private readonly IScanManifestSigner _manifestSigner;
|
||||
private readonly IScoringService _scoringService;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly ILogger<ScoreReplayService> _logger;
|
||||
|
||||
public ScoreReplayService(
|
||||
@@ -31,6 +32,7 @@ public sealed class ScoreReplayService : IScoreReplayService
|
||||
IProofBundleWriter bundleWriter,
|
||||
IScanManifestSigner manifestSigner,
|
||||
IScoringService scoringService,
|
||||
TimeProvider timeProvider,
|
||||
ILogger<ScoreReplayService> logger)
|
||||
{
|
||||
_manifestRepository = manifestRepository ?? throw new ArgumentNullException(nameof(manifestRepository));
|
||||
@@ -38,6 +40,7 @@ public sealed class ScoreReplayService : IScoreReplayService
|
||||
_bundleWriter = bundleWriter ?? throw new ArgumentNullException(nameof(bundleWriter));
|
||||
_manifestSigner = manifestSigner ?? throw new ArgumentNullException(nameof(manifestSigner));
|
||||
_scoringService = scoringService ?? throw new ArgumentNullException(nameof(scoringService));
|
||||
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
@@ -99,7 +102,7 @@ public sealed class ScoreReplayService : IScoreReplayService
|
||||
RootHash: bundle.RootHash,
|
||||
BundleUri: bundle.BundleUri,
|
||||
ManifestHash: manifest.ComputeHash(),
|
||||
ReplayedAt: DateTimeOffset.UtcNow,
|
||||
ReplayedAt: _timeProvider.GetUtcNow(),
|
||||
Deterministic: manifest.Deterministic);
|
||||
}
|
||||
finally
|
||||
@@ -164,7 +167,7 @@ public sealed class ScoreReplayService : IScoreReplayService
|
||||
ComputedRootHash: computedRootHash,
|
||||
ManifestValid: manifestVerify.IsValid,
|
||||
LedgerValid: ledgerValid,
|
||||
VerifiedAt: DateTimeOffset.UtcNow,
|
||||
VerifiedAt: _timeProvider.GetUtcNow(),
|
||||
ErrorMessage: string.Join("; ", errors));
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ public sealed class SliceQueryService : ISliceQueryService
|
||||
private readonly SliceHasher _hasher;
|
||||
private readonly IFileContentAddressableStore _cas;
|
||||
private readonly IScanMetadataRepository _scanRepo;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly SliceQueryServiceOptions _options;
|
||||
private readonly ILogger<SliceQueryService> _logger;
|
||||
|
||||
@@ -51,6 +52,7 @@ public sealed class SliceQueryService : ISliceQueryService
|
||||
SliceHasher hasher,
|
||||
IFileContentAddressableStore cas,
|
||||
IScanMetadataRepository scanRepo,
|
||||
TimeProvider timeProvider,
|
||||
IOptions<SliceQueryServiceOptions> options,
|
||||
ILogger<SliceQueryService> logger)
|
||||
{
|
||||
@@ -61,6 +63,7 @@ public sealed class SliceQueryService : ISliceQueryService
|
||||
_hasher = hasher ?? throw new ArgumentNullException(nameof(hasher));
|
||||
_cas = cas ?? throw new ArgumentNullException(nameof(cas));
|
||||
_scanRepo = scanRepo ?? throw new ArgumentNullException(nameof(scanRepo));
|
||||
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||
_options = options?.Value ?? new SliceQueryServiceOptions();
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
@@ -121,7 +124,7 @@ public sealed class SliceQueryService : ISliceQueryService
|
||||
PathWitnesses = slice.Verdict.PathWitnesses.IsDefaultOrEmpty
|
||||
? Array.Empty<string>()
|
||||
: slice.Verdict.PathWitnesses.ToList(),
|
||||
CachedAt = DateTimeOffset.UtcNow
|
||||
CachedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
await _cache.SetAsync(cacheKey, cacheEntry, TimeSpan.FromHours(1), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -64,6 +64,12 @@ public sealed class TestProofBundleRepository : StellaOps.Scanner.Storage.Reposi
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, ProofBundleRow> _bundlesByRootHash = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly ConcurrentDictionary<Guid, List<ProofBundleRow>> _bundlesByScanId = new();
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public TestProofBundleRepository(TimeProvider timeProvider)
|
||||
{
|
||||
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||
}
|
||||
|
||||
public Task<ProofBundleRow?> GetByRootHashAsync(string rootHash, CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -112,8 +118,8 @@ public sealed class TestProofBundleRepository : StellaOps.Scanner.Storage.Reposi
|
||||
public Task<int> DeleteExpiredAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
|
||||
var now = _timeProvider.GetUtcNow();
|
||||
var expired = _bundlesByRootHash.Values
|
||||
.Where(b => b.ExpiresAt.HasValue && b.ExpiresAt.Value < now)
|
||||
.ToList();
|
||||
|
||||
@@ -22,6 +22,7 @@ public sealed class UnifiedEvidenceService : IUnifiedEvidenceService
|
||||
private readonly TriageDbContext _dbContext;
|
||||
private readonly IGatingReasonService _gatingService;
|
||||
private readonly IReplayCommandService _replayService;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly ILogger<UnifiedEvidenceService> _logger;
|
||||
|
||||
private const double DefaultPolicyTrustThreshold = 0.7;
|
||||
@@ -30,11 +31,13 @@ public sealed class UnifiedEvidenceService : IUnifiedEvidenceService
|
||||
TriageDbContext dbContext,
|
||||
IGatingReasonService gatingService,
|
||||
IReplayCommandService replayService,
|
||||
TimeProvider timeProvider,
|
||||
ILogger<UnifiedEvidenceService> logger)
|
||||
{
|
||||
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
||||
_gatingService = gatingService ?? throw new ArgumentNullException(nameof(gatingService));
|
||||
_replayService = replayService ?? throw new ArgumentNullException(nameof(replayService));
|
||||
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
@@ -106,7 +109,7 @@ public sealed class UnifiedEvidenceService : IUnifiedEvidenceService
|
||||
ReplayCommand = replayResponse?.FullCommand?.Command,
|
||||
ShortReplayCommand = replayResponse?.ShortCommand?.Command,
|
||||
EvidenceBundleUrl = replayResponse?.Bundle?.DownloadUri,
|
||||
GeneratedAt = DateTimeOffset.UtcNow,
|
||||
GeneratedAt = _timeProvider.GetUtcNow(),
|
||||
CacheKey = cacheKey
|
||||
};
|
||||
}
|
||||
@@ -277,11 +280,11 @@ public sealed class UnifiedEvidenceService : IUnifiedEvidenceService
|
||||
AttestationsVerified = hasAttestations,
|
||||
EvidenceComplete = hasVex && hasReachability,
|
||||
Issues = issues.Count > 0 ? issues : null,
|
||||
VerifiedAt = DateTimeOffset.UtcNow
|
||||
VerifiedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
private static double ComputeVexTrustScore(TriageEffectiveVex vex)
|
||||
private double ComputeVexTrustScore(TriageEffectiveVex vex)
|
||||
{
|
||||
const double IssuerWeight = 0.4;
|
||||
const double RecencyWeight = 0.2;
|
||||
@@ -289,7 +292,7 @@ public sealed class UnifiedEvidenceService : IUnifiedEvidenceService
|
||||
const double EvidenceWeight = 0.2;
|
||||
|
||||
var issuerTrust = GetIssuerTrust(vex.Issuer);
|
||||
var recencyTrust = GetRecencyTrust((DateTimeOffset?)vex.ValidFrom);
|
||||
var recencyTrust = GetRecencyTrust((DateTimeOffset?)vex.ValidFrom, _timeProvider.GetUtcNow());
|
||||
var justificationTrust = GetJustificationTrust(vex.PrunedSourcesJson);
|
||||
var evidenceTrust = !string.IsNullOrEmpty(vex.DsseEnvelopeHash) ? 0.8 : 0.3;
|
||||
|
||||
@@ -309,10 +312,10 @@ public sealed class UnifiedEvidenceService : IUnifiedEvidenceService
|
||||
_ => 0.5
|
||||
};
|
||||
|
||||
private static double GetRecencyTrust(DateTimeOffset? timestamp)
|
||||
private static double GetRecencyTrust(DateTimeOffset? timestamp, DateTimeOffset now)
|
||||
{
|
||||
if (timestamp is null) return 0.3;
|
||||
var age = DateTimeOffset.UtcNow - timestamp.Value;
|
||||
var age = now - timestamp.Value;
|
||||
return age.TotalDays switch { <= 7 => 1.0, <= 30 => 0.9, <= 90 => 0.7, <= 365 => 0.5, _ => 0.3 };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user