/**
* Trust Label and Principal - Trust algebra components.
* Sprint: SPRINT_3600_0001_0001 (Trust Algebra and Lattice Engine)
* Tasks: TRUST-005, TRUST-006
*
* Trust is not a single number; it must represent:
* - Cryptographic verification
* - Identity assurance
* - Authority scope
* - Freshness/revocation
* - Evidence strength
*
* These models enable policy-driven trust evaluation that is
* deterministic and explainable.
*/
namespace StellaOps.Policy.TrustLattice;
///
/// Assurance level for cryptographic and identity verification.
/// Increasing levels from A0 (weakest) to A4 (strongest).
///
public enum AssuranceLevel
{
///
/// A0: Unsigned or unverifiable assertion.
/// No cryptographic backing.
///
A0_Unsigned = 0,
///
/// A1: Signed, but weak identity binding.
/// Key is known but identity not strongly verified.
///
A1_WeakIdentity = 1,
///
/// A2: Signed with verified identity.
/// Certificate chain or keyless identity (OIDC) verified.
///
A2_VerifiedIdentity = 2,
///
/// A3: Signed with provenance binding.
/// Signature bound to artifact digest via attestation.
///
A3_ProvenanceBound = 3,
///
/// A4: Full transparency log inclusion.
/// Signed + provenance + Rekor/transparency log entry.
///
A4_TransparencyLog = 4,
}
///
/// Freshness class for temporal validity of assertions.
///
public enum FreshnessClass
{
///
/// Unknown or missing timestamp.
///
Unknown = 0,
///
/// Expired assertion (past valid_until).
///
Expired = 1,
///
/// Stale assertion (older than freshness threshold).
///
Stale = 2,
///
/// Fresh assertion (within freshness threshold).
///
Fresh = 3,
///
/// Live assertion (just issued or real-time).
///
Live = 4,
}
///
/// Evidence class describing the strength of supporting evidence.
///
public enum EvidenceClass
{
///
/// E0: Statement only (no supporting evidence refs).
///
E0_StatementOnly = 0,
///
/// E1: SBOM linkage evidence.
/// Component present + version evidence.
///
E1_SbomLinkage = 1,
///
/// E2: Reachability/mitigation evidence.
/// Call paths, config snapshots, runtime proofs.
///
E2_ReachabilityMitigation = 2,
///
/// E3: Remediation evidence.
/// Patch diffs, pedigree/commit chain, fix verification.
///
E3_Remediation = 3,
}
///
/// Role that a principal can play in the trust model.
///
[Flags]
public enum PrincipalRole
{
None = 0,
///
/// Vendor: Original software vendor.
/// Authoritative for their own products.
///
Vendor = 1 << 0,
///
/// Distributor: OS/distro package maintainer.
/// Authoritative for packages in their repositories.
///
Distributor = 1 << 1,
///
/// Scanner: Automated vulnerability scanner.
/// Provides detection evidence.
///
Scanner = 1 << 2,
///
/// Auditor: Security auditor or penetration tester.
/// Provides expert assessment evidence.
///
Auditor = 1 << 3,
///
/// InternalSecurity: Internal security team.
/// Authoritative for internal artifact reachability/mitigation.
///
InternalSecurity = 1 << 4,
///
/// BuildSystem: CI/CD build system.
/// Provides provenance and build evidence.
///
BuildSystem = 1 << 5,
///
/// RuntimeMonitor: Runtime observability system.
/// Provides runtime behavior evidence.
///
RuntimeMonitor = 1 << 6,
}
///
/// Authority scope defining what subjects a principal is authoritative for.
///
public sealed record AuthorityScope
{
///
/// Scope type for canonical serialization.
///
public string Type { get; init; } = "default";
///
/// Constraint expression for the scope.
///
public string? Constraint { get; init; }
///
/// Product namespace patterns (e.g., "vendor.example/*").
/// Principal is authoritative for these products.
///
public IReadOnlyList? Products { get; init; }
///
/// Package namespace patterns (e.g., "pkg:npm/*", "pkg:maven/org.example/*").
///
public IReadOnlyList? Packages { get; init; }
///
/// Artifact digest patterns (e.g., "sha256:*" for internal artifacts).
///
public IReadOnlyList? Artifacts { get; init; }
///
/// Vulnerability source patterns (e.g., "nvd", "osv").
///
public IReadOnlyList? VulnerabilitySources { get; init; }
///
/// Checks if this scope covers a given subject.
///
public bool Covers(Subject subject)
{
// Check artifacts
if (Artifacts is { Count: > 0 })
{
if (!MatchesAny(subject.Artifact.Digest, Artifacts))
return false;
}
// Check packages
if (Packages is { Count: > 0 })
{
var componentId = subject.Component.Purl ?? subject.Component.Id;
if (!MatchesAny(componentId, Packages))
return false;
}
// Check vulnerability sources
if (VulnerabilitySources is { Count: > 0 })
{
var source = subject.Vulnerability.Source ?? "";
if (!MatchesAny(source, VulnerabilitySources))
return false;
}
return true;
}
///
/// Checks if this scope covers (is a superset of) another scope.
///
public bool Covers(AuthorityScope other)
{
// A scope covers another if all patterns in other are covered by patterns in this scope
// Universal scope (*) covers everything
if (Artifacts is { Count: > 0 } && Artifacts.Contains("*"))
return true;
// Check that we cover all artifact patterns from the other scope
if (other.Artifacts is { Count: > 0 })
{
if (Artifacts is null || Artifacts.Count == 0)
return false;
foreach (var pattern in other.Artifacts)
{
if (!Artifacts.Any(a => PatternCovers(a, pattern)))
return false;
}
}
// Check that we cover all package patterns from the other scope
if (other.Packages is { Count: > 0 })
{
if (Packages is null || Packages.Count == 0)
return false;
foreach (var pattern in other.Packages)
{
if (!Packages.Any(p => PatternCovers(p, pattern)))
return false;
}
}
// Check vulnerability sources
if (other.VulnerabilitySources is { Count: > 0 })
{
if (VulnerabilitySources is null || VulnerabilitySources.Count == 0)
return false;
foreach (var source in other.VulnerabilitySources)
{
if (!VulnerabilitySources.Any(s => PatternCovers(s, source)))
return false;
}
}
return true;
}
private static bool PatternCovers(string coveringPattern, string coveredPattern)
{
// Universal pattern covers everything
if (coveringPattern == "*") return true;
// Exact match
if (coveringPattern.Equals(coveredPattern, StringComparison.OrdinalIgnoreCase))
return true;
// Prefix pattern (e.g., "pkg:npm/*" covers "pkg:npm/express")
if (coveringPattern.EndsWith("/*"))
{
var prefix = coveringPattern[..^1];
if (coveredPattern.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
return true;
// Also check if covered pattern is a more specific prefix pattern
if (coveredPattern.EndsWith("/*"))
{
var otherPrefix = coveredPattern[..^1];
if (otherPrefix.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
return true;
}
}
return false;
}
private static bool MatchesAny(string value, IReadOnlyList patterns)
{
foreach (var pattern in patterns)
{
if (pattern == "*") return true;
if (pattern.EndsWith("/*"))
{
var prefix = pattern[..^1];
if (value.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
return true;
}
else if (pattern.Equals(value, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
///
/// Universal scope that covers all subjects.
///
public static AuthorityScope Universal { get; } = new()
{
Artifacts = ["*"],
};
}
///
/// A principal is an issuer identity with verifiable keys.
///
public sealed record Principal
{
///
/// Principal identifier (URI-like, e.g., "did:web:vendor.example").
///
public required string Id { get; init; }
///
/// Principal type for canonical serialization.
///
public string Type { get; init; } = "identity";
///
/// Key identifiers for verification.
///
public IReadOnlyList? KeyIds { get; init; }
///
/// Identity claims (e.g., cert SANs, OIDC subject, org, repo).
///
public IReadOnlyDictionary? IdentityClaims { get; init; }
///
/// Roles this principal can play.
///
public PrincipalRole Roles { get; init; } = PrincipalRole.None;
///
/// Display name for human readability.
///
public string? DisplayName { get; init; }
///
/// An unknown principal used as a fallback when no issuer is specified.
///
public static Principal Unknown { get; } = new Principal
{
Id = "urn:stellaops:principal:unknown",
DisplayName = "Unknown"
};
}
///
/// Trust label computed from policy and verification.
/// Affects decision selection without destroying underlying knowledge.
///
public sealed record TrustLabel : IComparable
{
///
/// Cryptographic and identity verification strength.
///
public required AssuranceLevel AssuranceLevel { get; init; }
///
/// Scope of subjects this trust applies to.
///
public required AuthorityScope AuthorityScope { get; init; }
///
/// Temporal validity of the assertion.
///
public required FreshnessClass Freshness { get; init; }
///
/// Strength of attached evidence.
///
public required EvidenceClass EvidenceClass { get; init; }
///
/// The principal providing this trust.
///
public Principal? Principal { get; init; }
///
/// Computes an overall trust score for ordering.
/// Higher is more trustworthy.
///
public int ComputeScore()
{
// Weighted combination (can be policy-configurable)
return (int)AssuranceLevel * 100
+ (int)EvidenceClass * 10
+ (int)Freshness;
}
///
/// Compares trust labels by overall score.
///
public int CompareTo(TrustLabel? other)
{
if (other is null) return 1;
return ComputeScore().CompareTo(other.ComputeScore());
}
///
/// Returns the higher trust label (join operation).
///
public static TrustLabel Max(TrustLabel a, TrustLabel b)
=> a.CompareTo(b) >= 0 ? a : b;
///
/// Returns the lower trust label (meet operation).
///
public static TrustLabel Min(TrustLabel a, TrustLabel b)
=> a.CompareTo(b) <= 0 ? a : b;
///
/// Creates a minimal trust label (unsigned, no evidence).
///
public static TrustLabel Minimal { get; } = new()
{
AssuranceLevel = AssuranceLevel.A0_Unsigned,
AuthorityScope = new AuthorityScope(),
Freshness = FreshnessClass.Unknown,
EvidenceClass = EvidenceClass.E0_StatementOnly,
};
}