Fix build and code structure improvements. New but essential UI functionality. CI improvements. Documentation improvements. AI module improvements.

This commit is contained in:
StellaOps Bot
2025-12-26 21:54:17 +02:00
parent 335ff7da16
commit c2b9cd8d1f
3717 changed files with 264714 additions and 48202 deletions

View File

@@ -137,11 +137,11 @@ internal static class MirrorRegistrationEndpoints
record.Signature,
record.PayloadUrl,
record.TransparencyLog,
record.PortableManifestHash);
record.PortableManifestHash ?? string.Empty);
var paths = new MirrorBundlePaths(
record.PortableManifestPath,
record.EvidenceLockerPath);
record.PortableManifestPath ?? string.Empty,
record.EvidenceLockerPath ?? string.Empty);
var timeline = record.Timeline
.OrderByDescending(e => e.CreatedAt)

View File

@@ -13,8 +13,8 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using StellaOps.Excititor.Attestation;
using StellaOps.Excititor.Attestation.Dsse;
using StellaOps.Excititor.Attestation.Signing;
using StellaOps.Excititor.Core.Dsse;
using StellaOps.Excititor.Core;
using StellaOps.Excititor.Core.Lattice;
using StellaOps.Excititor.Formats.OpenVEX;

View File

@@ -23,12 +23,13 @@ using StellaOps.Excititor.Connectors.RedHat.CSAF.DependencyInjection;
using StellaOps.Excititor.Core;
using StellaOps.Excititor.Core.Evidence;
using StellaOps.Excititor.Core.Observations;
using StellaOps.Excititor.Core.Verification;
using StellaOps.Excititor.Export;
using StellaOps.Excititor.Formats.CSAF;
using StellaOps.Excititor.Formats.CycloneDX;
using StellaOps.Excititor.Formats.OpenVEX;
using StellaOps.Excititor.Policy;
using StellaOps.Excititor.Storage.Postgres;
using StellaOps.Excititor.Persistence.Extensions;
using StellaOps.Infrastructure.Postgres.Options;
using StellaOps.Excititor.WebService.Endpoints;
using StellaOps.Excititor.WebService.Extensions;
@@ -41,6 +42,7 @@ using StellaOps.Excititor.WebService.Contracts;
using System.Globalization;
using StellaOps.Excititor.WebService.Graph;
using StellaOps.Excititor.Core.Storage;
using StellaOps.Excititor.Persistence.Postgres;
using StellaOps.Router.AspNet;
var builder = WebApplication.CreateBuilder(args);
@@ -52,15 +54,36 @@ services.AddOptions<VexStorageOptions>()
services.AddOptions<GraphOptions>()
.Bind(configuration.GetSection("Excititor:Graph"));
services.AddExcititorPostgresStorage(configuration);
services.AddExcititorPersistence(configuration);
services.TryAddSingleton<IVexProviderStore, InMemoryVexProviderStore>();
services.TryAddScoped<IVexConnectorStateRepository, InMemoryVexConnectorStateRepository>();
services.TryAddSingleton<IVexClaimStore, InMemoryVexClaimStore>();
services.AddCsafNormalizer();
services.AddCycloneDxNormalizer();
services.AddOpenVexNormalizer();
services.AddSingleton<IVexSignatureVerifier, NoopVexSignatureVerifier>();
// TODO: replace NoopVexSignatureVerifier with hardened verifier once portable bundle signatures are finalized.
// VEX Signature Verification (SPRINT_1227_0004_0001)
// Feature flag controls whether production verification is active.
// When VexSignatureVerification:Enabled is false, NoopVexSignatureVerifier is used.
services.AddVexSignatureVerification(configuration);
// Legacy V1 interface - maintained for backward compatibility during migration
if (configuration.GetValue<bool>("VexSignatureVerification:Enabled", false))
{
services.AddSingleton<IVexSignatureVerifier>(sp =>
{
// Adapter from V2 to V1 interface
return new VexSignatureVerifierV1Adapter(
sp.GetRequiredService<IVexSignatureVerifierV2>(),
sp.GetRequiredService<IOptions<VexSignatureVerifierOptions>>(),
sp.GetRequiredService<ILogger<VexSignatureVerifierV1Adapter>>());
});
}
else
{
services.AddSingleton<IVexSignatureVerifier, NoopVexSignatureVerifier>();
}
services.Configure<AirgapOptions>(configuration.GetSection(AirgapOptions.SectionName));
services.AddSingleton<AirgapImportValidator>();
services.AddSingleton<AirgapSignerTrustService>();
@@ -2264,6 +2287,7 @@ internal sealed record ExcititorTimelineEvent(
string? TraceId,
string OccurredAt);
// Program class public for WebApplicationFactory<Program>
public partial class Program;
internal sealed record StatusResponse(DateTimeOffset UtcNow, int InlineThreshold, string[] ArtifactStores);

View File

@@ -0,0 +1,12 @@
{
"profiles": {
"StellaOps.Excititor.WebService": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:62513;http://localhost:62514"
}
}
}

View File

@@ -4,7 +4,7 @@ using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
using Npgsql;
using NpgsqlTypes;
using StellaOps.Excititor.Storage.Postgres;
using StellaOps.Excititor.Persistence.Postgres;
using StellaOps.Excititor.WebService.Contracts;
namespace StellaOps.Excititor.WebService.Services;

View File

