release orchestrator v1 draft and build fixes

This commit is contained in:
master
2026-01-12 12:24:17 +02:00
parent f3de858c59
commit 9873f80830
1598 changed files with 240385 additions and 5944 deletions

View File

@@ -17,18 +17,33 @@ public sealed class DeltaSignatureMatcher : IDeltaSignatureMatcher
{
private readonly DisassemblyService _disassemblyService;
private readonly NormalizationService _normalizationService;
private readonly ISymbolChangeTracer _changeTracer;
private readonly ILogger<DeltaSignatureMatcher> _logger;
public DeltaSignatureMatcher(
DisassemblyService disassemblyService,
NormalizationService normalizationService,
ISymbolChangeTracer changeTracer,
ILogger<DeltaSignatureMatcher> logger)
{
_disassemblyService = disassemblyService;
_normalizationService = normalizationService;
_changeTracer = changeTracer;
_logger = logger;
}
/// <summary>
/// Legacy constructor for backward compatibility.
/// Creates an internal SymbolChangeTracer instance.
/// </summary>
public DeltaSignatureMatcher(
DisassemblyService disassemblyService,
NormalizationService normalizationService,
ILogger<DeltaSignatureMatcher> logger)
: this(disassemblyService, normalizationService, new SymbolChangeTracer(), logger)
{
}
/// <inheritdoc />
public async Task<IReadOnlyList<MatchResult>> MatchAsync(
Stream binaryStream,
@@ -329,6 +344,58 @@ public sealed class DeltaSignatureMatcher : IDeltaSignatureMatcher
}
}
/// <inheritdoc />
public Task<DeltaComparisonResult> CompareSignaturesAsync(
DeltaSignature fromSignature,
DeltaSignature toSignature,
CancellationToken ct = default)
{
ArgumentNullException.ThrowIfNull(fromSignature);
ArgumentNullException.ThrowIfNull(toSignature);
_logger.LogDebug(
"Comparing signatures: {From} ({FromSymbols} symbols) -> {To} ({ToSymbols} symbols)",
fromSignature.SignatureId,
fromSignature.Symbols.Length,
toSignature.SignatureId,
toSignature.Symbols.Length);
var symbolResults = _changeTracer.CompareAllSymbols(fromSignature, toSignature);
var summary = new DeltaComparisonSummary
{
TotalSymbols = symbolResults.Count,
UnchangedSymbols = symbolResults.Count(r => r.ChangeType == SymbolChangeType.Unchanged),
AddedSymbols = symbolResults.Count(r => r.ChangeType == SymbolChangeType.Added),
RemovedSymbols = symbolResults.Count(r => r.ChangeType == SymbolChangeType.Removed),
ModifiedSymbols = symbolResults.Count(r => r.ChangeType == SymbolChangeType.Modified),
PatchedSymbols = symbolResults.Count(r => r.ChangeType == SymbolChangeType.Patched),
AverageConfidence = symbolResults.Count > 0
? symbolResults.Average(r => r.Confidence)
: 0.0,
TotalSizeDelta = symbolResults.Sum(r => r.SizeDelta)
};
_logger.LogInformation(
"Comparison complete: {Total} symbols, {Unchanged} unchanged, {Added} added, {Removed} removed, {Modified} modified, {Patched} patched",
summary.TotalSymbols,
summary.UnchangedSymbols,
summary.AddedSymbols,
summary.RemovedSymbols,
summary.ModifiedSymbols,
summary.PatchedSymbols);
var result = new DeltaComparisonResult
{
FromSignatureId = fromSignature.SignatureId,
ToSignatureId = toSignature.SignatureId,
SymbolResults = [.. symbolResults],
Summary = summary
};
return Task.FromResult(result);
}
private static byte[] GetNormalizedBytes(NormalizedFunction normalized)
{
var totalSize = normalized.Instructions.Sum(i => i.NormalizedBytes.Length);

View File

@@ -35,4 +35,16 @@ public interface IDeltaSignatureMatcher
string symbolHash,
string symbolName,
IEnumerable<DeltaSignature> signatures);
/// <summary>
/// Compare two delta signatures and return detailed change information.
/// </summary>
/// <param name="fromSignature">The "before" signature.</param>
/// <param name="toSignature">The "after" signature.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>Detailed comparison result with symbol-level changes.</returns>
Task<DeltaComparisonResult> CompareSignaturesAsync(
DeltaSignature fromSignature,
DeltaSignature toSignature,
CancellationToken ct = default);
}

