namespace StellaOps.Concelier.ProofService.Postgres; using Dapper; using Microsoft.Extensions.Logging; using Npgsql; using StellaOps.Concelier.ProofService; using StellaOps.Feedser.BinaryAnalysis.Models; /// /// PostgreSQL implementation of patch repository. /// Queries vuln.patch_evidence and feedser.binary_fingerprints tables. /// public sealed class PostgresPatchRepository : IPatchRepository { private readonly string _connectionString; private readonly ILogger _logger; public PostgresPatchRepository( string connectionString, ILogger logger) { _connectionString = connectionString; _logger = logger; } /// /// Find patch headers mentioning the given CVE ID. /// Returns all matching patch headers ordered by parsed date (newest first). /// public async Task> FindPatchHeadersByCveAsync( string cveId, CancellationToken ct) { const string sql = @" SELECT patch_file_path AS PatchFilePath, origin AS Origin, parsed_at AS ParsedAt, cve_ids AS CveIds FROM vuln.patch_evidence WHERE @CveId = ANY(cve_ids) ORDER BY parsed_at DESC; "; try { await using var connection = new NpgsqlConnection(_connectionString); await connection.OpenAsync(ct); var results = await connection.QueryAsync( new CommandDefinition(sql, new { CveId = cveId }, cancellationToken: ct)); var patchList = results.ToList(); _logger.LogDebug( "Found {Count} patch headers for {CveId}", patchList.Count, cveId); return patchList; } catch (Exception ex) { _logger.LogError(ex, "Failed to query patch headers for {CveId}", cveId); throw; } } /// /// Find patch signatures (HunkSig matches) for the given CVE ID. /// Returns all matching signatures ordered by extraction date (newest first). /// public async Task> FindPatchSignaturesByCveAsync( string cveId, CancellationToken ct) { const string sql = @" SELECT commit_sha AS CommitSha, upstream_repo AS UpstreamRepo, extracted_at AS ExtractedAt, hunk_hash AS HunkHash FROM vuln.patch_signatures WHERE cve_id = @CveId ORDER BY extracted_at DESC; "; try { await using var connection = new NpgsqlConnection(_connectionString); await connection.OpenAsync(ct); var results = await connection.QueryAsync( new CommandDefinition(sql, new { CveId = cveId }, cancellationToken: ct)); var sigList = results.ToList(); _logger.LogDebug( "Found {Count} patch signatures for {CveId}", sigList.Count, cveId); return sigList; } catch (Exception ex) { _logger.LogError(ex, "Failed to query patch signatures for {CveId}", cveId); throw; } } /// /// Find binary fingerprints for the given CVE ID. /// Returns all matching fingerprints ordered by extraction date (newest first). /// public async Task> FindBinaryFingerprintsByCveAsync( string cveId, CancellationToken ct) { const string sql = @" SELECT fingerprint_id AS FingerprintId, cve_id AS CveId, method AS Method, fingerprint_value AS FingerprintValue, target_binary AS TargetBinary, target_function AS TargetFunction, architecture AS Architecture, format AS Format, compiler AS Compiler, optimization_level AS OptimizationLevel, has_debug_symbols AS HasDebugSymbols, file_offset AS FileOffset, region_size AS RegionSize, extracted_at AS ExtractedAt, extractor_version AS ExtractorVersion FROM feedser.binary_fingerprints WHERE cve_id = @CveId ORDER BY extracted_at DESC; "; try { await using var connection = new NpgsqlConnection(_connectionString); await connection.OpenAsync(ct); var results = await connection.QueryAsync( new CommandDefinition(sql, new { CveId = cveId }, cancellationToken: ct)); var fingerprints = results.Select(row => new BinaryFingerprint { FingerprintId = row.FingerprintId, CveId = row.CveId, Method = Enum.Parse(row.Method, ignoreCase: true), FingerprintValue = row.FingerprintValue, TargetBinary = row.TargetBinary, TargetFunction = row.TargetFunction, Metadata = new FingerprintMetadata { Architecture = row.Architecture, Format = row.Format, Compiler = row.Compiler, OptimizationLevel = row.OptimizationLevel, HasDebugSymbols = row.HasDebugSymbols, FileOffset = row.FileOffset, RegionSize = row.RegionSize }, ExtractedAt = row.ExtractedAt, ExtractorVersion = row.ExtractorVersion }).ToList(); _logger.LogDebug( "Found {Count} binary fingerprints for {CveId}", fingerprints.Count, cveId); return fingerprints; } catch (Exception ex) { _logger.LogError(ex, "Failed to query binary fingerprints for {CveId}", cveId); throw; } } // Internal row mapping class for Dapper private sealed class BinaryFingerprintRow { public required string FingerprintId { get; init; } public required string CveId { get; init; } public required string Method { get; init; } public required string FingerprintValue { get; init; } public required string TargetBinary { get; init; } public string? TargetFunction { get; init; } public required string Architecture { get; init; } public required string Format { get; init; } public string? Compiler { get; init; } public string? OptimizationLevel { get; init; } public required bool HasDebugSymbols { get; init; } public long? FileOffset { get; init; } public long? RegionSize { get; init; } public required DateTimeOffset ExtractedAt { get; init; } public required string ExtractorVersion { get; init; } } }