license switch agpl -> busl1, sprints work, new product advisories

This commit is contained in:
master
2026-01-20 15:32:20 +02:00
parent 4903395618
commit c32fff8f86
1835 changed files with 38630 additions and 4359 deletions

View File

@@ -26,7 +26,8 @@
<ProjectReference Include="../../../__Libraries/StellaOps.Configuration/StellaOps.Configuration.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.DependencyInjection/StellaOps.DependencyInjection.csproj" />
<ProjectReference Include="..\..\StellaOps.Attestor.Verify\StellaOps.Attestor.Verify.csproj" />
<ProjectReference Include="..\..\__Libraries\StellaOps.Attestor.Timestamping\StellaOps.Attestor.Timestamping.csproj" />
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Cryptography.Plugin.SmSoft\StellaOps.Cryptography.Plugin.SmSoft.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
</Project>
</Project>

View File

@@ -8,3 +8,7 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
| AUDIT-0066-M | DONE | Revalidated 2026-01-06 (maintainability audit). |
| AUDIT-0066-T | DONE | Revalidated 2026-01-06 (test coverage audit). |
| AUDIT-0066-A | DONE | Waived (test project; revalidated 2026-01-06). |
| ATT-001 | DONE | Timestamping service unit tests for envelope digest handling. |
| ATT-002 | DONE | Timestamp verification scenarios covered. |
| ATT-003 | DONE | Timestamp policy evaluator scenarios covered. |
| ATT-006 | DONE | Time correlation validator unit tests added. |

View File

