audit work, fixed StellaOps.sln warnings/errors, fixed tests, sprints work, new advisories

This commit is contained in:
master
2026-01-07 18:49:59 +02:00
parent 04ec098046
commit 608a7f85c0
866 changed files with 56323 additions and 6231 deletions

View File

@@ -12,10 +12,6 @@
<ItemGroup>
<PackageReference Include="FsCheck.Xunit.v3" />
<PackageReference Include="FsCheck" />
<PackageReference Include="xunit.runner.visualstudio" >
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" >
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>

View File

@@ -1,5 +1,6 @@
using System.Collections.Immutable;
using System.Formats.Asn1;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
@@ -626,7 +627,7 @@ public sealed class AttestorVerificationEngine : IAttestorVerificationEngine
if (entry.Proof?.Checkpoint?.Timestamp is not null)
{
attributes = attributes.Add("checkpointTs", entry.Proof.Checkpoint.Timestamp.Value.ToString("O"));
attributes = attributes.Add("checkpointTs", entry.Proof.Checkpoint.Timestamp.Value.ToString("O", CultureInfo.InvariantCulture));
}
return new PolicyEvaluationResult

View File

@@ -26,25 +26,28 @@ public class DistributedVerificationProvider : IVerificationProvider
private readonly ILogger<DistributedVerificationProvider> _logger;
private readonly DistributedVerificationOptions _options;
private readonly HttpClient _httpClient;
private readonly TimeProvider _timeProvider;
private readonly ConcurrentDictionary<string, CircuitBreakerState> _circuitStates = new();
private readonly ConsistentHashRing _hashRing;
public DistributedVerificationProvider(
ILogger<DistributedVerificationProvider> logger,
IOptions<DistributedVerificationOptions> options,
HttpClient httpClient)
HttpClient httpClient,
TimeProvider? timeProvider = null)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_timeProvider = timeProvider ?? TimeProvider.System;
if (_options.Nodes == null || _options.Nodes.Count == 0)
{
throw new ArgumentException("At least one verification node must be configured");
}
_hashRing = new ConsistentHashRing(_options.Nodes, _options.VirtualNodeMultiplier);
_logger.LogInformation("Initialized distributed verification provider with {NodeCount} nodes", _options.Nodes.Count);
}
@@ -106,7 +109,7 @@ public class DistributedVerificationProvider : IVerificationProvider
RequestId = request.RequestId,
Status = VerificationStatus.Error,
ErrorMessage = $"All verification nodes failed. {exceptions.Count} errors occurred.",
Timestamp = DateTimeOffset.UtcNow,
Timestamp = _timeProvider.GetUtcNow(),
};
}
@@ -144,7 +147,7 @@ public class DistributedVerificationProvider : IVerificationProvider
HealthyNodeCount = healthyCount,
TotalNodeCount = totalCount,
NodeStatuses = results.ToDictionary(r => r.Key, r => r.Value),
Timestamp = DateTimeOffset.UtcNow,
Timestamp = _timeProvider.GetUtcNow(),
};
}
@@ -237,8 +240,8 @@ public class DistributedVerificationProvider : IVerificationProvider
}
// Allow recovery after cooldown period
if (state.LastFailure.HasValue &&
DateTimeOffset.UtcNow - state.LastFailure.Value > _options.CircuitBreakerCooldown)
if (state.LastFailure.HasValue &&
_timeProvider.GetUtcNow() - state.LastFailure.Value > _options.CircuitBreakerCooldown)
{
state.FailureCount = 0;
state.LastFailure = null;
@@ -252,7 +255,7 @@ public class DistributedVerificationProvider : IVerificationProvider
{
var state = _circuitStates.GetOrAdd(node.Id, _ => new CircuitBreakerState());
state.FailureCount++;
state.LastFailure = DateTimeOffset.UtcNow;
state.LastFailure = _timeProvider.GetUtcNow();
if (state.FailureCount >= _options.CircuitBreakerThreshold)
{

View File

@@ -13,10 +13,6 @@
<ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" />
<PackageReference Include="FluentAssertions" />
<PackageReference Include="xunit.runner.visualstudio" >
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" >
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
@@ -645,7 +646,7 @@ internal sealed class AttestorSubmissionService : IAttestorSubmissionService
Aggregator = witness.Aggregator,
Status = witness.Status,
RootHash = witness.RootHash,
RetrievedAt = witness.RetrievedAt == default ? null : witness.RetrievedAt.ToString("O"),
RetrievedAt = witness.RetrievedAt == default ? null : witness.RetrievedAt.ToString("O", CultureInfo.InvariantCulture),
Statement = witness.Statement,
Signature = witness.Signature,
KeyId = witness.KeyId,
@@ -667,7 +668,7 @@ internal sealed class AttestorSubmissionService : IAttestorSubmissionService
Origin = proof.Checkpoint.Origin,
Size = proof.Checkpoint.Size,
RootHash = proof.Checkpoint.RootHash,
Timestamp = proof.Checkpoint.Timestamp?.ToString("O")
Timestamp = proof.Checkpoint.Timestamp?.ToString("O", CultureInfo.InvariantCulture)
},
Inclusion = proof.Inclusion is null ? null : new AttestorSubmissionResult.InclusionProof
{

View File

@@ -13,10 +13,6 @@
<PackageReference Include="NSubstitute" />
<PackageReference Include="Testcontainers" />
<PackageReference Include="Testcontainers.PostgreSql" />
<PackageReference Include="xunit.runner.visualstudio" >
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" >
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>

View File

@@ -1,3 +1,4 @@
using System.Globalization;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Text.Json;
@@ -131,7 +132,7 @@ internal static class AttestorWebServiceEndpoints
Algorithm = result.Algorithm,
Mode = result.Mode,
Provider = result.Provider,
SignedAt = result.SignedAt.ToString("O")
SignedAt = result.SignedAt.ToString("O", CultureInfo.InvariantCulture)
}
};
return Results.Ok(response);
@@ -251,14 +252,14 @@ internal static class AttestorWebServiceEndpoints
{
KeyId = result.SignerKeyId,
Algorithm = result.Algorithm,
SignedAt = result.SignedAt.ToString("O")
SignedAt = result.SignedAt.ToString("O", CultureInfo.InvariantCulture)
},
Rekor = result.RekorEntry is null ? null : new InTotoRekorEntryDto
{
LogId = result.RekorEntry.LogId,
LogIndex = result.RekorEntry.LogIndex,
Uuid = result.RekorEntry.Uuid,
IntegratedTime = result.RekorEntry.IntegratedTime?.ToString("O")
IntegratedTime = result.RekorEntry.IntegratedTime?.ToString("O", CultureInfo.InvariantCulture)
}
};
@@ -424,7 +425,7 @@ internal static class AttestorWebServiceEndpoints
Origin = entry.Proof.Checkpoint.Origin,
Size = entry.Proof.Checkpoint.Size,
RootHash = entry.Proof.Checkpoint.RootHash,
Timestamp = entry.Proof.Checkpoint.Timestamp?.ToString("O")
Timestamp = entry.Proof.Checkpoint.Timestamp?.ToString("O", CultureInfo.InvariantCulture)
},
Inclusion = entry.Proof.Inclusion is null ? null : new AttestationInclusionDto
{
@@ -448,7 +449,7 @@ internal static class AttestorWebServiceEndpoints
Origin = entry.Mirror.Proof.Checkpoint.Origin,
Size = entry.Mirror.Proof.Checkpoint.Size,
RootHash = entry.Mirror.Proof.Checkpoint.RootHash,
Timestamp = entry.Mirror.Proof.Checkpoint.Timestamp?.ToString("O")
Timestamp = entry.Mirror.Proof.Checkpoint.Timestamp?.ToString("O", CultureInfo.InvariantCulture)
},
Inclusion = entry.Mirror.Proof.Inclusion is null ? null : new AttestationInclusionDto
{
@@ -474,7 +475,7 @@ internal static class AttestorWebServiceEndpoints
{
Uuid = entry.RekorUuid,
Status = entry.Status,
CreatedAt = entry.CreatedAt.ToString("O"),
CreatedAt = entry.CreatedAt.ToString("O", CultureInfo.InvariantCulture),
Artifact = new AttestationArtifactDto
{
Sha256 = entry.Artifact.Sha256,

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.AspNetCore.Http;
using StellaOps.Attestor.Core.Bulk;
@@ -84,9 +85,9 @@ internal static class BulkVerificationContracts
{
Id = job.Id,
Status = job.Status.ToString().ToLowerInvariant(),
CreatedAt = job.CreatedAt.ToString("O"),
StartedAt = job.StartedAt?.ToString("O"),
CompletedAt = job.CompletedAt?.ToString("O"),
CreatedAt = job.CreatedAt.ToString("O", CultureInfo.InvariantCulture),
StartedAt = job.StartedAt?.ToString("O", CultureInfo.InvariantCulture),
CompletedAt = job.CompletedAt?.ToString("O", CultureInfo.InvariantCulture),
Processed = job.ProcessedCount,
Succeeded = job.SucceededCount,
Failed = job.FailedCount,
@@ -112,8 +113,8 @@ internal static class BulkVerificationContracts
{
Index = item.Index,
Status = item.Status.ToString().ToLowerInvariant(),
StartedAt = item.StartedAt?.ToString("O"),
CompletedAt = item.CompletedAt?.ToString("O"),
StartedAt = item.StartedAt?.ToString("O", CultureInfo.InvariantCulture),
CompletedAt = item.CompletedAt?.ToString("O", CultureInfo.InvariantCulture),
Error = item.Error,
Request = new BulkVerificationItemRequestDto
{

View File

@@ -1,4 +1,5 @@
using System;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
@@ -154,7 +155,7 @@ public class VerdictController : ControllerBase
Envelope = Convert.ToBase64String(Encoding.UTF8.GetBytes(envelopeJson)),
RekorLogIndex = rekorLogIndex,
KeyId = signResult.KeyId ?? request.KeyId ?? "default",
CreatedAt = _timeProvider.GetUtcNow().ToString("O")
CreatedAt = _timeProvider.GetUtcNow().ToString("O", CultureInfo.InvariantCulture)
};
_logger.LogInformation(

View File

@@ -23,6 +23,7 @@ public sealed class KmsOrgKeySigner : IOrgKeySigner
private readonly IKmsProvider _kmsProvider;
private readonly ILogger<KmsOrgKeySigner> _logger;
private readonly OrgSigningOptions _options;
private readonly TimeProvider _timeProvider;
/// <summary>
/// Create a new KMS organization key signer.
@@ -30,11 +31,13 @@ public sealed class KmsOrgKeySigner : IOrgKeySigner
public KmsOrgKeySigner(
IKmsProvider kmsProvider,
ILogger<KmsOrgKeySigner> logger,
IOptions<OrgSigningOptions> options)
IOptions<OrgSigningOptions> options,
TimeProvider? timeProvider = null)
{
_kmsProvider = kmsProvider ?? throw new ArgumentNullException(nameof(kmsProvider));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_options = options?.Value ?? new OrgSigningOptions();
_timeProvider = timeProvider ?? TimeProvider.System;
}
/// <inheritdoc />
@@ -62,7 +65,7 @@ public sealed class KmsOrgKeySigner : IOrgKeySigner
}
// Check key expiry
if (keyInfo.ValidUntil.HasValue && keyInfo.ValidUntil.Value < DateTimeOffset.UtcNow)
if (keyInfo.ValidUntil.HasValue && keyInfo.ValidUntil.Value < _timeProvider.GetUtcNow())
{
throw new InvalidOperationException($"Signing key '{keyId}' has expired.");
}
@@ -87,7 +90,7 @@ public sealed class KmsOrgKeySigner : IOrgKeySigner
KeyId = keyId,
Algorithm = keyInfo.Algorithm,
Signature = Convert.ToBase64String(signatureBytes),
SignedAt = DateTimeOffset.UtcNow,
SignedAt = _timeProvider.GetUtcNow(),
CertificateChain = certChain
};
}
@@ -140,9 +143,10 @@ public sealed class KmsOrgKeySigner : IOrgKeySigner
// List keys and find the active one based on rotation policy
var keys = await ListKeysAsync(cancellationToken);
var now = _timeProvider.GetUtcNow();
var activeKey = keys
.Where(k => k.IsActive)
.Where(k => !k.ValidUntil.HasValue || k.ValidUntil.Value > DateTimeOffset.UtcNow)
.Where(k => !k.ValidUntil.HasValue || k.ValidUntil.Value > now)
.OrderByDescending(k => k.ValidFrom)
.FirstOrDefault();
@@ -253,14 +257,16 @@ public sealed class LocalOrgKeySigner : IOrgKeySigner
{
private readonly Dictionary<string, (ECDsa Key, OrgKeyInfo Info)> _keys = new();
private readonly ILogger<LocalOrgKeySigner> _logger;
private readonly TimeProvider _timeProvider;
private string? _activeKeyId;
/// <summary>
/// Create a new local key signer.
/// </summary>
public LocalOrgKeySigner(ILogger<LocalOrgKeySigner> logger)
public LocalOrgKeySigner(ILogger<LocalOrgKeySigner> logger, TimeProvider? timeProvider = null)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_timeProvider = timeProvider ?? TimeProvider.System;
}
/// <summary>
@@ -276,7 +282,7 @@ public sealed class LocalOrgKeySigner : IOrgKeySigner
keyId,
"ECDSA_P256",
fingerprint,
DateTimeOffset.UtcNow,
_timeProvider.GetUtcNow(),
null,
isActive);
@@ -308,7 +314,7 @@ public sealed class LocalOrgKeySigner : IOrgKeySigner
KeyId = keyId,
Algorithm = "ECDSA_P256",
Signature = Convert.ToBase64String(signature),
SignedAt = DateTimeOffset.UtcNow,
SignedAt = _timeProvider.GetUtcNow(),
CertificateChain = null
});
}