@@ -0,0 +1,141 @@
// VexSignatureVerifierV1Adapter - Adapts V2 interface to V1 for backward compatibility
// Part of SPRINT_1227_0004_0001: Activate VEX Signature Verification Pipeline
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Excititor.Core;
using StellaOps.Excititor.Core.Verification;
namespace StellaOps.Excititor.WebService.Services;
/// <summary>
/// Adapter that bridges the new IVexSignatureVerifierV2 interface to the legacy IVexSignatureVerifier interface.
/// This allows gradual migration while maintaining backward compatibility.
/// </summary>
public sealed class VexSignatureVerifierV1Adapter : IVexSignatureVerifier
{
private readonly IVexSignatureVerifierV2 _v2Verifier;
private readonly VexSignatureVerifierOptions _options;
private readonly ILogger<VexSignatureVerifierV1Adapter> _logger;
public VexSignatureVerifierV1Adapter(
IVexSignatureVerifierV2 v2Verifier,
IOptions<VexSignatureVerifierOptions> options,
ILogger<VexSignatureVerifierV1Adapter> logger)
{
_v2Verifier = v2Verifier ?? throw new ArgumentNullException(nameof(v2Verifier));
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <inheritdoc />
public async ValueTask<VexSignatureMetadata?> VerifyAsync(
VexRawDocument document,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(document);
try
{
// Create verification context from options
var context = new VexVerificationContext
{
TenantId = ExtractTenantId(document),
CryptoProfile = _options.DefaultProfile,
AllowExpiredCerts = _options.AllowExpiredCerts,
RequireSignature = _options.RequireSignature,
ClockTolerance = _options.ClockTolerance
};
// Call V2 verifier
var result = await _v2Verifier.VerifyAsync(document, context, cancellationToken);
// Convert V2 result to V1 VexSignatureMetadata
return ConvertToV1Metadata(result);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "V1 adapter verification failed for document {Digest}", document.Digest);
// Return null on error (V1 behavior - treat as unsigned)
return null;
}
}
private static string ExtractTenantId(VexRawDocument document)
{
// Try to extract tenant from document metadata
if (document.Metadata.TryGetValue("tenant-id", out var tenantId) &&
!string.IsNullOrWhiteSpace(tenantId))
{
return tenantId;
}
if (document.Metadata.TryGetValue("x-stellaops-tenant", out tenantId) &&
!string.IsNullOrWhiteSpace(tenantId))
{
return tenantId;
}
// Default to global tenant
return "@global";
}
private static VexSignatureMetadata? ConvertToV1Metadata(VexSignatureVerificationResult result)
{
// No signature case
if (result.Method == VerificationMethod.None && !result.Verified)
{
return null;
}
// Failed verification - still return metadata but with details
// This allows the caller to see that verification was attempted
var type = result.Method switch
{
VerificationMethod.Dsse => "dsse",
VerificationMethod.DsseKeyless => "dsse-keyless",
VerificationMethod.Cosign => "cosign",
VerificationMethod.CosignKeyless => "cosign-keyless",
VerificationMethod.Pgp => "pgp",
VerificationMethod.X509 => "x509",
VerificationMethod.InToto => "in-toto",
VerificationMethod.None => "none",
_ => "unknown"
};
// Build transparency log reference
string? transparencyLogRef = null;
if (result.RekorLogIndex.HasValue)
{
transparencyLogRef = result.RekorLogId != null
? $"{result.RekorLogId}:{result.RekorLogIndex}"
: $"rekor:{result.RekorLogIndex}";
}
// Build trust metadata if issuer is known
VexSignatureTrustMetadata? trust = null;
if (!string.IsNullOrEmpty(result.IssuerId))
{
trust = new VexSignatureTrustMetadata(
effectiveWeight: 1.0m, // Full trust for verified signatures
tenantId: "@global",
issuerId: result.IssuerId,
tenantOverrideApplied: false,
retrievedAtUtc: result.VerifiedAt);
}
return new VexSignatureMetadata(
type: type,
subject: result.CertSubject,
issuer: result.IssuerName,
keyId: result.KeyId,
verifiedAt: result.Verified ? result.VerifiedAt : null,
transparencyLogReference: transparencyLogRef,
trust: trust);
}
}

View File

@@ -8,17 +8,17 @@
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Exporter.Console" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../__Libraries/StellaOps.Excititor.Core/StellaOps.Excititor.Core.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Messaging/StellaOps.Messaging.csproj" />
<ProjectReference Include="../__Libraries/StellaOps.Excititor.Storage.Postgres/StellaOps.Excititor.Storage.Postgres.csproj" />
<ProjectReference Include="../../Router/__Libraries/StellaOps.Messaging/StellaOps.Messaging.csproj" />
<ProjectReference Include="../__Libraries/StellaOps.Excititor.Persistence/StellaOps.Excititor.Persistence.csproj" />
<ProjectReference Include="../__Libraries/StellaOps.Excititor.Export/StellaOps.Excititor.Export.csproj" />
<ProjectReference Include="../__Libraries/StellaOps.Excititor.Connectors.Abstractions/StellaOps.Excititor.Connectors.Abstractions.csproj" />
<ProjectReference Include="../__Libraries/StellaOps.Excititor.Policy/StellaOps.Excititor.Policy.csproj" />
@@ -30,6 +30,6 @@
<ProjectReference Include="../__Libraries/StellaOps.Excititor.Formats.OpenVEX/StellaOps.Excititor.Formats.OpenVEX.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Ingestion.Telemetry/StellaOps.Ingestion.Telemetry.csproj" />
<ProjectReference Include="../../Aoc/__Libraries/StellaOps.Aoc/StellaOps.Aoc.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Router.AspNet/StellaOps.Router.AspNet.csproj" />
<ProjectReference Include="../../Router/__Libraries/StellaOps.Router.AspNet/StellaOps.Router.AspNet.csproj" />
</ItemGroup>
</Project>