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

This commit is contained in:
StellaOps Bot
2025-12-13 00:20:26 +02:00
parent e1f1bef4c1
commit 564df71bfb
2376 changed files with 334389 additions and 328032 deletions

View File

@@ -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;
}