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

@@ -12,6 +12,28 @@ public sealed record PolicyGateContext
public bool HasReachabilityProof { get; init; }
public string? Severity { get; init; }
public IReadOnlyCollection<string> ReasonCodes { get; init; } = Array.Empty<string>();
/// <summary>
/// Subgraph slice for reachability proof.
/// Required for high-severity findings when RequireSubgraphProofForHighSeverity is enabled.
/// </summary>
public SubgraphSlice? SubgraphSlice { get; init; }
/// <summary>
/// Subject key for Signals lookup.
/// </summary>
public string? SubjectKey { get; init; }
/// <summary>
/// CVE ID if applicable.
/// </summary>
public string? CveId { get; init; }
/// <summary>
/// Mutable metadata for audit trail.
/// Gates can add metadata here for later inspection.
/// </summary>
public Dictionary<string, string>? Metadata { get; init; }
}
public sealed record GateResult

View File

@@ -8,6 +8,12 @@ public sealed record ReachabilityRequirementGateOptions
{
public bool Enabled { get; init; } = true;
public string SeverityThreshold { get; init; } = "CRITICAL";
/// <summary>
/// When true, requires subgraph proof for high-severity findings (CRITICAL, HIGH).
/// </summary>
public bool RequireSubgraphProofForHighSeverity { get; init; } = true;
public IReadOnlyCollection<VexStatus> RequiredForStatuses { get; init; } = new[]
{
VexStatus.NotAffected,
@@ -52,6 +58,12 @@ public sealed class ReachabilityRequirementGate : IPolicyGate
return Task.FromResult(Pass("bypass_reason"));
}
// Check for subgraph proof for high-severity findings
if (_options.RequireSubgraphProofForHighSeverity && severityRank >= 3) // HIGH or CRITICAL
{
return EvaluateSubgraphProof(context, severityRank);
}
var passed = context.HasReachabilityProof;
var details = ImmutableDictionary<string, object>.Empty
.Add("severity", context.Severity ?? string.Empty)
@@ -67,6 +79,71 @@ public sealed class ReachabilityRequirementGate : IPolicyGate
});
}
private Task<GateResult> EvaluateSubgraphProof(PolicyGateContext context, int severityRank)
{
// Check if subgraph slice is available
if (context.SubgraphSlice is null)
{
var details = ImmutableDictionary<string, object>.Empty
.Add("severity", context.Severity ?? string.Empty)
.Add("severityRank", severityRank)
.Add("reason", "high_severity_requires_subgraph_proof");
return Task.FromResult(new GateResult
{
GateName = nameof(ReachabilityRequirementGate),
Passed = false,
Reason = "subgraph_proof_required_for_high_severity",
Details = details,
});
}
// Validate that subgraph shows actual reachable path
if (!HasReachablePath(context.SubgraphSlice))
{
// No reachable path in subgraph = can pass with "not reachable" justification
var passDetails = ImmutableDictionary<string, object>.Empty
.Add("severity", context.Severity ?? string.Empty)
.Add("subgraphDigest", context.SubgraphSlice.Digest ?? string.Empty)
.Add("reason", "subgraph_shows_no_reachable_path");
return Task.FromResult(new GateResult
{
GateName = nameof(ReachabilityRequirementGate),
Passed = true,
Reason = "not_reachable_per_subgraph",
Details = passDetails,
});
}
// Subgraph shows reachable path, include digest in audit metadata
var detailsWithDigest = ImmutableDictionary<string, object>.Empty
.Add("severity", context.Severity ?? string.Empty)
.Add("subgraphDigest", context.SubgraphSlice.Digest ?? string.Empty)
.Add("reachablePathCount", context.SubgraphSlice.Paths?.Count ?? 0)
.Add("hasReachabilityProof", true);
// Store digest in metadata for audit trail
if (context.Metadata is not null && context.SubgraphSlice.Digest is not null)
{
context.Metadata["reachgraph_digest"] = context.SubgraphSlice.Digest;
}
return Task.FromResult(new GateResult
{
GateName = nameof(ReachabilityRequirementGate),
Passed = true,
Reason = "reachable_with_subgraph_proof",
Details = detailsWithDigest,
});
}
private static bool HasReachablePath(SubgraphSlice slice)
{
return slice.Paths?.Count > 0 &&
slice.Paths.Any(p => p.Hops is { Count: > 0 });
}
private bool HasBypass(IReadOnlyCollection<string> reasons)
=> reasons.Any(reason => _options.BypassReasons.Contains(reason, StringComparer.OrdinalIgnoreCase));
@@ -95,3 +172,22 @@ public sealed class ReachabilityRequirementGate : IPolicyGate
Details = ImmutableDictionary<string, object>.Empty,
};
}
/// <summary>
/// Subgraph slice data for policy evaluation.
/// </summary>
public sealed record SubgraphSlice
{
public string? Digest { get; init; }
public List<SubgraphPath>? Paths { get; init; }
}
/// <summary>
/// Path in a subgraph slice.
/// </summary>
public sealed record SubgraphPath
{
public string? Entrypoint { get; init; }
public string? Sink { get; init; }
public List<string>? Hops { get; init; }
}