View File

@@ -4,6 +4,7 @@
// Task: Implement OCI registry attachment via ORAS
// -----------------------------------------------------------------------------
using System.Globalization;
using System.Security.Cryptography;
using System.Text.Json;
using Microsoft.Extensions.Logging;
@@ -327,7 +328,7 @@ public sealed class OrasAttestationAttacher : IOciAttestationAttacher
{
var annotations = new Dictionary<string, string>(StringComparer.Ordinal)
{
[AnnotationKeys.Created] = createdAt.ToString("O"),
[AnnotationKeys.Created] = createdAt.ToString("O", CultureInfo.InvariantCulture),
[AnnotationKeys.PredicateType] = predicateType,
[AnnotationKeys.CosignSignature] = "" // Cosign compatibility placeholder
};

View File

@@ -12,7 +12,6 @@
<ItemGroup>
<PackageReference Include="coverlet.collector" />
<PackageReference Include="xunit.runner.visualstudio" />
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" />
<PackageReference Include="Moq" />
<PackageReference Include="FluentAssertions" />

View File

@@ -11,10 +11,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="xunit.runner.visualstudio" >
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Moq" />
<PackageReference Include="Testcontainers" />

View File

@@ -15,10 +15,6 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
<PackageReference Include="NSubstitute" />
<PackageReference Include="xunit.runner.visualstudio" >
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" >
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>

View File

@@ -14,10 +14,6 @@
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
<PackageReference Include="NSubstitute" />
<PackageReference Include="xunit.runner.visualstudio" >
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>

View File

@@ -12,11 +12,6 @@
<ItemGroup>
<PackageReference Include="FluentAssertions" />
<PackageReference Include="JsonSchema.Net" />
<PackageReference Include="xunit.v3" />
<PackageReference Include="xunit.runner.visualstudio" >
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" >
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>

View File

@@ -12,11 +12,6 @@
<ItemGroup>
<PackageReference Include="FluentAssertions" />
<PackageReference Include="xunit.v3" />
<PackageReference Include="xunit.runner.visualstudio" >
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" >
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>