audit notes work completed, test fixes work (95% done), new sprints, new data sources setup and configuration
This commit is contained in:
@@ -367,5 +367,5 @@ app.Run();
|
||||
// Make Program class internal to prevent type conflicts when referencing this assembly
|
||||
namespace StellaOps.Policy.Engine
|
||||
{
|
||||
internal partial class Program { }
|
||||
public partial class Program { }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
using StellaOps.Policy.AuthSignals;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Policy.AuthSignals.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for PolicyAuthSignal and related models.
|
||||
/// </summary>
|
||||
public sealed class PolicyAuthSignalTests
|
||||
{
|
||||
[Fact]
|
||||
public void PolicyAuthSignal_RequiredProperties_MustBeSet()
|
||||
{
|
||||
var signal = new PolicyAuthSignal
|
||||
{
|
||||
Id = "sig-001",
|
||||
Tenant = "tenant-abc",
|
||||
Subject = "artifact:sha256:abc123",
|
||||
SignalType = "reachability",
|
||||
Source = "scanner-v1",
|
||||
Created = DateTime.UtcNow
|
||||
};
|
||||
|
||||
Assert.Equal("sig-001", signal.Id);
|
||||
Assert.Equal("tenant-abc", signal.Tenant);
|
||||
Assert.Equal("artifact:sha256:abc123", signal.Subject);
|
||||
Assert.Equal("reachability", signal.SignalType);
|
||||
Assert.Equal("scanner-v1", signal.Source);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("reachability")]
|
||||
[InlineData("attestation")]
|
||||
[InlineData("risk")]
|
||||
[InlineData("vex")]
|
||||
public void PolicyAuthSignal_SignalType_SupportedValues(string signalType)
|
||||
{
|
||||
var signal = new PolicyAuthSignal
|
||||
{
|
||||
Id = "sig-type-test",
|
||||
Tenant = "t1",
|
||||
Subject = "s1",
|
||||
SignalType = signalType,
|
||||
Source = "test",
|
||||
Created = DateTime.UtcNow
|
||||
};
|
||||
|
||||
Assert.Equal(signalType, signal.SignalType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PolicyAuthSignal_WithConfidence_ContainsValue()
|
||||
{
|
||||
var signal = new PolicyAuthSignal
|
||||
{
|
||||
Id = "sig-conf",
|
||||
Tenant = "t1",
|
||||
Subject = "s1",
|
||||
SignalType = "risk",
|
||||
Source = "risk-engine",
|
||||
Confidence = 0.95,
|
||||
Created = DateTime.UtcNow
|
||||
};
|
||||
|
||||
Assert.Equal(0.95, signal.Confidence);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PolicyAuthSignal_WithEvidence_ContainsRefs()
|
||||
{
|
||||
var evidence = new[]
|
||||
{
|
||||
new EvidenceRef
|
||||
{
|
||||
Kind = "attestation",
|
||||
Uri = "oci://registry.io/attestation@sha256:xyz",
|
||||
Digest = "sha256:xyz123"
|
||||
},
|
||||
new EvidenceRef
|
||||
{
|
||||
Kind = "linkset",
|
||||
Uri = "https://transparency.example.com/entry/123",
|
||||
Digest = "sha256:link456",
|
||||
Scope = "org.example.project"
|
||||
}
|
||||
};
|
||||
|
||||
var signal = new PolicyAuthSignal
|
||||
{
|
||||
Id = "sig-ev",
|
||||
Tenant = "t1",
|
||||
Subject = "s1",
|
||||
SignalType = "attestation",
|
||||
Source = "attestor",
|
||||
Evidence = evidence,
|
||||
Created = DateTime.UtcNow
|
||||
};
|
||||
|
||||
Assert.Equal(2, signal.Evidence.Count);
|
||||
Assert.Equal("attestation", signal.Evidence[0].Kind);
|
||||
Assert.Equal("linkset", signal.Evidence[1].Kind);
|
||||
Assert.Equal("org.example.project", signal.Evidence[1].Scope);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PolicyAuthSignal_WithProvenance_ContainsDetails()
|
||||
{
|
||||
var provenance = new Provenance
|
||||
{
|
||||
Pipeline = "ci/build-and-scan",
|
||||
Inputs = new[] { "dockerfile:sha256:abc", "sources:sha256:def" },
|
||||
Signer = "build-bot@ci.example.com",
|
||||
Transparency = new Transparency
|
||||
{
|
||||
RekorUuid = "rekor-uuid-123456"
|
||||
}
|
||||
};
|
||||
|
||||
var signal = new PolicyAuthSignal
|
||||
{
|
||||
Id = "sig-prov",
|
||||
Tenant = "t1",
|
||||
Subject = "s1",
|
||||
SignalType = "attestation",
|
||||
Source = "signer",
|
||||
Provenance = provenance,
|
||||
Created = DateTime.UtcNow
|
||||
};
|
||||
|
||||
Assert.NotNull(signal.Provenance);
|
||||
Assert.Equal("ci/build-and-scan", signal.Provenance.Pipeline);
|
||||
Assert.Equal(2, signal.Provenance.Inputs!.Count);
|
||||
Assert.Equal("build-bot@ci.example.com", signal.Provenance.Signer);
|
||||
Assert.Equal("rekor-uuid-123456", signal.Provenance.Transparency!.RekorUuid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PolicyAuthSignal_DefaultEvidence_IsEmpty()
|
||||
{
|
||||
var signal = new PolicyAuthSignal
|
||||
{
|
||||
Id = "sig-default",
|
||||
Tenant = "t1",
|
||||
Subject = "s1",
|
||||
SignalType = "vex",
|
||||
Source = "scanner",
|
||||
Created = DateTime.UtcNow
|
||||
};
|
||||
|
||||
Assert.Empty(signal.Evidence);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Transparency_WithSkipReason_NoRekorUuid()
|
||||
{
|
||||
var transparency = new Transparency
|
||||
{
|
||||
SkipReason = "airgapped_environment"
|
||||
};
|
||||
|
||||
Assert.Null(transparency.RekorUuid);
|
||||
Assert.Equal("airgapped_environment", transparency.SkipReason);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PolicyAuthSignal_RecordEquality_WorksCorrectly()
|
||||
{
|
||||
var created = DateTime.UtcNow;
|
||||
|
||||
var signal1 = new PolicyAuthSignal
|
||||
{
|
||||
Id = "sig-eq",
|
||||
Tenant = "t1",
|
||||
Subject = "s1",
|
||||
SignalType = "risk",
|
||||
Source = "engine",
|
||||
Confidence = 0.8,
|
||||
Created = created
|
||||
};
|
||||
|
||||
var signal2 = new PolicyAuthSignal
|
||||
{
|
||||
Id = "sig-eq",
|
||||
Tenant = "t1",
|
||||
Subject = "s1",
|
||||
SignalType = "risk",
|
||||
Source = "engine",
|
||||
Confidence = 0.8,
|
||||
Created = created
|
||||
};
|
||||
|
||||
Assert.Equal(signal1, signal2);
|
||||
Assert.Equal(signal1.GetHashCode(), signal2.GetHashCode());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests for EvidenceRef record.
|
||||
/// </summary>
|
||||
public sealed class EvidenceRefTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("linkset")]
|
||||
[InlineData("runtime")]
|
||||
[InlineData("attestation")]
|
||||
[InlineData("bundle")]
|
||||
public void EvidenceRef_Kind_SupportedValues(string kind)
|
||||
{
|
||||
var evidenceRef = new EvidenceRef
|
||||
{
|
||||
Kind = kind,
|
||||
Uri = "https://example.com/evidence",
|
||||
Digest = "sha256:abc123"
|
||||
};
|
||||
|
||||
Assert.Equal(kind, evidenceRef.Kind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvidenceRef_DefaultValues_AreEmpty()
|
||||
{
|
||||
var evidenceRef = new EvidenceRef();
|
||||
|
||||
Assert.Equal(string.Empty, evidenceRef.Kind);
|
||||
Assert.Equal(string.Empty, evidenceRef.Uri);
|
||||
Assert.Equal(string.Empty, evidenceRef.Digest);
|
||||
Assert.Null(evidenceRef.Scope);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvidenceRef_WithScope_ContainsValue()
|
||||
{
|
||||
var evidenceRef = new EvidenceRef
|
||||
{
|
||||
Kind = "runtime",
|
||||
Uri = "https://example.com/runtime-check",
|
||||
Digest = "sha256:runtime123",
|
||||
Scope = "org.example.service.api"
|
||||
};
|
||||
|
||||
Assert.Equal("org.example.service.api", evidenceRef.Scope);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests for Provenance record.
|
||||
/// </summary>
|
||||
public sealed class ProvenanceTests
|
||||
{
|
||||
[Fact]
|
||||
public void Provenance_AllPropertiesOptional()
|
||||
{
|
||||
var provenance = new Provenance();
|
||||
|
||||
Assert.Null(provenance.Pipeline);
|
||||
Assert.Null(provenance.Inputs);
|
||||
Assert.Null(provenance.Signer);
|
||||
Assert.Null(provenance.Transparency);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Provenance_WithAllProperties_ContainsValues()
|
||||
{
|
||||
var provenance = new Provenance
|
||||
{
|
||||
Pipeline = "github-actions/build",
|
||||
Inputs = new[] { "src:sha256:123", "config:sha256:456" },
|
||||
Signer = "sigstore-bot",
|
||||
Transparency = new Transparency { RekorUuid = "uuid-789" }
|
||||
};
|
||||
|
||||
Assert.Equal("github-actions/build", provenance.Pipeline);
|
||||
Assert.Equal(2, provenance.Inputs!.Count);
|
||||
Assert.Equal("sigstore-bot", provenance.Signer);
|
||||
Assert.NotNull(provenance.Transparency);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<IsPackable>false</IsPackable>
|
||||
<OutputType>Exe</OutputType>
|
||||
<UseXunitV3>true</UseXunitV3>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Moq" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\__Libraries\StellaOps.Policy.AuthSignals\StellaOps.Policy.AuthSignals.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
|
||||
"diagnosticMessages": true,
|
||||
"parallelizeAssembly": true,
|
||||
"parallelizeTestCollections": true,
|
||||
"maxParallelThreads": -1
|
||||
}
|
||||
@@ -127,7 +127,7 @@ internal sealed class TestAuthHandler : AuthenticationHandler<AuthenticationSche
|
||||
IOptionsMonitor<AuthenticationSchemeOptions> options,
|
||||
ILoggerFactory logger,
|
||||
UrlEncoder encoder,
|
||||
ISystemClock clock)
|
||||
TimeProvider clock)
|
||||
: base(options, logger, encoder, clock)
|
||||
{
|
||||
}
|
||||
@@ -154,3 +154,4 @@ internal sealed class TestAuthHandler : AuthenticationHandler<AuthenticationSche
|
||||
return Task.FromResult(AuthenticateResult.Success(ticket));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,308 @@
|
||||
using StellaOps.Policy.Predicates.FixChain;
|
||||
using System.Collections.Immutable;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Policy.Predicates.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for FixChainGateAction enum.
|
||||
/// </summary>
|
||||
public sealed class FixChainGateActionTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(FixChainGateAction.Block)]
|
||||
[InlineData(FixChainGateAction.Warn)]
|
||||
public void FixChainGateAction_AllValues_AreDefined(FixChainGateAction action)
|
||||
{
|
||||
Assert.True(Enum.IsDefined(action));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FixChainGateAction_AllValues_AreCounted()
|
||||
{
|
||||
var values = Enum.GetValues<FixChainGateAction>();
|
||||
Assert.Equal(2, values.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests for FixChainGateOutcome enum.
|
||||
/// </summary>
|
||||
public sealed class FixChainGateOutcomeTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(FixChainGateOutcome.FixVerified)]
|
||||
[InlineData(FixChainGateOutcome.SeverityExempt)]
|
||||
[InlineData(FixChainGateOutcome.GracePeriod)]
|
||||
[InlineData(FixChainGateOutcome.AttestationRequired)]
|
||||
[InlineData(FixChainGateOutcome.InsufficientConfidence)]
|
||||
[InlineData(FixChainGateOutcome.InconclusiveNotAllowed)]
|
||||
[InlineData(FixChainGateOutcome.StillVulnerable)]
|
||||
[InlineData(FixChainGateOutcome.GoldenSetNotApproved)]
|
||||
[InlineData(FixChainGateOutcome.PartialFix)]
|
||||
public void FixChainGateOutcome_AllValues_AreDefined(FixChainGateOutcome outcome)
|
||||
{
|
||||
Assert.True(Enum.IsDefined(outcome));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FixChainGateOutcome_AllValues_AreCounted()
|
||||
{
|
||||
var values = Enum.GetValues<FixChainGateOutcome>();
|
||||
Assert.Equal(9, values.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests for FixChainGateContext record.
|
||||
/// </summary>
|
||||
public sealed class FixChainGateContextTests
|
||||
{
|
||||
[Fact]
|
||||
public void FixChainGateContext_RequiredProperties_MustBeSet()
|
||||
{
|
||||
var context = new FixChainGateContext
|
||||
{
|
||||
CveId = "CVE-2024-0001",
|
||||
ComponentPurl = "pkg:deb/debian/nginx@1.18.0-6.1",
|
||||
Severity = "critical",
|
||||
CvssScore = 9.8m
|
||||
};
|
||||
|
||||
Assert.Equal("CVE-2024-0001", context.CveId);
|
||||
Assert.Equal("pkg:deb/debian/nginx@1.18.0-6.1", context.ComponentPurl);
|
||||
Assert.Equal("critical", context.Severity);
|
||||
Assert.Equal(9.8m, context.CvssScore);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FixChainGateContext_Environment_DefaultsToProduction()
|
||||
{
|
||||
var context = new FixChainGateContext
|
||||
{
|
||||
CveId = "CVE-2024-0001",
|
||||
ComponentPurl = "pkg:npm/%40example/lib@1.0.0",
|
||||
Severity = "high",
|
||||
CvssScore = 7.5m
|
||||
};
|
||||
|
||||
Assert.Equal("production", context.Environment);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FixChainGateContext_OptionalProperties_CanBeNull()
|
||||
{
|
||||
var context = new FixChainGateContext
|
||||
{
|
||||
CveId = "CVE-2024-0001",
|
||||
ComponentPurl = "pkg:pypi/requests@2.28.0",
|
||||
Severity = "medium",
|
||||
CvssScore = 5.3m
|
||||
};
|
||||
|
||||
Assert.Null(context.BinarySha256);
|
||||
Assert.Null(context.CvePublishedAt);
|
||||
Assert.Null(context.Metadata);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FixChainGateContext_WithAllOptionalProperties_ContainsValues()
|
||||
{
|
||||
var publishedAt = DateTimeOffset.UtcNow.AddDays(-10);
|
||||
var metadata = new Dictionary<string, string>
|
||||
{
|
||||
["ticket"] = "SEC-123",
|
||||
["reviewer"] = "security-team"
|
||||
};
|
||||
|
||||
var context = new FixChainGateContext
|
||||
{
|
||||
CveId = "CVE-2024-0001",
|
||||
ComponentPurl = "pkg:golang/github.com/example/lib@v1.2.3",
|
||||
Severity = "high",
|
||||
CvssScore = 8.1m,
|
||||
BinarySha256 = "sha256:abc123def456",
|
||||
CvePublishedAt = publishedAt,
|
||||
Environment = "staging",
|
||||
Metadata = metadata
|
||||
};
|
||||
|
||||
Assert.Equal("sha256:abc123def456", context.BinarySha256);
|
||||
Assert.Equal(publishedAt, context.CvePublishedAt);
|
||||
Assert.Equal("staging", context.Environment);
|
||||
Assert.NotNull(context.Metadata);
|
||||
Assert.Equal("SEC-123", context.Metadata["ticket"]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests for FixChainGateParameters record.
|
||||
/// </summary>
|
||||
public sealed class FixChainGateParametersTests
|
||||
{
|
||||
[Fact]
|
||||
public void FixChainGateParameters_HasSensibleDefaults()
|
||||
{
|
||||
var parameters = new FixChainGateParameters();
|
||||
|
||||
Assert.Equal(2, parameters.Severities.Length);
|
||||
Assert.Contains("critical", parameters.Severities);
|
||||
Assert.Contains("high", parameters.Severities);
|
||||
Assert.Equal(0.85m, parameters.MinConfidence);
|
||||
Assert.Equal(7, parameters.GracePeriodDays);
|
||||
Assert.True(parameters.RequireApprovedGoldenSet);
|
||||
Assert.Equal(FixChainGateAction.Block, parameters.FailureAction);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FixChainGateParameters_AllowInconclusive_DefaultsFalse()
|
||||
{
|
||||
var parameters = new FixChainGateParameters();
|
||||
|
||||
Assert.False(parameters.AllowInconclusive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FixChainGateParameters_CustomConfiguration_OverridesDefaults()
|
||||
{
|
||||
var parameters = new FixChainGateParameters
|
||||
{
|
||||
Severities = ["critical"],
|
||||
MinConfidence = 0.95m,
|
||||
GracePeriodDays = 14,
|
||||
AllowInconclusive = true,
|
||||
RequireApprovedGoldenSet = false,
|
||||
FailureAction = FixChainGateAction.Warn
|
||||
};
|
||||
|
||||
Assert.Single(parameters.Severities);
|
||||
Assert.Equal(0.95m, parameters.MinConfidence);
|
||||
Assert.Equal(14, parameters.GracePeriodDays);
|
||||
Assert.True(parameters.AllowInconclusive);
|
||||
Assert.False(parameters.RequireApprovedGoldenSet);
|
||||
Assert.Equal(FixChainGateAction.Warn, parameters.FailureAction);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests for FixChainGateResult record.
|
||||
/// </summary>
|
||||
public sealed class FixChainGateResultTests
|
||||
{
|
||||
[Fact]
|
||||
public void FixChainGateResult_PassingResult_HasCorrectProperties()
|
||||
{
|
||||
var result = new FixChainGateResult
|
||||
{
|
||||
Passed = true,
|
||||
Outcome = FixChainGateOutcome.FixVerified,
|
||||
Reason = "Fix verified with 0.92 confidence",
|
||||
Action = FixChainGateAction.Block,
|
||||
EvaluatedAt = DateTimeOffset.UtcNow
|
||||
};
|
||||
|
||||
Assert.True(result.Passed);
|
||||
Assert.Equal(FixChainGateOutcome.FixVerified, result.Outcome);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FixChainGateResult_FailingResult_HasRecommendations()
|
||||
{
|
||||
var result = new FixChainGateResult
|
||||
{
|
||||
Passed = false,
|
||||
Outcome = FixChainGateOutcome.AttestationRequired,
|
||||
Reason = "No fix attestation found for critical CVE",
|
||||
Action = FixChainGateAction.Block,
|
||||
EvaluatedAt = DateTimeOffset.UtcNow,
|
||||
Recommendations = ["Run stella-cli verify-fix CVE-2024-0001", "Update component to patched version"]
|
||||
};
|
||||
|
||||
Assert.False(result.Passed);
|
||||
Assert.Equal(2, result.Recommendations.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FixChainGateResult_WithAttestation_ContainsAttestationInfo()
|
||||
{
|
||||
var attestation = new FixChainAttestationInfo
|
||||
{
|
||||
ContentDigest = "sha256:abc123",
|
||||
VerdictStatus = "fixed",
|
||||
Confidence = 0.95m,
|
||||
GoldenSetId = "golden-001",
|
||||
VerifiedAt = DateTimeOffset.UtcNow.AddHours(-1)
|
||||
};
|
||||
|
||||
var result = new FixChainGateResult
|
||||
{
|
||||
Passed = true,
|
||||
Outcome = FixChainGateOutcome.FixVerified,
|
||||
Reason = "Fix verified",
|
||||
Action = FixChainGateAction.Block,
|
||||
EvaluatedAt = DateTimeOffset.UtcNow,
|
||||
Attestation = attestation
|
||||
};
|
||||
|
||||
Assert.NotNull(result.Attestation);
|
||||
Assert.Equal("sha256:abc123", result.Attestation.ContentDigest);
|
||||
Assert.Equal(0.95m, result.Attestation.Confidence);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests for FixChainAttestationInfo record.
|
||||
/// </summary>
|
||||
public sealed class FixChainAttestationInfoTests
|
||||
{
|
||||
[Fact]
|
||||
public void FixChainAttestationInfo_RequiredProperties_MustBeSet()
|
||||
{
|
||||
var verifiedAt = DateTimeOffset.UtcNow;
|
||||
|
||||
var info = new FixChainAttestationInfo
|
||||
{
|
||||
ContentDigest = "sha256:xyz789",
|
||||
VerdictStatus = "fixed",
|
||||
Confidence = 0.88m,
|
||||
VerifiedAt = verifiedAt
|
||||
};
|
||||
|
||||
Assert.Equal("sha256:xyz789", info.ContentDigest);
|
||||
Assert.Equal("fixed", info.VerdictStatus);
|
||||
Assert.Equal(0.88m, info.Confidence);
|
||||
Assert.Equal(verifiedAt, info.VerifiedAt);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FixChainAttestationInfo_WithRationale_ContainsItems()
|
||||
{
|
||||
var info = new FixChainAttestationInfo
|
||||
{
|
||||
ContentDigest = "sha256:abc",
|
||||
VerdictStatus = "fixed",
|
||||
Confidence = 0.90m,
|
||||
VerifiedAt = DateTimeOffset.UtcNow,
|
||||
Rationale = ["Patch applied in version 1.2.3", "Binary hash matches golden set"]
|
||||
};
|
||||
|
||||
Assert.Equal(2, info.Rationale.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests for FixChainGateOptions record.
|
||||
/// </summary>
|
||||
public sealed class FixChainGateOptionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void FixChainGateOptions_HasSensibleDefaults()
|
||||
{
|
||||
var options = new FixChainGateOptions();
|
||||
|
||||
Assert.True(options.Enabled);
|
||||
Assert.Equal(0.85m, options.DefaultMinConfidence);
|
||||
Assert.Equal(7, options.DefaultGracePeriodDays);
|
||||
Assert.True(options.NotifyOnBlock);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<IsPackable>false</IsPackable>
|
||||
<OutputType>Exe</OutputType>
|
||||
<UseXunitV3>true</UseXunitV3>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Moq" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\__Libraries\StellaOps.Policy.Predicates\StellaOps.Policy.Predicates.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
|
||||
"diagnosticMessages": true,
|
||||
"parallelizeAssembly": true,
|
||||
"parallelizeTestCollections": true,
|
||||
"maxParallelThreads": -1
|
||||
}
|
||||
Reference in New Issue
Block a user