up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (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
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (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

This commit is contained in:
StellaOps Bot
2025-12-13 00:20:26 +02:00
parent e1f1bef4c1
commit 564df71bfb
2376 changed files with 334389 additions and 328032 deletions

View File

@@ -1,283 +1,283 @@
using System;
using System.Collections.Immutable;
using StellaOps.PolicyDsl;
namespace StellaOps.Policy.Engine.Compilation;
/// <summary>
/// Computes deterministic complexity metrics for compiled policies.
/// </summary>
internal sealed class PolicyComplexityAnalyzer
{
public PolicyComplexityReport Analyze(PolicyIrDocument document)
{
ArgumentNullException.ThrowIfNull(document);
var metrics = new ComplexityMetrics();
metrics.RuleCount = document.Rules.IsDefault ? 0 : document.Rules.Length;
VisitMetadata(document.Metadata.Values, metrics);
VisitMetadata(document.Settings.Values, metrics);
VisitProfiles(document.Profiles, metrics);
if (!document.Rules.IsDefaultOrEmpty)
{
foreach (var rule in document.Rules)
{
metrics.ConditionCount++;
VisitExpression(rule.When, metrics, depth: 0);
VisitActions(rule.ThenActions, metrics);
VisitActions(rule.ElseActions, metrics);
}
}
var score = CalculateScore(metrics);
var roundedScore = Math.Round(score, 3, MidpointRounding.AwayFromZero);
return new PolicyComplexityReport(
roundedScore,
metrics.RuleCount,
metrics.ActionCount,
metrics.ExpressionCount,
metrics.InvocationCount,
metrics.MemberAccessCount,
metrics.IdentifierCount,
metrics.LiteralCount,
metrics.MaxDepth,
metrics.ProfileCount,
metrics.ProfileBindings,
metrics.ConditionCount,
metrics.ListItems);
}
private static void VisitProfiles(ImmutableArray<PolicyIrProfile> profiles, ComplexityMetrics metrics)
{
if (profiles.IsDefaultOrEmpty)
{
return;
}
foreach (var profile in profiles)
{
metrics.ProfileCount++;
if (!profile.Maps.IsDefaultOrEmpty)
{
foreach (var map in profile.Maps)
{
if (map.Entries.IsDefaultOrEmpty)
{
continue;
}
foreach (var entry in map.Entries)
{
metrics.ProfileBindings++;
metrics.LiteralCount++; // weight values contribute to literal count
}
}
}
if (!profile.Environments.IsDefaultOrEmpty)
{
foreach (var environment in profile.Environments)
{
if (environment.Entries.IsDefaultOrEmpty)
{
continue;
}
foreach (var entry in environment.Entries)
{
metrics.ProfileBindings++;
metrics.ConditionCount++;
VisitExpression(entry.Condition, metrics, depth: 0);
}
}
}
if (!profile.Scalars.IsDefaultOrEmpty)
{
foreach (var scalar in profile.Scalars)
{
metrics.ProfileBindings++;
VisitLiteral(scalar.Value, metrics);
}
}
}
}
private static void VisitMetadata(IEnumerable<PolicyIrLiteral> literals, ComplexityMetrics metrics)
{
foreach (var literal in literals)
{
VisitLiteral(literal, metrics);
}
}
private static void VisitLiteral(PolicyIrLiteral literal, ComplexityMetrics metrics)
{
switch (literal)
{
case PolicyIrListLiteral list when !list.Items.IsDefaultOrEmpty:
foreach (var item in list.Items)
{
VisitLiteral(item, metrics);
}
break;
}
metrics.LiteralCount++;
}
private static void VisitActions(ImmutableArray<PolicyIrAction> actions, ComplexityMetrics metrics)
{
if (actions.IsDefaultOrEmpty)
{
return;
}
foreach (var action in actions)
{
metrics.ActionCount++;
switch (action)
{
case PolicyIrAssignmentAction assignment:
VisitExpression(assignment.Value, metrics, depth: 0);
break;
case PolicyIrAnnotateAction annotate:
VisitExpression(annotate.Value, metrics, depth: 0);
break;
case PolicyIrIgnoreAction ignore when ignore.Until is not null:
VisitExpression(ignore.Until, metrics, depth: 0);
break;
case PolicyIrEscalateAction escalate:
VisitExpression(escalate.To, metrics, depth: 0);
VisitExpression(escalate.When, metrics, depth: 0);
break;
case PolicyIrRequireVexAction require when !require.Conditions.IsEmpty:
foreach (var condition in require.Conditions.Values)
{
VisitExpression(condition, metrics, depth: 0);
}
break;
case PolicyIrWarnAction warn when warn.Message is not null:
VisitExpression(warn.Message, metrics, depth: 0);
break;
case PolicyIrDeferAction defer when defer.Until is not null:
VisitExpression(defer.Until, metrics, depth: 0);
break;
}
}
}
private static void VisitExpression(PolicyExpression? expression, ComplexityMetrics metrics, int depth)
{
if (expression is null)
{
return;
}
metrics.ExpressionCount++;
var currentDepth = depth + 1;
if (currentDepth > metrics.MaxDepth)
{
metrics.MaxDepth = currentDepth;
}
switch (expression)
{
case PolicyLiteralExpression:
metrics.LiteralCount++;
break;
case PolicyListExpression listExpression:
if (!listExpression.Items.IsDefaultOrEmpty)
{
foreach (var item in listExpression.Items)
{
metrics.ListItems++;
VisitExpression(item, metrics, currentDepth);
}
}
break;
case PolicyIdentifierExpression:
metrics.IdentifierCount++;
break;
case PolicyMemberAccessExpression member:
metrics.MemberAccessCount++;
VisitExpression(member.Target, metrics, currentDepth);
break;
case PolicyInvocationExpression invocation:
metrics.InvocationCount++;
VisitExpression(invocation.Target, metrics, currentDepth);
if (!invocation.Arguments.IsDefaultOrEmpty)
{
foreach (var argument in invocation.Arguments)
{
VisitExpression(argument, metrics, currentDepth);
}
}
break;
case PolicyIndexerExpression indexer:
VisitExpression(indexer.Target, metrics, currentDepth);
VisitExpression(indexer.Index, metrics, currentDepth);
break;
case PolicyUnaryExpression unary:
VisitExpression(unary.Operand, metrics, currentDepth);
break;
case PolicyBinaryExpression binary:
VisitExpression(binary.Left, metrics, currentDepth);
VisitExpression(binary.Right, metrics, currentDepth);
break;
default:
break;
}
}
private static double CalculateScore(ComplexityMetrics metrics)
{
return metrics.RuleCount * 5d
+ metrics.ActionCount * 1.5d
+ metrics.ExpressionCount * 0.75d
+ metrics.InvocationCount * 1.5d
+ metrics.MemberAccessCount * 1.0d
+ metrics.IdentifierCount * 0.5d
+ metrics.LiteralCount * 0.25d
+ metrics.ProfileBindings * 0.5d
+ metrics.ConditionCount * 1.25d
+ metrics.MaxDepth * 2d
+ metrics.ListItems * 0.25d;
}
private sealed class ComplexityMetrics
{
public int RuleCount;
public int ActionCount;
public int ExpressionCount;
public int InvocationCount;
public int MemberAccessCount;
public int IdentifierCount;
public int LiteralCount;
public int ProfileCount;
public int ProfileBindings;
public int ConditionCount;
public int MaxDepth;
public int ListItems;
}
}
internal sealed record PolicyComplexityReport(
double Score,
int RuleCount,
int ActionCount,
int ExpressionCount,
int InvocationCount,
int MemberAccessCount,
int IdentifierCount,
int LiteralCount,
int MaxExpressionDepth,
int ProfileCount,
int ProfileBindingCount,
int ConditionCount,
int ListItemCount);
using System;
using System.Collections.Immutable;
using StellaOps.PolicyDsl;
namespace StellaOps.Policy.Engine.Compilation;
/// <summary>
/// Computes deterministic complexity metrics for compiled policies.
/// </summary>
internal sealed class PolicyComplexityAnalyzer
{
public PolicyComplexityReport Analyze(PolicyIrDocument document)
{
ArgumentNullException.ThrowIfNull(document);
var metrics = new ComplexityMetrics();
metrics.RuleCount = document.Rules.IsDefault ? 0 : document.Rules.Length;
VisitMetadata(document.Metadata.Values, metrics);
VisitMetadata(document.Settings.Values, metrics);
VisitProfiles(document.Profiles, metrics);
if (!document.Rules.IsDefaultOrEmpty)
{
foreach (var rule in document.Rules)
{
metrics.ConditionCount++;
VisitExpression(rule.When, metrics, depth: 0);
VisitActions(rule.ThenActions, metrics);
VisitActions(rule.ElseActions, metrics);
}
}
var score = CalculateScore(metrics);
var roundedScore = Math.Round(score, 3, MidpointRounding.AwayFromZero);
return new PolicyComplexityReport(
roundedScore,
metrics.RuleCount,
metrics.ActionCount,
metrics.ExpressionCount,
metrics.InvocationCount,
metrics.MemberAccessCount,
metrics.IdentifierCount,
metrics.LiteralCount,
metrics.MaxDepth,
metrics.ProfileCount,
metrics.ProfileBindings,
metrics.ConditionCount,
metrics.ListItems);
}
private static void VisitProfiles(ImmutableArray<PolicyIrProfile> profiles, ComplexityMetrics metrics)
{
if (profiles.IsDefaultOrEmpty)
{
return;
}
foreach (var profile in profiles)
{
metrics.ProfileCount++;
if (!profile.Maps.IsDefaultOrEmpty)
{
foreach (var map in profile.Maps)
{
if (map.Entries.IsDefaultOrEmpty)
{
continue;
}
foreach (var entry in map.Entries)
{
metrics.ProfileBindings++;
metrics.LiteralCount++; // weight values contribute to literal count
}
}
}
if (!profile.Environments.IsDefaultOrEmpty)
{
foreach (var environment in profile.Environments)
{
if (environment.Entries.IsDefaultOrEmpty)
{
continue;
}
foreach (var entry in environment.Entries)
{
metrics.ProfileBindings++;
metrics.ConditionCount++;
VisitExpression(entry.Condition, metrics, depth: 0);
}
}
}
if (!profile.Scalars.IsDefaultOrEmpty)
{
foreach (var scalar in profile.Scalars)
{
metrics.ProfileBindings++;
VisitLiteral(scalar.Value, metrics);
}
}
}
}
private static void VisitMetadata(IEnumerable<PolicyIrLiteral> literals, ComplexityMetrics metrics)
{
foreach (var literal in literals)
{
VisitLiteral(literal, metrics);
}
}
private static void VisitLiteral(PolicyIrLiteral literal, ComplexityMetrics metrics)
{
switch (literal)
{
case PolicyIrListLiteral list when !list.Items.IsDefaultOrEmpty:
foreach (var item in list.Items)
{
VisitLiteral(item, metrics);
}
break;
}
metrics.LiteralCount++;
}
private static void VisitActions(ImmutableArray<PolicyIrAction> actions, ComplexityMetrics metrics)
{
if (actions.IsDefaultOrEmpty)
{
return;
}
foreach (var action in actions)
{
metrics.ActionCount++;
switch (action)
{
case PolicyIrAssignmentAction assignment:
VisitExpression(assignment.Value, metrics, depth: 0);
break;
case PolicyIrAnnotateAction annotate:
VisitExpression(annotate.Value, metrics, depth: 0);
break;
case PolicyIrIgnoreAction ignore when ignore.Until is not null:
VisitExpression(ignore.Until, metrics, depth: 0);
break;
case PolicyIrEscalateAction escalate:
VisitExpression(escalate.To, metrics, depth: 0);
VisitExpression(escalate.When, metrics, depth: 0);
break;
case PolicyIrRequireVexAction require when !require.Conditions.IsEmpty:
foreach (var condition in require.Conditions.Values)
{
VisitExpression(condition, metrics, depth: 0);
}
break;
case PolicyIrWarnAction warn when warn.Message is not null:
VisitExpression(warn.Message, metrics, depth: 0);
break;
case PolicyIrDeferAction defer when defer.Until is not null:
VisitExpression(defer.Until, metrics, depth: 0);
break;
}
}
}
private static void VisitExpression(PolicyExpression? expression, ComplexityMetrics metrics, int depth)
{
if (expression is null)
{
return;
}
metrics.ExpressionCount++;
var currentDepth = depth + 1;
if (currentDepth > metrics.MaxDepth)
{
metrics.MaxDepth = currentDepth;
}
switch (expression)
{
case PolicyLiteralExpression:
metrics.LiteralCount++;
break;
case PolicyListExpression listExpression:
if (!listExpression.Items.IsDefaultOrEmpty)
{
foreach (var item in listExpression.Items)
{
metrics.ListItems++;
VisitExpression(item, metrics, currentDepth);
}
}
break;
case PolicyIdentifierExpression:
metrics.IdentifierCount++;
break;
case PolicyMemberAccessExpression member:
metrics.MemberAccessCount++;
VisitExpression(member.Target, metrics, currentDepth);
break;
case PolicyInvocationExpression invocation:
metrics.InvocationCount++;
VisitExpression(invocation.Target, metrics, currentDepth);
if (!invocation.Arguments.IsDefaultOrEmpty)
{
foreach (var argument in invocation.Arguments)
{
VisitExpression(argument, metrics, currentDepth);
}
}
break;
case PolicyIndexerExpression indexer:
VisitExpression(indexer.Target, metrics, currentDepth);
VisitExpression(indexer.Index, metrics, currentDepth);
break;
case PolicyUnaryExpression unary:
VisitExpression(unary.Operand, metrics, currentDepth);
break;
case PolicyBinaryExpression binary:
VisitExpression(binary.Left, metrics, currentDepth);
VisitExpression(binary.Right, metrics, currentDepth);
break;
default:
break;
}
}
private static double CalculateScore(ComplexityMetrics metrics)
{
return metrics.RuleCount * 5d
+ metrics.ActionCount * 1.5d
+ metrics.ExpressionCount * 0.75d
+ metrics.InvocationCount * 1.5d
+ metrics.MemberAccessCount * 1.0d
+ metrics.IdentifierCount * 0.5d
+ metrics.LiteralCount * 0.25d
+ metrics.ProfileBindings * 0.5d
+ metrics.ConditionCount * 1.25d
+ metrics.MaxDepth * 2d
+ metrics.ListItems * 0.25d;
}
private sealed class ComplexityMetrics
{
public int RuleCount;
public int ActionCount;
public int ExpressionCount;
public int InvocationCount;
public int MemberAccessCount;
public int IdentifierCount;
public int LiteralCount;
public int ProfileCount;
public int ProfileBindings;
public int ConditionCount;
public int MaxDepth;
public int ListItems;
}
}
internal sealed record PolicyComplexityReport(
double Score,
int RuleCount,
int ActionCount,
int ExpressionCount,
int InvocationCount,
int MemberAccessCount,
int IdentifierCount,
int LiteralCount,
int MaxExpressionDepth,
int ProfileCount,
int ProfileBindingCount,
int ConditionCount,
int ListItemCount);