tests fixes and sprints work

This commit is contained in:
master
2026-01-22 19:08:46 +02:00
parent c32fff8f86
commit 726d70dc7f
881 changed files with 134434 additions and 6228 deletions

View File

@@ -0,0 +1,67 @@
using System.Collections.Immutable;
using StellaOps.Concelier.SbomIntegration.Models;
using StellaOps.Scanner.CryptoAnalysis.Analyzers;
using StellaOps.Scanner.CryptoAnalysis.Models;
using StellaOps.Scanner.CryptoAnalysis.Policy;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Scanner.CryptoAnalysis.Tests;
public sealed class AlgorithmStrengthAnalyzerTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AnalyzeAsync_FlagsWeakAlgorithmAndShortKeyLength()
{
var components = new[]
{
BuildAlgorithmComponent("comp-md5", "MD5", keySize: null, functions: ["hash"]),
BuildAlgorithmComponent("comp-rsa", "RSA", keySize: 1024, functions: ["encryption"])
};
var policy = CryptoPolicyDefaults.Default with
{
MinimumKeyLengths = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
{
["RSA"] = 2048
}.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase),
RequiredFeatures = new CryptoRequiredFeatures
{
AuthenticatedEncryption = true,
PerfectForwardSecrecy = false
}
};
var context = CryptoAnalysisContext.Create(components, policy, TimeProvider.System);
var analyzer = new AlgorithmStrengthAnalyzer();
var result = await analyzer.AnalyzeAsync(context);
Assert.Contains(result.Findings, f => f.Type == CryptoFindingType.WeakAlgorithm);
Assert.Contains(result.Findings, f => f.Type == CryptoFindingType.ShortKeyLength);
Assert.Contains(result.Findings, f => f.Type == CryptoFindingType.MissingIntegrity);
}
private static ParsedComponent BuildAlgorithmComponent(
string bomRef,
string name,
int? keySize,
ImmutableArray<string> functions)
{
return new ParsedComponent
{
BomRef = bomRef,
Name = name,
Type = "library",
CryptoProperties = new ParsedCryptoProperties
{
AssetType = CryptoAssetType.Algorithm,
AlgorithmProperties = new ParsedAlgorithmProperties
{
Primitive = CryptoPrimitive.Asymmetric,
KeySize = keySize,
CryptoFunctions = functions
}
}
};
}
}

View File

@@ -0,0 +1,46 @@
using StellaOps.Concelier.SbomIntegration.Models;
using StellaOps.Scanner.CryptoAnalysis.Analyzers;
using StellaOps.Scanner.CryptoAnalysis.Models;
using StellaOps.Scanner.CryptoAnalysis.Policy;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Scanner.CryptoAnalysis.Tests;
public sealed class CertificateAnalyzerTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AnalyzeAsync_FlagsExpiredAndWeakSignature()
{
var expiredAt = DateTimeOffset.UtcNow.AddDays(-1);
var components = new[]
{
new ParsedComponent
{
BomRef = "cert-1",
Name = "signing-cert",
Type = "file",
CryptoProperties = new ParsedCryptoProperties
{
AssetType = CryptoAssetType.Certificate,
CertificateProperties = new ParsedCertificateProperties
{
SubjectName = "CN=example",
IssuerName = "CN=issuer",
NotValidAfter = expiredAt,
SignatureAlgorithmRef = "SHA1"
}
}
}
};
var policy = CryptoPolicyDefaults.Default;
var context = CryptoAnalysisContext.Create(components, policy, TimeProvider.System);
var analyzer = new CertificateAnalyzer();
var result = await analyzer.AnalyzeAsync(context);
Assert.Contains(result.Findings, f => f.Type == CryptoFindingType.ExpiredCertificate);
Assert.Contains(result.Findings, f => f.Type == CryptoFindingType.WeakAlgorithm);
}
}

View File

