Files
git.stella-ops.org/src/Policy/__Libraries/StellaOps.Policy/PolicyDocument.cs
StellaOps Bot 564df71bfb
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
up
2025-12-13 00:20:26 +02:00

242 lines
6.7 KiB
C#

using System;
using System.Collections.Immutable;
using System.Linq;
namespace StellaOps.Policy;
/// <summary>
/// Canonical representation of a StellaOps policy document.
/// </summary>
public sealed record PolicyDocument(
string Version,
ImmutableArray<PolicyRule> Rules,
ImmutableDictionary<string, string> Metadata,
PolicyExceptionConfiguration Exceptions)
{
public static PolicyDocument Empty { get; } = new(
PolicySchema.CurrentVersion,
ImmutableArray<PolicyRule>.Empty,
ImmutableDictionary<string, string>.Empty,
PolicyExceptionConfiguration.Empty);
}
public static class PolicySchema
{
public const string SchemaId = "https://schemas.stella-ops.org/policy/policy-schema@1.json";
public const string CurrentVersion = "1.0";
public static PolicyDocumentFormat DetectFormat(string fileName)
{
if (fileName is null)
{
throw new ArgumentNullException(nameof(fileName));
}
var lower = fileName.Trim().ToLowerInvariant();
if (lower.EndsWith(".yaml", StringComparison.Ordinal)
|| lower.EndsWith(".yml", StringComparison.Ordinal)
|| lower.EndsWith(".stella", StringComparison.Ordinal))
{
return PolicyDocumentFormat.Yaml;
}
return PolicyDocumentFormat.Json;
}
}
public sealed record PolicyRule(
string Name,
string? Identifier,
string? Description,
PolicyAction Action,
ImmutableArray<PolicySeverity> Severities,
ImmutableArray<string> Environments,
ImmutableArray<string> Sources,
ImmutableArray<string> Vendors,
ImmutableArray<string> Licenses,
ImmutableArray<string> Tags,
PolicyRuleMatchCriteria Match,
DateTimeOffset? Expires,
string? Justification,
ImmutableDictionary<string, string> Metadata)
{
public static PolicyRuleMatchCriteria EmptyMatch { get; } = new(
ImmutableArray<string>.Empty,
ImmutableArray<string>.Empty,
ImmutableArray<string>.Empty,
ImmutableArray<string>.Empty,
ImmutableArray<string>.Empty,
ImmutableArray<string>.Empty,
ImmutableArray<string>.Empty,
ImmutableArray<string>.Empty);
public static PolicyRule Create(
string name,
PolicyAction action,
ImmutableArray<PolicySeverity> severities,
ImmutableArray<string> environments,
ImmutableArray<string> sources,
ImmutableArray<string> vendors,
ImmutableArray<string> licenses,
ImmutableArray<string> tags,
PolicyRuleMatchCriteria match,
DateTimeOffset? expires,
string? justification,
string? identifier = null,
string? description = null,
ImmutableDictionary<string, string>? metadata = null)
{
metadata ??= ImmutableDictionary<string, string>.Empty;
return new PolicyRule(
name,
identifier,
description,
action,
severities,
environments,
sources,
vendors,
licenses,
tags,
match,
expires,
justification,
metadata);
}
public bool MatchesAnyEnvironment => Environments.IsDefaultOrEmpty;
}
public sealed record PolicyRuleMatchCriteria(
ImmutableArray<string> Images,
ImmutableArray<string> Repositories,
ImmutableArray<string> Packages,
ImmutableArray<string> Purls,
ImmutableArray<string> Cves,
ImmutableArray<string> Paths,
ImmutableArray<string> LayerDigests,
ImmutableArray<string> UsedByEntrypoint)
{
public static PolicyRuleMatchCriteria Create(
ImmutableArray<string> images,
ImmutableArray<string> repositories,
ImmutableArray<string> packages,
ImmutableArray<string> purls,
ImmutableArray<string> cves,
ImmutableArray<string> paths,
ImmutableArray<string> layerDigests,
ImmutableArray<string> usedByEntrypoint)
=> new(
images,
repositories,
packages,
purls,
cves,
paths,
layerDigests,
usedByEntrypoint);
public static PolicyRuleMatchCriteria Empty { get; } = new(
ImmutableArray<string>.Empty,
ImmutableArray<string>.Empty,
ImmutableArray<string>.Empty,
ImmutableArray<string>.Empty,
ImmutableArray<string>.Empty,
ImmutableArray<string>.Empty,
ImmutableArray<string>.Empty,
ImmutableArray<string>.Empty);
public bool IsEmpty =>
Images.IsDefaultOrEmpty &&
Repositories.IsDefaultOrEmpty &&
Packages.IsDefaultOrEmpty &&
Purls.IsDefaultOrEmpty &&
Cves.IsDefaultOrEmpty &&
Paths.IsDefaultOrEmpty &&
LayerDigests.IsDefaultOrEmpty &&
UsedByEntrypoint.IsDefaultOrEmpty;
}
public sealed record PolicyAction(
PolicyActionType Type,
PolicyIgnoreOptions? Ignore,
PolicyEscalateOptions? Escalate,
PolicyRequireVexOptions? RequireVex,
bool Quiet);
public enum PolicyActionType
{
Block,
Ignore,
Warn,
Defer,
Escalate,
RequireVex,
}
public sealed record PolicyIgnoreOptions(DateTimeOffset? Until, string? Justification);
public sealed record PolicyEscalateOptions(
PolicySeverity? MinimumSeverity,
bool RequireKev,
double? MinimumEpss);
public sealed record PolicyRequireVexOptions(
ImmutableArray<string> Vendors,
ImmutableArray<string> Justifications);
public enum PolicySeverity
{
Critical,
High,
Medium,
Low,
Informational,
None,
Unknown,
}
public sealed record PolicyExceptionConfiguration(
ImmutableArray<PolicyExceptionEffect> Effects,
ImmutableArray<PolicyExceptionRoutingTemplate> RoutingTemplates)
{
public static PolicyExceptionConfiguration Empty { get; } = new(
ImmutableArray<PolicyExceptionEffect>.Empty,
ImmutableArray<PolicyExceptionRoutingTemplate>.Empty);
public PolicyExceptionEffect? FindEffect(string effectId)
{
if (string.IsNullOrWhiteSpace(effectId) || Effects.IsDefaultOrEmpty)
{
return null;
}
return Effects.FirstOrDefault(effect =>
string.Equals(effect.Id, effectId, StringComparison.OrdinalIgnoreCase));
}
}
public sealed record PolicyExceptionEffect(
string Id,
string? Name,
PolicyExceptionEffectType Effect,
PolicySeverity? DowngradeSeverity,
string? RequiredControlId,
string? RoutingTemplate,
int? MaxDurationDays,
string? Description);
public enum PolicyExceptionEffectType
{
Suppress,
Defer,
Downgrade,
RequireControl,
}
public sealed record PolicyExceptionRoutingTemplate(
string Id,
string AuthorityRouteId,
bool RequireMfa,
string? Description);