up
This commit is contained in:
216
src/Policy/StellaOps.PolicyDsl/SignalContext.cs
Normal file
216
src/Policy/StellaOps.PolicyDsl/SignalContext.cs
Normal file
@@ -0,0 +1,216 @@
|
||||
namespace StellaOps.PolicyDsl;
|
||||
|
||||
/// <summary>
|
||||
/// Provides signal values for policy evaluation.
|
||||
/// </summary>
|
||||
public sealed class SignalContext
|
||||
{
|
||||
private readonly Dictionary<string, object?> _signals;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an empty signal context.
|
||||
/// </summary>
|
||||
public SignalContext()
|
||||
{
|
||||
_signals = new Dictionary<string, object?>(StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a signal context with initial values.
|
||||
/// </summary>
|
||||
/// <param name="signals">Initial signal values.</param>
|
||||
public SignalContext(IDictionary<string, object?> signals)
|
||||
{
|
||||
_signals = new Dictionary<string, object?>(signals, StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether a signal exists.
|
||||
/// </summary>
|
||||
/// <param name="name">The signal name.</param>
|
||||
/// <returns>True if the signal exists.</returns>
|
||||
public bool HasSignal(string name) => _signals.ContainsKey(name);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a signal value.
|
||||
/// </summary>
|
||||
/// <param name="name">The signal name.</param>
|
||||
/// <returns>The signal value, or null if not found.</returns>
|
||||
public object? GetSignal(string name) => _signals.TryGetValue(name, out var value) ? value : null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a signal value as a specific type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The expected type.</typeparam>
|
||||
/// <param name="name">The signal name.</param>
|
||||
/// <returns>The signal value, or default if not found or wrong type.</returns>
|
||||
public T? GetSignal<T>(string name) => _signals.TryGetValue(name, out var value) && value is T t ? t : default;
|
||||
|
||||
/// <summary>
|
||||
/// Sets a signal value.
|
||||
/// </summary>
|
||||
/// <param name="name">The signal name.</param>
|
||||
/// <param name="value">The signal value.</param>
|
||||
/// <returns>This context for chaining.</returns>
|
||||
public SignalContext SetSignal(string name, object? value)
|
||||
{
|
||||
_signals[name] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a signal.
|
||||
/// </summary>
|
||||
/// <param name="name">The signal name.</param>
|
||||
/// <returns>This context for chaining.</returns>
|
||||
public SignalContext RemoveSignal(string name)
|
||||
{
|
||||
_signals.Remove(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all signal names.
|
||||
/// </summary>
|
||||
public IEnumerable<string> SignalNames => _signals.Keys;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all signals as a read-only dictionary.
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, object?> Signals => _signals;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy of this context.
|
||||
/// </summary>
|
||||
/// <returns>A new context with the same signals.</returns>
|
||||
public SignalContext Clone() => new(_signals);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a signal context builder for fluent construction.
|
||||
/// </summary>
|
||||
/// <returns>A new builder.</returns>
|
||||
public static SignalContextBuilder Builder() => new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builder for creating signal contexts with fluent API.
|
||||
/// </summary>
|
||||
public sealed class SignalContextBuilder
|
||||
{
|
||||
private readonly Dictionary<string, object?> _signals = new(StringComparer.Ordinal);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a signal to the context.
|
||||
/// </summary>
|
||||
/// <param name="name">The signal name.</param>
|
||||
/// <param name="value">The signal value.</param>
|
||||
/// <returns>This builder for chaining.</returns>
|
||||
public SignalContextBuilder WithSignal(string name, object? value)
|
||||
{
|
||||
_signals[name] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a boolean signal to the context.
|
||||
/// </summary>
|
||||
/// <param name="name">The signal name.</param>
|
||||
/// <param name="value">The boolean value.</param>
|
||||
/// <returns>This builder for chaining.</returns>
|
||||
public SignalContextBuilder WithFlag(string name, bool value = true)
|
||||
{
|
||||
_signals[name] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a numeric signal to the context.
|
||||
/// </summary>
|
||||
/// <param name="name">The signal name.</param>
|
||||
/// <param name="value">The numeric value.</param>
|
||||
/// <returns>This builder for chaining.</returns>
|
||||
public SignalContextBuilder WithNumber(string name, decimal value)
|
||||
{
|
||||
_signals[name] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a string signal to the context.
|
||||
/// </summary>
|
||||
/// <param name="name">The signal name.</param>
|
||||
/// <param name="value">The string value.</param>
|
||||
/// <returns>This builder for chaining.</returns>
|
||||
public SignalContextBuilder WithString(string name, string value)
|
||||
{
|
||||
_signals[name] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a nested object signal to the context.
|
||||
/// </summary>
|
||||
/// <param name="name">The signal name.</param>
|
||||
/// <param name="properties">The nested properties.</param>
|
||||
/// <returns>This builder for chaining.</returns>
|
||||
public SignalContextBuilder WithObject(string name, IDictionary<string, object?> properties)
|
||||
{
|
||||
_signals[name] = new Dictionary<string, object?>(properties, StringComparer.Ordinal);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds common finding signals.
|
||||
/// </summary>
|
||||
/// <param name="severity">The finding severity (e.g., "critical", "high", "medium", "low").</param>
|
||||
/// <param name="confidence">The confidence score (0.0 to 1.0).</param>
|
||||
/// <param name="cveId">Optional CVE identifier.</param>
|
||||
/// <returns>This builder for chaining.</returns>
|
||||
public SignalContextBuilder WithFinding(string severity, decimal confidence, string? cveId = null)
|
||||
{
|
||||
_signals["finding"] = new Dictionary<string, object?>(StringComparer.Ordinal)
|
||||
{
|
||||
["severity"] = severity,
|
||||
["confidence"] = confidence,
|
||||
["cve_id"] = cveId,
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds common reachability signals.
|
||||
/// </summary>
|
||||
/// <param name="state">The reachability state (e.g., "reachable", "unreachable", "unknown").</param>
|
||||
/// <param name="confidence">The confidence score (0.0 to 1.0).</param>
|
||||
/// <param name="hasRuntimeEvidence">Whether there is runtime evidence.</param>
|
||||
/// <returns>This builder for chaining.</returns>
|
||||
public SignalContextBuilder WithReachability(string state, decimal confidence, bool hasRuntimeEvidence = false)
|
||||
{
|
||||
_signals["reachability"] = new Dictionary<string, object?>(StringComparer.Ordinal)
|
||||
{
|
||||
["state"] = state,
|
||||
["confidence"] = confidence,
|
||||
["has_runtime_evidence"] = hasRuntimeEvidence,
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds common trust score signals.
|
||||
/// </summary>
|
||||
/// <param name="score">The trust score (0.0 to 1.0).</param>
|
||||
/// <param name="verified">Whether the source is verified.</param>
|
||||
/// <returns>This builder for chaining.</returns>
|
||||
public SignalContextBuilder WithTrustScore(decimal score, bool verified = false)
|
||||
{
|
||||
_signals["trust_score"] = score;
|
||||
_signals["trust_verified"] = verified;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the signal context.
|
||||
/// </summary>
|
||||
/// <returns>A new signal context with the configured signals.</returns>
|
||||
public SignalContext Build() => new(_signals);
|
||||
}
|
||||
Reference in New Issue
Block a user