audit work, fixed StellaOps.sln warnings/errors, fixed tests, sprints work, new advisories
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user