@@ -0,0 +1,58 @@
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Concelier.SbomIntegration.Models;
using StellaOps.Concelier.SbomIntegration.Parsing;
using StellaOps.Scanner.CryptoAnalysis;
using StellaOps.Scanner.CryptoAnalysis.Analyzers;
using StellaOps.Scanner.CryptoAnalysis.Models;
using StellaOps.Scanner.CryptoAnalysis.Policy;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Scanner.CryptoAnalysis.Tests;
public sealed class CryptoAnalysisIntegrationTests
{
[Trait("Category", TestCategories.Integration)]
[Fact]
public async Task AnalyzeAsync_ParsesCbomFixture()
{
var fixturePath = Path.Combine(AppContext.BaseDirectory, "Fixtures", "sample-cbom.cdx.json");
Assert.True(File.Exists(fixturePath));
var parser = new ParsedSbomParser(NullLogger<ParsedSbomParser>.Instance);
await using var stream = File.OpenRead(fixturePath);
var parsed = await parser.ParseAsync(stream, SbomFormat.CycloneDX);
var checks = new ICryptoCheck[]
{
new CryptoInventoryGenerator(),
new AlgorithmStrengthAnalyzer(),
new FipsComplianceChecker(),
new RegionalComplianceChecker(),
new PostQuantumAnalyzer(),
new CertificateAnalyzer(),
new ProtocolAnalyzer()
};
var analyzer = new CryptoAnalysisAnalyzer(checks, TimeProvider.System);
var policy = CryptoPolicyDefaults.Default with
{
ComplianceFramework = "FIPS-140-3",
PostQuantum = new PostQuantumPolicy { Enabled = true }
};
var componentsWithCrypto = parsed.Components
.Where(component => component.CryptoProperties is not null)
.ToArray();
var report = await analyzer.AnalyzeAsync(componentsWithCrypto, policy);
Assert.Equal(2, report.Inventory.Algorithms.Length);
Assert.Equal(1, report.Inventory.Certificates.Length);
Assert.Equal(1, report.Inventory.Protocols.Length);
Assert.Contains(report.Findings, f => f.Type == CryptoFindingType.ShortKeyLength);
Assert.Contains(report.Findings, f => f.Type == CryptoFindingType.ExpiredCertificate);
Assert.Contains(report.Findings, f => f.Type == CryptoFindingType.DeprecatedProtocol);
Assert.True(report.QuantumReadiness.TotalAlgorithms > 0);
Assert.Contains(report.ComplianceStatus.Frameworks, f => f.Framework.Contains("FIPS", StringComparison.OrdinalIgnoreCase));
}
}

View File

@@ -0,0 +1,40 @@
using System.Text;
using StellaOps.Scanner.CryptoAnalysis.Models;
using StellaOps.Scanner.CryptoAnalysis.Reporting;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Scanner.CryptoAnalysis.Tests;
public sealed class CryptoInventoryExporterTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Export_CsvAndXlsxEmitExpectedHeaders()
{
var inventory = new CryptoInventory
{
Algorithms =
[
new CryptoAlgorithmUsage
{
ComponentBomRef = "comp-1",
ComponentName = "RSA",
Algorithm = "RSA",
AlgorithmIdentifier = "1.2.840.113549.1.1.1",
KeySize = 2048
}
]
};
var csvBytes = CryptoInventoryExporter.Export(inventory, CryptoInventoryFormat.Csv);
var csv = Encoding.UTF8.GetString(csvBytes);
Assert.Contains("assetType", csv, StringComparison.OrdinalIgnoreCase);
Assert.Contains("algorithm", csv, StringComparison.OrdinalIgnoreCase);
var xlsxBytes = CryptoInventoryExporter.Export(inventory, CryptoInventoryFormat.Xlsx);
Assert.True(xlsxBytes.Length > 4);
Assert.Equal('P', (char)xlsxBytes[0]);
Assert.Equal('K', (char)xlsxBytes[1]);
}
}

View File

@@ -0,0 +1,77 @@
using StellaOps.Scanner.CryptoAnalysis.Policy;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Scanner.CryptoAnalysis.Tests;
public sealed class CryptoPolicyLoaderTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task LoadAsync_ReturnsDefaultWhenMissing()
{
var loader = new CryptoPolicyLoader();
var policy = await loader.LoadAsync(path: null);
Assert.True(policy.MinimumKeyLengths.ContainsKey("RSA"));
Assert.Contains("MD5", policy.ProhibitedAlgorithms);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task LoadAsync_LoadsYamlPolicy()
{
var yaml = """
cryptoPolicy:
complianceFramework: FIPS-140-3
minimumKeyLengths:
RSA: 4096
prohibitedAlgorithms: [MD5, SHA1]
requiredFeatures:
perfectForwardSecrecy: true
authenticatedEncryption: true
postQuantum:
enabled: true
requireHybridForLongLived: true
longLivedDataThresholdYears: 5
certificates:
expirationWarningDays: 30
minimumSignatureAlgorithm: SHA384
regionalRequirements:
eidas: true
gost: true
sm: false
exemptions:
- componentPattern: "legacy-*"
algorithms: [3DES]
expirationDate: "2027-01-01"
version: "policy-1"
""";
var loader = new CryptoPolicyLoader();
var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid():N}.yaml");
try
{
await File.WriteAllTextAsync(path, yaml);
var policy = await loader.LoadAsync(path);
Assert.Equal("FIPS-140-3", policy.ComplianceFramework);
Assert.Equal(4096, policy.MinimumKeyLengths["RSA"]);
Assert.Contains("MD5", policy.ProhibitedAlgorithms);
Assert.True(policy.RequiredFeatures.PerfectForwardSecrecy);
Assert.True(policy.PostQuantum.Enabled);
Assert.True(policy.RegionalRequirements.Eidas);
Assert.True(policy.RegionalRequirements.Gost);
Assert.False(policy.RegionalRequirements.Sm);
Assert.Single(policy.Exemptions);
Assert.Equal("policy-1", policy.Version);
}
finally
{
if (File.Exists(path))
{
File.Delete(path);
}
}
}
}

