tests fixes and some product advisories tunes ups
This commit is contained in:
@@ -159,6 +159,147 @@ public sealed class OpaGateAdapterTests
|
||||
Assert.Equal("decision-abc-123", result.Details["opaDecisionId"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EvaluateAsync_WithSupplyChainEvidence_IncludesEvidenceInInput()
|
||||
{
|
||||
object? capturedInput = null;
|
||||
var mockClient = new CapturingMockOpaClient(
|
||||
new OpaTypedResult<object>
|
||||
{
|
||||
Success = true,
|
||||
DecisionId = "evidence-test",
|
||||
Result = new { allow = true }
|
||||
},
|
||||
input => capturedInput = input);
|
||||
|
||||
var options = Options.Create(new OpaGateOptions
|
||||
{
|
||||
GateName = "TestOpaGate",
|
||||
PolicyPath = "stella/test/allow"
|
||||
});
|
||||
|
||||
var evidence = new OpaSupplyChainEvidence
|
||||
{
|
||||
Artifact = new OpaArtifactDescriptor
|
||||
{
|
||||
Digest = "sha256:abc123def456",
|
||||
MediaType = "application/vnd.oci.image.manifest.v1+json",
|
||||
Reference = "registry.example.com/app:v1.0.0"
|
||||
},
|
||||
Sbom = new OpaSbomReference
|
||||
{
|
||||
Digest = "sha256:sbom123",
|
||||
Format = "cyclonedx-1.7",
|
||||
ComponentCount = 42
|
||||
},
|
||||
Attestations = new[]
|
||||
{
|
||||
new OpaAttestationReference
|
||||
{
|
||||
Digest = "sha256:att123",
|
||||
PredicateType = "https://slsa.dev/provenance/v1",
|
||||
SignatureVerified = true,
|
||||
RekorLogIndex = 12345
|
||||
}
|
||||
},
|
||||
Transparency = new OpaTransparencyEvidence
|
||||
{
|
||||
Rekor = new[]
|
||||
{
|
||||
new OpaRekorReceipt
|
||||
{
|
||||
LogId = "rekor.sigstore.dev",
|
||||
Uuid = "abc123",
|
||||
LogIndex = 12345,
|
||||
IntegratedTime = 1700000000,
|
||||
Verified = true
|
||||
}
|
||||
}
|
||||
},
|
||||
Vex = new OpaVexEvidence
|
||||
{
|
||||
MergeDecision = new OpaVexMergeDecision
|
||||
{
|
||||
Algorithm = "trust-weighted-lattice-v1",
|
||||
Inputs = new[]
|
||||
{
|
||||
new OpaVexMergeInput { Source = "vendor", Digest = "sha256:vex1", TrustTier = "authoritative" }
|
||||
},
|
||||
Decisions = new[]
|
||||
{
|
||||
new OpaVexDecision { Vuln = "CVE-2024-1234", Status = "not_affected", Justification = "component_not_present" }
|
||||
},
|
||||
HadConflicts = false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var context = new PolicyGateContext
|
||||
{
|
||||
Environment = "production",
|
||||
HasReachabilityProof = true,
|
||||
SupplyChainEvidence = evidence
|
||||
};
|
||||
|
||||
var gate = new OpaGateAdapter(mockClient, options, NullLogger<OpaGateAdapter>.Instance);
|
||||
await gate.EvaluateAsync(CreateMergeResult(), context);
|
||||
|
||||
Assert.NotNull(capturedInput);
|
||||
|
||||
// Serialize to JSON and verify structure
|
||||
var json = JsonSerializer.Serialize(capturedInput, new JsonSerializerOptions { WriteIndented = true });
|
||||
|
||||
Assert.Contains("artifact", json);
|
||||
Assert.Contains("sha256:abc123def456", json);
|
||||
Assert.Contains("sbom", json);
|
||||
Assert.Contains("cyclonedx-1.7", json);
|
||||
Assert.Contains("attestations", json);
|
||||
Assert.Contains("https://slsa.dev/provenance/v1", json);
|
||||
Assert.Contains("transparency", json);
|
||||
Assert.Contains("rekor", json);
|
||||
Assert.Contains("vex", json);
|
||||
Assert.Contains("trust-weighted-lattice-v1", json);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EvaluateAsync_WithoutSupplyChainEvidence_DoesNotIncludeEvidenceFields()
|
||||
{
|
||||
object? capturedInput = null;
|
||||
var mockClient = new CapturingMockOpaClient(
|
||||
new OpaTypedResult<object>
|
||||
{
|
||||
Success = true,
|
||||
DecisionId = "no-evidence-test",
|
||||
Result = new { allow = true }
|
||||
},
|
||||
input => capturedInput = input);
|
||||
|
||||
var options = Options.Create(new OpaGateOptions
|
||||
{
|
||||
GateName = "TestOpaGate",
|
||||
PolicyPath = "stella/test/allow"
|
||||
});
|
||||
|
||||
var gate = new OpaGateAdapter(mockClient, options, NullLogger<OpaGateAdapter>.Instance);
|
||||
await gate.EvaluateAsync(CreateMergeResult(), CreateContext());
|
||||
|
||||
Assert.NotNull(capturedInput);
|
||||
|
||||
var json = JsonSerializer.Serialize(capturedInput);
|
||||
|
||||
// Should not contain evidence fields when not provided
|
||||
Assert.DoesNotContain("\"artifact\"", json);
|
||||
Assert.DoesNotContain("\"sbom\"", json);
|
||||
Assert.DoesNotContain("\"attestations\"", json);
|
||||
Assert.DoesNotContain("\"transparency\"", json);
|
||||
Assert.DoesNotContain("\"vex\"", json);
|
||||
|
||||
// Should still contain standard fields
|
||||
Assert.Contains("mergeResult", json);
|
||||
Assert.Contains("context", json);
|
||||
Assert.Contains("policy", json);
|
||||
}
|
||||
|
||||
private sealed class MockOpaClient : IOpaClient
|
||||
{
|
||||
private readonly OpaTypedResult<object> _result;
|
||||
@@ -207,4 +348,56 @@ public sealed class OpaGateAdapterTests
|
||||
public Task DeletePolicyAsync(string policyId, CancellationToken cancellationToken = default)
|
||||
=> Task.CompletedTask;
|
||||
}
|
||||
|
||||
private sealed class CapturingMockOpaClient : IOpaClient
|
||||
{
|
||||
private readonly OpaTypedResult<object> _result;
|
||||
private readonly Action<object> _captureInput;
|
||||
|
||||
public CapturingMockOpaClient(OpaTypedResult<object> result, Action<object> captureInput)
|
||||
{
|
||||
_result = result;
|
||||
_captureInput = captureInput;
|
||||
}
|
||||
|
||||
public Task<OpaEvaluationResult> EvaluateAsync(string policyPath, object input, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_captureInput(input);
|
||||
return Task.FromResult(new OpaEvaluationResult
|
||||
{
|
||||
Success = _result.Success,
|
||||
DecisionId = _result.DecisionId,
|
||||
Result = _result.Result,
|
||||
Error = _result.Error
|
||||
});
|
||||
}
|
||||
|
||||
public Task<OpaTypedResult<TResult>> EvaluateAsync<TResult>(string policyPath, object input, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_captureInput(input);
|
||||
TResult? typedResult = default;
|
||||
if (_result.Result is not null)
|
||||
{
|
||||
var json = JsonSerializer.Serialize(_result.Result, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
|
||||
typedResult = JsonSerializer.Deserialize<TResult>(json, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
|
||||
}
|
||||
|
||||
return Task.FromResult(new OpaTypedResult<TResult>
|
||||
{
|
||||
Success = _result.Success,
|
||||
DecisionId = _result.DecisionId,
|
||||
Result = typedResult,
|
||||
Error = _result.Error
|
||||
});
|
||||
}
|
||||
|
||||
public Task<bool> HealthCheckAsync(CancellationToken cancellationToken = default)
|
||||
=> Task.FromResult(true);
|
||||
|
||||
public Task UploadPolicyAsync(string policyId, string regoContent, CancellationToken cancellationToken = default)
|
||||
=> Task.CompletedTask;
|
||||
|
||||
public Task DeletePolicyAsync(string policyId, CancellationToken cancellationToken = default)
|
||||
=> Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user