fix tests. new product advisories enhancements

This commit is contained in:
master
2026-01-25 19:11:36 +02:00
parent c70e83719e
commit 6e687b523a
504 changed files with 40610 additions and 3785 deletions

View File

@@ -91,6 +91,12 @@ public sealed class LanguageComponentWriter
_builder = builder ?? throw new ArgumentNullException(nameof(builder));
}
/// <summary>
/// Creates a writer for testing purposes. Components added to this writer will be discarded.
/// </summary>
public static LanguageComponentWriter CreateNull()
=> new(new LanguageAnalyzerResultBuilder());
public void Add(LanguageComponentRecord record)
=> _builder.Add(record);

View File

@@ -93,10 +93,14 @@ public sealed class SpdxLayerWriter : ILayerSbomWriter
SbomTypes = new[] { "build" }.ToImmutableArray()
};
var displayDigest = layerDigestShort.Length > 12
? $"{layerDigestShort[..12]}..."
: layerDigestShort;
return new SpdxDocument
{
DocumentNamespace = idBuilder.DocumentNamespace,
Name = $"SBOM for layer {request.LayerOrder} ({layerDigestShort[..12]}...)",
Name = $"SBOM for layer {request.LayerOrder} ({displayDigest})",
CreationInfo = creationInfo,
Sbom = sbom,
Elements = packages.Cast<SpdxElement>().ToImmutableArray(),

View File

@@ -237,14 +237,27 @@ public sealed class FuncProofBuilder
/// Computes the content-addressable proof ID.
/// Uses ICryptoHash for regional compliance (defaults to BLAKE3 in "world" profile).
/// </summary>
/// <remarks>
/// The proof ID is computed over the structural content only (binary identity, sections,
/// functions, traces) and excludes volatile fields (proofId, generatedAt, generatorVersion)
/// to ensure deterministic output for identical inputs.
/// </remarks>
public static string ComputeProofId(FuncProof proof, ICryptoHash? cryptoHash = null)
{
// Create a version without proofId for hashing
var forHashing = proof with { ProofId = string.Empty };
// Create a version without volatile fields for hashing.
// ProofId is excluded because it is the output.
// GeneratedAt and GeneratorVersion are excluded because they are not
// structural content and would break determinism across invocations.
var forHashing = proof with
{
ProofId = string.Empty,
GeneratedAt = default,
GeneratorVersion = string.Empty
};
var json = JsonSerializer.Serialize(forHashing, CanonicalJsonOptions);
var bytes = Encoding.UTF8.GetBytes(json);
var hash = ComputeHashForGraph(bytes, cryptoHash);
// Prefix indicates algorithm used (determined by compliance profile)
var algorithmPrefix = cryptoHash is not null ? "graph" : "sha256";
return $"{algorithmPrefix}:{hash}";

View File

@@ -238,7 +238,7 @@ public sealed class SbomFuncProofLinker : ISbomFuncProofLinker
}
}
// Check externalReferences for FuncProof links
// Check externalReferences for FuncProof links and merge with callflow data
var externalRefs = targetComponent["externalReferences"] as JsonArray;
if (externalRefs != null)
{
@@ -263,10 +263,13 @@ public sealed class SbomFuncProofLinker : ISbomFuncProofLinker
{
// Parse additional metadata from comment
var metadata = ParseCommentMetadata(comment);
var extProofId = metadata.TryGetValue("proofId", out var pid) ? pid : "unknown";
references.Add(new FuncProofEvidenceRef
// Check if we already have a callflow entry for the same ProofId
var existingIndex = references.FindIndex(r => r.ProofId == extProofId);
var extRef2 = new FuncProofEvidenceRef
{
ProofId = metadata.TryGetValue("proofId", out var pid) ? pid : "unknown",
ProofId = extProofId,
BuildId = metadata.TryGetValue("buildId", out var bid) ? bid : "unknown",
FileSha256 = metadata.TryGetValue("fileSha256", out var fsha) ? fsha : "unknown",
ProofDigest = sha256Hash ?? "unknown",
@@ -277,7 +280,18 @@ public sealed class SbomFuncProofLinker : ISbomFuncProofLinker
TraceCount = int.TryParse(
metadata.TryGetValue("traceCount", out var tc) ? tc : "0",
out var tcInt) ? tcInt : 0
});
};
if (existingIndex >= 0)
{
// Merge: replace the callflow-only entry with the more complete
// externalReferences entry (which has location and digest)
references[existingIndex] = extRef2;
}
else
{
references.Add(extRef2);
}
}
}
}

View File

@@ -333,7 +333,7 @@ public sealed class PostgresEpssSignalRepository : IEpssSignalRepository
{
SignalId = row.signal_id,
TenantId = row.tenant_id,
ModelDate = DateOnly.FromDateTime(row.model_date),
ModelDate = row.model_date,
CveId = row.cve_id,
EventType = row.event_type,
RiskBand = row.risk_band,
@@ -370,7 +370,7 @@ public sealed class PostgresEpssSignalRepository : IEpssSignalRepository
private readonly record struct SignalRow(
long signal_id,
Guid tenant_id,
DateTime model_date,
DateOnly model_date,
string cve_id,
string event_type,
string? risk_band,

View File

@@ -36,10 +36,12 @@ public sealed class PostgresScanManifestRepository : IScanManifestRepository
created_at AS CreatedAt
FROM {TableName}
WHERE manifest_hash = @ManifestHash
ORDER BY created_at DESC
LIMIT 1
""";
await using var connection = await _dataSource.OpenSystemConnectionAsync(cancellationToken).ConfigureAwait(false);
return await connection.QuerySingleOrDefaultAsync<ScanManifestRow>(
return await connection.QueryFirstOrDefaultAsync<ScanManifestRow>(
new CommandDefinition(sql, new { ManifestHash = manifestHash }, cancellationToken: cancellationToken))
.ConfigureAwait(false);
}