View File

@@ -0,0 +1,28 @@
using System.Text;
using StellaOps.Scanner.CryptoAnalysis.Models;
using StellaOps.Scanner.CryptoAnalysis.Reporting;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Scanner.CryptoAnalysis.Tests;
public sealed class CryptoReportFormatterTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ToPdfBytes_EmitsPdfHeader()
{
var report = new CryptoAnalysisReport
{
Inventory = CryptoInventory.Empty,
Findings = [],
ComplianceStatus = CryptoComplianceStatus.Empty,
QuantumReadiness = PostQuantumReadiness.Empty,
Summary = CryptoSummary.Empty
};
var pdfBytes = CryptoAnalysisReportFormatter.ToPdfBytes(report);
var header = Encoding.ASCII.GetString(pdfBytes[..5]);
Assert.Equal("%PDF-", header);
}
}

View File

@@ -0,0 +1,77 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.7",
"serialNumber": "urn:uuid:11111111-1111-1111-1111-111111111111",
"version": 1,
"metadata": {
"component": {
"bom-ref": "root",
"name": "crypto-sample",
"version": "1.0.0"
}
},
"components": [
{
"bom-ref": "crypto-alg-rsa",
"type": "library",
"name": "RSA",
"version": "1.0",
"cryptoProperties": {
"assetType": "algorithm",
"oid": "1.2.840.113549.1.1.1",
"algorithmProperties": {
"primitive": "asymmetric",
"cryptoFunctions": ["encryption"],
"keySize": 1024,
"mode": "cbc",
"padding": "pkcs1"
}
}
},
{
"bom-ref": "crypto-alg-kyber",
"type": "library",
"name": "Kyber",
"version": "1.0",
"cryptoProperties": {
"assetType": "algorithm",
"algorithmProperties": {
"primitive": "asymmetric",
"cryptoFunctions": ["key-encapsulation"],
"keySize": 256
}
}
},
{
"bom-ref": "crypto-cert",
"type": "file",
"name": "signing-cert",
"version": "2024",
"cryptoProperties": {
"assetType": "certificate",
"certificateProperties": {
"subjectName": "CN=example",
"issuerName": "CN=issuer",
"notValidBefore": "2023-01-01T00:00:00Z",
"notValidAfter": "2024-01-01T00:00:00Z",
"signatureAlgorithmRef": "SHA1",
"certificateFormat": "x.509"
}
}
},
{
"bom-ref": "crypto-proto",
"type": "application",
"name": "tls-stack",
"version": "1.0",
"cryptoProperties": {
"assetType": "protocol",
"protocolProperties": {
"type": "TLS",
"version": "1.0",
"cipherSuites": ["TLS_RSA_WITH_RC4_128_SHA"]
}
}
}
]
}

View File

