new advisories work and features gaps work
This commit is contained in:
@@ -14,7 +14,7 @@ public sealed class BinaryDiffPredicateBuilderTests
|
||||
public void Build_RequiresSubject()
|
||||
{
|
||||
var options = Options.Create(new BinaryDiffOptions { ToolVersion = "1.0.0" });
|
||||
var builder = new BinaryDiffPredicateBuilder(options, BinaryDiffTestData.FixedTimeProvider);
|
||||
var builder = new BinaryDiffPredicateBuilder(options, BinaryDiffTestData.TestTimeProvider);
|
||||
|
||||
builder.WithInputs(
|
||||
new BinaryDiffImageReference { Digest = "sha256:base" },
|
||||
@@ -30,7 +30,7 @@ public sealed class BinaryDiffPredicateBuilderTests
|
||||
public void Build_RequiresInputs()
|
||||
{
|
||||
var options = Options.Create(new BinaryDiffOptions { ToolVersion = "1.0.0" });
|
||||
var builder = new BinaryDiffPredicateBuilder(options, BinaryDiffTestData.FixedTimeProvider);
|
||||
var builder = new BinaryDiffPredicateBuilder(options, BinaryDiffTestData.TestTimeProvider);
|
||||
|
||||
builder.WithSubject("docker://example/app@sha256:base", "sha256:aaaa");
|
||||
|
||||
@@ -44,7 +44,7 @@ public sealed class BinaryDiffPredicateBuilderTests
|
||||
public void Build_SortsFindingsAndSections()
|
||||
{
|
||||
var options = Options.Create(new BinaryDiffOptions { ToolVersion = "1.0.0" });
|
||||
var builder = new BinaryDiffPredicateBuilder(options, BinaryDiffTestData.FixedTimeProvider);
|
||||
var builder = new BinaryDiffPredicateBuilder(options, BinaryDiffTestData.TestTimeProvider);
|
||||
|
||||
builder.WithSubject("docker://example/app@sha256:base", "sha256:aaaa")
|
||||
.WithInputs(
|
||||
@@ -106,7 +106,7 @@ public sealed class BinaryDiffPredicateBuilderTests
|
||||
AnalyzedSections = [".z", ".a"]
|
||||
});
|
||||
|
||||
var builder = new BinaryDiffPredicateBuilder(options, BinaryDiffTestData.FixedTimeProvider);
|
||||
var builder = new BinaryDiffPredicateBuilder(options, BinaryDiffTestData.TestTimeProvider);
|
||||
builder.WithSubject("docker://example/app@sha256:base", "sha256:aaaa")
|
||||
.WithInputs(
|
||||
new BinaryDiffImageReference { Digest = "sha256:base" },
|
||||
@@ -116,7 +116,7 @@ public sealed class BinaryDiffPredicateBuilderTests
|
||||
|
||||
predicate.Metadata.ToolVersion.Should().Be("2.0.0");
|
||||
predicate.Metadata.ConfigDigest.Should().Be("sha256:cfg");
|
||||
predicate.Metadata.AnalysisTimestamp.Should().Be(BinaryDiffTestData.FixedTimeProvider.GetUtcNow());
|
||||
predicate.Metadata.AnalysisTimestamp.Should().Be(BinaryDiffTestData.TestTimeProvider.GetUtcNow());
|
||||
predicate.Metadata.AnalyzedSections.Should().Equal(".a", ".z");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace StellaOps.Attestor.StandardPredicates.Tests.BinaryDiff;
|
||||
|
||||
internal static class BinaryDiffTestData
|
||||
{
|
||||
internal static readonly TimeProvider FixedTimeProvider =
|
||||
internal static readonly TimeProvider TestTimeProvider =
|
||||
new FixedTimeProvider(new DateTimeOffset(2026, 1, 13, 12, 0, 0, TimeSpan.Zero));
|
||||
|
||||
internal static BinaryDiffPredicate CreatePredicate()
|
||||
@@ -20,7 +20,7 @@ internal static class BinaryDiffTestData
|
||||
AnalyzedSections = [".text", ".rodata", ".data"]
|
||||
});
|
||||
|
||||
var builder = new BinaryDiffPredicateBuilder(options, FixedTimeProvider);
|
||||
var builder = new BinaryDiffPredicateBuilder(options, TestTimeProvider);
|
||||
builder.WithSubject("docker://example/app@sha256:base", "sha256:aaaaaaaa")
|
||||
.WithInputs(
|
||||
new BinaryDiffImageReference
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// VexOverridePredicateBuilderTests.cs
|
||||
// Sprint: SPRINT_20260112_004_ATTESTOR_vex_override_predicate (ATT-VEX-002)
|
||||
// Description: Tests for VEX override predicate builder
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Text.Json;
|
||||
using StellaOps.Attestor.StandardPredicates.VexOverride;
|
||||
using StellaOps.TestKit;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Attestor.StandardPredicates.Tests.VexOverride;
|
||||
|
||||
public sealed class VexOverridePredicateBuilderTests
|
||||
{
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Build_WithRequiredFields_CreatesPredicate()
|
||||
{
|
||||
var decisionTime = new DateTimeOffset(2026, 1, 14, 10, 0, 0, TimeSpan.Zero);
|
||||
|
||||
var predicate = new VexOverridePredicateBuilder()
|
||||
.WithArtifactDigest("sha256:abc123")
|
||||
.WithVulnerabilityId("CVE-2024-12345")
|
||||
.WithDecision(VexOverrideDecision.NotAffected)
|
||||
.WithJustification("Component is not in use")
|
||||
.WithDecisionTime(decisionTime)
|
||||
.WithOperatorId("user@example.com")
|
||||
.Build();
|
||||
|
||||
Assert.Equal("sha256:abc123", predicate.ArtifactDigest);
|
||||
Assert.Equal("CVE-2024-12345", predicate.VulnerabilityId);
|
||||
Assert.Equal(VexOverrideDecision.NotAffected, predicate.Decision);
|
||||
Assert.Equal("Component is not in use", predicate.Justification);
|
||||
Assert.Equal(decisionTime, predicate.DecisionTime);
|
||||
Assert.Equal("user@example.com", predicate.OperatorId);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Build_MissingArtifactDigest_Throws()
|
||||
{
|
||||
var builder = new VexOverridePredicateBuilder()
|
||||
.WithVulnerabilityId("CVE-2024-12345")
|
||||
.WithDecision(VexOverrideDecision.NotAffected)
|
||||
.WithJustification("Test")
|
||||
.WithDecisionTime(DateTimeOffset.UtcNow)
|
||||
.WithOperatorId("user@example.com");
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => builder.Build());
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Build_WithEvidenceRefs_AddsToList()
|
||||
{
|
||||
var predicate = new VexOverridePredicateBuilder()
|
||||
.WithArtifactDigest("sha256:abc123")
|
||||
.WithVulnerabilityId("CVE-2024-12345")
|
||||
.WithDecision(VexOverrideDecision.Mitigated)
|
||||
.WithJustification("Compensating control")
|
||||
.WithDecisionTime(DateTimeOffset.UtcNow)
|
||||
.WithOperatorId("user@example.com")
|
||||
.AddEvidenceRef("document", "https://example.com/doc", "sha256:def456", "Design doc")
|
||||
.AddEvidenceRef(new EvidenceReference
|
||||
{
|
||||
Type = "ticket",
|
||||
Uri = "https://jira.example.com/PROJ-123"
|
||||
})
|
||||
.Build();
|
||||
|
||||
Assert.Equal(2, predicate.EvidenceRefs.Length);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Build_WithTool_SetsTool()
|
||||
{
|
||||
var predicate = new VexOverridePredicateBuilder()
|
||||
.WithArtifactDigest("sha256:abc123")
|
||||
.WithVulnerabilityId("CVE-2024-12345")
|
||||
.WithDecision(VexOverrideDecision.Accepted)
|
||||
.WithJustification("Accepted risk")
|
||||
.WithDecisionTime(DateTimeOffset.UtcNow)
|
||||
.WithOperatorId("user@example.com")
|
||||
.WithTool("StellaOps", "1.0.0", "StellaOps Inc")
|
||||
.Build();
|
||||
|
||||
Assert.NotNull(predicate.Tool);
|
||||
Assert.Equal("StellaOps", predicate.Tool.Name);
|
||||
Assert.Equal("1.0.0", predicate.Tool.Version);
|
||||
Assert.Equal("StellaOps Inc", predicate.Tool.Vendor);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Build_WithMetadata_AddsMetadata()
|
||||
{
|
||||
var predicate = new VexOverridePredicateBuilder()
|
||||
.WithArtifactDigest("sha256:abc123")
|
||||
.WithVulnerabilityId("CVE-2024-12345")
|
||||
.WithDecision(VexOverrideDecision.NotAffected)
|
||||
.WithJustification("Test")
|
||||
.WithDecisionTime(DateTimeOffset.UtcNow)
|
||||
.WithOperatorId("user@example.com")
|
||||
.WithMetadata("tenant", "acme-corp")
|
||||
.WithMetadata("environment", "production")
|
||||
.Build();
|
||||
|
||||
Assert.Equal(2, predicate.Metadata.Count);
|
||||
Assert.Equal("acme-corp", predicate.Metadata["tenant"]);
|
||||
Assert.Equal("production", predicate.Metadata["environment"]);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void BuildCanonicalJson_ProducesDeterministicOutput()
|
||||
{
|
||||
var decisionTime = new DateTimeOffset(2026, 1, 14, 10, 0, 0, TimeSpan.Zero);
|
||||
|
||||
var json1 = new VexOverridePredicateBuilder()
|
||||
.WithArtifactDigest("sha256:abc123")
|
||||
.WithVulnerabilityId("CVE-2024-12345")
|
||||
.WithDecision(VexOverrideDecision.NotAffected)
|
||||
.WithJustification("Test")
|
||||
.WithDecisionTime(decisionTime)
|
||||
.WithOperatorId("user@example.com")
|
||||
.BuildCanonicalJson();
|
||||
|
||||
var json2 = new VexOverridePredicateBuilder()
|
||||
.WithOperatorId("user@example.com") // Different order
|
||||
.WithDecisionTime(decisionTime)
|
||||
.WithJustification("Test")
|
||||
.WithDecision(VexOverrideDecision.NotAffected)
|
||||
.WithVulnerabilityId("CVE-2024-12345")
|
||||
.WithArtifactDigest("sha256:abc123")
|
||||
.BuildCanonicalJson();
|
||||
|
||||
Assert.Equal(json1, json2);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void BuildCanonicalJson_HasSortedKeys()
|
||||
{
|
||||
var decisionTime = new DateTimeOffset(2026, 1, 14, 10, 0, 0, TimeSpan.Zero);
|
||||
|
||||
var json = new VexOverridePredicateBuilder()
|
||||
.WithArtifactDigest("sha256:abc123")
|
||||
.WithVulnerabilityId("CVE-2024-12345")
|
||||
.WithDecision(VexOverrideDecision.NotAffected)
|
||||
.WithJustification("Test")
|
||||
.WithDecisionTime(decisionTime)
|
||||
.WithOperatorId("user@example.com")
|
||||
.BuildCanonicalJson();
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var keys = document.RootElement.EnumerateObject().Select(p => p.Name).ToList();
|
||||
|
||||
// Verify keys are alphabetically sorted
|
||||
var sortedKeys = keys.OrderBy(k => k, StringComparer.Ordinal).ToList();
|
||||
Assert.Equal(sortedKeys, keys);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void BuildJsonBytes_ReturnsUtf8Bytes()
|
||||
{
|
||||
var decisionTime = new DateTimeOffset(2026, 1, 14, 10, 0, 0, TimeSpan.Zero);
|
||||
|
||||
var bytes = new VexOverridePredicateBuilder()
|
||||
.WithArtifactDigest("sha256:abc123")
|
||||
.WithVulnerabilityId("CVE-2024-12345")
|
||||
.WithDecision(VexOverrideDecision.NotAffected)
|
||||
.WithJustification("Test")
|
||||
.WithDecisionTime(decisionTime)
|
||||
.WithOperatorId("user@example.com")
|
||||
.BuildJsonBytes();
|
||||
|
||||
Assert.NotEmpty(bytes);
|
||||
|
||||
var json = System.Text.Encoding.UTF8.GetString(bytes);
|
||||
using var document = JsonDocument.Parse(json);
|
||||
Assert.Equal(JsonValueKind.Object, document.RootElement.ValueKind);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Build_WithExpiresAt_SetsExpiration()
|
||||
{
|
||||
var decisionTime = new DateTimeOffset(2026, 1, 14, 10, 0, 0, TimeSpan.Zero);
|
||||
var expiresAt = new DateTimeOffset(2026, 4, 14, 10, 0, 0, TimeSpan.Zero);
|
||||
|
||||
var predicate = new VexOverridePredicateBuilder()
|
||||
.WithArtifactDigest("sha256:abc123")
|
||||
.WithVulnerabilityId("CVE-2024-12345")
|
||||
.WithDecision(VexOverrideDecision.Accepted)
|
||||
.WithJustification("Temporary acceptance")
|
||||
.WithDecisionTime(decisionTime)
|
||||
.WithOperatorId("user@example.com")
|
||||
.WithExpiresAt(expiresAt)
|
||||
.Build();
|
||||
|
||||
Assert.Equal(expiresAt, predicate.ExpiresAt);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Build_WithRuleDigestAndTraceHash_SetsValues()
|
||||
{
|
||||
var predicate = new VexOverridePredicateBuilder()
|
||||
.WithArtifactDigest("sha256:abc123")
|
||||
.WithVulnerabilityId("CVE-2024-12345")
|
||||
.WithDecision(VexOverrideDecision.NotAffected)
|
||||
.WithJustification("Test")
|
||||
.WithDecisionTime(DateTimeOffset.UtcNow)
|
||||
.WithOperatorId("user@example.com")
|
||||
.WithRuleDigest("sha256:rule123")
|
||||
.WithTraceHash("sha256:trace456")
|
||||
.Build();
|
||||
|
||||
Assert.Equal("sha256:rule123", predicate.RuleDigest);
|
||||
Assert.Equal("sha256:trace456", predicate.TraceHash);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// VexOverridePredicateParserTests.cs
|
||||
// Sprint: SPRINT_20260112_004_ATTESTOR_vex_override_predicate (ATT-VEX-002)
|
||||
// Description: Tests for VEX override predicate parsing
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using StellaOps.Attestor.StandardPredicates.VexOverride;
|
||||
using StellaOps.TestKit;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Attestor.StandardPredicates.Tests.VexOverride;
|
||||
|
||||
public sealed class VexOverridePredicateParserTests
|
||||
{
|
||||
private readonly VexOverridePredicateParser _parser;
|
||||
|
||||
public VexOverridePredicateParserTests()
|
||||
{
|
||||
_parser = new VexOverridePredicateParser(NullLogger<VexOverridePredicateParser>.Instance);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void PredicateType_ReturnsCorrectUri()
|
||||
{
|
||||
Assert.Equal(VexOverridePredicateTypes.PredicateTypeUri, _parser.PredicateType);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Parse_ValidPredicate_ReturnsValid()
|
||||
{
|
||||
var json = """
|
||||
{
|
||||
"artifactDigest": "sha256:abc123",
|
||||
"vulnerabilityId": "CVE-2024-12345",
|
||||
"decision": "not_affected",
|
||||
"justification": "Component is not in use",
|
||||
"decisionTime": "2026-01-14T10:00:00Z",
|
||||
"operatorId": "user@example.com"
|
||||
}
|
||||
""";
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var result = _parser.Parse(document.RootElement);
|
||||
|
||||
Assert.True(result.IsValid);
|
||||
Assert.Empty(result.Errors);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Parse_MissingArtifactDigest_ReturnsError()
|
||||
{
|
||||
var json = """
|
||||
{
|
||||
"vulnerabilityId": "CVE-2024-12345",
|
||||
"decision": "not_affected",
|
||||
"justification": "Component is not in use",
|
||||
"decisionTime": "2026-01-14T10:00:00Z",
|
||||
"operatorId": "user@example.com"
|
||||
}
|
||||
""";
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var result = _parser.Parse(document.RootElement);
|
||||
|
||||
Assert.False(result.IsValid);
|
||||
Assert.Contains(result.Errors, e => e.Code == "VEX_MISSING_ARTIFACT_DIGEST");
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Parse_MissingVulnerabilityId_ReturnsError()
|
||||
{
|
||||
var json = """
|
||||
{
|
||||
"artifactDigest": "sha256:abc123",
|
||||
"decision": "not_affected",
|
||||
"justification": "Component is not in use",
|
||||
"decisionTime": "2026-01-14T10:00:00Z",
|
||||
"operatorId": "user@example.com"
|
||||
}
|
||||
""";
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var result = _parser.Parse(document.RootElement);
|
||||
|
||||
Assert.False(result.IsValid);
|
||||
Assert.Contains(result.Errors, e => e.Code == "VEX_MISSING_VULN_ID");
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Parse_InvalidDecision_ReturnsError()
|
||||
{
|
||||
var json = """
|
||||
{
|
||||
"artifactDigest": "sha256:abc123",
|
||||
"vulnerabilityId": "CVE-2024-12345",
|
||||
"decision": "invalid_decision",
|
||||
"justification": "Component is not in use",
|
||||
"decisionTime": "2026-01-14T10:00:00Z",
|
||||
"operatorId": "user@example.com"
|
||||
}
|
||||
""";
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var result = _parser.Parse(document.RootElement);
|
||||
|
||||
Assert.False(result.IsValid);
|
||||
Assert.Contains(result.Errors, e => e.Code == "VEX_INVALID_DECISION");
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Theory]
|
||||
[InlineData("not_affected", VexOverrideDecision.NotAffected)]
|
||||
[InlineData("mitigated", VexOverrideDecision.Mitigated)]
|
||||
[InlineData("accepted", VexOverrideDecision.Accepted)]
|
||||
[InlineData("under_investigation", VexOverrideDecision.UnderInvestigation)]
|
||||
public void Parse_AllDecisionValues_Accepted(string decisionValue, VexOverrideDecision expected)
|
||||
{
|
||||
var json = $$"""
|
||||
{
|
||||
"artifactDigest": "sha256:abc123",
|
||||
"vulnerabilityId": "CVE-2024-12345",
|
||||
"decision": "{{decisionValue}}",
|
||||
"justification": "Test",
|
||||
"decisionTime": "2026-01-14T10:00:00Z",
|
||||
"operatorId": "user@example.com"
|
||||
}
|
||||
""";
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var result = _parser.Parse(document.RootElement);
|
||||
|
||||
Assert.True(result.IsValid);
|
||||
|
||||
var predicate = _parser.ParsePredicate(document.RootElement);
|
||||
Assert.NotNull(predicate);
|
||||
Assert.Equal(expected, predicate.Decision);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Parse_NumericDecision_Accepted()
|
||||
{
|
||||
var json = """
|
||||
{
|
||||
"artifactDigest": "sha256:abc123",
|
||||
"vulnerabilityId": "CVE-2024-12345",
|
||||
"decision": 1,
|
||||
"justification": "Test",
|
||||
"decisionTime": "2026-01-14T10:00:00Z",
|
||||
"operatorId": "user@example.com"
|
||||
}
|
||||
""";
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var result = _parser.Parse(document.RootElement);
|
||||
|
||||
Assert.True(result.IsValid);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Parse_WithEvidenceRefs_ParsesCorrectly()
|
||||
{
|
||||
var json = """
|
||||
{
|
||||
"artifactDigest": "sha256:abc123",
|
||||
"vulnerabilityId": "CVE-2024-12345",
|
||||
"decision": "not_affected",
|
||||
"justification": "Test",
|
||||
"decisionTime": "2026-01-14T10:00:00Z",
|
||||
"operatorId": "user@example.com",
|
||||
"evidenceRefs": [
|
||||
{
|
||||
"type": "document",
|
||||
"uri": "https://example.com/doc",
|
||||
"digest": "sha256:def456",
|
||||
"description": "Design document"
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var result = _parser.Parse(document.RootElement);
|
||||
|
||||
Assert.True(result.IsValid);
|
||||
|
||||
var predicate = _parser.ParsePredicate(document.RootElement);
|
||||
Assert.NotNull(predicate);
|
||||
Assert.Single(predicate.EvidenceRefs);
|
||||
Assert.Equal("document", predicate.EvidenceRefs[0].Type);
|
||||
Assert.Equal("https://example.com/doc", predicate.EvidenceRefs[0].Uri);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void Parse_WithTool_ParsesCorrectly()
|
||||
{
|
||||
var json = """
|
||||
{
|
||||
"artifactDigest": "sha256:abc123",
|
||||
"vulnerabilityId": "CVE-2024-12345",
|
||||
"decision": "mitigated",
|
||||
"justification": "Compensating control applied",
|
||||
"decisionTime": "2026-01-14T10:00:00Z",
|
||||
"operatorId": "user@example.com",
|
||||
"tool": {
|
||||
"name": "StellaOps",
|
||||
"version": "1.0.0",
|
||||
"vendor": "StellaOps Inc"
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var result = _parser.Parse(document.RootElement);
|
||||
|
||||
Assert.True(result.IsValid);
|
||||
|
||||
var predicate = _parser.ParsePredicate(document.RootElement);
|
||||
Assert.NotNull(predicate);
|
||||
Assert.NotNull(predicate.Tool);
|
||||
Assert.Equal("StellaOps", predicate.Tool.Name);
|
||||
Assert.Equal("1.0.0", predicate.Tool.Version);
|
||||
Assert.Equal("StellaOps Inc", predicate.Tool.Vendor);
|
||||
}
|
||||
|
||||
[Trait("Category", TestCategories.Unit)]
|
||||
[Fact]
|
||||
public void ExtractSbom_ReturnsNull()
|
||||
{
|
||||
var json = """
|
||||
{
|
||||
"artifactDigest": "sha256:abc123",
|
||||
"vulnerabilityId": "CVE-2024-12345",
|
||||
"decision": "not_affected",
|
||||
"justification": "Test",
|
||||
"decisionTime": "2026-01-14T10:00:00Z",
|
||||
"operatorId": "user@example.com"
|
||||
}
|
||||
""";
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var result = _parser.ExtractSbom(document.RootElement);
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user