Fix build and code structure improvements. New but essential UI functionality. CI improvements. Documentation improvements. AI module improvements.

This commit is contained in:
StellaOps Bot
2025-12-26 21:54:17 +02:00
parent 335ff7da16
commit c2b9cd8d1f
3717 changed files with 264714 additions and 48202 deletions

View File

@@ -0,0 +1,200 @@
using System.Collections.Immutable;
namespace StellaOps.Policy.Engine.Crypto;
/// <summary>
/// Policy atoms for cryptographic risk evaluation.
/// These atoms can be used in policy rules to gate or warn on crypto-related issues.
/// </summary>
public static class CryptoRiskAtoms
{
#region Atom Names
/// <summary>
/// Atom for deprecated cryptographic algorithms (MD5, SHA-1, DES, etc.).
/// </summary>
public const string WeakCrypto = "WEAK_CRYPTO";
/// <summary>
/// Atom for algorithms vulnerable to quantum computing attacks.
/// </summary>
public const string QuantumVulnerable = "QUANTUM_VULNERABLE";
/// <summary>
/// Atom for deprecated algorithms that are cryptographically broken.
/// </summary>
public const string DeprecatedCrypto = "DEPRECATED_CRYPTO";
/// <summary>
/// Atom for algorithms with insufficient key sizes.
/// </summary>
public const string InsufficientKeySize = "INSUFFICIENT_KEY_SIZE";
/// <summary>
/// Atom for post-quantum ready algorithms (positive indicator).
/// </summary>
public const string PostQuantumReady = "POST_QUANTUM_READY";
/// <summary>
/// Atom for algorithms that are FIPS-140 compliant.
/// </summary>
public const string FipsCompliant = "FIPS_COMPLIANT";
#endregion
#region Algorithm Classifications
/// <summary>
/// Deprecated/broken algorithms that should never be used for security.
/// </summary>
public static readonly ImmutableHashSet<string> DeprecatedAlgorithms = ImmutableHashSet.Create(
StringComparer.OrdinalIgnoreCase,
"MD2", "MD4", "MD5",
"SHA1", "SHA-1",
"DES", "3DES", "TRIPLE-DES", "TRIPLEDES",
"RC2", "RC4",
"BLOWFISH",
"IDEA");
/// <summary>
/// Algorithms considered weak but not yet deprecated.
/// </summary>
public static readonly ImmutableHashSet<string> WeakAlgorithms = ImmutableHashSet.Create(
StringComparer.OrdinalIgnoreCase,
"SHA-224",
"AES-128-ECB", "AES-192-ECB", "AES-256-ECB");
/// <summary>
/// Algorithms vulnerable to quantum computing attacks (Shor's algorithm).
/// </summary>
public static readonly ImmutableHashSet<string> QuantumVulnerableAlgorithms = ImmutableHashSet.Create(
StringComparer.OrdinalIgnoreCase,
"RSA", "RSA-1024", "RSA-2048", "RSA-3072", "RSA-4096",
"DSA", "DSA-1024", "DSA-2048", "DSA-3072",
"ECDSA", "ECDSA-P256", "ECDSA-P384", "ECDSA-P521",
"ECDH", "ECDHE", "ECDH-P256", "ECDH-P384",
"DH", "DHE", "DH-2048", "DH-3072",
"ED25519", "ED448",
"X25519", "X448");
/// <summary>
/// Post-quantum cryptography algorithms (NIST standards and candidates).
/// </summary>
public static readonly ImmutableHashSet<string> PostQuantumAlgorithms = ImmutableHashSet.Create(
StringComparer.OrdinalIgnoreCase,
// NIST PQC Standards
"ML-KEM", "ML-KEM-512", "ML-KEM-768", "ML-KEM-1024",
"ML-DSA", "ML-DSA-44", "ML-DSA-65", "ML-DSA-87",
"SLH-DSA", "SLH-DSA-128s", "SLH-DSA-128f", "SLH-DSA-192s", "SLH-DSA-192f", "SLH-DSA-256s", "SLH-DSA-256f",
// Legacy names
"KYBER", "KYBER-512", "KYBER-768", "KYBER-1024",
"DILITHIUM", "DILITHIUM-2", "DILITHIUM-3", "DILITHIUM-5",
"FALCON", "FALCON-512", "FALCON-1024",
"SPHINCS+", "SPHINCS+-128s", "SPHINCS+-128f", "SPHINCS+-192s", "SPHINCS+-192f", "SPHINCS+-256s", "SPHINCS+-256f");
/// <summary>
/// FIPS 140-2/140-3 approved algorithms.
/// </summary>
public static readonly ImmutableHashSet<string> FipsApprovedAlgorithms = ImmutableHashSet.Create(
StringComparer.OrdinalIgnoreCase,
// Hash functions
"SHA-224", "SHA-256", "SHA-384", "SHA-512", "SHA-512/224", "SHA-512/256",
"SHA3-224", "SHA3-256", "SHA3-384", "SHA3-512",
"SHAKE128", "SHAKE256",
// Symmetric encryption
"AES-128", "AES-192", "AES-256",
"AES-128-CBC", "AES-192-CBC", "AES-256-CBC",
"AES-128-GCM", "AES-192-GCM", "AES-256-GCM",
"AES-128-CCM", "AES-192-CCM", "AES-256-CCM",
"AES-128-CTR", "AES-192-CTR", "AES-256-CTR",
// Asymmetric
"RSA-2048", "RSA-3072", "RSA-4096",
"ECDSA-P256", "ECDSA-P384", "ECDSA-P521",
"ECDH-P256", "ECDH-P384", "ECDH-P521",
// MACs
"HMAC-SHA-256", "HMAC-SHA-384", "HMAC-SHA-512",
"CMAC-AES-128", "CMAC-AES-192", "CMAC-AES-256");
#endregion
#region Minimum Key Sizes
/// <summary>
/// Minimum acceptable key sizes by algorithm type.
/// </summary>
public static readonly ImmutableDictionary<string, int> MinimumKeySizes = ImmutableDictionary.CreateRange(
StringComparer.OrdinalIgnoreCase,
new Dictionary<string, int>
{
["RSA"] = 2048,
["DSA"] = 2048,
["DH"] = 2048,
["DHE"] = 2048,
["ECDSA"] = 256,
["ECDH"] = 256,
["ECDHE"] = 256,
["ED25519"] = 256,
["AES"] = 128,
["CHACHA20"] = 256,
});
/// <summary>
/// Recommended key sizes for future-proofing.
/// </summary>
public static readonly ImmutableDictionary<string, int> RecommendedKeySizes = ImmutableDictionary.CreateRange(
StringComparer.OrdinalIgnoreCase,
new Dictionary<string, int>
{
["RSA"] = 3072,
["DSA"] = 3072,
["DH"] = 3072,
["DHE"] = 3072,
["ECDSA"] = 384,
["ECDH"] = 384,
["ECDHE"] = 384,
["AES"] = 256,
});
#endregion
}
/// <summary>
/// Result of evaluating a crypto risk atom.
/// </summary>
public sealed record CryptoAtomResult
{
/// <summary>Atom name that was evaluated.</summary>
public required string AtomName { get; init; }
/// <summary>Whether the atom condition is triggered.</summary>
public required bool Triggered { get; init; }
/// <summary>Severity level when triggered.</summary>
public required CryptoSeverity Severity { get; init; }
/// <summary>Human-readable explanation.</summary>
public required string Reason { get; init; }
/// <summary>Algorithms that caused this atom to trigger.</summary>
public ImmutableArray<string> TriggeringAlgorithms { get; init; } = ImmutableArray<string>.Empty;
/// <summary>Recommendation for remediation.</summary>
public string? Recommendation { get; init; }
}
/// <summary>
/// Severity levels for crypto findings.
/// </summary>
public enum CryptoSeverity
{
/// <summary>Informational only.</summary>
Info,
/// <summary>Low severity - should be addressed eventually.</summary>
Low,
/// <summary>Medium severity - should be addressed in near term.</summary>
Medium,
/// <summary>High severity - should be addressed promptly.</summary>
High,
/// <summary>Critical severity - immediate action required.</summary>
Critical
}

