audit, advisories and doctors/setup work

This commit is contained in:
master
2026-01-13 18:53:39 +02:00
parent 9ca7cb183e
commit d7be6ba34b
811 changed files with 54242 additions and 4056 deletions

View File

@@ -1,5 +1,6 @@
#pragma warning disable ASPDEPR002 // WithOpenApi is deprecated - will migrate to new OpenAPI approach
using System.Globalization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -166,10 +167,7 @@ public static partial class ProvcacheEndpointExtensions
catch (Exception ex)
{
logger.LogError(ex, "Error getting cache entry for VeriKey {VeriKey}", veriKey);
return Results.Problem(
detail: ex.Message,
statusCode: StatusCodes.Status500InternalServerError,
title: "Cache lookup failed");
return InternalError("Cache lookup failed");
}
}
@@ -214,10 +212,7 @@ public static partial class ProvcacheEndpointExtensions
catch (Exception ex)
{
logger.LogError(ex, "Error storing cache entry for VeriKey {VeriKey}", request.Entry?.VeriKey);
return Results.Problem(
detail: ex.Message,
statusCode: StatusCodes.Status500InternalServerError,
title: "Cache write failed");
return InternalError("Cache write failed");
}
}
@@ -269,10 +264,7 @@ public static partial class ProvcacheEndpointExtensions
catch (Exception ex)
{
logger.LogError(ex, "Error invalidating cache entries");
return Results.Problem(
detail: ex.Message,
statusCode: StatusCodes.Status500InternalServerError,
title: "Cache invalidation failed");
return InternalError("Cache invalidation failed");
}
}
@@ -312,10 +304,7 @@ public static partial class ProvcacheEndpointExtensions
catch (Exception ex)
{
logger.LogError(ex, "Error getting cache metrics");
return Results.Problem(
detail: ex.Message,
statusCode: StatusCodes.Status500InternalServerError,
title: "Metrics retrieval failed");
return InternalError("Metrics retrieval failed");
}
}
@@ -377,10 +366,7 @@ public static partial class ProvcacheEndpointExtensions
catch (Exception ex)
{
logger.LogError(ex, "Error getting input manifest for VeriKey {VeriKey}", veriKey);
return Results.Problem(
detail: ex.Message,
statusCode: StatusCodes.Status500InternalServerError,
title: "Manifest retrieval failed");
return InternalError("Manifest retrieval failed");
}
}
@@ -391,7 +377,7 @@ public static partial class ProvcacheEndpointExtensions
{
// Build input manifest from the entry and its embedded DecisionDigest
// The DecisionDigest contains the VeriKey components as hashes
var decision = entry.Decision;
var placeholderHash = BuildPlaceholderHash(entry.VeriKey);
return new InputManifestResponse
{
@@ -405,12 +391,12 @@ public static partial class ProvcacheEndpointExtensions
{
// SBOM hash is embedded in VeriKey computation
// In a full implementation, we'd resolve this from the SBOM store
Hash = $"sha256:{entry.VeriKey[7..39]}...", // Placeholder - actual hash would come from VeriKey decomposition
Hash = placeholderHash, // Placeholder - actual hash would come from VeriKey decomposition
},
Vex = new VexInfoDto
{
// VEX hash set is embedded in VeriKey computation
HashSetHash = $"sha256:{entry.VeriKey[7..39]}...", // Placeholder
HashSetHash = placeholderHash, // Placeholder
StatementCount = 0, // Would be resolved from VEX store
},
Policy = new PolicyInfoDto
@@ -431,6 +417,43 @@ public static partial class ProvcacheEndpointExtensions
GeneratedAt = timeProvider.GetUtcNow(),
};
}
private static string BuildPlaceholderHash(string veriKey)
{
if (string.IsNullOrWhiteSpace(veriKey))
{
return "sha256:unknown";
}
var trimmed = veriKey;
if (trimmed.StartsWith("sha256:", StringComparison.OrdinalIgnoreCase))
{
trimmed = trimmed["sha256:".Length..];
}
if (trimmed.Length < 32)
{
return "sha256:unknown";
}
return $"sha256:{trimmed[..32]}...";
}
private static IResult BadRequest(string detail, string title)
{
return Results.Problem(
detail: detail,
statusCode: StatusCodes.Status400BadRequest,
title: title);
}
private static IResult InternalError(string title)
{
return Results.Problem(
detail: "An unexpected error occurred while processing the request.",
statusCode: StatusCodes.Status500InternalServerError,
title: title);
}
}
/// <summary>
@@ -471,6 +494,16 @@ partial class ProvcacheEndpointExtensions
try
{
if (offset is < 0)
{
return BadRequest("Offset must be zero or greater.", "Invalid pagination");
}
if (limit is <= 0)
{
return BadRequest("Limit must be greater than zero.", "Invalid pagination");
}
var startIndex = offset ?? 0;
var pageSize = Math.Min(limit ?? DefaultPageSize, MaxPageSize);
@@ -481,10 +514,25 @@ partial class ProvcacheEndpointExtensions
return Results.NotFound();
}
if (startIndex >= manifest.TotalChunks)
{
return Results.Ok(new ProofEvidenceResponse
{
ProofRoot = proofRoot,
TotalChunks = manifest.TotalChunks,
TotalSize = manifest.TotalSize,
Chunks = [],
NextCursor = null,
HasMore = false
});
}
// Get chunk range
var chunks = await chunkRepository.GetChunkRangeAsync(proofRoot, startIndex, pageSize, cancellationToken);
var chunkResponses = chunks.Select(c => new ProofChunkResponse
var chunkResponses = chunks
.OrderBy(c => c.ChunkIndex)
.Select(c => new ProofChunkResponse
{
ChunkId = c.ChunkId,
Index = c.ChunkIndex,
@@ -495,7 +543,9 @@ partial class ProvcacheEndpointExtensions
}).ToList();
var hasMore = startIndex + chunks.Count < manifest.TotalChunks;
var nextCursor = hasMore ? (startIndex + pageSize).ToString() : null;
var nextCursor = hasMore
? (startIndex + pageSize).ToString(CultureInfo.InvariantCulture)
: null;
return Results.Ok(new ProofEvidenceResponse
{
@@ -510,10 +560,7 @@ partial class ProvcacheEndpointExtensions
catch (Exception ex)
{
logger.LogError(ex, "Error getting evidence chunks for proof root {ProofRoot}", proofRoot);
return Results.Problem(
detail: ex.Message,
statusCode: StatusCodes.Status500InternalServerError,
title: "Evidence retrieval failed");
return InternalError("Evidence retrieval failed");
}
}
@@ -536,7 +583,9 @@ partial class ProvcacheEndpointExtensions
return Results.NotFound();
}
var chunkMetadata = manifest.Chunks.Select(c => new ChunkMetadataResponse
var chunkMetadata = manifest.Chunks
.OrderBy(c => c.Index)
.Select(c => new ChunkMetadataResponse
{
ChunkId = c.ChunkId,
Index = c.Index,
@@ -557,10 +606,7 @@ partial class ProvcacheEndpointExtensions
catch (Exception ex)
{
logger.LogError(ex, "Error getting manifest for proof root {ProofRoot}", proofRoot);
return Results.Problem(
detail: ex.Message,
statusCode: StatusCodes.Status500InternalServerError,
title: "Manifest retrieval failed");
return InternalError("Manifest retrieval failed");
}
}
@@ -597,10 +643,7 @@ partial class ProvcacheEndpointExtensions
catch (Exception ex)
{
logger.LogError(ex, "Error getting chunk {ChunkIndex} for proof root {ProofRoot}", chunkIndex, proofRoot);
return Results.Problem(
detail: ex.Message,
statusCode: StatusCodes.Status500InternalServerError,
title: "Chunk retrieval failed");
return InternalError("Chunk retrieval failed");
}
}
@@ -624,10 +667,11 @@ partial class ProvcacheEndpointExtensions
return Results.NotFound();
}
var orderedChunks = chunks.OrderBy(c => c.ChunkIndex).ToList();
var chunkResults = new List<ChunkVerificationResult>();
var allValid = true;
foreach (var chunk in chunks)
foreach (var chunk in orderedChunks)
{
var isValid = chunker.VerifyChunk(chunk);
var computedHash = isValid ? chunk.ChunkHash : ComputeChunkHash(chunk.Blob);
@@ -647,7 +691,7 @@ partial class ProvcacheEndpointExtensions
}
// Verify Merkle root
var chunkHashes = chunks.Select(c => c.ChunkHash).ToList();
var chunkHashes = orderedChunks.Select(c => c.ChunkHash).ToList();
var computedRoot = chunker.ComputeMerkleRoot(chunkHashes);
var rootMatches = string.Equals(computedRoot, proofRoot, StringComparison.OrdinalIgnoreCase);
@@ -662,10 +706,7 @@ partial class ProvcacheEndpointExtensions
catch (Exception ex)
{
logger.LogError(ex, "Error verifying proof root {ProofRoot}", proofRoot);
return Results.Problem(
detail: ex.Message,
statusCode: StatusCodes.Status500InternalServerError,
title: "Proof verification failed");
return InternalError("Proof verification failed");
}
}

View File

@@ -7,4 +7,4 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
| --- | --- | --- |
| AUDIT-0098-M | DONE | Revalidated 2026-01-08; maintainability audit for Provcache.Api. |
| AUDIT-0098-T | DONE | Revalidated 2026-01-08; test coverage audit for Provcache.Api. |
| AUDIT-0098-A | TODO | Pending approval (revalidated 2026-01-08). |
| AUDIT-0098-A | DONE | Applied 2026-01-13 (error redaction, ordering/pagination, placeholder guard, tests). |