save progress
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Determinism.Abstractions;
|
||||
using StellaOps.Scanner.Storage.Models;
|
||||
using StellaOps.Scanner.Storage.Repositories;
|
||||
|
||||
@@ -20,6 +21,8 @@ public sealed class ScanMetricsCollector : IDisposable
|
||||
{
|
||||
private readonly IScanMetricsRepository _repository;
|
||||
private readonly ILogger<ScanMetricsCollector> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly IGuidProvider _guidProvider;
|
||||
|
||||
private readonly Guid _scanId;
|
||||
private readonly Guid _tenantId;
|
||||
@@ -58,7 +61,9 @@ public sealed class ScanMetricsCollector : IDisposable
|
||||
Guid tenantId,
|
||||
string artifactDigest,
|
||||
string artifactType,
|
||||
string scannerVersion)
|
||||
string scannerVersion,
|
||||
TimeProvider? timeProvider = null,
|
||||
IGuidProvider? guidProvider = null)
|
||||
{
|
||||
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
@@ -67,7 +72,9 @@ public sealed class ScanMetricsCollector : IDisposable
|
||||
_artifactDigest = artifactDigest ?? throw new ArgumentNullException(nameof(artifactDigest));
|
||||
_artifactType = artifactType ?? throw new ArgumentNullException(nameof(artifactType));
|
||||
_scannerVersion = scannerVersion ?? throw new ArgumentNullException(nameof(scannerVersion));
|
||||
_metricsId = Guid.NewGuid();
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
|
||||
_metricsId = _guidProvider.NewGuid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -80,7 +87,7 @@ public sealed class ScanMetricsCollector : IDisposable
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
_startedAt = DateTimeOffset.UtcNow;
|
||||
_startedAt = _timeProvider.GetUtcNow();
|
||||
_totalStopwatch.Start();
|
||||
_logger.LogDebug("Started metrics collection for scan {ScanId}", _scanId);
|
||||
}
|
||||
@@ -98,7 +105,7 @@ public sealed class ScanMetricsCollector : IDisposable
|
||||
return NoOpDisposable.Instance;
|
||||
}
|
||||
|
||||
var tracker = new PhaseTracker(this, phaseName, DateTimeOffset.UtcNow);
|
||||
var tracker = new PhaseTracker(this, phaseName, _timeProvider.GetUtcNow());
|
||||
_phases[phaseName] = tracker;
|
||||
_logger.LogDebug("Started phase {PhaseName} for scan {ScanId}", phaseName, _scanId);
|
||||
return tracker;
|
||||
@@ -138,7 +145,7 @@ public sealed class ScanMetricsCollector : IDisposable
|
||||
|
||||
_phases.Remove(phaseName);
|
||||
|
||||
var finishedAt = DateTimeOffset.UtcNow;
|
||||
var finishedAt = _timeProvider.GetUtcNow();
|
||||
var phase = new ExecutionPhase
|
||||
{
|
||||
MetricsId = _metricsId,
|
||||
@@ -214,7 +221,7 @@ public sealed class ScanMetricsCollector : IDisposable
|
||||
public async Task CompleteAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
_totalStopwatch.Stop();
|
||||
var finishedAt = DateTimeOffset.UtcNow;
|
||||
var finishedAt = _timeProvider.GetUtcNow();
|
||||
|
||||
// Calculate phase timings
|
||||
var phases = BuildPhaseTimings();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Scanner.Explainability.Assumptions;
|
||||
|
||||
namespace StellaOps.Scanner.Explainability.Falsifiability;
|
||||
@@ -60,10 +61,17 @@ public interface IFalsifiabilityGenerator
|
||||
public sealed class FalsifiabilityGenerator : IFalsifiabilityGenerator
|
||||
{
|
||||
private readonly ILogger<FalsifiabilityGenerator> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly IGuidProvider _guidProvider;
|
||||
|
||||
public FalsifiabilityGenerator(ILogger<FalsifiabilityGenerator> logger)
|
||||
public FalsifiabilityGenerator(
|
||||
ILogger<FalsifiabilityGenerator> logger,
|
||||
TimeProvider? timeProvider = null,
|
||||
IGuidProvider? guidProvider = null)
|
||||
{
|
||||
_logger = logger;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -164,12 +172,12 @@ public sealed class FalsifiabilityGenerator : IFalsifiabilityGenerator
|
||||
|
||||
return new FalsifiabilityCriteria
|
||||
{
|
||||
Id = Guid.NewGuid().ToString("N"),
|
||||
Id = _guidProvider.NewGuid().ToString("N"),
|
||||
FindingId = input.FindingId,
|
||||
Criteria = [.. criteria],
|
||||
Status = status,
|
||||
Summary = summary,
|
||||
GeneratedAt = DateTimeOffset.UtcNow
|
||||
GeneratedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Copyright (c) StellaOps
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Scanner.Explainability.Assumptions;
|
||||
using StellaOps.Scanner.Explainability.Confidence;
|
||||
using StellaOps.Scanner.Explainability.Falsifiability;
|
||||
@@ -118,10 +119,17 @@ public sealed class RiskReportGenerator : IRiskReportGenerator
|
||||
private const string EngineVersionValue = "1.0.0";
|
||||
|
||||
private readonly IEvidenceDensityScorer _scorer;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly IGuidProvider _guidProvider;
|
||||
|
||||
public RiskReportGenerator(IEvidenceDensityScorer scorer)
|
||||
public RiskReportGenerator(
|
||||
IEvidenceDensityScorer scorer,
|
||||
TimeProvider? timeProvider = null,
|
||||
IGuidProvider? guidProvider = null)
|
||||
{
|
||||
_scorer = scorer;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -140,7 +148,7 @@ public sealed class RiskReportGenerator : IRiskReportGenerator
|
||||
|
||||
return new RiskReport
|
||||
{
|
||||
Id = Guid.NewGuid().ToString("N"),
|
||||
Id = _guidProvider.NewGuid().ToString("N"),
|
||||
FindingId = input.FindingId,
|
||||
VulnerabilityId = input.VulnerabilityId,
|
||||
PackageName = input.PackageName,
|
||||
@@ -151,7 +159,7 @@ public sealed class RiskReportGenerator : IRiskReportGenerator
|
||||
Explanation = explanation,
|
||||
DetailedNarrative = narrative,
|
||||
RecommendedActions = [.. actions],
|
||||
GeneratedAt = DateTimeOffset.UtcNow,
|
||||
GeneratedAt = _timeProvider.GetUtcNow(),
|
||||
EngineVersion = EngineVersionValue
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,4 +12,8 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Determinism.Abstractions/StellaOps.Determinism.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -16,6 +16,7 @@ public sealed class DockerConnectionTester : ISourceTypeConnectionTester
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly ICredentialResolver _credentialResolver;
|
||||
private readonly ILogger<DockerConnectionTester> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
@@ -28,11 +29,13 @@ public sealed class DockerConnectionTester : ISourceTypeConnectionTester
|
||||
public DockerConnectionTester(
|
||||
IHttpClientFactory httpClientFactory,
|
||||
ICredentialResolver credentialResolver,
|
||||
ILogger<DockerConnectionTester> logger)
|
||||
ILogger<DockerConnectionTester> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_credentialResolver = credentialResolver;
|
||||
_logger = logger;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public async Task<ConnectionTestResult> TestAsync(
|
||||
@@ -47,7 +50,7 @@ public sealed class DockerConnectionTester : ISourceTypeConnectionTester
|
||||
{
|
||||
Success = false,
|
||||
Message = "Invalid configuration format",
|
||||
TestedAt = DateTimeOffset.UtcNow
|
||||
TestedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -100,7 +103,7 @@ public sealed class DockerConnectionTester : ISourceTypeConnectionTester
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Registry accessible but image test failed: {imageTestResult.Message}",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = details
|
||||
};
|
||||
}
|
||||
@@ -112,7 +115,7 @@ public sealed class DockerConnectionTester : ISourceTypeConnectionTester
|
||||
{
|
||||
Success = true,
|
||||
Message = "Successfully connected to Docker registry",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = details
|
||||
};
|
||||
}
|
||||
@@ -125,21 +128,21 @@ public sealed class DockerConnectionTester : ISourceTypeConnectionTester
|
||||
{
|
||||
Success = false,
|
||||
Message = "Authentication required - configure credentials",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = details
|
||||
},
|
||||
HttpStatusCode.Forbidden => new ConnectionTestResult
|
||||
{
|
||||
Success = false,
|
||||
Message = "Access denied - check permissions",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = details
|
||||
},
|
||||
_ => new ConnectionTestResult
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Registry returned {response.StatusCode}",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = details
|
||||
}
|
||||
};
|
||||
@@ -151,7 +154,7 @@ public sealed class DockerConnectionTester : ISourceTypeConnectionTester
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Connection failed: {ex.Message}",
|
||||
TestedAt = DateTimeOffset.UtcNow
|
||||
TestedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
catch (TaskCanceledException) when (!ct.IsCancellationRequested)
|
||||
@@ -160,7 +163,7 @@ public sealed class DockerConnectionTester : ISourceTypeConnectionTester
|
||||
{
|
||||
Success = false,
|
||||
Message = "Connection timed out",
|
||||
TestedAt = DateTimeOffset.UtcNow
|
||||
TestedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ public sealed class GitConnectionTester : ISourceTypeConnectionTester
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly ICredentialResolver _credentialResolver;
|
||||
private readonly ILogger<GitConnectionTester> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
@@ -28,11 +29,13 @@ public sealed class GitConnectionTester : ISourceTypeConnectionTester
|
||||
public GitConnectionTester(
|
||||
IHttpClientFactory httpClientFactory,
|
||||
ICredentialResolver credentialResolver,
|
||||
ILogger<GitConnectionTester> logger)
|
||||
ILogger<GitConnectionTester> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_credentialResolver = credentialResolver;
|
||||
_logger = logger;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public async Task<ConnectionTestResult> TestAsync(
|
||||
@@ -47,7 +50,7 @@ public sealed class GitConnectionTester : ISourceTypeConnectionTester
|
||||
{
|
||||
Success = false,
|
||||
Message = "Invalid configuration format",
|
||||
TestedAt = DateTimeOffset.UtcNow
|
||||
TestedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -126,7 +129,7 @@ public sealed class GitConnectionTester : ISourceTypeConnectionTester
|
||||
{
|
||||
Success = true,
|
||||
Message = "Successfully connected to Git repository",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = details
|
||||
};
|
||||
}
|
||||
@@ -139,28 +142,28 @@ public sealed class GitConnectionTester : ISourceTypeConnectionTester
|
||||
{
|
||||
Success = false,
|
||||
Message = "Authentication required - configure credentials",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = details
|
||||
},
|
||||
HttpStatusCode.Forbidden => new ConnectionTestResult
|
||||
{
|
||||
Success = false,
|
||||
Message = "Access denied - check token permissions",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = details
|
||||
},
|
||||
HttpStatusCode.NotFound => new ConnectionTestResult
|
||||
{
|
||||
Success = false,
|
||||
Message = "Repository not found - check URL and access",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = details
|
||||
},
|
||||
_ => new ConnectionTestResult
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Server returned {response.StatusCode}",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = details
|
||||
}
|
||||
};
|
||||
@@ -172,7 +175,7 @@ public sealed class GitConnectionTester : ISourceTypeConnectionTester
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Connection failed: {ex.Message}",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = new Dictionary<string, object>
|
||||
{
|
||||
["repositoryUrl"] = config.RepositoryUrl
|
||||
@@ -185,7 +188,7 @@ public sealed class GitConnectionTester : ISourceTypeConnectionTester
|
||||
{
|
||||
Success = false,
|
||||
Message = "Connection timed out",
|
||||
TestedAt = DateTimeOffset.UtcNow
|
||||
TestedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -202,7 +205,7 @@ public sealed class GitConnectionTester : ISourceTypeConnectionTester
|
||||
{
|
||||
Success = true,
|
||||
Message = "SSH configuration accepted - connection will be validated on first scan",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = new Dictionary<string, object>
|
||||
{
|
||||
["repositoryUrl"] = config.RepositoryUrl,
|
||||
|
||||
@@ -17,6 +17,7 @@ public sealed class ZastavaConnectionTester : ISourceTypeConnectionTester
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly ICredentialResolver _credentialResolver;
|
||||
private readonly ILogger<ZastavaConnectionTester> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
@@ -29,11 +30,13 @@ public sealed class ZastavaConnectionTester : ISourceTypeConnectionTester
|
||||
public ZastavaConnectionTester(
|
||||
IHttpClientFactory httpClientFactory,
|
||||
ICredentialResolver credentialResolver,
|
||||
ILogger<ZastavaConnectionTester> logger)
|
||||
ILogger<ZastavaConnectionTester> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_credentialResolver = credentialResolver;
|
||||
_logger = logger;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public async Task<ConnectionTestResult> TestAsync(
|
||||
@@ -48,7 +51,7 @@ public sealed class ZastavaConnectionTester : ISourceTypeConnectionTester
|
||||
{
|
||||
Success = false,
|
||||
Message = "Invalid configuration format",
|
||||
TestedAt = DateTimeOffset.UtcNow
|
||||
TestedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -90,7 +93,7 @@ public sealed class ZastavaConnectionTester : ISourceTypeConnectionTester
|
||||
{
|
||||
Success = true,
|
||||
Message = "Successfully connected to registry",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = details
|
||||
};
|
||||
}
|
||||
@@ -104,28 +107,28 @@ public sealed class ZastavaConnectionTester : ISourceTypeConnectionTester
|
||||
{
|
||||
Success = false,
|
||||
Message = "Authentication failed - check credentials",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = details
|
||||
},
|
||||
HttpStatusCode.Forbidden => new ConnectionTestResult
|
||||
{
|
||||
Success = false,
|
||||
Message = "Access denied - insufficient permissions",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = details
|
||||
},
|
||||
HttpStatusCode.NotFound => new ConnectionTestResult
|
||||
{
|
||||
Success = false,
|
||||
Message = "Registry endpoint not found - check URL",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = details
|
||||
},
|
||||
_ => new ConnectionTestResult
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Registry returned {response.StatusCode}",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = details
|
||||
}
|
||||
};
|
||||
@@ -137,7 +140,7 @@ public sealed class ZastavaConnectionTester : ISourceTypeConnectionTester
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Connection failed: {ex.Message}",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = new Dictionary<string, object>
|
||||
{
|
||||
["registryUrl"] = config.RegistryUrl,
|
||||
@@ -151,7 +154,7 @@ public sealed class ZastavaConnectionTester : ISourceTypeConnectionTester
|
||||
{
|
||||
Success = false,
|
||||
Message = "Connection timed out",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = new Dictionary<string, object>
|
||||
{
|
||||
["registryUrl"] = config.RegistryUrl,
|
||||
|
||||
@@ -24,6 +24,7 @@ public sealed class CliSourceHandler : ISourceTypeHandler
|
||||
{
|
||||
private readonly ISourceConfigValidator _configValidator;
|
||||
private readonly ILogger<CliSourceHandler> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
@@ -38,10 +39,12 @@ public sealed class CliSourceHandler : ISourceTypeHandler
|
||||
|
||||
public CliSourceHandler(
|
||||
ISourceConfigValidator configValidator,
|
||||
ILogger<CliSourceHandler> logger)
|
||||
ILogger<CliSourceHandler> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_configValidator = configValidator;
|
||||
_logger = logger;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -102,7 +105,7 @@ public sealed class CliSourceHandler : ISourceTypeHandler
|
||||
{
|
||||
Success = false,
|
||||
Message = "Invalid configuration",
|
||||
TestedAt = DateTimeOffset.UtcNow
|
||||
TestedAt = _timeProvider.GetUtcNow()
|
||||
});
|
||||
}
|
||||
|
||||
@@ -112,7 +115,7 @@ public sealed class CliSourceHandler : ISourceTypeHandler
|
||||
{
|
||||
Success = true,
|
||||
Message = "CLI source configuration is valid",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = new Dictionary<string, object>
|
||||
{
|
||||
["allowedTools"] = config.AllowedTools,
|
||||
@@ -242,8 +245,8 @@ public sealed class CliSourceHandler : ISourceTypeHandler
|
||||
Token = token,
|
||||
TokenHash = Convert.ToHexString(tokenHash).ToLowerInvariant(),
|
||||
SourceId = source.SourceId,
|
||||
ExpiresAt = DateTimeOffset.UtcNow.Add(validity),
|
||||
CreatedAt = DateTimeOffset.UtcNow
|
||||
ExpiresAt = _timeProvider.GetUtcNow().Add(validity),
|
||||
CreatedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ public sealed class DockerSourceHandler : ISourceTypeHandler
|
||||
private readonly ISourceConfigValidator _configValidator;
|
||||
private readonly IImageDiscoveryService _discoveryService;
|
||||
private readonly ILogger<DockerSourceHandler> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
@@ -38,13 +39,15 @@ public sealed class DockerSourceHandler : ISourceTypeHandler
|
||||
ICredentialResolver credentialResolver,
|
||||
ISourceConfigValidator configValidator,
|
||||
IImageDiscoveryService discoveryService,
|
||||
ILogger<DockerSourceHandler> logger)
|
||||
ILogger<DockerSourceHandler> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_clientFactory = clientFactory;
|
||||
_credentialResolver = credentialResolver;
|
||||
_configValidator = configValidator;
|
||||
_discoveryService = discoveryService;
|
||||
_logger = logger;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<ScanTarget>> DiscoverTargetsAsync(
|
||||
@@ -136,7 +139,7 @@ public sealed class DockerSourceHandler : ISourceTypeHandler
|
||||
// Apply age filter if specified
|
||||
if (imageSpec.MaxAgeHours.HasValue)
|
||||
{
|
||||
var cutoff = DateTimeOffset.UtcNow.AddHours(-imageSpec.MaxAgeHours.Value);
|
||||
var cutoff = _timeProvider.GetUtcNow().AddHours(-imageSpec.MaxAgeHours.Value);
|
||||
sortedTags = sortedTags
|
||||
.Where(t => t.LastUpdated == null || t.LastUpdated >= cutoff)
|
||||
.ToList();
|
||||
@@ -181,7 +184,7 @@ public sealed class DockerSourceHandler : ISourceTypeHandler
|
||||
{
|
||||
Success = false,
|
||||
Message = "Invalid configuration",
|
||||
TestedAt = DateTimeOffset.UtcNow
|
||||
TestedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -198,7 +201,7 @@ public sealed class DockerSourceHandler : ISourceTypeHandler
|
||||
{
|
||||
Success = false,
|
||||
Message = "Registry ping failed",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = new Dictionary<string, object>
|
||||
{
|
||||
["registryUrl"] = config.RegistryUrl
|
||||
@@ -216,7 +219,7 @@ public sealed class DockerSourceHandler : ISourceTypeHandler
|
||||
{
|
||||
Success = true,
|
||||
Message = "Successfully connected to registry",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = new Dictionary<string, object>
|
||||
{
|
||||
["registryUrl"] = config.RegistryUrl,
|
||||
@@ -230,7 +233,7 @@ public sealed class DockerSourceHandler : ISourceTypeHandler
|
||||
{
|
||||
Success = true,
|
||||
Message = "Successfully connected to registry",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = new Dictionary<string, object>
|
||||
{
|
||||
["registryUrl"] = config.RegistryUrl
|
||||
@@ -244,7 +247,7 @@ public sealed class DockerSourceHandler : ISourceTypeHandler
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Connection failed: {ex.Message}",
|
||||
TestedAt = DateTimeOffset.UtcNow
|
||||
TestedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ public sealed class GitSourceHandler : ISourceTypeHandler, IWebhookCapableHandle
|
||||
private readonly ICredentialResolver _credentialResolver;
|
||||
private readonly ISourceConfigValidator _configValidator;
|
||||
private readonly ILogger<GitSourceHandler> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
@@ -35,12 +36,14 @@ public sealed class GitSourceHandler : ISourceTypeHandler, IWebhookCapableHandle
|
||||
IGitClientFactory gitClientFactory,
|
||||
ICredentialResolver credentialResolver,
|
||||
ISourceConfigValidator configValidator,
|
||||
ILogger<GitSourceHandler> logger)
|
||||
ILogger<GitSourceHandler> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_gitClientFactory = gitClientFactory;
|
||||
_credentialResolver = credentialResolver;
|
||||
_configValidator = configValidator;
|
||||
_logger = logger;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<ScanTarget>> DiscoverTargetsAsync(
|
||||
@@ -160,7 +163,7 @@ public sealed class GitSourceHandler : ISourceTypeHandler, IWebhookCapableHandle
|
||||
{
|
||||
Success = false,
|
||||
Message = "Invalid configuration",
|
||||
TestedAt = DateTimeOffset.UtcNow
|
||||
TestedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -176,7 +179,7 @@ public sealed class GitSourceHandler : ISourceTypeHandler, IWebhookCapableHandle
|
||||
{
|
||||
Success = false,
|
||||
Message = "Repository not found or inaccessible",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = new Dictionary<string, object>
|
||||
{
|
||||
["repositoryUrl"] = config.RepositoryUrl,
|
||||
@@ -189,7 +192,7 @@ public sealed class GitSourceHandler : ISourceTypeHandler, IWebhookCapableHandle
|
||||
{
|
||||
Success = true,
|
||||
Message = "Successfully connected to repository",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = new Dictionary<string, object>
|
||||
{
|
||||
["repositoryUrl"] = config.RepositoryUrl,
|
||||
@@ -206,7 +209,7 @@ public sealed class GitSourceHandler : ISourceTypeHandler, IWebhookCapableHandle
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Connection failed: {ex.Message}",
|
||||
TestedAt = DateTimeOffset.UtcNow
|
||||
TestedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -270,7 +273,7 @@ public sealed class GitSourceHandler : ISourceTypeHandler, IWebhookCapableHandle
|
||||
sender.TryGetProperty("login", out var login)
|
||||
? login.GetString()
|
||||
: null,
|
||||
Timestamp = DateTimeOffset.UtcNow
|
||||
Timestamp = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -303,7 +306,7 @@ public sealed class GitSourceHandler : ISourceTypeHandler, IWebhookCapableHandle
|
||||
? num.GetInt32().ToString()
|
||||
: ""
|
||||
},
|
||||
Timestamp = DateTimeOffset.UtcNow
|
||||
Timestamp = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -330,7 +333,7 @@ public sealed class GitSourceHandler : ISourceTypeHandler, IWebhookCapableHandle
|
||||
Actor = root.TryGetProperty("user_name", out var userName)
|
||||
? userName.GetString()
|
||||
: null,
|
||||
Timestamp = DateTimeOffset.UtcNow
|
||||
Timestamp = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -361,7 +364,7 @@ public sealed class GitSourceHandler : ISourceTypeHandler, IWebhookCapableHandle
|
||||
? mrAction.GetString() ?? ""
|
||||
: ""
|
||||
},
|
||||
Timestamp = DateTimeOffset.UtcNow
|
||||
Timestamp = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -371,7 +374,7 @@ public sealed class GitSourceHandler : ISourceTypeHandler, IWebhookCapableHandle
|
||||
{
|
||||
EventType = "unknown",
|
||||
Reference = "",
|
||||
Timestamp = DateTimeOffset.UtcNow
|
||||
Timestamp = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ public sealed class ZastavaSourceHandler : ISourceTypeHandler, IWebhookCapableHa
|
||||
private readonly ICredentialResolver _credentialResolver;
|
||||
private readonly ISourceConfigValidator _configValidator;
|
||||
private readonly ILogger<ZastavaSourceHandler> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
@@ -36,12 +37,14 @@ public sealed class ZastavaSourceHandler : ISourceTypeHandler, IWebhookCapableHa
|
||||
IRegistryClientFactory clientFactory,
|
||||
ICredentialResolver credentialResolver,
|
||||
ISourceConfigValidator configValidator,
|
||||
ILogger<ZastavaSourceHandler> logger)
|
||||
ILogger<ZastavaSourceHandler> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_clientFactory = clientFactory;
|
||||
_credentialResolver = credentialResolver;
|
||||
_configValidator = configValidator;
|
||||
_logger = logger;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<ScanTarget>> DiscoverTargetsAsync(
|
||||
@@ -167,7 +170,7 @@ public sealed class ZastavaSourceHandler : ISourceTypeHandler, IWebhookCapableHa
|
||||
{
|
||||
Success = false,
|
||||
Message = "Invalid configuration",
|
||||
TestedAt = DateTimeOffset.UtcNow
|
||||
TestedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -183,7 +186,7 @@ public sealed class ZastavaSourceHandler : ISourceTypeHandler, IWebhookCapableHa
|
||||
{
|
||||
Success = false,
|
||||
Message = "Registry ping failed",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = new Dictionary<string, object>
|
||||
{
|
||||
["registryUrl"] = config.RegistryUrl,
|
||||
@@ -199,7 +202,7 @@ public sealed class ZastavaSourceHandler : ISourceTypeHandler, IWebhookCapableHa
|
||||
{
|
||||
Success = true,
|
||||
Message = "Successfully connected to registry",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = new Dictionary<string, object>
|
||||
{
|
||||
["registryUrl"] = config.RegistryUrl,
|
||||
@@ -215,7 +218,7 @@ public sealed class ZastavaSourceHandler : ISourceTypeHandler, IWebhookCapableHa
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Connection failed: {ex.Message}",
|
||||
TestedAt = DateTimeOffset.UtcNow
|
||||
TestedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -281,7 +284,7 @@ public sealed class ZastavaSourceHandler : ISourceTypeHandler, IWebhookCapableHa
|
||||
: repository.GetProperty("name").GetString()!,
|
||||
Tag = pushData.TryGetProperty("tag", out var tag) ? tag.GetString() : "latest",
|
||||
Actor = pushData.TryGetProperty("pusher", out var pusher) ? pusher.GetString() : null,
|
||||
Timestamp = DateTimeOffset.UtcNow
|
||||
Timestamp = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -309,7 +312,7 @@ public sealed class ZastavaSourceHandler : ISourceTypeHandler, IWebhookCapableHa
|
||||
? digest.GetString()
|
||||
: null,
|
||||
Actor = eventData.TryGetProperty("operator", out var op) ? op.GetString() : null,
|
||||
Timestamp = DateTimeOffset.UtcNow
|
||||
Timestamp = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -338,7 +341,7 @@ public sealed class ZastavaSourceHandler : ISourceTypeHandler, IWebhookCapableHa
|
||||
actor.TryGetProperty("name", out var actorName)
|
||||
? actorName.GetString()
|
||||
: null,
|
||||
Timestamp = DateTimeOffset.UtcNow
|
||||
Timestamp = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -347,7 +350,7 @@ public sealed class ZastavaSourceHandler : ISourceTypeHandler, IWebhookCapableHa
|
||||
{
|
||||
EventType = "unknown",
|
||||
Reference = "",
|
||||
Timestamp = DateTimeOffset.UtcNow
|
||||
Timestamp = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -17,12 +17,15 @@ public sealed class SbomSourceRepository : RepositoryBase<ScannerSourcesDataSour
|
||||
private const string Schema = "scanner";
|
||||
private const string Table = "sbom_sources";
|
||||
private const string FullTable = $"{Schema}.{Table}";
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public SbomSourceRepository(
|
||||
ScannerSourcesDataSource dataSource,
|
||||
ILogger<SbomSourceRepository> logger)
|
||||
ILogger<SbomSourceRepository> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
: base(dataSource, logger)
|
||||
{
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public async Task<SbomSource?> GetByIdAsync(string tenantId, Guid sourceId, CancellationToken ct = default)
|
||||
@@ -317,7 +320,7 @@ public sealed class SbomSourceRepository : RepositoryBase<ScannerSourcesDataSour
|
||||
|
||||
public Task<IReadOnlyList<SbomSource>> GetDueForScheduledRunAsync(CancellationToken ct = default)
|
||||
{
|
||||
return GetDueScheduledSourcesAsync(DateTimeOffset.UtcNow, 100, ct);
|
||||
return GetDueScheduledSourcesAsync(_timeProvider.GetUtcNow(), 100, ct);
|
||||
}
|
||||
|
||||
private void ConfigureSourceParams(NpgsqlCommand cmd, SbomSource source)
|
||||
|
||||
@@ -16,12 +16,15 @@ public sealed class SbomSourceRunRepository : RepositoryBase<ScannerSourcesDataS
|
||||
private const string Schema = "scanner";
|
||||
private const string Table = "sbom_source_runs";
|
||||
private const string FullTable = $"{Schema}.{Table}";
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public SbomSourceRunRepository(
|
||||
ScannerSourcesDataSource dataSource,
|
||||
ILogger<SbomSourceRunRepository> logger)
|
||||
ILogger<SbomSourceRunRepository> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
: base(dataSource, logger)
|
||||
{
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public async Task<SbomSourceRun?> GetByIdAsync(Guid runId, CancellationToken ct = default)
|
||||
@@ -188,7 +191,7 @@ public sealed class SbomSourceRunRepository : RepositoryBase<ScannerSourcesDataS
|
||||
sql,
|
||||
cmd =>
|
||||
{
|
||||
AddParameter(cmd, "threshold", DateTimeOffset.UtcNow - olderThan);
|
||||
AddParameter(cmd, "threshold", _timeProvider.GetUtcNow() - olderThan);
|
||||
AddParameter(cmd, "limit", limit);
|
||||
},
|
||||
MapRun,
|
||||
|
||||
@@ -17,19 +17,22 @@ public sealed class SbomSourceService : ISbomSourceService
|
||||
private readonly ISourceConfigValidator _configValidator;
|
||||
private readonly ISourceConnectionTester _connectionTester;
|
||||
private readonly ILogger<SbomSourceService> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public SbomSourceService(
|
||||
ISbomSourceRepository sourceRepository,
|
||||
ISbomSourceRunRepository runRepository,
|
||||
ISourceConfigValidator configValidator,
|
||||
ISourceConnectionTester connectionTester,
|
||||
ILogger<SbomSourceService> logger)
|
||||
ILogger<SbomSourceService> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_sourceRepository = sourceRepository;
|
||||
_runRepository = runRepository;
|
||||
_configValidator = configValidator;
|
||||
_connectionTester = connectionTester;
|
||||
_logger = logger;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public async Task<SourceResponse?> GetAsync(string tenantId, Guid sourceId, CancellationToken ct = default)
|
||||
@@ -215,7 +218,7 @@ public sealed class SbomSourceService : ISbomSourceService
|
||||
}
|
||||
|
||||
// Touch updated fields
|
||||
SetProperty(source, "UpdatedAt", DateTimeOffset.UtcNow);
|
||||
SetProperty(source, "UpdatedAt", _timeProvider.GetUtcNow());
|
||||
SetProperty(source, "UpdatedBy", updatedBy);
|
||||
|
||||
await _sourceRepository.UpdateAsync(source, ct);
|
||||
|
||||
@@ -12,13 +12,16 @@ public sealed class SourceConnectionTester : ISourceConnectionTester
|
||||
{
|
||||
private readonly IEnumerable<ISourceTypeConnectionTester> _testers;
|
||||
private readonly ILogger<SourceConnectionTester> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public SourceConnectionTester(
|
||||
IEnumerable<ISourceTypeConnectionTester> testers,
|
||||
ILogger<SourceConnectionTester> logger)
|
||||
ILogger<SourceConnectionTester> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_testers = testers;
|
||||
_logger = logger;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
}
|
||||
|
||||
public Task<ConnectionTestResult> TestAsync(SbomSource source, CancellationToken ct = default)
|
||||
@@ -42,7 +45,7 @@ public sealed class SourceConnectionTester : ISourceConnectionTester
|
||||
{
|
||||
Success = false,
|
||||
Message = $"No connection tester available for source type {source.SourceType}",
|
||||
TestedAt = DateTimeOffset.UtcNow
|
||||
TestedAt = _timeProvider.GetUtcNow()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -74,7 +77,7 @@ public sealed class SourceConnectionTester : ISourceConnectionTester
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Connection test error: {ex.Message}",
|
||||
TestedAt = DateTimeOffset.UtcNow,
|
||||
TestedAt = _timeProvider.GetUtcNow(),
|
||||
Details = new Dictionary<string, object>
|
||||
{
|
||||
["exceptionType"] = ex.GetType().Name
|
||||
|
||||
@@ -22,5 +22,6 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Infrastructure.Postgres/StellaOps.Infrastructure.Postgres.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Determinism.Abstractions/StellaOps.Determinism.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Scanner.Sources.Domain;
|
||||
using StellaOps.Scanner.Sources.Handlers;
|
||||
using StellaOps.Scanner.Sources.Persistence;
|
||||
@@ -15,19 +16,25 @@ public sealed class SourceTriggerDispatcher : ISourceTriggerDispatcher
|
||||
private readonly IEnumerable<ISourceTypeHandler> _handlers;
|
||||
private readonly IScanJobQueue _scanJobQueue;
|
||||
private readonly ILogger<SourceTriggerDispatcher> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly IGuidProvider _guidProvider;
|
||||
|
||||
public SourceTriggerDispatcher(
|
||||
ISbomSourceRepository sourceRepository,
|
||||
ISbomSourceRunRepository runRepository,
|
||||
IEnumerable<ISourceTypeHandler> handlers,
|
||||
IScanJobQueue scanJobQueue,
|
||||
ILogger<SourceTriggerDispatcher> logger)
|
||||
ILogger<SourceTriggerDispatcher> logger,
|
||||
TimeProvider? timeProvider = null,
|
||||
IGuidProvider? guidProvider = null)
|
||||
{
|
||||
_sourceRepository = sourceRepository;
|
||||
_runRepository = runRepository;
|
||||
_handlers = handlers;
|
||||
_scanJobQueue = scanJobQueue;
|
||||
_logger = logger;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
|
||||
}
|
||||
|
||||
public Task<TriggerDispatchResult> DispatchAsync(
|
||||
@@ -40,7 +47,7 @@ public sealed class SourceTriggerDispatcher : ISourceTriggerDispatcher
|
||||
{
|
||||
Trigger = trigger,
|
||||
TriggerDetails = triggerDetails,
|
||||
CorrelationId = Guid.NewGuid().ToString("N")
|
||||
CorrelationId = _guidProvider.NewGuid().ToString("N")
|
||||
};
|
||||
|
||||
return DispatchAsync(sourceId, context, ct);
|
||||
@@ -128,7 +135,7 @@ public sealed class SourceTriggerDispatcher : ISourceTriggerDispatcher
|
||||
{
|
||||
run.Complete();
|
||||
await _runRepository.UpdateAsync(run, ct);
|
||||
source.RecordSuccessfulRun(DateTimeOffset.UtcNow);
|
||||
source.RecordSuccessfulRun(_timeProvider.GetUtcNow());
|
||||
await _sourceRepository.UpdateAsync(source, ct);
|
||||
|
||||
return new TriggerDispatchResult
|
||||
@@ -170,12 +177,12 @@ public sealed class SourceTriggerDispatcher : ISourceTriggerDispatcher
|
||||
if (run.ItemsFailed == run.ItemsDiscovered)
|
||||
{
|
||||
run.Fail("All targets failed to queue");
|
||||
source.RecordFailedRun(DateTimeOffset.UtcNow, run.ErrorMessage!);
|
||||
source.RecordFailedRun(_timeProvider.GetUtcNow(), run.ErrorMessage!);
|
||||
}
|
||||
else
|
||||
{
|
||||
run.Complete();
|
||||
source.RecordSuccessfulRun(DateTimeOffset.UtcNow);
|
||||
source.RecordSuccessfulRun(_timeProvider.GetUtcNow());
|
||||
}
|
||||
|
||||
await _runRepository.UpdateAsync(run, ct);
|
||||
@@ -195,7 +202,7 @@ public sealed class SourceTriggerDispatcher : ISourceTriggerDispatcher
|
||||
run.Fail(ex.Message);
|
||||
await _runRepository.UpdateAsync(run, ct);
|
||||
|
||||
source.RecordFailedRun(DateTimeOffset.UtcNow, ex.Message);
|
||||
source.RecordFailedRun(_timeProvider.GetUtcNow(), ex.Message);
|
||||
await _sourceRepository.UpdateAsync(source, ct);
|
||||
|
||||
return new TriggerDispatchResult
|
||||
@@ -247,7 +254,7 @@ public sealed class SourceTriggerDispatcher : ISourceTriggerDispatcher
|
||||
{
|
||||
Trigger = originalRun.Trigger,
|
||||
TriggerDetails = $"Retry of run {originalRunId}",
|
||||
CorrelationId = Guid.NewGuid().ToString("N"),
|
||||
CorrelationId = _guidProvider.NewGuid().ToString("N"),
|
||||
Metadata = new() { ["originalRunId"] = originalRunId.ToString() }
|
||||
};
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ public sealed class SlicePullService : IDisposable
|
||||
private readonly OciRegistryAuthorization _authorization;
|
||||
private readonly SlicePullOptions _options;
|
||||
private readonly ILogger<SlicePullService> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly Dictionary<string, CachedSlice> _cache = new(StringComparer.Ordinal);
|
||||
private readonly Lock _cacheLock = new();
|
||||
|
||||
@@ -70,12 +71,14 @@ public sealed class SlicePullService : IDisposable
|
||||
HttpClient httpClient,
|
||||
OciRegistryAuthorization authorization,
|
||||
SlicePullOptions? options = null,
|
||||
ILogger<SlicePullService>? logger = null)
|
||||
ILogger<SlicePullService>? logger = null,
|
||||
TimeProvider? timeProvider = null)
|
||||
{
|
||||
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
||||
_authorization = authorization ?? throw new ArgumentNullException(nameof(authorization));
|
||||
_options = options ?? new SlicePullOptions();
|
||||
_logger = logger ?? Microsoft.Extensions.Logging.Abstractions.NullLogger<SlicePullService>.Instance;
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
_httpClient.Timeout = _options.RequestTimeout;
|
||||
}
|
||||
|
||||
@@ -211,7 +214,7 @@ public sealed class SlicePullService : IDisposable
|
||||
var dsseLayer = manifest.Layers?.FirstOrDefault(l =>
|
||||
l.MediaType == OciMediaTypes.DsseEnvelope);
|
||||
|
||||
if (dsseLayer != null && _options.VerifySignature)
|
||||
if (dsseLayer?.Digest != null && _options.VerifySignature)
|
||||
{
|
||||
var dsseResult = await FetchAndVerifyDsseAsync(reference, dsseLayer.Digest, sliceBytes, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
@@ -227,7 +230,7 @@ public sealed class SlicePullService : IDisposable
|
||||
SliceData = sliceData,
|
||||
DsseEnvelope = dsseEnvelope,
|
||||
SignatureVerified = signatureVerified,
|
||||
ExpiresAt = DateTimeOffset.UtcNow.Add(_options.CacheTtl)
|
||||
ExpiresAt = _timeProvider.GetUtcNow().Add(_options.CacheTtl)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -411,7 +414,7 @@ public sealed class SlicePullService : IDisposable
|
||||
{
|
||||
if (_cache.TryGetValue(key, out cached))
|
||||
{
|
||||
if (cached.ExpiresAt > DateTimeOffset.UtcNow)
|
||||
if (cached.ExpiresAt > _timeProvider.GetUtcNow())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
using System.Text.Json;
|
||||
using Npgsql;
|
||||
using NpgsqlTypes;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Scanner.Storage.Entities;
|
||||
|
||||
namespace StellaOps.Scanner.Storage.Postgres;
|
||||
@@ -64,10 +65,17 @@ public interface IFuncProofRepository
|
||||
public sealed class PostgresFuncProofRepository : IFuncProofRepository
|
||||
{
|
||||
private readonly NpgsqlDataSource _dataSource;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly IGuidProvider _guidProvider;
|
||||
|
||||
public PostgresFuncProofRepository(NpgsqlDataSource dataSource)
|
||||
public PostgresFuncProofRepository(
|
||||
NpgsqlDataSource dataSource,
|
||||
TimeProvider? timeProvider = null,
|
||||
IGuidProvider? guidProvider = null)
|
||||
{
|
||||
_dataSource = dataSource ?? throw new ArgumentNullException(nameof(dataSource));
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
|
||||
}
|
||||
|
||||
public async Task<Guid> StoreAsync(FuncProofDocumentRow document, CancellationToken ct = default)
|
||||
@@ -94,7 +102,7 @@ public sealed class PostgresFuncProofRepository : IFuncProofRepository
|
||||
await using var conn = await _dataSource.OpenConnectionAsync(ct);
|
||||
await using var cmd = new NpgsqlCommand(sql, conn);
|
||||
|
||||
var id = document.Id == Guid.Empty ? Guid.NewGuid() : document.Id;
|
||||
var id = document.Id == Guid.Empty ? _guidProvider.NewGuid() : document.Id;
|
||||
|
||||
cmd.Parameters.AddWithValue("id", id);
|
||||
cmd.Parameters.AddWithValue("scan_id", document.ScanId);
|
||||
@@ -118,7 +126,7 @@ public sealed class PostgresFuncProofRepository : IFuncProofRepository
|
||||
document.RekorEntryId is null ? DBNull.Value : document.RekorEntryId);
|
||||
cmd.Parameters.AddWithValue("generator_version", document.GeneratorVersion);
|
||||
cmd.Parameters.AddWithValue("generated_at_utc", document.GeneratedAtUtc);
|
||||
cmd.Parameters.AddWithValue("created_at_utc", DateTimeOffset.UtcNow);
|
||||
cmd.Parameters.AddWithValue("created_at_utc", _timeProvider.GetUtcNow());
|
||||
|
||||
var result = await cmd.ExecuteScalarAsync(ct);
|
||||
return result is Guid returnedId ? returnedId : id;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Npgsql;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Scanner.Storage.Entities;
|
||||
using StellaOps.Scanner.Storage.Repositories;
|
||||
|
||||
@@ -20,14 +21,17 @@ public sealed class PostgresIdempotencyKeyRepository : IIdempotencyKeyRepository
|
||||
{
|
||||
private readonly ScannerDataSource _dataSource;
|
||||
private readonly ILogger<PostgresIdempotencyKeyRepository> _logger;
|
||||
private readonly IGuidProvider _guidProvider;
|
||||
private string SchemaName => _dataSource.SchemaName ?? ScannerDataSource.DefaultSchema;
|
||||
|
||||
public PostgresIdempotencyKeyRepository(
|
||||
ScannerDataSource dataSource,
|
||||
ILogger<PostgresIdempotencyKeyRepository> logger)
|
||||
ILogger<PostgresIdempotencyKeyRepository> logger,
|
||||
IGuidProvider? guidProvider = null)
|
||||
{
|
||||
_dataSource = dataSource ?? throw new ArgumentNullException(nameof(dataSource));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -68,7 +72,7 @@ public sealed class PostgresIdempotencyKeyRepository : IIdempotencyKeyRepository
|
||||
{
|
||||
if (key.KeyId == Guid.Empty)
|
||||
{
|
||||
key.KeyId = Guid.NewGuid();
|
||||
key.KeyId = _guidProvider.NewGuid();
|
||||
}
|
||||
|
||||
var sql = $"""
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Npgsql;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Infrastructure.Postgres.Repositories;
|
||||
using StellaOps.Replay.Core;
|
||||
using StellaOps.Scanner.ProofSpine;
|
||||
@@ -28,14 +29,17 @@ public sealed class PostgresProofSpineRepository : RepositoryBase<ScannerDataSou
|
||||
};
|
||||
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly IGuidProvider _guidProvider;
|
||||
|
||||
public PostgresProofSpineRepository(
|
||||
ScannerDataSource dataSource,
|
||||
ILogger<PostgresProofSpineRepository> logger,
|
||||
TimeProvider? timeProvider = null)
|
||||
TimeProvider? timeProvider = null,
|
||||
IGuidProvider? guidProvider = null)
|
||||
: base(dataSource, logger)
|
||||
{
|
||||
_timeProvider = timeProvider ?? TimeProvider.System;
|
||||
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
|
||||
}
|
||||
|
||||
public Task<ProofSpineModel?> GetByIdAsync(string spineId, CancellationToken cancellationToken = default)
|
||||
@@ -249,7 +253,7 @@ public sealed class PostgresProofSpineRepository : RepositoryBase<ScannerDataSou
|
||||
await using (var command = CreateCommand(insertHistory, connection))
|
||||
{
|
||||
command.Transaction = transaction;
|
||||
AddParameter(command, "id", Guid.NewGuid().ToString("N"));
|
||||
AddParameter(command, "id", _guidProvider.NewGuid().ToString("N"));
|
||||
AddParameter(command, "old_spine_id", oldSpineId.Trim());
|
||||
AddParameter(command, "new_spine_id", newSpineId.Trim());
|
||||
AddParameter(command, "reason", reason);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Npgsql;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Scanner.Storage.Models;
|
||||
|
||||
namespace StellaOps.Scanner.Storage.Repositories;
|
||||
@@ -19,13 +20,16 @@ public sealed class PostgresScanMetricsRepository : IScanMetricsRepository
|
||||
{
|
||||
private readonly NpgsqlDataSource _dataSource;
|
||||
private readonly ILogger<PostgresScanMetricsRepository> _logger;
|
||||
private readonly IGuidProvider _guidProvider;
|
||||
|
||||
public PostgresScanMetricsRepository(
|
||||
NpgsqlDataSource dataSource,
|
||||
ILogger<PostgresScanMetricsRepository> logger)
|
||||
ILogger<PostgresScanMetricsRepository> logger,
|
||||
IGuidProvider? guidProvider = null)
|
||||
{
|
||||
_dataSource = dataSource ?? throw new ArgumentNullException(nameof(dataSource));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -67,7 +71,7 @@ public sealed class PostgresScanMetricsRepository : IScanMetricsRepository
|
||||
|
||||
await using var cmd = _dataSource.CreateCommand(sql);
|
||||
|
||||
var metricsId = metrics.MetricsId == Guid.Empty ? Guid.NewGuid() : metrics.MetricsId;
|
||||
var metricsId = metrics.MetricsId == Guid.Empty ? _guidProvider.NewGuid() : metrics.MetricsId;
|
||||
|
||||
cmd.Parameters.AddWithValue("metricsId", metricsId);
|
||||
cmd.Parameters.AddWithValue("scanId", metrics.ScanId);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Npgsql;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Infrastructure.Postgres.Repositories;
|
||||
using StellaOps.Scanner.Storage.Catalog;
|
||||
using StellaOps.Scanner.Storage.Postgres;
|
||||
@@ -16,10 +17,15 @@ public sealed class RuntimeEventRepository : RepositoryBase<ScannerDataSource>
|
||||
private string Table => $"{SchemaName}.runtime_events";
|
||||
private string SchemaName => DataSource.SchemaName ?? ScannerDataSource.DefaultSchema;
|
||||
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
|
||||
private readonly IGuidProvider _guidProvider;
|
||||
|
||||
public RuntimeEventRepository(ScannerDataSource dataSource, ILogger<RuntimeEventRepository> logger)
|
||||
public RuntimeEventRepository(
|
||||
ScannerDataSource dataSource,
|
||||
ILogger<RuntimeEventRepository> logger,
|
||||
IGuidProvider? guidProvider = null)
|
||||
: base(dataSource, logger)
|
||||
{
|
||||
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
|
||||
}
|
||||
|
||||
public async Task<RuntimeEventInsertResult> InsertAsync(
|
||||
@@ -52,7 +58,7 @@ public sealed class RuntimeEventRepository : RepositoryBase<ScannerDataSource>
|
||||
foreach (var document in documents)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var id = string.IsNullOrWhiteSpace(document.Id) ? Guid.NewGuid().ToString("N") : document.Id;
|
||||
var id = string.IsNullOrWhiteSpace(document.Id) ? _guidProvider.NewGuid().ToString("N") : document.Id;
|
||||
|
||||
var rows = await ExecuteAsync(
|
||||
Tenant,
|
||||
|
||||
@@ -28,5 +28,6 @@
|
||||
<ProjectReference Include="..\\StellaOps.Scanner.SmartDiff\\StellaOps.Scanner.SmartDiff.csproj" />
|
||||
<ProjectReference Include="..\\..\\..\\__Libraries\\StellaOps.Infrastructure.Postgres\\StellaOps.Infrastructure.Postgres.csproj" />
|
||||
<ProjectReference Include="..\\..\\..\\Router\\__Libraries\\StellaOps.Messaging\\StellaOps.Messaging.csproj" />
|
||||
<ProjectReference Include="..\\..\\..\\__Libraries\\StellaOps.Determinism.Abstractions\\StellaOps.Determinism.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -21,5 +21,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Scanner.Core\StellaOps.Scanner.Core.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Determinism.Abstractions/StellaOps.Determinism.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Npgsql;
|
||||
using StellaOps.Determinism;
|
||||
using StellaOps.Scanner.VulnSurfaces.Models;
|
||||
|
||||
namespace StellaOps.Scanner.VulnSurfaces.Storage;
|
||||
@@ -18,15 +19,18 @@ public sealed class PostgresVulnSurfaceRepository : IVulnSurfaceRepository
|
||||
{
|
||||
private readonly NpgsqlDataSource _dataSource;
|
||||
private readonly ILogger<PostgresVulnSurfaceRepository> _logger;
|
||||
private readonly IGuidProvider _guidProvider;
|
||||
private readonly int _commandTimeoutSeconds;
|
||||
|
||||
public PostgresVulnSurfaceRepository(
|
||||
NpgsqlDataSource dataSource,
|
||||
ILogger<PostgresVulnSurfaceRepository> logger,
|
||||
IGuidProvider? guidProvider = null,
|
||||
int commandTimeoutSeconds = 30)
|
||||
{
|
||||
_dataSource = dataSource ?? throw new ArgumentNullException(nameof(dataSource));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
|
||||
_commandTimeoutSeconds = commandTimeoutSeconds;
|
||||
}
|
||||
|
||||
@@ -45,7 +49,7 @@ public sealed class PostgresVulnSurfaceRepository : IVulnSurfaceRepository
|
||||
string? attestationDigest,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var id = Guid.NewGuid();
|
||||
var id = _guidProvider.NewGuid();
|
||||
|
||||
const string sql = """
|
||||
INSERT INTO scanner.vuln_surfaces (
|
||||
@@ -106,7 +110,7 @@ public sealed class PostgresVulnSurfaceRepository : IVulnSurfaceRepository
|
||||
string? fixedHash,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var id = Guid.NewGuid();
|
||||
var id = _guidProvider.NewGuid();
|
||||
|
||||
const string sql = """
|
||||
INSERT INTO scanner.vuln_surface_sinks (
|
||||
@@ -148,7 +152,7 @@ public sealed class PostgresVulnSurfaceRepository : IVulnSurfaceRepository
|
||||
double confidence,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var id = Guid.NewGuid();
|
||||
var id = _guidProvider.NewGuid();
|
||||
|
||||
const string sql = """
|
||||
INSERT INTO scanner.vuln_surface_triggers (
|
||||
|
||||
Reference in New Issue
Block a user