View File

@@ -0,0 +1,30 @@
// Copyright (c) StellaOps. All rights reserved.
// Licensed under AGPL-3.0-or-later. See LICENSE in the project root.
namespace StellaOps.BinaryIndex.DeltaSig;
/// <summary>
/// Service for detailed symbol comparison between binary versions.
/// </summary>
public interface ISymbolChangeTracer
{
/// <summary>
/// Compare two symbol signatures and compute detailed change metrics.
/// </summary>
/// <param name="fromSymbol">Symbol from the "before" version (null if added).</param>
/// <param name="toSymbol">Symbol from the "after" version (null if removed).</param>
/// <returns>Detailed symbol match result with change tracking.</returns>
SymbolMatchResult CompareSymbols(
SymbolSignature? fromSymbol,
SymbolSignature? toSymbol);
/// <summary>
/// Compare all symbols between two delta signatures.
/// </summary>
/// <param name="fromSignature">The "before" delta signature.</param>
/// <param name="toSignature">The "after" delta signature.</param>
/// <returns>List of symbol comparison results.</returns>
IReadOnlyList<SymbolMatchResult> CompareAllSymbols(
DeltaSignature fromSignature,
DeltaSignature toSignature);
}

View File

@@ -72,6 +72,11 @@ public sealed record DeltaSignatureRequest
/// </summary>
public sealed record DeltaSignature
{
/// <summary>
/// Unique identifier for this signature.
/// </summary>
public string SignatureId { get; init; } = Guid.NewGuid().ToString("N");
/// <summary>
/// Schema identifier for this signature format.
/// </summary>
@@ -278,6 +283,79 @@ public sealed record SymbolMatchResult
/// Match confidence (0.0 - 1.0).
/// </summary>
public double Confidence { get; init; }
// ====== CHANGE TRACKING FIELDS ======
/// <summary>
/// Type of change detected.
/// </summary>
public SymbolChangeType ChangeType { get; init; } = SymbolChangeType.Unchanged;
/// <summary>
/// Size delta in bytes (positive = larger, negative = smaller).
/// </summary>
public int SizeDelta { get; init; }
/// <summary>
/// CFG basic block count delta (if available).
/// </summary>
public int? CfgBlockDelta { get; init; }
/// <summary>
/// Indices of chunks that matched (for partial match analysis).
/// </summary>
public ImmutableArray<int> MatchedChunkIndices { get; init; } = [];
/// <summary>
/// Human-readable explanation of the change.
/// </summary>
public string? ChangeExplanation { get; init; }
/// <summary>
/// Hash of the "from" version (before change).
/// </summary>
public string? FromHash { get; init; }
/// <summary>
/// Hash of the "to" version (after change).
/// </summary>
public string? ToHash { get; init; }
/// <summary>
/// Method used for matching (CFGHash, InstructionHash, SemanticHash, ChunkHash).
/// </summary>
public string? MatchMethod { get; init; }
}
/// <summary>
/// Type of symbol change detected.
/// </summary>
public enum SymbolChangeType
{
/// <summary>
/// No change detected.
/// </summary>
Unchanged,
/// <summary>
/// Symbol was added (not present in "from" version).
/// </summary>
Added,
/// <summary>
/// Symbol was removed (not present in "to" version).
/// </summary>
Removed,
/// <summary>
/// Symbol was modified (hash changed).
/// </summary>
Modified,
/// <summary>
/// Symbol was patched (security fix applied, verified).
/// </summary>
Patched
}
/// <summary>
@@ -310,3 +388,75 @@ public sealed record AuthoringResult
/// </summary>
public string? Error { get; init; }
}
/// <summary>
/// Result of comparing two delta signatures.
/// </summary>
public sealed record DeltaComparisonResult
{
/// <summary>
/// Identifier for the "from" signature.
/// </summary>
public required string FromSignatureId { get; init; }
/// <summary>
/// Identifier for the "to" signature.
/// </summary>
public required string ToSignatureId { get; init; }
/// <summary>
/// Individual symbol comparison results.
/// </summary>
public ImmutableArray<SymbolMatchResult> SymbolResults { get; init; } = [];
/// <summary>
/// Summary of the comparison.
/// </summary>
public required DeltaComparisonSummary Summary { get; init; }
}
/// <summary>
/// Summary of a delta comparison between two signatures.
/// </summary>
public sealed record DeltaComparisonSummary
{
/// <summary>
/// Total number of symbols compared.
/// </summary>
public int TotalSymbols { get; init; }
/// <summary>
/// Number of unchanged symbols.
/// </summary>
public int UnchangedSymbols { get; init; }
/// <summary>
/// Number of added symbols.
/// </summary>
public int AddedSymbols { get; init; }
/// <summary>
/// Number of removed symbols.
/// </summary>
public int RemovedSymbols { get; init; }
/// <summary>
/// Number of modified symbols.
/// </summary>
public int ModifiedSymbols { get; init; }
/// <summary>
/// Number of patched symbols (security fixes).
/// </summary>
public int PatchedSymbols { get; init; }
/// <summary>
/// Average confidence across all symbol comparisons.
/// </summary>
public double AverageConfidence { get; init; }
/// <summary>
/// Total size delta in bytes.
/// </summary>
public int TotalSizeDelta { get; init; }
}

