feat: Enhance traceability and logging in Risk and Vulnerability clients

- Implemented shared trace ID generation utility for Risk and Vulnerability clients, ensuring consistent trace headers across API calls.
- Updated RiskHttpClient and VulnerabilityHttpClient to utilize the new trace ID generation method.
- Added validation for artifact metadata in PackRun endpoints, ensuring all artifacts include a digest and positive size.
- Enhanced logging payloads in PackRun to include artifact digests and sizes.
- Created a utility for generating trace IDs, preferring crypto.randomUUID when available, with a fallback to a ULID-style string.
- Added unit tests to verify the presence of trace IDs in HTTP requests for VulnerabilityHttpClient.
- Documented query-hash metrics for Vuln Explorer, detailing hashing rules and logging filters to ensure compliance with privacy standards.
- Consolidated findings from late-November reviews into a comprehensive advisory for Scanner and SBOM/VEX areas, outlining remediation tracks and gaps.
This commit is contained in:
StellaOps Bot
2025-12-02 19:24:26 +02:00
parent 76ecea482e
commit acbb0ff637
20 changed files with 186 additions and 71 deletions

View File

@@ -2,9 +2,12 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography;
using StellaOps.Scanner.Surface.FS;
using Xunit;
@@ -34,7 +37,8 @@ public sealed class FileSurfaceManifestStoreTests : IAsyncDisposable
_store = new FileSurfaceManifestStore(
cacheOptions,
manifestOptions,
NullLogger<FileSurfaceManifestStore>.Instance);
NullLogger<FileSurfaceManifestStore>.Instance,
new TestCryptoHash());
}
[Fact]
@@ -201,6 +205,31 @@ public sealed class FileSurfaceManifestStoreTests : IAsyncDisposable
$"{hex}.json");
}
private sealed class TestCryptoHash : ICryptoHash
{
public byte[] ComputeHash(ReadOnlySpan<byte> data, string? algorithmId = null)
=> SHA256.HashData(data);
public string ComputeHashHex(ReadOnlySpan<byte> data, string? algorithmId = null)
=> Convert.ToHexString(ComputeHash(data, algorithmId)).ToLowerInvariant();
public string ComputeHashBase64(ReadOnlySpan<byte> data, string? algorithmId = null)
=> Convert.ToBase64String(ComputeHash(data, algorithmId));
public async ValueTask<byte[]> ComputeHashAsync(Stream stream, string? algorithmId = null, CancellationToken cancellationToken = default)
{
await using var buffer = new MemoryStream();
await stream.CopyToAsync(buffer, cancellationToken).ConfigureAwait(false);
return SHA256.HashData(buffer.ToArray());
}
public async ValueTask<string> ComputeHashHexAsync(Stream stream, string? algorithmId = null, CancellationToken cancellationToken = default)
{
var hash = await ComputeHashAsync(stream, algorithmId, cancellationToken).ConfigureAwait(false);
return Convert.ToHexString(hash).ToLowerInvariant();
}
}
public async ValueTask DisposeAsync()
{
await Task.Run(() =>