This commit is contained in:
StellaOps Bot
2025-11-23 23:40:10 +02:00
parent c13355923f
commit 029002ad05
93 changed files with 2160 additions and 285 deletions

View File

@@ -0,0 +1,86 @@
using System.Security.Cryptography;
using StellaOps.Policy.Engine.Streaming;
namespace StellaOps.Policy.Engine.Services;
public sealed partial class PolicyEvaluationService
{
public Task<JsonObject> EvaluatePathScopeAsync(
PathScopeSimulationRequest request,
PathScopeTarget target,
CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
var stableKey = string.Create(CultureInfo.InvariantCulture, $"{request.BasePolicyRef}|{request.CandidatePolicyRef}|{target.FilePath}|{target.Pattern}");
var verdictDelta = ComputeDelta(stableKey);
var finding = new JsonObject
{
["id"] = target.EvidenceHash ?? "stub-ghsa",
["ruleId"] = "policy.rules.path-scope.stub",
["severity"] = "info",
["verdict"] = new JsonObject
{
["base"] = verdictDelta.baseVerdict,
["candidate"] = verdictDelta.candidateVerdict,
["delta"] = verdictDelta.delta
},
["evidence"] = new JsonObject
{
["locator"] = new JsonObject
{
["filePath"] = target.FilePath,
["digest"] = target.Digest
},
["provenance"] = new JsonObject
{
["ingestedAt"] = target.IngestedAt?.ToString("O", CultureInfo.InvariantCulture),
["connectorId"] = target.ConnectorId
}
}
};
var envelope = new JsonObject
{
["tenant"] = request.Tenant,
["subject"] = JsonSerializer.SerializeToNode(request.Subject, SerializerOptions),
["target"] = new JsonObject
{
["filePath"] = target.FilePath,
["pattern"] = target.Pattern,
["pathMatch"] = target.PathMatch,
["confidence"] = target.Confidence,
["evidenceHash"] = target.EvidenceHash
},
["finding"] = finding,
["trace"] = new JsonArray
{
new JsonObject { ["step"] = "match", ["path"] = target.FilePath },
new JsonObject { ["step"] = "decision", ["effect"] = verdictDelta.candidateVerdict }
},
["metrics"] = new JsonObject
{
["evalTicks"] = stableKey.Length,
["rulesEvaluated"] = 1,
["bindings"] = 1
}
};
return Task.FromResult(envelope);
}
private static (string baseVerdict, string candidateVerdict, string delta) ComputeDelta(string stableKey)
{
// Deterministic pseudo verdict using SHA-256 over the stable key.
Span<byte> hashBytes = stackalloc byte[32];
SHA256.HashData(Encoding.UTF8.GetBytes(stableKey), hashBytes);
// Use lowest byte to determine delta.
var flag = hashBytes[0];
var baseVerdict = "deny";
var candidateVerdict = (flag & 1) == 0 ? "warn" : "deny";
var delta = baseVerdict == candidateVerdict ? "unchanged" : "softened";
return (baseVerdict, candidateVerdict, delta);
}
}

View File

@@ -1,17 +1,27 @@
using System.Collections.Immutable;
using StellaOps.Policy.Engine.Compilation;
using StellaOps.Policy.Engine.Evaluation;
namespace StellaOps.Policy.Engine.Services;
internal sealed class PolicyEvaluationService
{
private readonly PolicyEvaluator evaluator = new();
public PolicyEvaluationResult Evaluate(PolicyIrDocument document, PolicyEvaluationContext context)
{
if (document is null)
{
using System.Collections.Immutable;
using StellaOps.Policy.Engine.Compilation;
using StellaOps.Policy.Engine.Evaluation;
namespace StellaOps.Policy.Engine.Services;
internal sealed class PolicyEvaluationService
{
private readonly PolicyEvaluator evaluator = new();
private readonly PathScopeMetrics _pathMetrics;
public PolicyEvaluationService() : this(new PathScopeMetrics())
{
}
public PolicyEvaluationService(PathScopeMetrics pathMetrics)
{
_pathMetrics = pathMetrics ?? throw new ArgumentNullException(nameof(pathMetrics));
}
public PolicyEvaluationResult Evaluate(PolicyIrDocument document, PolicyEvaluationContext context)
{
if (document is null)
{
throw new ArgumentNullException(nameof(document));
}
@@ -19,8 +29,10 @@ internal sealed class PolicyEvaluationService
{
throw new ArgumentNullException(nameof(context));
}
var request = new PolicyEvaluationRequest(document, context);
return evaluator.Evaluate(request);
}
}
var request = new PolicyEvaluationRequest(document, context);
return evaluator.Evaluate(request);
}
// PathScopeSimulationService partial class relies on _pathMetrics.
}