View File

@@ -43,6 +43,7 @@ public static class ServiceCollectionExtensions
logger);
});
services.AddSingleton<ISymbolChangeTracer, SymbolChangeTracer>();
services.AddSingleton<IDeltaSignatureMatcher, DeltaSignatureMatcher>();
return services;

View File

@@ -0,0 +1,237 @@
// Copyright (c) StellaOps. All rights reserved.
// Licensed under AGPL-3.0-or-later. See LICENSE in the project root.
using System.Collections.Immutable;
using System.Globalization;
namespace StellaOps.BinaryIndex.DeltaSig;
/// <summary>
/// Service for detailed symbol comparison between binary versions.
/// Determines change type, similarity, and generates explanations.
/// </summary>
public sealed class SymbolChangeTracer : ISymbolChangeTracer
{
/// <inheritdoc />
public SymbolMatchResult CompareSymbols(
SymbolSignature? fromSymbol,
SymbolSignature? toSymbol)
{
// Case 1: Symbol added
if (fromSymbol is null && toSymbol is not null)
{
return new SymbolMatchResult
{
SymbolName = toSymbol.Name,
ExactMatch = false,
Confidence = 1.0,
ChangeType = SymbolChangeType.Added,
SizeDelta = toSymbol.SizeBytes,
ToHash = toSymbol.HashHex,
ChangeExplanation = "Symbol added in new version"
};
}
// Case 2: Symbol removed
if (fromSymbol is not null && toSymbol is null)
{
return new SymbolMatchResult
{
SymbolName = fromSymbol.Name,
ExactMatch = false,
Confidence = 1.0,
ChangeType = SymbolChangeType.Removed,
SizeDelta = -fromSymbol.SizeBytes,
FromHash = fromSymbol.HashHex,
ChangeExplanation = "Symbol removed in new version"
};
}
// Case 3: Both exist - compare
if (fromSymbol is not null && toSymbol is not null)
{
return CompareExistingSymbols(fromSymbol, toSymbol);
}
// Case 4: Both null (shouldn't happen)
throw new ArgumentException("Both symbols cannot be null");
}
/// <inheritdoc />
public IReadOnlyList<SymbolMatchResult> CompareAllSymbols(
DeltaSignature fromSignature,
DeltaSignature toSignature)
{
ArgumentNullException.ThrowIfNull(fromSignature);
ArgumentNullException.ThrowIfNull(toSignature);
var fromSymbols = fromSignature.Symbols
.ToDictionary(s => s.Name, StringComparer.Ordinal);
var toSymbols = toSignature.Symbols
.ToDictionary(s => s.Name, StringComparer.Ordinal);
var allNames = fromSymbols.Keys
.Union(toSymbols.Keys, StringComparer.Ordinal)
.OrderBy(n => n, StringComparer.Ordinal);
var results = new List<SymbolMatchResult>();
foreach (var name in allNames)
{
fromSymbols.TryGetValue(name, out var fromSymbol);
toSymbols.TryGetValue(name, out var toSymbol);
var result = CompareSymbols(fromSymbol, toSymbol);
results.Add(result);
}
return results;
}
private static SymbolMatchResult CompareExistingSymbols(
SymbolSignature from,
SymbolSignature to)
{
var exactMatch = string.Equals(from.HashHex, to.HashHex, StringComparison.OrdinalIgnoreCase);
var sizeDelta = to.SizeBytes - from.SizeBytes;
var cfgDelta = (from.CfgBbCount.HasValue && to.CfgBbCount.HasValue)
? to.CfgBbCount.Value - from.CfgBbCount.Value
: (int?)null;
if (exactMatch)
{
return new SymbolMatchResult
{
SymbolName = from.Name,
ExactMatch = true,
Confidence = 1.0,
ChangeType = SymbolChangeType.Unchanged,
SizeDelta = 0,
FromHash = from.HashHex,
ToHash = to.HashHex,
MatchMethod = "ExactHash",
ChangeExplanation = "No change detected"
};
}
// Compute chunk matches
var fromChunks = from.Chunks ?? [];
var toChunks = to.Chunks ?? [];
var (chunksMatched, matchedIndices) = CompareChunks(fromChunks, toChunks);
var chunkSimilarity = fromChunks.Length > 0
? (double)chunksMatched / fromChunks.Length
: 0.0;
// Determine change type and confidence
var (changeType, confidence, explanation, method) = DetermineChange(
from, to, chunkSimilarity, cfgDelta);
return new SymbolMatchResult
{
SymbolName = from.Name,
ExactMatch = false,
ChunksMatched = chunksMatched,
ChunksTotal = Math.Max(fromChunks.Length, toChunks.Length),
Confidence = confidence,
ChangeType = changeType,
SizeDelta = sizeDelta,
CfgBlockDelta = cfgDelta,
MatchedChunkIndices = matchedIndices,
FromHash = from.HashHex,
ToHash = to.HashHex,
MatchMethod = method,
ChangeExplanation = explanation
};
}
private static (int matched, ImmutableArray<int> indices) CompareChunks(
ImmutableArray<ChunkHash> fromChunks,
ImmutableArray<ChunkHash> toChunks)
{
if (fromChunks.Length == 0 || toChunks.Length == 0)
{
return (0, []);
}
var toChunkSet = toChunks
.Select(c => c.HashHex)
.ToHashSet(StringComparer.OrdinalIgnoreCase);
var matchedIndices = new List<int>();
var matched = 0;
for (var i = 0; i < fromChunks.Length; i++)
{
if (toChunkSet.Contains(fromChunks[i].HashHex))
{
matched++;
matchedIndices.Add(i);
}
}
return (matched, matchedIndices.ToImmutableArray());
}
private static (SymbolChangeType type, double confidence, string explanation, string method)
DetermineChange(
SymbolSignature from,
SymbolSignature to,
double chunkSimilarity,
int? cfgDelta)
{
// High chunk similarity with CFG change = likely patch
if (chunkSimilarity >= 0.85 && cfgDelta.HasValue && Math.Abs(cfgDelta.Value) <= 5)
{
return (
SymbolChangeType.Patched,
Math.Min(0.95, chunkSimilarity),
string.Format(
CultureInfo.InvariantCulture,
"Function patched: {0} basic blocks changed",
Math.Abs(cfgDelta.Value)),
"CFGHash+ChunkMatch"
);
}
// High chunk similarity = minor modification
if (chunkSimilarity >= 0.7)
{
return (
SymbolChangeType.Modified,
chunkSimilarity,
string.Format(
CultureInfo.InvariantCulture,
"Function modified: {0:P0} of code changed",
1 - chunkSimilarity),
"ChunkMatch"
);
}
// Semantic match check (if available)
if (!string.IsNullOrEmpty(from.SemanticHashHex) &&
!string.IsNullOrEmpty(to.SemanticHashHex))
{
var semanticMatch = string.Equals(
from.SemanticHashHex, to.SemanticHashHex,
StringComparison.OrdinalIgnoreCase);
if (semanticMatch)
{
return (
SymbolChangeType.Modified,
0.80,
"Function semantically equivalent (compiler variation)",
"SemanticHash"
);
}
}
// Low similarity = significant modification
return (
SymbolChangeType.Modified,
Math.Max(0.4, chunkSimilarity),
"Function significantly modified",
"ChunkMatch"
);
}
}

