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
242 lines
6.7 KiB
C#
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);
|