Files
git.stella-ops.org/tests/offline/StellaOps.Offline.E2E.Tests/OfflineE2ETests.cs

191 lines
5.4 KiB
C#

namespace StellaOps.Offline.E2E.Tests;
using StellaOps.Testing.AirGap;
[Trait("Category", "AirGap")]
[Trait("Category", "E2E")]
public class OfflineE2ETests : NetworkIsolatedTestBase
{
[Fact]
public async Task Scan_WithOfflineBundle_ProducesVerdict()
{
// Arrange
var bundlePath = GetOfflineBundlePath();
var imageTarball = Path.Combine(bundlePath, "images", "test-image.tar");
// Skip if bundle doesn't exist (local dev)
if (!Directory.Exists(bundlePath))
{
// Skip - requires offline bundle
return;
}
// Act
// TODO: Implement scanner offline execution
var result = await SimulateScanAsync(imageTarball, bundlePath);
// Assert
result.Success.Should().BeTrue();
result.Verdict.Should().NotBeNull();
AssertNoNetworkCalls();
}
[Fact]
public async Task Scan_ProducesSbom_WithOfflineBundle()
{
var bundlePath = GetOfflineBundlePath();
var imageTarball = Path.Combine(bundlePath, "images", "test-image.tar");
if (!Directory.Exists(bundlePath))
{
return;
}
var result = await SimulateScanAsync(imageTarball, bundlePath);
result.Sbom.Should().NotBeNull();
result.Sbom?.Components.Should().NotBeEmpty();
AssertNoNetworkCalls();
}
[Fact]
public async Task Attestation_SignAndVerify_WithOfflineBundle()
{
var bundlePath = GetOfflineBundlePath();
var imageTarball = Path.Combine(bundlePath, "images", "test-image.tar");
if (!Directory.Exists(bundlePath))
{
return;
}
// Scan and generate attestation
var scanResult = await SimulateScanAsync(imageTarball, bundlePath);
// Sign attestation (offline with local keys)
var keyPath = Path.Combine(bundlePath, "keys", "signing-key.pem");
var signResult = await SimulateSignAttestationAsync(
scanResult.Sbom!,
keyPath);
signResult.Success.Should().BeTrue();
// Verify signature (offline with local trust roots)
var trustRootPath = Path.Combine(bundlePath, "certs", "trust-root.pem");
var verifyResult = await SimulateVerifyAttestationAsync(
signResult.Attestation,
trustRootPath);
verifyResult.Valid.Should().BeTrue();
AssertNoNetworkCalls();
}
[Fact]
public async Task PolicyEvaluation_WithOfflineBundle_Works()
{
var bundlePath = GetOfflineBundlePath();
var imageTarball = Path.Combine(bundlePath, "images", "vuln-image.tar");
if (!Directory.Exists(bundlePath))
{
return;
}
var scanResult = await SimulateScanAsync(imageTarball, bundlePath);
// Policy evaluation should work offline
var policyPath = Path.Combine(bundlePath, "policies", "default.rego");
var policyResult = await SimulatePolicyEvaluationAsync(
scanResult.Verdict,
policyPath);
policyResult.Should().NotBeNull();
policyResult?.Decision.Should().BeOneOf("allow", "deny", "warn");
AssertNoNetworkCalls();
}
[Fact]
public async Task VexApplication_WithOfflineBundle_Works()
{
var bundlePath = GetOfflineBundlePath();
var imageTarball = Path.Combine(bundlePath, "images", "vuln-with-vex.tar");
if (!Directory.Exists(bundlePath))
{
return;
}
var scanResult = await SimulateScanAsync(imageTarball, bundlePath);
// VEX should be applied from offline bundle
var vexApplied = scanResult.Verdict?.VexStatements?.Any() ?? false;
vexApplied.Should().BeTrue("VEX from offline bundle should be applied");
AssertNoNetworkCalls();
}
// Simulation methods for testing infrastructure
private static async Task<ScanResult> SimulateScanAsync(string imagePath, string bundlePath)
{
await Task.CompletedTask;
return new ScanResult
{
Success = true,
Verdict = new Verdict { VexStatements = [] },
Sbom = new Sbom { Components = ["test-component"] }
};
}
private static async Task<SignResult> SimulateSignAttestationAsync(Sbom sbom, string keyPath)
{
await Task.CompletedTask;
return new SignResult { Success = true, Attestation = "mock-attestation" };
}
private static async Task<VerifyResult> SimulateVerifyAttestationAsync(string attestation, string trustRoot)
{
await Task.CompletedTask;
return new VerifyResult { Valid = true };
}
private static async Task<PolicyResult> SimulatePolicyEvaluationAsync(Verdict? verdict, string policyPath)
{
await Task.CompletedTask;
return new PolicyResult { Decision = "allow" };
}
}
// Mock types for testing
public record ScanResult
{
public bool Success { get; init; }
public Verdict? Verdict { get; init; }
public Sbom? Sbom { get; init; }
}
public record Verdict
{
public IReadOnlyList<string>? VexStatements { get; init; }
}
public record Sbom
{
public IReadOnlyList<string> Components { get; init; } = [];
}
public record SignResult
{
public bool Success { get; init; }
public string? Attestation { get; init; }
}
public record VerifyResult
{
public bool Valid { get; init; }
}
public record PolicyResult
{
public string? Decision { get; init; }
}