View File

@@ -5,6 +5,7 @@ using System.Collections.Immutable;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.BinaryIndex.Disassembly;
using StellaOps.Determinism;
namespace StellaOps.BinaryIndex.Ghidra;
@@ -29,6 +30,7 @@ public sealed class GhidraDisassemblyPlugin : IDisassemblyPlugin, IDisposable
private readonly GhidraOptions _options;
private readonly ILogger<GhidraDisassemblyPlugin> _logger;
private readonly TimeProvider _timeProvider;
private readonly IGuidProvider _guidProvider;
private bool _disposed;
private static readonly DisassemblyCapabilities s_capabilities = new()
@@ -74,16 +76,19 @@ public sealed class GhidraDisassemblyPlugin : IDisassemblyPlugin, IDisposable
/// <param name="options">Ghidra options.</param>
/// <param name="logger">Logger instance.</param>
/// <param name="timeProvider">Time provider for timestamps.</param>
/// <param name="guidProvider">GUID provider for deterministic IDs.</param>
public GhidraDisassemblyPlugin(
IGhidraService ghidraService,
IOptions<GhidraOptions> options,
ILogger<GhidraDisassemblyPlugin> logger,
TimeProvider timeProvider)
TimeProvider timeProvider,
IGuidProvider? guidProvider = null)
{
_ghidraService = ghidraService ?? throw new ArgumentNullException(nameof(ghidraService));
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_timeProvider = timeProvider ?? throw new ArgumentNullException(nameof(timeProvider));
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
}
/// <inheritdoc />
@@ -297,7 +302,7 @@ public sealed class GhidraDisassemblyPlugin : IDisassemblyPlugin, IDisposable
// Write bytes to temp file
var tempPath = Path.Combine(
_options.WorkDir,
$"disasm_{_timeProvider.GetUtcNow():yyyyMMddHHmmssfff}_{Guid.NewGuid():N}.bin");
$"disasm_{_timeProvider.GetUtcNow():yyyyMMddHHmmssfff}_{_guidProvider.NewGuid():N}.bin");
try
{

View File

@@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Determinism;
namespace StellaOps.BinaryIndex.Ghidra;
@@ -18,6 +19,8 @@ public sealed class GhidraHeadlessManager : IAsyncDisposable
{
private readonly GhidraOptions _options;
private readonly ILogger<GhidraHeadlessManager> _logger;
private readonly TimeProvider _timeProvider;
private readonly IGuidProvider _guidProvider;
private readonly SemaphoreSlim _semaphore;
private bool _disposed;
@@ -26,12 +29,18 @@ public sealed class GhidraHeadlessManager : IAsyncDisposable
/// </summary>
/// <param name="options">Ghidra configuration options.</param>
/// <param name="logger">Logger instance.</param>
/// <param name="timeProvider">Time provider for deterministic time.</param>
/// <param name="guidProvider">GUID provider for deterministic IDs.</param>
public GhidraHeadlessManager(
IOptions<GhidraOptions> options,
ILogger<GhidraHeadlessManager> logger)
ILogger<GhidraHeadlessManager> logger,
TimeProvider? timeProvider = null,
IGuidProvider? guidProvider = null)
{
_options = options.Value;
_logger = logger;
_timeProvider = timeProvider ?? TimeProvider.System;
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
_semaphore = new SemaphoreSlim(_options.MaxConcurrentInstances, _options.MaxConcurrentInstances);
EnsureWorkDirectoryExists();
@@ -180,7 +189,7 @@ public sealed class GhidraHeadlessManager : IAsyncDisposable
{
var projectDir = Path.Combine(
_options.WorkDir,
$"project_{DateTime.UtcNow:yyyyMMddHHmmssfff}_{Guid.NewGuid():N}");
$"project_{_timeProvider.GetUtcNow():yyyyMMddHHmmssfff}_{_guidProvider.NewGuid():N}");
Directory.CreateDirectory(projectDir);
_logger.LogDebug("Created temp project directory: {Path}", projectDir);

View File

@@ -7,6 +7,7 @@ using System.Security.Cryptography;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Determinism;
namespace StellaOps.BinaryIndex.Ghidra;
@@ -25,6 +26,7 @@ public sealed class GhidraService : IGhidraService, IAsyncDisposable
private readonly GhidraOptions _options;
private readonly ILogger<GhidraService> _logger;
private readonly TimeProvider _timeProvider;
private readonly IGuidProvider _guidProvider;
/// <summary>
/// Creates a new GhidraService.
@@ -33,16 +35,19 @@ public sealed class GhidraService : IGhidraService, IAsyncDisposable
/// <param name="options">Ghidra options.</param>
/// <param name="logger">Logger instance.</param>
/// <param name="timeProvider">Time provider for timestamps.</param>
/// <param name="guidProvider">GUID provider for deterministic IDs.</param>
public GhidraService(
GhidraHeadlessManager headlessManager,
IOptions<GhidraOptions> options,
ILogger<GhidraService> logger,
TimeProvider timeProvider)
TimeProvider timeProvider,
IGuidProvider? guidProvider = null)
{
_headlessManager = headlessManager;
_options = options.Value;
_logger = logger;
_timeProvider = timeProvider;
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
}
/// <inheritdoc />
@@ -56,7 +61,7 @@ public sealed class GhidraService : IGhidraService, IAsyncDisposable
// Write stream to temp file
var tempPath = Path.Combine(
_options.WorkDir,
$"binary_{_timeProvider.GetUtcNow():yyyyMMddHHmmssfff}_{Guid.NewGuid():N}.bin");
$"binary_{_timeProvider.GetUtcNow():yyyyMMddHHmmssfff}_{_guidProvider.NewGuid():N}.bin");
try
{

View File

@@ -10,6 +10,7 @@ using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Determinism;
namespace StellaOps.BinaryIndex.Ghidra;
@@ -28,6 +29,7 @@ public sealed class GhidriffBridge : IGhidriffBridge
private readonly GhidraOptions _ghidraOptions;
private readonly ILogger<GhidriffBridge> _logger;
private readonly TimeProvider _timeProvider;
private readonly IGuidProvider _guidProvider;
/// <summary>
/// Creates a new GhidriffBridge.
@@ -36,16 +38,19 @@ public sealed class GhidriffBridge : IGhidriffBridge
/// <param name="ghidraOptions">Ghidra options for path configuration.</param>
/// <param name="logger">Logger instance.</param>
/// <param name="timeProvider">Time provider.</param>
/// <param name="guidProvider">GUID provider for deterministic IDs.</param>
public GhidriffBridge(
IOptions<GhidriffOptions> options,
IOptions<GhidraOptions> ghidraOptions,
ILogger<GhidriffBridge> logger,
TimeProvider timeProvider)
TimeProvider timeProvider,
IGuidProvider? guidProvider = null)
{
_options = options.Value;
_ghidraOptions = ghidraOptions.Value;
_logger = logger;
_timeProvider = timeProvider;
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
EnsureWorkDirectoryExists();
}
@@ -212,7 +217,7 @@ public sealed class GhidriffBridge : IGhidriffBridge
{
var outputDir = Path.Combine(
_options.WorkDir,
$"diff_{_timeProvider.GetUtcNow():yyyyMMddHHmmssfff}_{Guid.NewGuid():N}");
$"diff_{_timeProvider.GetUtcNow():yyyyMMddHHmmssfff}_{_guidProvider.NewGuid():N}");
Directory.CreateDirectory(outputDir);
return outputDir;
@@ -523,7 +528,7 @@ public sealed class GhidriffBridge : IGhidriffBridge
{
var path = Path.Combine(
_options.WorkDir,
$"{prefix}_{_timeProvider.GetUtcNow():yyyyMMddHHmmssfff}_{Guid.NewGuid():N}.bin");
$"{prefix}_{_timeProvider.GetUtcNow():yyyyMMddHHmmssfff}_{_guidProvider.NewGuid():N}.bin");
Directory.CreateDirectory(Path.GetDirectoryName(path)!);

