Some checks failed
Docs CI / lint-and-preview (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
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Reachability Corpus Validation / validate-corpus (push) Has been cancelled
Reachability Corpus Validation / validate-ground-truths (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Reachability Corpus Validation / determinism-check (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (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
357 lines
18 KiB
C#
357 lines
18 KiB
C#
using System.IO;
|
|
using System.Threading.RateLimiting;
|
|
using Microsoft.AspNetCore.RateLimiting;
|
|
using Microsoft.Extensions.Options;
|
|
using NetEscapades.Configuration.Yaml;
|
|
using StellaOps.Auth.Abstractions;
|
|
using StellaOps.Auth.Client;
|
|
using StellaOps.Auth.ServerIntegration;
|
|
using StellaOps.Configuration;
|
|
using StellaOps.Policy.Engine.Hosting;
|
|
using StellaOps.Policy.Engine.Options;
|
|
using StellaOps.Policy.Engine.Compilation;
|
|
using StellaOps.Policy.Engine.Endpoints;
|
|
using StellaOps.Policy.Engine.BatchEvaluation;
|
|
using StellaOps.Policy.Engine.DependencyInjection;
|
|
using StellaOps.PolicyDsl;
|
|
using StellaOps.Policy.Engine.Services;
|
|
using StellaOps.Policy.Engine.Workers;
|
|
using StellaOps.Policy.Engine.Streaming;
|
|
using StellaOps.Policy.Engine.Telemetry;
|
|
using StellaOps.Policy.Engine.ConsoleSurface;
|
|
using StellaOps.AirGap.Policy;
|
|
using StellaOps.Policy.Engine.Orchestration;
|
|
using StellaOps.Policy.Engine.ReachabilityFacts;
|
|
using StellaOps.Policy.Engine.Storage.InMemory;
|
|
using StellaOps.Policy.Scoring.Engine;
|
|
using StellaOps.Policy.Scoring.Receipts;
|
|
using StellaOps.Policy.Storage.Postgres;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
var policyEngineConfigFiles = new[]
|
|
{
|
|
"../etc/policy-engine.yaml",
|
|
"../etc/policy-engine.local.yaml",
|
|
"policy-engine.yaml",
|
|
"policy-engine.local.yaml"
|
|
};
|
|
|
|
var policyEngineActivationConfigFiles = new[]
|
|
{
|
|
"../etc/policy-engine.activation.yaml",
|
|
"../etc/policy-engine.activation.local.yaml",
|
|
"/config/policy-engine/activation.yaml",
|
|
"policy-engine.activation.yaml",
|
|
"policy-engine.activation.local.yaml"
|
|
};
|
|
|
|
builder.Logging.ClearProviders();
|
|
builder.Logging.AddConsole();
|
|
|
|
builder.Configuration.AddStellaOpsDefaults(options =>
|
|
{
|
|
options.BasePath = builder.Environment.ContentRootPath;
|
|
options.EnvironmentPrefix = "STELLAOPS_POLICY_ENGINE_";
|
|
options.ConfigureBuilder = configurationBuilder =>
|
|
{
|
|
var contentRoot = builder.Environment.ContentRootPath;
|
|
foreach (var relative in policyEngineConfigFiles)
|
|
{
|
|
var path = Path.Combine(contentRoot, relative);
|
|
configurationBuilder.AddYamlFile(path, optional: true);
|
|
}
|
|
|
|
foreach (var relative in policyEngineActivationConfigFiles)
|
|
{
|
|
var path = Path.Combine(contentRoot, relative);
|
|
configurationBuilder.AddYamlFile(path, optional: true);
|
|
}
|
|
};
|
|
});
|
|
|
|
var bootstrap = StellaOpsConfigurationBootstrapper.Build<PolicyEngineOptions>(options =>
|
|
{
|
|
options.BasePath = builder.Environment.ContentRootPath;
|
|
options.EnvironmentPrefix = "STELLAOPS_POLICY_ENGINE_";
|
|
options.BindingSection = PolicyEngineOptions.SectionName;
|
|
options.ConfigureBuilder = configurationBuilder =>
|
|
{
|
|
foreach (var relative in policyEngineConfigFiles)
|
|
{
|
|
var path = Path.Combine(builder.Environment.ContentRootPath, relative);
|
|
configurationBuilder.AddYamlFile(path, optional: true);
|
|
}
|
|
|
|
foreach (var relative in policyEngineActivationConfigFiles)
|
|
{
|
|
var path = Path.Combine(builder.Environment.ContentRootPath, relative);
|
|
configurationBuilder.AddYamlFile(path, optional: true);
|
|
}
|
|
};
|
|
options.PostBind = static (value, _) => value.Validate();
|
|
});
|
|
|
|
builder.Configuration.AddConfiguration(bootstrap.Configuration);
|
|
|
|
builder.ConfigurePolicyEngineTelemetry(bootstrap.Options);
|
|
|
|
builder.Services.AddAirGapEgressPolicy(builder.Configuration, sectionName: "AirGap");
|
|
|
|
// CVSS receipts rely on PostgreSQL storage for deterministic persistence.
|
|
builder.Services.AddPolicyPostgresStorage(builder.Configuration, sectionName: "Postgres:Policy");
|
|
|
|
builder.Services.AddSingleton<ICvssV4Engine, CvssV4Engine>();
|
|
builder.Services.AddScoped<IReceiptBuilder, ReceiptBuilder>();
|
|
builder.Services.AddScoped<IReceiptHistoryService, ReceiptHistoryService>();
|
|
|
|
builder.Services.AddOptions<PolicyEngineOptions>()
|
|
.Bind(builder.Configuration.GetSection(PolicyEngineOptions.SectionName))
|
|
.Validate(options =>
|
|
{
|
|
try
|
|
{
|
|
options.Validate();
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new OptionsValidationException(
|
|
PolicyEngineOptions.SectionName,
|
|
typeof(PolicyEngineOptions),
|
|
new[] { ex.Message });
|
|
}
|
|
})
|
|
.ValidateOnStart();
|
|
|
|
builder.Services.AddSingleton(sp => sp.GetRequiredService<IOptions<PolicyEngineOptions>>().Value);
|
|
builder.Services.AddSingleton(sp => sp.GetRequiredService<PolicyEngineOptions>().ExceptionLifecycle);
|
|
builder.Services.AddSingleton(TimeProvider.System);
|
|
builder.Services.AddSingleton<PolicyEngineStartupDiagnostics>();
|
|
builder.Services.AddSingleton<PolicyTimelineEvents>();
|
|
builder.Services.AddSingleton<EvidenceBundleService>();
|
|
builder.Services.AddSingleton<PolicyEvaluationAttestationService>();
|
|
builder.Services.AddSingleton<IncidentModeService>();
|
|
builder.Services.AddSingleton<RiskProfileConfigurationService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.RiskProfile.Lifecycle.RiskProfileLifecycleService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.RiskProfile.Scope.ScopeAttachmentService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.RiskProfile.Scope.EffectivePolicyService>();
|
|
builder.Services.AddSingleton<IEffectivePolicyAuditor, EffectivePolicyAuditor>(); // CONTRACT-AUTHORITY-EFFECTIVE-WRITE-008
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Attestation.IVerificationPolicyStore, StellaOps.Policy.Engine.Attestation.InMemoryVerificationPolicyStore>(); // CONTRACT-VERIFICATION-POLICY-006
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Attestation.VerificationPolicyValidator>(); // CONTRACT-VERIFICATION-POLICY-006 validation
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Attestation.IAttestationReportStore, StellaOps.Policy.Engine.Attestation.InMemoryAttestationReportStore>(); // CONTRACT-VERIFICATION-POLICY-006 reports
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Attestation.IAttestationReportService, StellaOps.Policy.Engine.Attestation.AttestationReportService>(); // CONTRACT-VERIFICATION-POLICY-006 reports
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.ConsoleSurface.ConsoleAttestationReportService>(); // CONTRACT-VERIFICATION-POLICY-006 Console integration
|
|
builder.Services.AddSingleton<StellaOps.Policy.RiskProfile.Overrides.OverrideService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Scoring.IRiskScoringJobStore, StellaOps.Policy.Engine.Scoring.InMemoryRiskScoringJobStore>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Scoring.RiskScoringTriggerService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Simulation.RiskSimulationService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Signals.Entropy.EntropyPenaltyCalculator>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.RiskProfile.Export.ProfileExportService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Events.ProfileEventPublisher>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Events.IExceptionEventPublisher>(sp =>
|
|
new StellaOps.Policy.Engine.Events.LoggingExceptionEventPublisher(
|
|
sp.GetService<StellaOps.Policy.Engine.ExceptionCache.IExceptionEffectiveCache>(),
|
|
sp.GetRequiredService<ILogger<StellaOps.Policy.Engine.Events.LoggingExceptionEventPublisher>>()));
|
|
builder.Services.AddSingleton<ExceptionLifecycleService>();
|
|
builder.Services.AddHostedService<ExceptionLifecycleWorker>();
|
|
builder.Services.AddHostedService<IncidentModeExpirationWorker>();
|
|
builder.Services.AddHostedService<PolicyEngineBootstrapWorker>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Simulation.SimulationAnalyticsService>();
|
|
builder.Services.AddSingleton<ConsoleSimulationDiffService>();
|
|
builder.Services.AddSingleton<StellaOps.PolicyDsl.PolicyCompiler>();
|
|
builder.Services.AddSingleton<PolicyCompilationService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Services.PathScopeMetrics>();
|
|
builder.Services.AddSingleton<PolicyEvaluationService>();
|
|
builder.Services.AddPolicyEngineCore();
|
|
builder.Services.AddSingleton<PathScopeSimulationService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Overlay.OverlayProjectionService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Overlay.IOverlayEventSink, StellaOps.Policy.Engine.Overlay.LoggingOverlayEventSink>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Overlay.OverlayChangeEventPublisher>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Overlay.PathScopeSimulationBridgeService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.TrustWeighting.TrustWeightingService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.AdvisoryAI.AdvisoryAiKnobsService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.BatchContext.BatchContextService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Services.EvidenceSummaryService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Services.PolicyBundleService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Services.PolicyRuntimeEvaluator>();
|
|
builder.Services.AddSingleton<IPolicyPackRepository, InMemoryPolicyPackRepository>();
|
|
builder.Services.AddSingleton<IOrchestratorJobStore, InMemoryOrchestratorJobStore>();
|
|
builder.Services.AddSingleton<OrchestratorJobService>();
|
|
builder.Services.AddSingleton<IWorkerResultStore, InMemoryWorkerResultStore>();
|
|
builder.Services.AddSingleton<PolicyWorkerService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Ledger.ILedgerExportStore, StellaOps.Policy.Engine.Ledger.InMemoryLedgerExportStore>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Ledger.LedgerExportService>();
|
|
|
|
// Console export jobs per CONTRACT-EXPORT-BUNDLE-009
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.ConsoleExport.IConsoleExportJobStore, StellaOps.Policy.Engine.ConsoleExport.InMemoryConsoleExportJobStore>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.ConsoleExport.IConsoleExportExecutionStore, StellaOps.Policy.Engine.ConsoleExport.InMemoryConsoleExportExecutionStore>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.ConsoleExport.IConsoleExportBundleStore, StellaOps.Policy.Engine.ConsoleExport.InMemoryConsoleExportBundleStore>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.ConsoleExport.ConsoleExportJobService>();
|
|
|
|
// Air-gap bundle import per CONTRACT-MIRROR-BUNDLE-003
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.AirGap.IPolicyPackBundleStore, StellaOps.Policy.Engine.AirGap.InMemoryPolicyPackBundleStore>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.AirGap.PolicyPackBundleImportService>();
|
|
|
|
// Sealed-mode services per CONTRACT-SEALED-MODE-004
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.AirGap.ISealedModeStateStore, StellaOps.Policy.Engine.AirGap.InMemorySealedModeStateStore>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.AirGap.ISealedModeService, StellaOps.Policy.Engine.AirGap.SealedModeService>();
|
|
|
|
// Staleness signaling services per CONTRACT-SEALED-MODE-004
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.AirGap.IStalenessEventSink, StellaOps.Policy.Engine.AirGap.LoggingStalenessEventSink>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.AirGap.IStalenessSignalingService, StellaOps.Policy.Engine.AirGap.StalenessSignalingService>();
|
|
|
|
// Air-gap notification services
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.AirGap.IAirGapNotificationChannel, StellaOps.Policy.Engine.AirGap.LoggingNotificationChannel>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.AirGap.IAirGapNotificationService, StellaOps.Policy.Engine.AirGap.AirGapNotificationService>();
|
|
|
|
// Air-gap risk profile export/import per CONTRACT-MIRROR-BUNDLE-003
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.AirGap.RiskProfileAirGapExportService>();
|
|
// Also register as IStalenessEventSink to auto-notify on staleness events
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.AirGap.IStalenessEventSink>(sp =>
|
|
(StellaOps.Policy.Engine.AirGap.AirGapNotificationService)sp.GetRequiredService<StellaOps.Policy.Engine.AirGap.IAirGapNotificationService>());
|
|
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Snapshots.ISnapshotStore, StellaOps.Policy.Engine.Snapshots.InMemorySnapshotStore>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Snapshots.SnapshotService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Violations.IViolationEventStore, StellaOps.Policy.Engine.Violations.InMemoryViolationEventStore>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Violations.ViolationEventService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Violations.SeverityFusionService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Violations.ConflictHandlingService>();
|
|
builder.Services.AddSingleton<StellaOps.Policy.Engine.Services.PolicyDecisionService>();
|
|
builder.Services.AddSingleton<IReachabilityFactsStore, InMemoryReachabilityFactsStore>();
|
|
builder.Services.AddSingleton<IReachabilityFactsOverlayCache, InMemoryReachabilityFactsOverlayCache>();
|
|
builder.Services.AddSingleton<ReachabilityFactsJoiningService>();
|
|
builder.Services.AddSingleton<IRuntimeEvaluationExecutor, RuntimeEvaluationExecutor>();
|
|
builder.Services.AddVexDecisionEmitter(); // POLICY-VEX-401-006
|
|
|
|
builder.Services.AddHttpContextAccessor();
|
|
builder.Services.AddRouting(options => options.LowercaseUrls = true);
|
|
builder.Services.AddProblemDetails();
|
|
builder.Services.AddHealthChecks();
|
|
|
|
// Rate limiting configuration for simulation endpoints
|
|
var rateLimitOptions = builder.Configuration
|
|
.GetSection(PolicyEngineRateLimitOptions.SectionName)
|
|
.Get<PolicyEngineRateLimitOptions>() ?? new PolicyEngineRateLimitOptions();
|
|
|
|
if (rateLimitOptions.Enabled)
|
|
{
|
|
builder.Services.AddRateLimiter(options =>
|
|
{
|
|
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
|
|
|
|
options.AddTokenBucketLimiter(PolicyEngineRateLimitOptions.PolicyName, limiterOptions =>
|
|
{
|
|
limiterOptions.TokenLimit = rateLimitOptions.SimulationPermitLimit;
|
|
limiterOptions.ReplenishmentPeriod = TimeSpan.FromSeconds(rateLimitOptions.WindowSeconds);
|
|
limiterOptions.TokensPerPeriod = rateLimitOptions.SimulationPermitLimit;
|
|
limiterOptions.QueueLimit = rateLimitOptions.QueueLimit;
|
|
limiterOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
|
|
});
|
|
|
|
options.OnRejected = async (context, cancellationToken) =>
|
|
{
|
|
var tenant = context.HttpContext.User.FindFirst("tenant_id")?.Value;
|
|
var endpoint = context.HttpContext.Request.Path.Value;
|
|
PolicyEngineTelemetry.RecordRateLimitExceeded(tenant, endpoint);
|
|
|
|
context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
|
|
context.HttpContext.Response.Headers.RetryAfter = rateLimitOptions.WindowSeconds.ToString();
|
|
|
|
await context.HttpContext.Response.WriteAsJsonAsync(new
|
|
{
|
|
error = "ERR_POL_007",
|
|
message = "Rate limit exceeded. Please retry after the reset window.",
|
|
retryAfterSeconds = rateLimitOptions.WindowSeconds
|
|
}, cancellationToken);
|
|
};
|
|
});
|
|
}
|
|
|
|
builder.Services.AddAuthentication();
|
|
builder.Services.AddAuthorization();
|
|
builder.Services.AddStellaOpsScopeHandler();
|
|
builder.Services.AddStellaOpsResourceServerAuthentication(
|
|
builder.Configuration,
|
|
configurationSection: $"{PolicyEngineOptions.SectionName}:ResourceServer");
|
|
|
|
if (bootstrap.Options.Authority.Enabled)
|
|
{
|
|
builder.Services.AddStellaOpsAuthClient(clientOptions =>
|
|
{
|
|
clientOptions.Authority = bootstrap.Options.Authority.Issuer;
|
|
clientOptions.ClientId = bootstrap.Options.Authority.ClientId;
|
|
clientOptions.ClientSecret = bootstrap.Options.Authority.ClientSecret;
|
|
clientOptions.HttpTimeout = TimeSpan.FromSeconds(bootstrap.Options.Authority.BackchannelTimeoutSeconds);
|
|
|
|
clientOptions.DefaultScopes.Clear();
|
|
foreach (var scope in bootstrap.Options.Authority.Scopes)
|
|
{
|
|
clientOptions.DefaultScopes.Add(scope);
|
|
}
|
|
});
|
|
}
|
|
|
|
var app = builder.Build();
|
|
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
|
|
if (rateLimitOptions.Enabled)
|
|
{
|
|
app.UseRateLimiter();
|
|
}
|
|
|
|
app.MapHealthChecks("/healthz");
|
|
app.MapGet("/readyz", (PolicyEngineStartupDiagnostics diagnostics) =>
|
|
diagnostics.IsReady
|
|
? Results.Ok(new { status = "ready" })
|
|
: Results.StatusCode(StatusCodes.Status503ServiceUnavailable))
|
|
.WithName("Readiness");
|
|
|
|
app.MapGet("/", () => Results.Redirect("/healthz"));
|
|
|
|
app.MapPolicyCompilation();
|
|
app.MapPolicyPacks();
|
|
app.MapPathScopeSimulation();
|
|
app.MapOverlaySimulation();
|
|
app.MapEvidenceSummaries();
|
|
app.MapBatchEvaluation();
|
|
app.MapConsoleSimulationDiff();
|
|
app.MapTrustWeighting();
|
|
app.MapAdvisoryAiKnobs();
|
|
app.MapBatchContext();
|
|
app.MapOrchestratorJobs();
|
|
app.MapPolicyWorker();
|
|
app.MapLedgerExport();
|
|
app.MapConsoleExportJobs(); // CONTRACT-EXPORT-BUNDLE-009
|
|
app.MapPolicyPackBundles(); // CONTRACT-MIRROR-BUNDLE-003
|
|
app.MapSealedMode(); // CONTRACT-SEALED-MODE-004
|
|
app.MapStalenessSignaling(); // CONTRACT-SEALED-MODE-004 staleness
|
|
app.MapAirGapNotifications(); // Air-gap notifications
|
|
app.MapPolicyLint(); // POLICY-AOC-19-001 determinism linting
|
|
app.MapVerificationPolicies(); // CONTRACT-VERIFICATION-POLICY-006 attestation policies
|
|
app.MapVerificationPolicyEditor(); // CONTRACT-VERIFICATION-POLICY-006 editor DTOs/validation
|
|
app.MapAttestationReports(); // CONTRACT-VERIFICATION-POLICY-006 attestation reports
|
|
app.MapConsoleAttestationReports(); // CONTRACT-VERIFICATION-POLICY-006 Console integration
|
|
app.MapSnapshots();
|
|
app.MapViolations();
|
|
app.MapPolicyDecisions();
|
|
app.MapRiskProfiles();
|
|
app.MapRiskProfileSchema();
|
|
app.MapScopeAttachments();
|
|
app.MapEffectivePolicies(); // CONTRACT-AUTHORITY-EFFECTIVE-WRITE-008
|
|
app.MapRiskSimulation();
|
|
app.MapOverrides();
|
|
app.MapProfileExport();
|
|
app.MapRiskProfileAirGap(); // CONTRACT-MIRROR-BUNDLE-003 risk profile air-gap
|
|
app.MapProfileEvents();
|
|
app.MapCvssReceipts(); // CVSS v4 receipt CRUD & history
|
|
|
|
// Phase 5: Multi-tenant PostgreSQL-backed API endpoints
|
|
app.MapPolicySnapshotsApi();
|
|
app.MapViolationEventsApi();
|
|
app.MapConflictsApi();
|
|
|
|
app.Run();
|