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,358 @@
using System.Collections.Immutable;
using StellaOps.Scanner.Core.Contracts;
using StellaOps.Scanner.Emit.Cbom;
namespace StellaOps.Scanner.Analyzers.Lang.DotNet.Internal.Crypto;
/// <summary>
/// Extracts cryptographic assets from .NET assemblies and NuGet packages.
/// Analyzes System.Security.Cryptography usage patterns.
/// </summary>
public sealed class DotNetCryptoExtractor : ICryptoAssetExtractor
{
private static readonly ImmutableArray<string> Ecosystems = ImmutableArray.Create("nuget", "dotnet");
public ImmutableArray<string> SupportedEcosystems => Ecosystems;
/// <summary>
/// Known crypto-related NuGet packages.
/// </summary>
private static readonly ImmutableHashSet<string> CryptoPackages = ImmutableHashSet.Create(
StringComparer.OrdinalIgnoreCase,
"System.Security.Cryptography.Algorithms",
"System.Security.Cryptography.Cng",
"System.Security.Cryptography.Csp",
"System.Security.Cryptography.OpenSsl",
"System.Security.Cryptography.Pkcs",
"System.Security.Cryptography.ProtectedData",
"System.Security.Cryptography.X509Certificates",
"System.Security.Cryptography.Xml",
"BouncyCastle.Cryptography",
"BouncyCastle.NetCore",
"Portable.BouncyCastle",
"libsodium",
"NSec.Cryptography",
"Microsoft.IdentityModel.Tokens",
"System.IdentityModel.Tokens.Jwt",
"Jose-jwt",
"jose-jwt",
"BCrypt.Net-Next",
"Scrypt.NET",
"Argon2.NetCore",
"Konscious.Security.Cryptography.Argon2",
"CryptoNet",
"NaCl.Core"
);
/// <summary>
/// Algorithm patterns to detect in package names and identifiers.
/// </summary>
private static readonly ImmutableArray<CryptoAlgorithmPattern> AlgorithmPatterns = ImmutableArray.Create(
// Hash algorithms
new CryptoAlgorithmPattern("MD5", "1.2.840.113549.2.5", CryptoPrimitive.Hash, CryptoFunction.Digest, 128, IsDeprecated: true),
new CryptoAlgorithmPattern("SHA1", "1.3.14.3.2.26", CryptoPrimitive.Hash, CryptoFunction.Digest, 160, IsDeprecated: true),
new CryptoAlgorithmPattern("SHA-1", "1.3.14.3.2.26", CryptoPrimitive.Hash, CryptoFunction.Digest, 160, IsDeprecated: true),
new CryptoAlgorithmPattern("SHA256", "2.16.840.1.101.3.4.2.1", CryptoPrimitive.Hash, CryptoFunction.Digest, 256),
new CryptoAlgorithmPattern("SHA-256", "2.16.840.1.101.3.4.2.1", CryptoPrimitive.Hash, CryptoFunction.Digest, 256),
new CryptoAlgorithmPattern("SHA384", "2.16.840.1.101.3.4.2.2", CryptoPrimitive.Hash, CryptoFunction.Digest, 384),
new CryptoAlgorithmPattern("SHA512", "2.16.840.1.101.3.4.2.3", CryptoPrimitive.Hash, CryptoFunction.Digest, 512),
new CryptoAlgorithmPattern("SHA3-256", "2.16.840.1.101.3.4.2.8", CryptoPrimitive.Hash, CryptoFunction.Digest, 256),
new CryptoAlgorithmPattern("SHA3-512", "2.16.840.1.101.3.4.2.10", CryptoPrimitive.Hash, CryptoFunction.Digest, 512),
// Symmetric ciphers
new CryptoAlgorithmPattern("AES", "2.16.840.1.101.3.4.1", CryptoPrimitive.BlockCipher, CryptoFunction.Encrypt, 256),
new CryptoAlgorithmPattern("AES-128", "2.16.840.1.101.3.4.1.1", CryptoPrimitive.BlockCipher, CryptoFunction.Encrypt, 128),
new CryptoAlgorithmPattern("AES-192", "2.16.840.1.101.3.4.1.21", CryptoPrimitive.BlockCipher, CryptoFunction.Encrypt, 192),
new CryptoAlgorithmPattern("AES-256", "2.16.840.1.101.3.4.1.41", CryptoPrimitive.BlockCipher, CryptoFunction.Encrypt, 256),
new CryptoAlgorithmPattern("AES-GCM", "2.16.840.1.101.3.4.1.46", CryptoPrimitive.Aead, CryptoFunction.Encrypt, 256),
new CryptoAlgorithmPattern("DES", "1.3.14.3.2.7", CryptoPrimitive.BlockCipher, CryptoFunction.Encrypt, 56, IsDeprecated: true),
new CryptoAlgorithmPattern("3DES", "1.2.840.113549.3.7", CryptoPrimitive.BlockCipher, CryptoFunction.Encrypt, 168, IsWeak: true),
new CryptoAlgorithmPattern("TripleDES", "1.2.840.113549.3.7", CryptoPrimitive.BlockCipher, CryptoFunction.Encrypt, 168, IsWeak: true),
new CryptoAlgorithmPattern("ChaCha20", null, CryptoPrimitive.StreamCipher, CryptoFunction.Encrypt, 256),
new CryptoAlgorithmPattern("ChaCha20Poly1305", null, CryptoPrimitive.Aead, CryptoFunction.Encrypt, 256),
new CryptoAlgorithmPattern("RC4", null, CryptoPrimitive.StreamCipher, CryptoFunction.Encrypt, 128, IsDeprecated: true),
new CryptoAlgorithmPattern("Blowfish", null, CryptoPrimitive.BlockCipher, CryptoFunction.Encrypt, 128, IsWeak: true),
// Asymmetric algorithms
new CryptoAlgorithmPattern("RSA", "1.2.840.113549.1.1.1", CryptoPrimitive.Rsa, CryptoFunction.Sign, 2048, IsQuantumVulnerable: true),
new CryptoAlgorithmPattern("DSA", "1.2.840.10040.4.1", CryptoPrimitive.Dlog, CryptoFunction.Sign, 2048, IsQuantumVulnerable: true),
new CryptoAlgorithmPattern("ECDSA", "1.2.840.10045.4.3", CryptoPrimitive.Ec, CryptoFunction.Sign, 256, IsQuantumVulnerable: true),
new CryptoAlgorithmPattern("ECDH", "1.3.132.1.12", CryptoPrimitive.Ec, CryptoFunction.KeyAgree, 256, IsQuantumVulnerable: true),
new CryptoAlgorithmPattern("DiffieHellman", null, CryptoPrimitive.Dlog, CryptoFunction.KeyAgree, 2048, IsQuantumVulnerable: true),
// Key derivation
new CryptoAlgorithmPattern("PBKDF2", "1.2.840.113549.1.5.12", CryptoPrimitive.Pbkdf, CryptoFunction.Derive, 256),
new CryptoAlgorithmPattern("Rfc2898", "1.2.840.113549.1.5.12", CryptoPrimitive.Pbkdf, CryptoFunction.Derive, 256),
new CryptoAlgorithmPattern("HKDF", null, CryptoPrimitive.Kdf, CryptoFunction.Derive, 256),
new CryptoAlgorithmPattern("BCrypt", null, CryptoPrimitive.Pbkdf, CryptoFunction.Derive, 184),
new CryptoAlgorithmPattern("SCrypt", null, CryptoPrimitive.Pbkdf, CryptoFunction.Derive, 256),
new CryptoAlgorithmPattern("Argon2", null, CryptoPrimitive.Pbkdf, CryptoFunction.Derive, 256),
// MACs
new CryptoAlgorithmPattern("HMAC", null, CryptoPrimitive.Mac, CryptoFunction.Tag, 256),
new CryptoAlgorithmPattern("HMACSHA256", "1.2.840.113549.2.9", CryptoPrimitive.Mac, CryptoFunction.Tag, 256),
new CryptoAlgorithmPattern("HMACSHA512", "1.2.840.113549.2.11", CryptoPrimitive.Mac, CryptoFunction.Tag, 512),
new CryptoAlgorithmPattern("HMACMD5", "1.3.6.1.5.5.8.1.1", CryptoPrimitive.Mac, CryptoFunction.Tag, 128, IsDeprecated: true),
// Post-quantum (emerging in .NET)
new CryptoAlgorithmPattern("ML-KEM", null, CryptoPrimitive.Kem, CryptoFunction.Encapsulate, 256, IsPostQuantum: true),
new CryptoAlgorithmPattern("ML-DSA", null, CryptoPrimitive.Lattice, CryptoFunction.Sign, 256, IsPostQuantum: true),
new CryptoAlgorithmPattern("SLH-DSA", null, CryptoPrimitive.Hash, CryptoFunction.Sign, 256, IsPostQuantum: true)
);
public Task<ImmutableArray<CryptoAsset>> ExtractAsync(
AggregatedComponent component,
CryptoAnalysisContext analysisContext,
CancellationToken cancellationToken = default)
{
var assets = new List<CryptoAsset>();
// Check if component is a known crypto package
var packageName = component.Identity.Name ?? string.Empty;
var purl = component.Identity.Purl ?? string.Empty;
// Skip if not a .NET package
if (!purl.StartsWith("pkg:nuget/", StringComparison.OrdinalIgnoreCase))
{
return Task.FromResult(ImmutableArray<CryptoAsset>.Empty);
}
// Check for known crypto packages
if (CryptoPackages.Contains(packageName))
{
var cryptoAssets = ExtractFromKnownPackage(component, packageName);
assets.AddRange(cryptoAssets);
}
// Check package name for algorithm patterns
foreach (var pattern in AlgorithmPatterns)
{
if (packageName.Contains(pattern.Name, StringComparison.OrdinalIgnoreCase))
{
var asset = CreateAssetFromPattern(component, pattern);
if (!assets.Any(a => a.AlgorithmName == asset.AlgorithmName))
{
assets.Add(asset);
}
}
}
// Check metadata for crypto evidence
if (component.Metadata?.Properties != null)
{
foreach (var (key, value) in component.Metadata.Properties)
{
foreach (var pattern in AlgorithmPatterns)
{
if (value.Contains(pattern.Name, StringComparison.OrdinalIgnoreCase))
{
var asset = CreateAssetFromPattern(component, pattern, $"property:{key}");
if (!assets.Any(a => a.AlgorithmName == asset.AlgorithmName))
{
assets.Add(asset);
}
}
}
}
}
return Task.FromResult(assets.ToImmutableArray());
}
private static IEnumerable<CryptoAsset> ExtractFromKnownPackage(AggregatedComponent component, string packageName)
{
var assets = new List<CryptoAsset>();
// System.Security.Cryptography packages include multiple algorithms
if (packageName.StartsWith("System.Security.Cryptography", StringComparison.OrdinalIgnoreCase))
{
// Add common algorithms from this namespace
var commonAlgorithms = new[]
{
AlgorithmPatterns.First(p => p.Name == "AES"),
AlgorithmPatterns.First(p => p.Name == "SHA256"),
AlgorithmPatterns.First(p => p.Name == "RSA"),
AlgorithmPatterns.First(p => p.Name == "ECDSA"),
AlgorithmPatterns.First(p => p.Name == "HMACSHA256")
};
foreach (var pattern in commonAlgorithms)
{
assets.Add(CreateAssetFromPattern(component, pattern, $"package:{packageName}"));
}
}
else if (packageName.Contains("BouncyCastle", StringComparison.OrdinalIgnoreCase))
{
// BouncyCastle provides many algorithms
var bcAlgorithms = AlgorithmPatterns.Where(p =>
p.Name == "AES" || p.Name == "RSA" || p.Name == "ECDSA" ||
p.Name == "SHA256" || p.Name == "SHA512" || p.Name == "ChaCha20Poly1305");
foreach (var pattern in bcAlgorithms)
{
assets.Add(CreateAssetFromPattern(component, pattern, $"package:{packageName}",
implementationPlatform: "BouncyCastle"));
}
}
else if (packageName.Contains("libsodium", StringComparison.OrdinalIgnoreCase) ||
packageName.Contains("NSec", StringComparison.OrdinalIgnoreCase))
{
// Modern crypto libraries
var modernAlgorithms = new[]
{
AlgorithmPatterns.First(p => p.Name == "ChaCha20Poly1305"),
AlgorithmPatterns.First(p => p.Name == "AES-GCM"),
AlgorithmPatterns.First(p => p.Name == "SHA512")
};
foreach (var pattern in modernAlgorithms)
{
assets.Add(CreateAssetFromPattern(component, pattern, $"package:{packageName}",
implementationPlatform: "libsodium"));
}
}
else if (packageName.Contains("BCrypt", StringComparison.OrdinalIgnoreCase))
{
assets.Add(CreateAssetFromPattern(component,
AlgorithmPatterns.First(p => p.Name == "BCrypt"), $"package:{packageName}"));
}
else if (packageName.Contains("Argon2", StringComparison.OrdinalIgnoreCase))
{
assets.Add(CreateAssetFromPattern(component,
AlgorithmPatterns.First(p => p.Name == "Argon2"), $"package:{packageName}"));
}
else if (packageName.Contains("Jwt", StringComparison.OrdinalIgnoreCase) ||
packageName.Contains("Jose", StringComparison.OrdinalIgnoreCase))
{
// JWT libraries use various algorithms
var jwtAlgorithms = new[]
{
AlgorithmPatterns.First(p => p.Name == "RSA"),
AlgorithmPatterns.First(p => p.Name == "ECDSA"),
AlgorithmPatterns.First(p => p.Name == "HMACSHA256"),
AlgorithmPatterns.First(p => p.Name == "AES")
};
foreach (var pattern in jwtAlgorithms)
{
assets.Add(CreateAssetFromPattern(component, pattern, $"package:{packageName}"));
}
}
return assets;
}
private static CryptoAsset CreateAssetFromPattern(
AggregatedComponent component,
CryptoAlgorithmPattern pattern,
string? evidenceSource = null,
string? implementationPlatform = null)
{
var riskFlags = new List<CryptoRiskFlag>();
if (pattern.IsDeprecated)
{
riskFlags.Add(new CryptoRiskFlag
{
RiskId = "DEPRECATED_ALGORITHM",
Severity = CryptoRiskSeverity.Critical,
Description = $"{pattern.Name} is deprecated and should not be used",
Recommendation = GetDeprecatedRecommendation(pattern.Name)
});
}
if (pattern.IsWeak)
{
riskFlags.Add(new CryptoRiskFlag
{
RiskId = "WEAK_ALGORITHM",
Severity = CryptoRiskSeverity.High,
Description = $"{pattern.Name} is considered weak by modern standards",
Recommendation = GetWeakRecommendation(pattern.Name)
});
}
if (pattern.IsQuantumVulnerable)
{
riskFlags.Add(new CryptoRiskFlag
{
RiskId = "QUANTUM_VULNERABLE",
Severity = CryptoRiskSeverity.Medium,
Description = $"{pattern.Name} is vulnerable to quantum computing attacks",
Recommendation = "Consider migration path to post-quantum algorithms (ML-KEM, ML-DSA)"
});
}
var evidence = new List<string> { $"component:{component.Identity.Key}" };
if (evidenceSource != null)
{
evidence.Add(evidenceSource);
}
var algorithmProperties = new AlgorithmProperties
{
Primitive = pattern.Primitive,
CryptoFunctions = ImmutableArray.Create(pattern.Function),
ClassicalSecurityLevel = pattern.KeySize,
ImplementationPlatform = implementationPlatform ?? "System.Security.Cryptography",
ExecutionEnvironment = ExecutionEnvironment.Software
};
return new CryptoAsset
{
Id = $"crypto:{component.Identity.Key}:{pattern.Name}",
ComponentKey = component.Identity.Key,
AssetType = CryptoAssetType.Algorithm,
AlgorithmName = pattern.Name,
Oid = pattern.Oid,
KeySizeBits = pattern.KeySize,
Primitive = pattern.Primitive,
Functions = ImmutableArray.Create(pattern.Function),
ImplementationPlatform = implementationPlatform ?? "System.Security.Cryptography",
ExecutionEnvironment = ExecutionEnvironment.Software,
Confidence = 0.9,
Evidence = evidence.ToImmutableArray(),
RiskFlags = riskFlags.ToImmutableArray(),
CryptoProperties = new CryptoProperties
{
AssetType = CryptoAssetType.Algorithm,
AlgorithmProperties = algorithmProperties,
Oid = pattern.Oid
}
};
}
private static string GetDeprecatedRecommendation(string algorithm)
{
return algorithm.ToUpperInvariant() switch
{
"MD5" => "Replace with SHA-256 or SHA-3-256",
"SHA1" or "SHA-1" => "Replace with SHA-256 or SHA-3-256",
"DES" => "Replace with AES-256-GCM",
"RC4" => "Replace with ChaCha20-Poly1305 or AES-GCM",
"HMACMD5" => "Replace with HMAC-SHA256",
_ => "Replace with a modern algorithm"
};
}
private static string GetWeakRecommendation(string algorithm)
{
return algorithm.ToUpperInvariant() switch
{
"3DES" or "TRIPLEDES" => "Replace with AES-256-GCM",
"BLOWFISH" => "Replace with AES-256-GCM or ChaCha20-Poly1305",
_ => "Consider using a stronger algorithm"
};
}
private sealed record CryptoAlgorithmPattern(
string Name,
string? Oid,
CryptoPrimitive Primitive,
CryptoFunction Function,
int KeySize,
bool IsDeprecated = false,
bool IsWeak = false,
bool IsQuantumVulnerable = false,
bool IsPostQuantum = false);
}

View File

@@ -20,5 +20,6 @@
<ItemGroup>
<ProjectReference Include="..\StellaOps.Scanner.Analyzers.Lang\StellaOps.Scanner.Analyzers.Lang.csproj" />
<ProjectReference Include="..\StellaOps.Scanner.Emit\StellaOps.Scanner.Emit.csproj" />
</ItemGroup>
</Project>