View File

@@ -7,6 +7,7 @@ using System.Security.Cryptography;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Determinism;
namespace StellaOps.BinaryIndex.Ghidra;
@@ -25,6 +26,7 @@ public sealed class VersionTrackingService : IVersionTrackingService
private readonly GhidraOptions _options;
private readonly ILogger<VersionTrackingService> _logger;
private readonly TimeProvider _timeProvider;
private readonly IGuidProvider _guidProvider;
/// <summary>
/// Creates a new VersionTrackingService.
@@ -33,16 +35,19 @@ public sealed class VersionTrackingService : IVersionTrackingService
/// <param name="options">Ghidra options.</param>
/// <param name="logger">Logger instance.</param>
/// <param name="timeProvider">Time provider.</param>
/// <param name="guidProvider">GUID provider for deterministic IDs.</param>
public VersionTrackingService(
GhidraHeadlessManager headlessManager,
IOptions<GhidraOptions> options,
ILogger<VersionTrackingService> logger,
TimeProvider timeProvider)
TimeProvider timeProvider,
IGuidProvider? guidProvider = null)
{
_headlessManager = headlessManager;
_options = options.Value;
_logger = logger;
_timeProvider = timeProvider;
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
}
/// <inheritdoc />
@@ -343,7 +348,7 @@ public sealed class VersionTrackingService : IVersionTrackingService
{
var path = Path.Combine(
_options.WorkDir,
$"{prefix}_{_timeProvider.GetUtcNow():yyyyMMddHHmmssfff}_{Guid.NewGuid():N}.bin");
$"{prefix}_{_timeProvider.GetUtcNow():yyyyMMddHHmmssfff}_{_guidProvider.NewGuid():N}.bin");
Directory.CreateDirectory(Path.GetDirectoryName(path)!);

