up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
using System.Collections.Concurrent;
|
||||
using StellaOps.VulnExplorer.Api.Models;
|
||||
|
||||
namespace StellaOps.VulnExplorer.Api.Data;
|
||||
|
||||
/// <summary>
|
||||
/// In-memory VEX decision store for development/testing.
|
||||
/// Production would use PostgreSQL repository.
|
||||
/// </summary>
|
||||
public sealed class VexDecisionStore
|
||||
{
|
||||
private readonly ConcurrentDictionary<Guid, VexDecisionDto> _decisions = new();
|
||||
|
||||
public VexDecisionDto Create(CreateVexDecisionRequest request, string userId, string userDisplayName)
|
||||
{
|
||||
var id = Guid.NewGuid();
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
|
||||
var decision = new VexDecisionDto(
|
||||
Id: id,
|
||||
VulnerabilityId: request.VulnerabilityId,
|
||||
Subject: request.Subject,
|
||||
Status: request.Status,
|
||||
JustificationType: request.JustificationType,
|
||||
JustificationText: request.JustificationText,
|
||||
EvidenceRefs: request.EvidenceRefs,
|
||||
Scope: request.Scope,
|
||||
ValidFor: request.ValidFor,
|
||||
AttestationRef: null, // Will be set when attestation is generated
|
||||
SupersedesDecisionId: request.SupersedesDecisionId,
|
||||
CreatedBy: new ActorRefDto(userId, userDisplayName),
|
||||
CreatedAt: now,
|
||||
UpdatedAt: null);
|
||||
|
||||
_decisions[id] = decision;
|
||||
return decision;
|
||||
}
|
||||
|
||||
public VexDecisionDto? Update(Guid id, UpdateVexDecisionRequest request)
|
||||
{
|
||||
if (!_decisions.TryGetValue(id, out var existing))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var updated = existing with
|
||||
{
|
||||
Status = request.Status ?? existing.Status,
|
||||
JustificationType = request.JustificationType ?? existing.JustificationType,
|
||||
JustificationText = request.JustificationText ?? existing.JustificationText,
|
||||
EvidenceRefs = request.EvidenceRefs ?? existing.EvidenceRefs,
|
||||
Scope = request.Scope ?? existing.Scope,
|
||||
ValidFor = request.ValidFor ?? existing.ValidFor,
|
||||
SupersedesDecisionId = request.SupersedesDecisionId ?? existing.SupersedesDecisionId,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
};
|
||||
|
||||
_decisions[id] = updated;
|
||||
return updated;
|
||||
}
|
||||
|
||||
public VexDecisionDto? Get(Guid id) =>
|
||||
_decisions.TryGetValue(id, out var decision) ? decision : null;
|
||||
|
||||
public IReadOnlyList<VexDecisionDto> Query(
|
||||
string? vulnerabilityId = null,
|
||||
string? subjectName = null,
|
||||
VexStatus? status = null,
|
||||
int skip = 0,
|
||||
int take = 50)
|
||||
{
|
||||
IEnumerable<VexDecisionDto> query = _decisions.Values;
|
||||
|
||||
if (vulnerabilityId is not null)
|
||||
{
|
||||
query = query.Where(d => string.Equals(d.VulnerabilityId, vulnerabilityId, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
if (subjectName is not null)
|
||||
{
|
||||
query = query.Where(d => d.Subject.Name.Contains(subjectName, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
if (status is not null)
|
||||
{
|
||||
query = query.Where(d => d.Status == status);
|
||||
}
|
||||
|
||||
// Deterministic ordering: createdAt desc, id asc
|
||||
return query
|
||||
.OrderByDescending(d => d.CreatedAt)
|
||||
.ThenBy(d => d.Id)
|
||||
.Skip(skip)
|
||||
.Take(take)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public int Count() => _decisions.Count;
|
||||
}
|
||||
Reference in New Issue
Block a user