@@ -0,0 +1,122 @@
using FluentAssertions;
using StellaOps.Attestor.Timestamping;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Attestor.Tests.Timestamping;
public sealed class AttestationTimestampPolicyTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Evaluate_WhenRfc3161RequiredAndMissing_Fails()
{
var evaluator = new TimestampPolicyEvaluator();
var context = new AttestationTimestampPolicyContext { HasValidTst = false };
var policy = new TimestampPolicy { RequireRfc3161 = true };
var result = evaluator.Evaluate(context, policy);
result.IsCompliant.Should().BeFalse();
result.Violations.Should().Contain(v => v.RuleId == "require-rfc3161");
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Evaluate_WhenSkewExceedsThreshold_Fails()
{
var evaluator = new TimestampPolicyEvaluator();
var context = new AttestationTimestampPolicyContext
{
HasValidTst = true,
TimeSkew = TimeSpan.FromMinutes(10)
};
var policy = new TimestampPolicy
{
RequireRfc3161 = true,
MaxTimeSkew = TimeSpan.FromMinutes(5)
};
var result = evaluator.Evaluate(context, policy);
result.IsCompliant.Should().BeFalse();
result.Violations.Should().Contain(v => v.RuleId == "time-skew");
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Evaluate_WhenRevocationStapleMissing_Fails()
{
var evaluator = new TimestampPolicyEvaluator();
var context = new AttestationTimestampPolicyContext
{
HasValidTst = true,
OcspStatus = null,
CrlChecked = false
};
var policy = new TimestampPolicy
{
RequireRfc3161 = true,
RequireRevocationStapling = true
};
var result = evaluator.Evaluate(context, policy);
result.IsCompliant.Should().BeFalse();
result.Violations.Should().Contain(v => v.RuleId == "revocation-staple");
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Evaluate_WhenTsaNotTrusted_Fails()
{
var evaluator = new TimestampPolicyEvaluator();
var context = new AttestationTimestampPolicyContext
{
HasValidTst = true,
TsaName = "Untrusted TSA",
OcspStatus = "Good",
CrlChecked = true
};
var policy = new TimestampPolicy
{
RequireRfc3161 = true,
RequireRevocationStapling = true,
TrustedTsas = new[] { "Trusted TSA" }
};
var result = evaluator.Evaluate(context, policy);
result.IsCompliant.Should().BeFalse();
result.Violations.Should().Contain(v => v.RuleId == "trusted-tsa");
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Evaluate_WhenAllRequirementsMet_Passes()
{
var evaluator = new TimestampPolicyEvaluator();
var context = new AttestationTimestampPolicyContext
{
HasValidTst = true,
TimeSkew = TimeSpan.FromMinutes(1),
TsaName = "Trusted TSA",
OcspStatus = "Good",
CrlChecked = true,
TsaCertificateExpires = DateTimeOffset.UtcNow.AddDays(365)
};
var policy = new TimestampPolicy
{
RequireRfc3161 = true,
RequireRevocationStapling = true,
MaxTimeSkew = TimeSpan.FromMinutes(5),
MinCertificateFreshness = TimeSpan.FromDays(180),
TrustedTsas = new[] { "Trusted TSA" }
};
var result = evaluator.Evaluate(context, policy);
result.IsCompliant.Should().BeTrue();
result.Violations.Should().BeEmpty();
}
}

View File

@@ -0,0 +1,115 @@
using System.Security.Cryptography;
using System.Text;
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.Attestor.Timestamping;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Attestor.Tests.Timestamping;
public sealed class AttestationTimestampServiceTests
{
private static AttestationTimestampService CreateService()
{
var options = Options.Create(new AttestationTimestampServiceOptions());
return new AttestationTimestampService(options, NullLogger<AttestationTimestampService>.Instance);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TimestampAsync_ComputesEnvelopeDigest()
{
var service = CreateService();
var envelope = Encoding.UTF8.GetBytes("{\"payload\":\"test\"}");
var result = await service.TimestampAsync(
envelope,
new AttestationTimestampOptions { HashAlgorithm = "SHA256" });
var expectedHash = Convert.ToHexString(SHA256.HashData(envelope)).ToLowerInvariant();
result.EnvelopeDigest.Should().Be($"sha256:{expectedHash}");
result.Envelope.Should().Equal(envelope);
result.TsaName.Should().NotBeNullOrWhiteSpace();
result.TsaPolicyOid.Should().NotBeNullOrWhiteSpace();
result.TimestampTime.Should().NotBe(default);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_WithConsistentRekorTime_ReturnsSuccess()
{
var service = CreateService();
var envelope = Encoding.UTF8.GetBytes("{\"payload\":\"test\"}");
var digest = "sha256:" + Convert.ToHexString(SHA256.HashData(envelope)).ToLowerInvariant();
var tstTime = new DateTimeOffset(2026, 1, 19, 12, 0, 0, TimeSpan.Zero);
var attestation = new TimestampedAttestation
{
Envelope = envelope,
EnvelopeDigest = digest,
TimeStampToken = Array.Empty<byte>(),
TimestampTime = tstTime,
TsaName = "Test TSA",
TsaPolicyOid = "1.2.3.4",
RekorReceipt = new RekorReceipt
{
LogId = "rekor",
LogIndex = 42,
IntegratedTime = tstTime.AddMinutes(1)
}
};
var options = new AttestationTimestampVerificationOptions
{
RequireRekorConsistency = true,
MaxTimeSkew = TimeSpan.FromMinutes(5),
VerifyTsaRevocation = false
};
var result = await service.VerifyAsync(attestation, options);
result.IsValid.Should().BeTrue();
result.TimeConsistency.Should().NotBeNull();
result.TimeConsistency!.WithinTolerance.Should().BeTrue();
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_WithRekorInconsistency_ReturnsFailure()
{
var service = CreateService();
var envelope = Encoding.UTF8.GetBytes("{\"payload\":\"test\"}");
var digest = "sha256:" + Convert.ToHexString(SHA256.HashData(envelope)).ToLowerInvariant();
var tstTime = new DateTimeOffset(2026, 1, 19, 12, 0, 0, TimeSpan.Zero);
var attestation = new TimestampedAttestation
{
Envelope = envelope,
EnvelopeDigest = digest,
TimeStampToken = Array.Empty<byte>(),
TimestampTime = tstTime,
TsaName = "Test TSA",
TsaPolicyOid = "1.2.3.4",
RekorReceipt = new RekorReceipt
{
LogId = "rekor",
LogIndex = 42,
IntegratedTime = tstTime.AddMinutes(-10)
}
};
var options = new AttestationTimestampVerificationOptions
{
RequireRekorConsistency = true,
MaxTimeSkew = TimeSpan.FromMinutes(5),
VerifyTsaRevocation = false
};
var result = await service.VerifyAsync(attestation, options);
result.IsValid.Should().BeFalse();
result.TstStatus.Should().Be(TstVerificationStatus.TimeInconsistency);
}
}

View File

@@ -0,0 +1,85 @@
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Attestor.Timestamping;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Attestor.Tests.Timestamping;
public sealed class TimeCorrelationValidatorTests
{
private static TimeCorrelationValidator CreateValidator()
=> new(NullLogger<TimeCorrelationValidator>.Instance);
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Validate_WhenGapWithinLimits_ReturnsValid()
{
var validator = CreateValidator();
var tstTime = new DateTimeOffset(2026, 1, 19, 12, 0, 0, TimeSpan.Zero);
var rekorTime = tstTime.AddSeconds(30);
var result = validator.Validate(tstTime, rekorTime);
result.Valid.Should().BeTrue();
result.Status.Should().Be(TimeCorrelationStatus.Valid);
result.Suspicious.Should().BeFalse();
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Validate_WhenGapExceedsMaximum_ReturnsGapExceeded()
{
var validator = CreateValidator();
var policy = new TimeCorrelationPolicy
{
MaximumGap = TimeSpan.FromMinutes(2),
SuspiciousGap = TimeSpan.FromMinutes(1)
};
var tstTime = new DateTimeOffset(2026, 1, 19, 12, 0, 0, TimeSpan.Zero);
var rekorTime = tstTime.AddMinutes(5);
var result = validator.Validate(tstTime, rekorTime, policy);
result.Valid.Should().BeFalse();
result.Status.Should().Be(TimeCorrelationStatus.GapExceeded);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Validate_WhenTstAfterRekor_ReturnsInvalid()
{
var validator = CreateValidator();
var policy = new TimeCorrelationPolicy
{
ClockSkewTolerance = TimeSpan.FromSeconds(5)
};
var tstTime = new DateTimeOffset(2026, 1, 19, 12, 0, 10, TimeSpan.Zero);
var rekorTime = tstTime.AddSeconds(-30);
var result = validator.Validate(tstTime, rekorTime, policy);
result.Valid.Should().BeFalse();
result.Status.Should().Be(TimeCorrelationStatus.TstAfterRekor);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Validate_WhenSuspiciousAndFailOnSuspicious_ReturnsFailure()
{
var validator = CreateValidator();
var policy = new TimeCorrelationPolicy
{
MaximumGap = TimeSpan.FromMinutes(5),
SuspiciousGap = TimeSpan.FromSeconds(10),
FailOnSuspicious = true
};
var tstTime = new DateTimeOffset(2026, 1, 19, 12, 0, 0, TimeSpan.Zero);
var rekorTime = tstTime.AddSeconds(30);
var result = validator.Validate(tstTime, rekorTime, policy);
result.Valid.Should().BeFalse();
result.Status.Should().Be(TimeCorrelationStatus.SuspiciousGapFailed);
}
}