Refactor code structure for improved readability and maintainability; optimize performance in key functions.

This commit is contained in:
master
2025-12-22 19:06:31 +02:00
parent dfaa2079aa
commit 4602ccc3a3
1444 changed files with 109919 additions and 8058 deletions

View File

@@ -0,0 +1,317 @@
using FluentAssertions;
using Moq;
using StellaOps.Findings.Ledger.WebService.Contracts;
using StellaOps.Findings.Ledger.WebService.Services;
using Xunit;
namespace StellaOps.Findings.Ledger.Tests.Services;
public class EvidenceGraphBuilderTests
{
private readonly Mock<IEvidenceRepository> _evidenceRepo = new();
private readonly Mock<IAttestationVerifier> _attestationVerifier = new();
private readonly EvidenceGraphBuilder _builder;
public EvidenceGraphBuilderTests()
{
_builder = new EvidenceGraphBuilder(_evidenceRepo.Object, _attestationVerifier.Object);
}
[Fact]
public async Task BuildAsync_FindingNotFound_ReturnsNull()
{
_evidenceRepo.Setup(r => r.GetFullEvidenceAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
.ReturnsAsync((FullEvidence?)null);
var result = await _builder.BuildAsync(Guid.NewGuid(), CancellationToken.None);
result.Should().BeNull();
}
[Fact]
public async Task BuildAsync_WithAllEvidence_ReturnsCompleteGraph()
{
var evidence = CreateFullEvidence();
_evidenceRepo.Setup(r => r.GetFullEvidenceAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(evidence);
_attestationVerifier.Setup(v => v.VerifyAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new AttestationVerificationResult
{
IsValid = true,
SignerIdentity = "test-signer",
SignedAt = DateTimeOffset.UtcNow,
KeyId = "key-123",
RekorLogIndex = 12345
});
var findingId = Guid.NewGuid();
var result = await _builder.BuildAsync(findingId, CancellationToken.None);
result.Should().NotBeNull();
result!.FindingId.Should().Be(findingId);
result.VulnerabilityId.Should().Be("CVE-2024-1234");
result.Nodes.Should().HaveCountGreaterThan(1);
result.Edges.Should().NotBeEmpty();
result.RootNodeId.Should().NotBeNullOrEmpty();
result.RootNodeId.Should().StartWith("verdict:");
}
[Fact]
public async Task BuildAsync_SignedAttestation_IncludesSignatureStatus()
{
var evidence = CreateEvidenceWithSignedAttestation();
_evidenceRepo.Setup(r => r.GetFullEvidenceAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(evidence);
_attestationVerifier.Setup(v => v.VerifyAsync("attestation-digest-123", It.IsAny<CancellationToken>()))
.ReturnsAsync(new AttestationVerificationResult
{
IsValid = true,
SignerIdentity = "trusted-signer@example.com",
SignedAt = DateTimeOffset.UtcNow.AddHours(-1),
KeyId = "key-abc",
RekorLogIndex = 54321
});
var result = await _builder.BuildAsync(Guid.NewGuid(), CancellationToken.None);
result.Should().NotBeNull();
var signedNode = result!.Nodes.FirstOrDefault(n => n.Signature.IsSigned);
signedNode.Should().NotBeNull();
signedNode!.Signature.IsValid.Should().BeTrue();
signedNode.Signature.SignerIdentity.Should().Be("trusted-signer@example.com");
signedNode.Signature.KeyId.Should().Be("key-abc");
signedNode.Signature.RekorLogIndex.Should().Be(54321);
}
[Fact]
public async Task BuildAsync_EdgeRelationships_CorrectlyLinked()
{
var evidence = CreateFullEvidence();
_evidenceRepo.Setup(r => r.GetFullEvidenceAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(evidence);
_attestationVerifier.Setup(v => v.VerifyAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new AttestationVerificationResult { IsValid = true });
var result = await _builder.BuildAsync(Guid.NewGuid(), CancellationToken.None);
result.Should().NotBeNull();
// Verify policy trace edge
var policyEdge = result!.Edges.FirstOrDefault(e => e.Label == "policy evaluation");
policyEdge.Should().NotBeNull();
policyEdge!.Relation.Should().Be(EvidenceRelation.DerivedFrom);
policyEdge.To.Should().Be(result.RootNodeId);
// Verify VEX edge
var vexEdge = result.Edges.FirstOrDefault(e => e.Label == "affected");
vexEdge.Should().NotBeNull();
vexEdge!.Relation.Should().Be(EvidenceRelation.DerivedFrom);
// Verify reachability edge
var reachEdge = result.Edges.FirstOrDefault(e => e.Label == "reachability analysis");
reachEdge.Should().NotBeNull();
reachEdge!.Relation.Should().Be(EvidenceRelation.Corroborates);
// Verify runtime edge
var runtimeEdge = result.Edges.FirstOrDefault(e => e.Label == "runtime observation");
runtimeEdge.Should().NotBeNull();
runtimeEdge!.Relation.Should().Be(EvidenceRelation.Corroborates);
}
[Fact]
public async Task BuildAsync_NodeTypes_CorrectlyAssigned()
{
var evidence = CreateFullEvidence();
_evidenceRepo.Setup(r => r.GetFullEvidenceAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(evidence);
_attestationVerifier.Setup(v => v.VerifyAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new AttestationVerificationResult { IsValid = true });
var result = await _builder.BuildAsync(Guid.NewGuid(), CancellationToken.None);
result.Should().NotBeNull();
result!.Nodes.Should().Contain(n => n.Type == EvidenceNodeType.Verdict);
result.Nodes.Should().Contain(n => n.Type == EvidenceNodeType.PolicyTrace);
result.Nodes.Should().Contain(n => n.Type == EvidenceNodeType.VexStatement);
result.Nodes.Should().Contain(n => n.Type == EvidenceNodeType.Reachability);
result.Nodes.Should().Contain(n => n.Type == EvidenceNodeType.RuntimeObservation);
result.Nodes.Should().Contain(n => n.Type == EvidenceNodeType.SbomComponent);
result.Nodes.Should().Contain(n => n.Type == EvidenceNodeType.Provenance);
}
[Fact]
public async Task BuildAsync_MinimalEvidence_CreatesVerdictOnly()
{
var evidence = CreateMinimalEvidence();
_evidenceRepo.Setup(r => r.GetFullEvidenceAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(evidence);
var result = await _builder.BuildAsync(Guid.NewGuid(), CancellationToken.None);
result.Should().NotBeNull();
result!.Nodes.Should().HaveCount(1);
result.Nodes[0].Type.Should().Be(EvidenceNodeType.Verdict);
result.Edges.Should().BeEmpty();
}
[Fact]
public async Task BuildAsync_UnsignedEvidence_HasUnsignedStatus()
{
var evidence = CreateMinimalEvidence();
_evidenceRepo.Setup(r => r.GetFullEvidenceAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(evidence);
var result = await _builder.BuildAsync(Guid.NewGuid(), CancellationToken.None);
result.Should().NotBeNull();
result!.Nodes.Should().AllSatisfy(n =>
{
if (n.Type == EvidenceNodeType.Verdict || n.Type == EvidenceNodeType.SbomComponent)
{
n.Signature.IsSigned.Should().BeFalse();
}
});
}
[Fact]
public async Task BuildAsync_NodeMetadata_PopulatedCorrectly()
{
var evidence = CreateFullEvidence();
_evidenceRepo.Setup(r => r.GetFullEvidenceAsync(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(evidence);
_attestationVerifier.Setup(v => v.VerifyAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new AttestationVerificationResult { IsValid = true });
var result = await _builder.BuildAsync(Guid.NewGuid(), CancellationToken.None);
result.Should().NotBeNull();
var policyNode = result!.Nodes.First(n => n.Type == EvidenceNodeType.PolicyTrace);
policyNode.Metadata.Should().ContainKey("policyName");
policyNode.Metadata["policyName"].Should().Be("security-baseline");
var vexNode = result.Nodes.First(n => n.Type == EvidenceNodeType.VexStatement);
vexNode.Metadata.Should().ContainKey("status");
vexNode.Metadata["status"].Should().Be("affected");
var sbomNode = result.Nodes.First(n => n.Type == EvidenceNodeType.SbomComponent);
sbomNode.Metadata.Should().ContainKey("purl");
sbomNode.Metadata["purl"].Should().Be("pkg:npm/lodash@4.17.20");
}
private static FullEvidence CreateFullEvidence()
{
return new FullEvidence
{
VulnerabilityId = "CVE-2024-1234",
Verdict = new VerdictEvidence
{
Status = "Affected",
Digest = "sha256:verdict123",
Issuer = "stellaops",
Timestamp = DateTimeOffset.UtcNow.AddDays(-1)
},
PolicyTrace = new PolicyTraceEvidence
{
PolicyName = "security-baseline",
PolicyVersion = "v1.0.0",
Digest = "sha256:policy123",
Issuer = "stellaops",
Timestamp = DateTimeOffset.UtcNow.AddDays(-1),
AttestationDigest = "attestation-policy-123"
},
VexStatements = new[]
{
new VexEvidence
{
Status = "affected",
Justification = "vulnerable_code_path_reachable",
Digest = "sha256:vex123",
Issuer = "vendor",
Timestamp = DateTimeOffset.UtcNow.AddDays(-2),
AttestationDigest = "attestation-vex-123"
}
},
Reachability = new ReachabilityEvidence
{
State = "StaticReachable",
Confidence = 0.92m,
Digest = "sha256:reach123",
Issuer = "stellaops",
Timestamp = DateTimeOffset.UtcNow.AddDays(-1),
AttestationDigest = "attestation-reach-123"
},
RuntimeObservations = new[]
{
new RuntimeEvidence
{
ObservationType = "ComponentLoaded",
DurationMinutes = 120,
Digest = "sha256:runtime123",
Issuer = "stellaops",
Timestamp = DateTimeOffset.UtcNow.AddHours(-2),
AttestationDigest = "attestation-runtime-123"
}
},
SbomComponent = new SbomComponentEvidence
{
ComponentName = "lodash",
Purl = "pkg:npm/lodash@4.17.20",
Version = "4.17.20",
Digest = "sha256:sbom123",
Issuer = "stellaops",
Timestamp = DateTimeOffset.UtcNow.AddDays(-3)
},
Provenance = new ProvenanceEvidence
{
BuilderType = "github-actions",
RepoUrl = "https://github.com/lodash/lodash",
Digest = "sha256:prov123",
Issuer = "github",
Timestamp = DateTimeOffset.UtcNow.AddDays(-30),
AttestationDigest = "attestation-prov-123"
}
};
}
private static FullEvidence CreateEvidenceWithSignedAttestation()
{
return new FullEvidence
{
VulnerabilityId = "CVE-2024-5678",
Verdict = new VerdictEvidence
{
Status = "Affected",
Digest = "sha256:verdict456",
Issuer = "stellaops",
Timestamp = DateTimeOffset.UtcNow
},
VexStatements = new[]
{
new VexEvidence
{
Status = "affected",
Digest = "sha256:vex456",
Issuer = "vendor",
Timestamp = DateTimeOffset.UtcNow,
AttestationDigest = "attestation-digest-123"
}
}
};
}
private static FullEvidence CreateMinimalEvidence()
{
return new FullEvidence
{
VulnerabilityId = "CVE-2024-9999",
Verdict = new VerdictEvidence
{
Status = "UnderReview",
Digest = "sha256:verdict999",
Issuer = "stellaops",
Timestamp = DateTimeOffset.UtcNow
}
};
}
}

View File

@@ -0,0 +1,176 @@
using FluentAssertions;
using StellaOps.Findings.Ledger.WebService.Contracts;
using StellaOps.Findings.Ledger.WebService.Services;
using Xunit;
namespace StellaOps.Findings.Ledger.Tests.Services;
public class FindingSummaryBuilderTests
{
private readonly FindingSummaryBuilder _builder = new();
[Fact]
public void Build_AffectedFinding_ReturnsRedChip()
{
var finding = CreateFinding(isAffected: true);
var result = _builder.Build(finding);
result.Status.Should().Be(VerdictStatus.Affected);
result.Chip.Color.Should().Be(ChipColor.Red);
result.Chip.Label.Should().Be("AFFECTED");
}
[Fact]
public void Build_NotAffectedFinding_ReturnsGreenChip()
{
var finding = CreateFinding(isAffected: false);
var result = _builder.Build(finding);
result.Status.Should().Be(VerdictStatus.NotAffected);
result.Chip.Color.Should().Be(ChipColor.Green);
result.Chip.Label.Should().Be("NOT AFFECTED");
}
[Fact]
public void Build_MitigatedFinding_ReturnsBlueChip()
{
var finding = CreateFinding(isAffected: true, isMitigated: true);
var result = _builder.Build(finding);
result.Status.Should().Be(VerdictStatus.Mitigated);
result.Chip.Color.Should().Be(ChipColor.Blue);
result.Chip.Label.Should().Be("MITIGATED");
}
[Fact]
public void Build_UnknownStatus_ReturnsYellowChip()
{
var finding = CreateFinding(isAffected: null);
var result = _builder.Build(finding);
result.Status.Should().Be(VerdictStatus.UnderReview);
result.Chip.Color.Should().Be(ChipColor.Yellow);
result.Chip.Label.Should().Be("REVIEW NEEDED");
}
[Fact]
public void Build_ReachableVulnerability_GeneratesAppropriateOneLiner()
{
var finding = CreateFinding(isAffected: true, isReachable: true);
var result = _builder.Build(finding);
result.OneLiner.Should().Contain("reachable");
result.OneLiner.Should().Contain("actively used");
}
[Fact]
public void Build_WithCallGraph_StrongReachabilityBadge()
{
var finding = CreateFinding(isAffected: true, isReachable: true, hasCallGraph: true);
var result = _builder.Build(finding);
result.Badges.Reachability.Should().Be(BadgeStatus.Strong);
}
[Fact]
public void Build_WithRuntimeEvidence_StrongRuntimeBadge()
{
var finding = CreateFinding(isAffected: true, hasRuntimeEvidence: true);
var result = _builder.Build(finding);
result.Badges.Runtime.Should().Be(BadgeStatus.Strong);
}
[Fact]
public void Build_WithVerifiedRuntime_VerifiedBadge()
{
var finding = CreateFinding(
isAffected: true,
hasRuntimeEvidence: true,
runtimeConfirmed: true);
var result = _builder.Build(finding);
result.Badges.Runtime.Should().Be(BadgeStatus.Verified);
}
[Fact]
public void Build_WithAttestation_StrongProvenanceBadge()
{
var finding = CreateFinding(isAffected: true, hasAttestation: true);
var result = _builder.Build(finding);
result.Badges.Provenance.Should().Be(BadgeStatus.Strong);
}
[Fact]
public void Build_WithVerifiedAttestation_VerifiedProvenanceBadge()
{
var finding = CreateFinding(
isAffected: true,
hasAttestation: true,
attestationVerified: true);
var result = _builder.Build(finding);
result.Badges.Provenance.Should().Be(BadgeStatus.Verified);
}
[Fact]
public void Build_CopiesAllBasicFields()
{
var finding = CreateFinding(isAffected: true);
var result = _builder.Build(finding);
result.FindingId.Should().Be(finding.Id);
result.VulnerabilityId.Should().Be(finding.VulnerabilityId);
result.Component.Should().Be(finding.ComponentPurl);
result.Confidence.Should().Be(finding.Confidence);
result.CvssScore.Should().Be(finding.CvssScore);
result.Severity.Should().Be(finding.Severity);
}
private static FindingData CreateFinding(
bool? isAffected = true,
bool isMitigated = false,
bool? isReachable = null,
bool hasCallGraph = false,
bool hasRuntimeEvidence = false,
bool runtimeConfirmed = false,
bool hasAttestation = false,
bool attestationVerified = false)
{
return new FindingData
{
Id = Guid.NewGuid(),
VulnerabilityId = "CVE-2024-1234",
Title = "Test Vulnerability",
ComponentPurl = "pkg:npm/test-package@1.0.0",
IsAffected = isAffected,
IsMitigated = isMitigated,
MitigationReason = isMitigated ? "Test mitigation" : null,
Confidence = 0.85m,
IsReachable = isReachable,
HasCallGraph = hasCallGraph,
HasRuntimeEvidence = hasRuntimeEvidence,
RuntimeConfirmed = runtimeConfirmed,
HasPolicyEvaluation = false,
PolicyPassed = false,
HasAttestation = hasAttestation,
AttestationVerified = attestationVerified,
CvssScore = 7.5m,
Severity = "High",
FirstSeen = DateTimeOffset.UtcNow.AddDays(-7),
LastUpdated = DateTimeOffset.UtcNow
};
}
}

View File

@@ -8,6 +8,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\StellaOps.Findings.Ledger\StellaOps.Findings.Ledger.csproj" />
<ProjectReference Include="..\..\StellaOps.Findings.Ledger.WebService\StellaOps.Findings.Ledger.WebService.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="17.14.0" />
@@ -17,5 +18,6 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Moq" Version="4.20.70" />
</ItemGroup>
</Project>