save progress

This commit is contained in:
StellaOps Bot
2025-12-18 09:53:46 +02:00
parent 28823a8960
commit 7d5250238c
87 changed files with 9750 additions and 2026 deletions

View File

@@ -3,6 +3,7 @@ using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using StellaOps.Scanner.WebService.Constants;
using StellaOps.Scanner.WebService.Contracts;
using StellaOps.Scanner.WebService.Domain;
@@ -64,12 +65,13 @@ internal static class ReachabilityEndpoints
string scanId,
ComputeReachabilityRequestDto? request,
IScanCoordinator coordinator,
[FromServices] IReachabilityComputeService computeService,
HttpContext context,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(coordinator);
ArgumentNullException.ThrowIfNull(computeService);
ArgumentNullException.ThrowIfNull(context);
var computeService = context.RequestServices.GetRequiredService<IReachabilityComputeService>();
if (!ScanId.TryParse(scanId, out var parsed))
{

View File

@@ -4,7 +4,6 @@ using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace StellaOps.Scanner.WebService.Infrastructure;
@@ -29,25 +28,56 @@ internal static class ProblemResultFactory
var traceId = Activity.Current?.TraceId.ToString() ?? context.TraceIdentifier;
var problem = new ProblemDetails
var mergedExtensions = new Dictionary<string, object?>(StringComparer.Ordinal)
{
["traceId"] = traceId
};
if (extensions is not null)
{
foreach (var entry in extensions)
{
if (string.IsNullOrWhiteSpace(entry.Key))
{
continue;
}
mergedExtensions[entry.Key] = entry.Value;
}
}
var problem = new ProblemDocument
{
Type = type,
Title = title,
Detail = detail,
Status = statusCode,
Instance = context.Request.Path
Instance = context.Request.Path,
Extensions = mergedExtensions
};
problem.Extensions["traceId"] = traceId;
if (extensions is not null)
{
foreach (var entry in extensions)
{
problem.Extensions[entry.Key] = entry.Value;
}
}
var payload = JsonSerializer.Serialize(problem, JsonOptions);
return Results.Content(payload, "application/problem+json", Encoding.UTF8, statusCode);
}
private sealed class ProblemDocument
{
[JsonPropertyName("type")]
public string? Type { get; init; }
[JsonPropertyName("title")]
public string? Title { get; init; }
[JsonPropertyName("detail")]
public string? Detail { get; init; }
[JsonPropertyName("status")]
public int Status { get; init; }
[JsonPropertyName("instance")]
public string? Instance { get; init; }
[JsonPropertyName("extensions")]
public Dictionary<string, object?>? Extensions { get; init; }
}
}

View File

@@ -544,21 +544,24 @@ internal sealed class OfflineKitImportService
long size = 0;
using var hasher = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
await using var output = File.Create(temp);
await using var input = file.OpenReadStream();
var buffer = new byte[128 * 1024];
while (true)
await using (var output = File.Create(temp))
await using (var input = file.OpenReadStream())
{
var read = await input.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
if (read == 0)
var buffer = new byte[128 * 1024];
while (true)
{
break;
var read = await input.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
if (read == 0)
{
break;
}
hasher.AppendData(buffer, 0, read);
await output.WriteAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false);
size += read;
}
hasher.AppendData(buffer, 0, read);
await output.WriteAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false);
size += read;
await output.FlushAsync(cancellationToken).ConfigureAwait(false);
}
var hash = hasher.GetHashAndReset();
@@ -579,9 +582,13 @@ internal sealed class OfflineKitImportService
Directory.CreateDirectory(directory);
}
await using var output = File.Create(path);
await using var input = file.OpenReadStream();
await input.CopyToAsync(output, cancellationToken).ConfigureAwait(false);
await using (var output = File.Create(path))
await using (var input = file.OpenReadStream())
{
await input.CopyToAsync(output, cancellationToken).ConfigureAwait(false);
await output.FlushAsync(cancellationToken).ConfigureAwait(false);
}
return await File.ReadAllBytesAsync(path, cancellationToken).ConfigureAwait(false);
}
@@ -695,4 +702,3 @@ internal sealed class OfflineKitImportService
return true;
}
}

View File

@@ -4,5 +4,6 @@
| --- | --- | --- | --- |
| `SCAN-API-3101-001` | `docs/implplan/SPRINT_3101_0001_0001_scanner_api_standardization.md` | DOING | Align Scanner OpenAPI spec with current endpoints and include ProofSpine routes; compose into `src/Api/StellaOps.Api.OpenApi/stella.yaml`. |
| `PROOFSPINE-3100-API` | `docs/implplan/SPRINT_3100_0001_0001_proof_spine_system.md` | DOING | Implement and test `/api/v1/spines/*` endpoints and wire verification output. |
| `SCAN-AIRGAP-0340-001` | `docs/implplan/SPRINT_0340_0001_0001_scanner_offline_config.md` | BLOCKED | Offline kit verification wiring is blocked on an import pipeline + offline Rekor verifier. |
| `SCAN-API-3103-001` | `docs/implplan/SPRINT_3103_0001_0001_scanner_api_ingestion_completion.md` | DOING | Implement missing ingestion services + DI for callgraph/SBOM endpoints and add deterministic integration tests. |
| `SCAN-AIRGAP-0340-001` | `docs/implplan/SPRINT_0340_0001_0001_scanner_offline_config.md` | DONE | Offline kit import + DSSE/offline Rekor verification wired; integration tests cover success/failure/audit. |
| `DRIFT-3600-API` | `docs/implplan/SPRINT_3600_0003_0001_drift_detection_engine.md` | DONE | Add reachability drift endpoints (`/api/v1/scans/{id}/drift`, `/api/v1/drift/{id}/sinks`) + integration tests. |
| `SCAN-API-3103-001` | `docs/implplan/SPRINT_3103_0001_0001_scanner_api_ingestion_completion.md` | DONE | Implement missing ingestion services + DI for callgraph/SBOM endpoints and add deterministic integration tests. |