feat(scanner): Complete PoE implementation with Windows compatibility fix
- Fix namespace conflicts (Subgraph → PoESubgraph) - Add hash sanitization for Windows filesystem (colon → underscore) - Update all test mocks to use It.IsAny<>() - Add direct orchestrator unit tests - All 8 PoE tests now passing (100% success rate) - Complete SPRINT_3500_0001_0001 documentation Fixes compilation errors and Windows filesystem compatibility issues. Tests: 8/8 passing Files: 8 modified, 1 new test, 1 completion report 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -229,28 +229,35 @@ public class VerdictController : ControllerBase
|
||||
|
||||
var client = _httpClientFactory.CreateClient("EvidenceLocker");
|
||||
|
||||
// Parse envelope to get predicate for digest calculation
|
||||
// Parse envelope to get predicate for digest calculation and metadata extraction
|
||||
var envelope = JsonSerializer.Deserialize<JsonElement>(envelopeJson);
|
||||
var payloadBase64 = envelope.GetProperty("payload").GetString() ?? string.Empty;
|
||||
var predicateBytes = Convert.FromBase64String(payloadBase64);
|
||||
var predicateDigest = $"sha256:{Convert.ToHexString(SHA256.HashData(predicateBytes)).ToLowerInvariant()}";
|
||||
|
||||
// Parse predicate JSON to extract verdict metadata
|
||||
var predicateJson = Encoding.UTF8.GetString(predicateBytes);
|
||||
var predicate = JsonSerializer.Deserialize<JsonElement>(predicateJson);
|
||||
|
||||
// Extract verdict metadata from predicate
|
||||
var (verdictStatus, verdictSeverity, verdictScore, evaluatedAt, determinismHash, policyRunId, policyId, policyVersion) = ExtractVerdictMetadata(predicate);
|
||||
|
||||
// Create Evidence Locker storage request
|
||||
var storeRequest = new
|
||||
{
|
||||
verdict_id = verdictId,
|
||||
tenant_id = "default", // TODO: Extract from auth context
|
||||
policy_run_id = "unknown", // TODO: Pass from caller
|
||||
policy_id = "unknown", // TODO: Pass from caller
|
||||
policy_version = 1, // TODO: Pass from caller
|
||||
tenant_id = "default", // TODO: Extract from auth context (requires CallerTenant from SubmissionContext)
|
||||
policy_run_id = policyRunId,
|
||||
policy_id = policyId,
|
||||
policy_version = policyVersion,
|
||||
finding_id = findingId,
|
||||
verdict_status = "unknown", // TODO: Extract from predicate
|
||||
verdict_severity = "unknown", // TODO: Extract from predicate
|
||||
verdict_score = 0.0m, // TODO: Extract from predicate
|
||||
evaluated_at = DateTimeOffset.UtcNow,
|
||||
verdict_status = verdictStatus,
|
||||
verdict_severity = verdictSeverity,
|
||||
verdict_score = verdictScore,
|
||||
evaluated_at = evaluatedAt,
|
||||
envelope = JsonSerializer.Deserialize<object>(envelopeJson),
|
||||
predicate_digest = predicateDigest,
|
||||
determinism_hash = (string?)null, // TODO: Pass from predicate
|
||||
determinism_hash = determinismHash,
|
||||
rekor_log_index = (long?)null // Not implemented yet
|
||||
};
|
||||
|
||||
@@ -280,4 +287,100 @@ public class VerdictController : ControllerBase
|
||||
// Non-fatal: attestation is still returned to caller
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts verdict metadata from predicate JSON.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Tuple of (status, severity, score, evaluatedAt, determinismHash, policyRunId, policyId, policyVersion)
|
||||
/// </returns>
|
||||
private static (string status, string severity, decimal score, DateTimeOffset evaluatedAt, string? determinismHash, string policyRunId, string policyId, int policyVersion)
|
||||
ExtractVerdictMetadata(JsonElement predicate)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Extract from verdict predicate structure (https://stellaops.dev/predicates/policy-verdict@v1)
|
||||
// Expected structure:
|
||||
// {
|
||||
// "verdict": { "status": "...", "severity": "...", "score": 0.0 },
|
||||
// "metadata": { "policyRunId": "...", "policyId": "...", "policyVersion": 1, "evaluatedAt": "..." },
|
||||
// "determinismHash": "..."
|
||||
// }
|
||||
|
||||
var status = "unknown";
|
||||
var severity = "unknown";
|
||||
var score = 0.0m;
|
||||
var evaluatedAt = DateTimeOffset.UtcNow;
|
||||
string? determinismHash = null;
|
||||
var policyRunId = "unknown";
|
||||
var policyId = "unknown";
|
||||
var policyVersion = 1;
|
||||
|
||||
// Extract verdict status/severity/score
|
||||
if (predicate.TryGetProperty("verdict", out var verdictElement))
|
||||
{
|
||||
if (verdictElement.TryGetProperty("status", out var statusElement))
|
||||
{
|
||||
status = statusElement.GetString() ?? "unknown";
|
||||
}
|
||||
|
||||
if (verdictElement.TryGetProperty("severity", out var severityElement))
|
||||
{
|
||||
severity = severityElement.GetString() ?? "unknown";
|
||||
}
|
||||
|
||||
if (verdictElement.TryGetProperty("score", out var scoreElement))
|
||||
{
|
||||
if (scoreElement.ValueKind == JsonValueKind.Number)
|
||||
{
|
||||
score = scoreElement.GetDecimal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract metadata
|
||||
if (predicate.TryGetProperty("metadata", out var metadataElement))
|
||||
{
|
||||
if (metadataElement.TryGetProperty("policyRunId", out var runIdElement))
|
||||
{
|
||||
policyRunId = runIdElement.GetString() ?? "unknown";
|
||||
}
|
||||
|
||||
if (metadataElement.TryGetProperty("policyId", out var policyIdElement))
|
||||
{
|
||||
policyId = policyIdElement.GetString() ?? "unknown";
|
||||
}
|
||||
|
||||
if (metadataElement.TryGetProperty("policyVersion", out var versionElement))
|
||||
{
|
||||
if (versionElement.ValueKind == JsonValueKind.Number)
|
||||
{
|
||||
policyVersion = versionElement.GetInt32();
|
||||
}
|
||||
}
|
||||
|
||||
if (metadataElement.TryGetProperty("evaluatedAt", out var evaluatedAtElement))
|
||||
{
|
||||
var evaluatedAtStr = evaluatedAtElement.GetString();
|
||||
if (!string.IsNullOrEmpty(evaluatedAtStr) && DateTimeOffset.TryParse(evaluatedAtStr, out var parsedDate))
|
||||
{
|
||||
evaluatedAt = parsedDate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract determinism hash
|
||||
if (predicate.TryGetProperty("determinismHash", out var hashElement))
|
||||
{
|
||||
determinismHash = hashElement.GetString();
|
||||
}
|
||||
|
||||
return (status, severity, score, evaluatedAt, determinismHash, policyRunId, policyId, policyVersion);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// If parsing fails, return defaults (non-fatal)
|
||||
return ("unknown", "unknown", 0.0m, DateTimeOffset.UtcNow, null, "unknown", "unknown", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user