View File

@@ -12,6 +12,7 @@
<ItemGroup>
<ProjectReference Include="..\StellaOps.BinaryIndex.Disassembly.Abstractions\StellaOps.BinaryIndex.Disassembly.Abstractions.csproj" />
<ProjectReference Include="..\StellaOps.BinaryIndex.Contracts\StellaOps.BinaryIndex.Contracts.csproj" />
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Determinism.Abstractions\StellaOps.Determinism.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -7,6 +7,7 @@ using System.Text.Json;
using Dapper;
using Microsoft.Extensions.Logging;
using StellaOps.BinaryIndex.DeltaSig;
using StellaOps.Determinism;
namespace StellaOps.BinaryIndex.Persistence.Repositories;
@@ -17,6 +18,8 @@ public sealed class DeltaSignatureRepository : IDeltaSignatureRepository
{
private readonly BinaryIndexDbContext _dbContext;
private readonly ILogger<DeltaSignatureRepository> _logger;
private readonly TimeProvider _timeProvider;
private readonly IGuidProvider _guidProvider;
private static readonly JsonSerializerOptions s_jsonOptions = new()
{
@@ -26,10 +29,14 @@ public sealed class DeltaSignatureRepository : IDeltaSignatureRepository
public DeltaSignatureRepository(
BinaryIndexDbContext dbContext,
ILogger<DeltaSignatureRepository> logger)
ILogger<DeltaSignatureRepository> logger,
TimeProvider? timeProvider = null,
IGuidProvider? guidProvider = null)
{
_dbContext = dbContext;
_logger = logger;
_timeProvider = timeProvider ?? TimeProvider.System;
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
}
/// <inheritdoc />
@@ -59,8 +66,8 @@ public sealed class DeltaSignatureRepository : IDeltaSignatureRepository
RETURNING id, created_at, updated_at
""";
var now = DateTimeOffset.UtcNow;
var id = entity.Id != Guid.Empty ? entity.Id : Guid.NewGuid();
var now = _timeProvider.GetUtcNow();
var id = entity.Id != Guid.Empty ? entity.Id : _guidProvider.NewGuid();
var command = new CommandDefinition(
sql,
@@ -362,7 +369,7 @@ public sealed class DeltaSignatureRepository : IDeltaSignatureRepository
RETURNING updated_at
""";
var now = DateTimeOffset.UtcNow;
var now = _timeProvider.GetUtcNow();
var command = new CommandDefinition(
sql,

View File

@@ -3,6 +3,8 @@ using Dapper;
using StellaOps.BinaryIndex.Fingerprints;
using StellaOps.BinaryIndex.Fingerprints.Models;
using System.Text.Json;
using IGuidProvider = StellaOps.Determinism.IGuidProvider;
using SystemGuidProvider = StellaOps.Determinism.SystemGuidProvider;
namespace StellaOps.BinaryIndex.Persistence.Repositories;
@@ -12,11 +14,13 @@ namespace StellaOps.BinaryIndex.Persistence.Repositories;
public sealed class FingerprintRepository : IFingerprintRepository
{
private readonly BinaryIndexDbContext _dbContext;
private readonly IGuidProvider _guidProvider;
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
public FingerprintRepository(BinaryIndexDbContext dbContext)
public FingerprintRepository(BinaryIndexDbContext dbContext, IGuidProvider? guidProvider = null)
{
_dbContext = dbContext;
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
}
public async Task<VulnFingerprint> CreateAsync(VulnFingerprint fingerprint, CancellationToken ct = default)
@@ -42,7 +46,7 @@ public sealed class FingerprintRepository : IFingerprintRepository
sql,
new
{
Id = fingerprint.Id != Guid.Empty ? fingerprint.Id : Guid.NewGuid(),
Id = fingerprint.Id != Guid.Empty ? fingerprint.Id : _guidProvider.NewGuid(),
fingerprint.CveId,
fingerprint.Component,
fingerprint.Purl,
@@ -256,10 +260,12 @@ public sealed class FingerprintRepository : IFingerprintRepository
public sealed class FingerprintMatchRepository : IFingerprintMatchRepository
{
private readonly BinaryIndexDbContext _dbContext;
private readonly IGuidProvider _guidProvider;
public FingerprintMatchRepository(BinaryIndexDbContext dbContext)
public FingerprintMatchRepository(BinaryIndexDbContext dbContext, IGuidProvider? guidProvider = null)
{
_dbContext = dbContext;
_guidProvider = guidProvider ?? SystemGuidProvider.Instance;
}
public async Task<FingerprintMatch> CreateAsync(FingerprintMatch match, CancellationToken ct = default)
@@ -284,7 +290,7 @@ public sealed class FingerprintMatchRepository : IFingerprintMatchRepository
sql,
new
{
Id = match.Id != Guid.Empty ? match.Id : Guid.NewGuid(),
Id = match.Id != Guid.Empty ? match.Id : _guidProvider.NewGuid(),
match.ScanId,
MatchType = match.Type.ToString().ToLowerInvariant(),
match.BinaryKey,

View File

@@ -21,6 +21,7 @@
<ProjectReference Include="..\StellaOps.BinaryIndex.FixIndex\StellaOps.BinaryIndex.FixIndex.csproj" />
<ProjectReference Include="..\StellaOps.BinaryIndex.Fingerprints\StellaOps.BinaryIndex.Fingerprints.csproj" />
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Infrastructure.Postgres\StellaOps.Infrastructure.Postgres.csproj" />
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Determinism.Abstractions\StellaOps.Determinism.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>