@@ -0,0 +1,64 @@
using StellaOps.Concelier.SbomIntegration.Models;
using StellaOps.Scanner.CryptoAnalysis.Analyzers;
using StellaOps.Scanner.CryptoAnalysis.Models;
using StellaOps.Scanner.CryptoAnalysis.Policy;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Scanner.CryptoAnalysis.Tests;
public sealed class PostQuantumAnalyzerTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AnalyzeAsync_FlagsQuantumVulnerableAlgorithms()
{
var components = new[]
{
new ParsedComponent
{
BomRef = "alg-rsa",
Name = "RSA",
Type = "library",
CryptoProperties = new ParsedCryptoProperties
{
AssetType = CryptoAssetType.Algorithm,
AlgorithmProperties = new ParsedAlgorithmProperties
{
Primitive = CryptoPrimitive.Asymmetric
}
}
},
new ParsedComponent
{
BomRef = "alg-kyber",
Name = "Kyber",
Type = "library",
CryptoProperties = new ParsedCryptoProperties
{
AssetType = CryptoAssetType.Algorithm,
AlgorithmProperties = new ParsedAlgorithmProperties
{
Primitive = CryptoPrimitive.Asymmetric
}
}
}
};
var policy = CryptoPolicyDefaults.Default with
{
PostQuantum = new PostQuantumPolicy
{
Enabled = true
}
};
var context = CryptoAnalysisContext.Create(components, policy, TimeProvider.System);
var analyzer = new PostQuantumAnalyzer();
var result = await analyzer.AnalyzeAsync(context);
Assert.Contains(result.Findings, f => f.Type == CryptoFindingType.QuantumVulnerable);
Assert.NotNull(result.QuantumReadiness);
Assert.True(result.QuantumReadiness!.Score >= 0);
}
}

View File

@@ -0,0 +1,51 @@
using StellaOps.Concelier.SbomIntegration.Models;
using StellaOps.Scanner.CryptoAnalysis.Analyzers;
using StellaOps.Scanner.CryptoAnalysis.Models;
using StellaOps.Scanner.CryptoAnalysis.Policy;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Scanner.CryptoAnalysis.Tests;
public sealed class ProtocolAnalyzerTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AnalyzeAsync_FlagsDeprecatedProtocolAndWeakCipherSuite()
{
var components = new[]
{
new ParsedComponent
{
BomRef = "proto-1",
Name = "tls-stack",
Type = "application",
CryptoProperties = new ParsedCryptoProperties
{
AssetType = CryptoAssetType.Protocol,
ProtocolProperties = new ParsedProtocolProperties
{
Type = "TLS",
Version = "1.0",
CipherSuites = ["TLS_RSA_WITH_RC4_128_SHA"]
}
}
}
};
var policy = CryptoPolicyDefaults.Default with
{
RequiredFeatures = new CryptoRequiredFeatures
{
PerfectForwardSecrecy = true
}
};
var context = CryptoAnalysisContext.Create(components, policy, TimeProvider.System);
var analyzer = new ProtocolAnalyzer();
var result = await analyzer.AnalyzeAsync(context);
Assert.Contains(result.Findings, f => f.Type == CryptoFindingType.DeprecatedProtocol);
Assert.Contains(result.Findings, f => f.Type == CryptoFindingType.WeakCipherSuite);
}
}

View File

@@ -0,0 +1,49 @@
using StellaOps.Concelier.SbomIntegration.Models;
using StellaOps.Scanner.CryptoAnalysis.Analyzers;
using StellaOps.Scanner.CryptoAnalysis.Models;
using StellaOps.Scanner.CryptoAnalysis.Policy;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Scanner.CryptoAnalysis.Tests;
public sealed class RegionalComplianceCheckerTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AnalyzeAsync_FlagsRegionalComplianceGap()
{
var components = new[]
{
new ParsedComponent
{
BomRef = "alg-aes",
Name = "AES",
Type = "library",
CryptoProperties = new ParsedCryptoProperties
{
AssetType = CryptoAssetType.Algorithm,
AlgorithmProperties = new ParsedAlgorithmProperties
{
Primitive = CryptoPrimitive.Symmetric
}
}
}
};
var policy = CryptoPolicyDefaults.Default with
{
RegionalRequirements = new RegionalCryptoPolicy
{
Eidas = true
}
};
var context = CryptoAnalysisContext.Create(components, policy, TimeProvider.System);
var analyzer = new RegionalComplianceChecker();
var result = await analyzer.AnalyzeAsync(context);
Assert.Contains(result.Findings, f => f.Type == CryptoFindingType.NonFipsCompliant);
}
}

View File

@@ -0,0 +1,19 @@
<?xml version='1.0' encoding='utf-8'?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>preview</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../__Libraries/StellaOps.Scanner.CryptoAnalysis/StellaOps.Scanner.CryptoAnalysis.csproj" />
<ProjectReference Include="../../../Concelier/__Libraries/StellaOps.Concelier.SbomIntegration/StellaOps.Concelier.SbomIntegration.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Fixtures\**\*" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>