View File

@@ -0,0 +1,319 @@
using System.Collections.Immutable;
using StellaOps.Policy.Crypto;
namespace StellaOps.Policy.Engine.Crypto;
/// <summary>
/// Evaluates cryptographic risk based on CBOM analysis results.
/// Produces policy findings for crypto-related issues.
/// </summary>
public sealed class CryptoRiskEvaluator
{
private readonly CryptoRiskOptions _options;
public CryptoRiskEvaluator(CryptoRiskOptions? options = null)
{
_options = options ?? CryptoRiskOptions.Default;
}
/// <summary>
/// Evaluates all crypto risk atoms for a set of crypto assets.
/// </summary>
public ImmutableArray<CryptoAtomResult> Evaluate(ImmutableArray<CryptoAsset> assets)
{
var results = new List<CryptoAtomResult>();
// Evaluate each atom type
results.Add(EvaluateDeprecatedCrypto(assets));
results.Add(EvaluateWeakCrypto(assets));
results.Add(EvaluateQuantumVulnerable(assets));
results.Add(EvaluateInsufficientKeySize(assets));
results.Add(EvaluatePostQuantumReady(assets));
results.Add(EvaluateFipsCompliance(assets));
return results.ToImmutableArray();
}
/// <summary>
/// Evaluates the DEPRECATED_CRYPTO atom.
/// Triggered when cryptographically broken algorithms are detected.
/// </summary>
public CryptoAtomResult EvaluateDeprecatedCrypto(ImmutableArray<CryptoAsset> assets)
{
var deprecated = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var asset in assets)
{
if (string.IsNullOrEmpty(asset.AlgorithmName))
continue;
var algUpper = asset.AlgorithmName.ToUpperInvariant();
if (CryptoRiskAtoms.DeprecatedAlgorithms.Contains(asset.AlgorithmName) ||
CryptoRiskAtoms.DeprecatedAlgorithms.Any(d => algUpper.Contains(d)))
{
deprecated.Add(asset.AlgorithmName);
}
}
return new CryptoAtomResult
{
AtomName = CryptoRiskAtoms.DeprecatedCrypto,
Triggered = deprecated.Count > 0,
Severity = deprecated.Count > 0 ? CryptoSeverity.Critical : CryptoSeverity.Info,
Reason = deprecated.Count > 0
? $"Found {deprecated.Count} deprecated cryptographic algorithm(s): {string.Join(", ", deprecated.OrderBy(x => x))}"
: "No deprecated algorithms detected",
TriggeringAlgorithms = deprecated.OrderBy(x => x).ToImmutableArray(),
Recommendation = deprecated.Count > 0
? "Replace deprecated algorithms with modern alternatives: MD5/SHA-1 → SHA-256+, DES/3DES → AES-256-GCM"
: null
};
}
/// <summary>
/// Evaluates the WEAK_CRYPTO atom.
/// Triggered when algorithms are weak but not yet deprecated.
/// </summary>
public CryptoAtomResult EvaluateWeakCrypto(ImmutableArray<CryptoAsset> assets)
{
var weak = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var asset in assets)
{
if (string.IsNullOrEmpty(asset.AlgorithmName))
continue;
var algUpper = asset.AlgorithmName.ToUpperInvariant();
// Check explicit weak algorithms
if (CryptoRiskAtoms.WeakAlgorithms.Contains(asset.AlgorithmName) ||
CryptoRiskAtoms.WeakAlgorithms.Any(w => algUpper.Contains(w)))
{
weak.Add(asset.AlgorithmName);
}
// Check ECB mode (weak for block ciphers)
if (algUpper.Contains("ECB"))
{
weak.Add(asset.AlgorithmName);
}
}
return new CryptoAtomResult
{
AtomName = CryptoRiskAtoms.WeakCrypto,
Triggered = weak.Count > 0,
Severity = weak.Count > 0 ? CryptoSeverity.High : CryptoSeverity.Info,
Reason = weak.Count > 0
? $"Found {weak.Count} weak cryptographic algorithm(s): {string.Join(", ", weak.OrderBy(x => x))}"
: "No weak algorithms detected",
TriggeringAlgorithms = weak.OrderBy(x => x).ToImmutableArray(),
Recommendation = weak.Count > 0
? "Replace weak algorithms with stronger variants: ECB → GCM/CBC, SHA-224 → SHA-256+"
: null
};
}
/// <summary>
/// Evaluates the QUANTUM_VULNERABLE atom.
/// Triggered when algorithms are vulnerable to quantum computing attacks.
/// </summary>
public CryptoAtomResult EvaluateQuantumVulnerable(ImmutableArray<CryptoAsset> assets)
{
var vulnerable = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var asset in assets)
{
if (string.IsNullOrEmpty(asset.AlgorithmName))
continue;
// Check if algorithm is quantum-vulnerable
if (CryptoRiskAtoms.QuantumVulnerableAlgorithms.Contains(asset.AlgorithmName) ||
CryptoRiskAtoms.QuantumVulnerableAlgorithms.Any(q => asset.AlgorithmName.StartsWith(q, StringComparison.OrdinalIgnoreCase)))
{
vulnerable.Add(asset.AlgorithmName);
}
// RSA, DSA, ECDSA, ECDH, DH are all vulnerable
var algUpper = asset.AlgorithmName.ToUpperInvariant();
if (algUpper.StartsWith("RSA") || algUpper.StartsWith("DSA") ||
algUpper.StartsWith("ECDSA") || algUpper.StartsWith("ECDH") ||
algUpper.StartsWith("DH") || algUpper.StartsWith("DHE"))
{
vulnerable.Add(asset.AlgorithmName);
}
}
// Determine severity based on options
var severity = _options.TreatQuantumVulnerableAs;
if (!_options.EnableQuantumRiskWarnings)
{
severity = CryptoSeverity.Info;
}
return new CryptoAtomResult
{
AtomName = CryptoRiskAtoms.QuantumVulnerable,
Triggered = vulnerable.Count > 0 && _options.EnableQuantumRiskWarnings,
Severity = vulnerable.Count > 0 ? severity : CryptoSeverity.Info,
Reason = vulnerable.Count > 0
? $"Found {vulnerable.Count} quantum-vulnerable algorithm(s): {string.Join(", ", vulnerable.OrderBy(x => x))}"
: "No quantum-vulnerable algorithms detected",
TriggeringAlgorithms = vulnerable.OrderBy(x => x).ToImmutableArray(),
Recommendation = vulnerable.Count > 0
? "Plan migration to post-quantum cryptography: RSA/ECDSA → ML-DSA, ECDH → ML-KEM"
: null
};
}
/// <summary>
/// Evaluates the INSUFFICIENT_KEY_SIZE atom.
/// Triggered when key sizes are below minimum thresholds.
/// </summary>
public CryptoAtomResult EvaluateInsufficientKeySize(ImmutableArray<CryptoAsset> assets)
{
var insufficient = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var asset in assets)
{
if (string.IsNullOrEmpty(asset.AlgorithmName) || !asset.KeySizeBits.HasValue)
continue;
var keySize = asset.KeySizeBits.Value;
// Check minimum key size for algorithm type
foreach (var (algType, minSize) in CryptoRiskAtoms.MinimumKeySizes)
{
if (asset.AlgorithmName.StartsWith(algType, StringComparison.OrdinalIgnoreCase))
{
if (keySize < minSize)
{
insufficient.Add($"{asset.AlgorithmName}-{keySize}");
}
break;
}
}
}
return new CryptoAtomResult
{
AtomName = CryptoRiskAtoms.InsufficientKeySize,
Triggered = insufficient.Count > 0,
Severity = insufficient.Count > 0 ? CryptoSeverity.High : CryptoSeverity.Info,
Reason = insufficient.Count > 0
? $"Found {insufficient.Count} algorithm(s) with insufficient key size: {string.Join(", ", insufficient.OrderBy(x => x))}"
: "All algorithms have sufficient key sizes",
TriggeringAlgorithms = insufficient.OrderBy(x => x).ToImmutableArray(),
Recommendation = insufficient.Count > 0
? "Increase key sizes: RSA ≥2048, ECDSA ≥256, AES ≥128"
: null
};
}
/// <summary>
/// Evaluates the POST_QUANTUM_READY atom.
/// This is a positive indicator - triggered when PQ algorithms are present.
/// </summary>
public CryptoAtomResult EvaluatePostQuantumReady(ImmutableArray<CryptoAsset> assets)
{
var pqReady = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var asset in assets)
{
if (string.IsNullOrEmpty(asset.AlgorithmName))
continue;
if (CryptoRiskAtoms.PostQuantumAlgorithms.Contains(asset.AlgorithmName) ||
CryptoRiskAtoms.PostQuantumAlgorithms.Any(pq => asset.AlgorithmName.StartsWith(pq, StringComparison.OrdinalIgnoreCase)))
{
pqReady.Add(asset.AlgorithmName);
}
}
return new CryptoAtomResult
{
AtomName = CryptoRiskAtoms.PostQuantumReady,
Triggered = pqReady.Count > 0,
Severity = CryptoSeverity.Info, // Positive indicator
Reason = pqReady.Count > 0
? $"Found {pqReady.Count} post-quantum algorithm(s): {string.Join(", ", pqReady.OrderBy(x => x))}"
: "No post-quantum algorithms detected",
TriggeringAlgorithms = pqReady.OrderBy(x => x).ToImmutableArray(),
Recommendation = pqReady.Count == 0
? "Consider adopting ML-KEM/ML-DSA for quantum-resistant cryptography"
: null
};
}
/// <summary>
/// Evaluates the FIPS_COMPLIANT atom.
/// Checks if all algorithms are FIPS 140-2/140-3 approved.
/// </summary>
public CryptoAtomResult EvaluateFipsCompliance(ImmutableArray<CryptoAsset> assets)
{
var nonFips = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
int fipsCount = 0;
foreach (var asset in assets)
{
if (string.IsNullOrEmpty(asset.AlgorithmName))
continue;
if (CryptoRiskAtoms.FipsApprovedAlgorithms.Contains(asset.AlgorithmName) ||
CryptoRiskAtoms.FipsApprovedAlgorithms.Any(f => asset.AlgorithmName.StartsWith(f, StringComparison.OrdinalIgnoreCase)))
{
fipsCount++;
}
else
{
nonFips.Add(asset.AlgorithmName);
}
}
var isCompliant = nonFips.Count == 0 && fipsCount > 0;
return new CryptoAtomResult
{
AtomName = CryptoRiskAtoms.FipsCompliant,
Triggered = !isCompliant && _options.RequireFipsCompliance,
Severity = !isCompliant && _options.RequireFipsCompliance ? CryptoSeverity.High : CryptoSeverity.Info,
Reason = isCompliant
? $"All {fipsCount} algorithm(s) are FIPS compliant"
: nonFips.Count > 0
? $"Found {nonFips.Count} non-FIPS algorithm(s): {string.Join(", ", nonFips.OrderBy(x => x))}"
: "No cryptographic algorithms detected",
TriggeringAlgorithms = nonFips.OrderBy(x => x).ToImmutableArray(),
Recommendation = !isCompliant && _options.RequireFipsCompliance
? "Replace non-FIPS algorithms with FIPS 140-2/140-3 approved alternatives"
: null
};
}
}
/// <summary>
/// Options for crypto risk evaluation.
/// </summary>
public sealed record CryptoRiskOptions
{
/// <summary>Default options.</summary>
public static readonly CryptoRiskOptions Default = new();
/// <summary>Whether to enable quantum vulnerability warnings.</summary>
public bool EnableQuantumRiskWarnings { get; init; } = true;
/// <summary>Severity level for quantum-vulnerable algorithms.</summary>
public CryptoSeverity TreatQuantumVulnerableAs { get; init; } = CryptoSeverity.Medium;
/// <summary>Whether FIPS compliance is required.</summary>
public bool RequireFipsCompliance { get; init; } = false;
/// <summary>Whether to block on deprecated algorithms.</summary>
public bool BlockOnDeprecated { get; init; } = true;
/// <summary>Whether to block on weak algorithms.</summary>
public bool BlockOnWeak { get; init; } = false;
/// <summary>Whether to block on insufficient key sizes.</summary>
public bool BlockOnInsufficientKeySize { get; init; } = true;
}