Refactor and enhance scanner worker functionality

- Cleaned up code formatting and organization across multiple files for improved readability.
- Introduced `OsScanAnalyzerDispatcher` to handle OS analyzer execution and plugin loading.
- Updated `ScanJobContext` to include an `Analysis` property for storing scan results.
- Enhanced `ScanJobProcessor` to utilize the new `OsScanAnalyzerDispatcher`.
- Improved logging and error handling in `ScanProgressReporter` for better traceability.
- Updated project dependencies and added references to new analyzer plugins.
- Revised task documentation to reflect current status and dependencies.
This commit is contained in:
master
2025-10-19 18:34:15 +03:00
parent aef7ffb535
commit 2062da7a8b
59 changed files with 5563 additions and 2288 deletions

View File

@@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Time.Testing;
using StellaOps.Scanner.Sbomer.BuildXPlugin.Descriptor;
using StellaOps.Scanner.Sbomer.BuildXPlugin.Tests.TestUtilities;
using Xunit;
namespace StellaOps.Scanner.Sbomer.BuildXPlugin.Tests.Descriptor;
public sealed class DescriptorGoldenTests
{
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web)
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
[Fact]
public async Task DescriptorMatchesBaselineFixture()
{
await using var temp = new TempDirectory();
var sbomPath = Path.Combine(temp.Path, "sample.cdx.json");
await File.WriteAllTextAsync(sbomPath, "{\"bomFormat\":\"CycloneDX\",\"specVersion\":\"1.5\"}");
var request = new DescriptorRequest
{
ImageDigest = "sha256:0123456789abcdef",
SbomPath = sbomPath,
SbomMediaType = "application/vnd.cyclonedx+json",
SbomFormat = "cyclonedx-json",
SbomKind = "inventory",
SbomArtifactType = "application/vnd.stellaops.sbom.layer+json",
SubjectMediaType = "application/vnd.oci.image.manifest.v1+json",
GeneratorVersion = "1.2.3",
GeneratorName = "StellaOps.Scanner.Sbomer.BuildXPlugin",
LicenseId = "lic-123",
SbomName = "sample.cdx.json",
Repository = "git.stella-ops.org/stellaops",
BuildRef = "refs/heads/main",
AttestorUri = "https://attestor.local/api/v1/provenance"
}.Validate();
var fakeTime = new FakeTimeProvider(new DateTimeOffset(2025, 10, 18, 12, 0, 0, TimeSpan.Zero));
var generator = new DescriptorGenerator(fakeTime);
var document = await generator.CreateAsync(request, CancellationToken.None);
var actualJson = JsonSerializer.Serialize(document, SerializerOptions);
var normalizedJson = NormalizeDescriptorJson(actualJson, Path.GetFileName(sbomPath));
var projectRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", ".."));
var fixturePath = Path.Combine(projectRoot, "Fixtures", "descriptor.baseline.json");
var updateRequested = string.Equals(Environment.GetEnvironmentVariable("UPDATE_BUILDX_FIXTURES"), "1", StringComparison.OrdinalIgnoreCase);
if (updateRequested)
{
Directory.CreateDirectory(Path.GetDirectoryName(fixturePath)!);
await File.WriteAllTextAsync(fixturePath, normalizedJson);
return;
}
if (!File.Exists(fixturePath))
{
throw new InvalidOperationException($"Baseline fixture '{fixturePath}' is missing. Set UPDATE_BUILDX_FIXTURES=1 and re-run the tests to generate it.");
}
var baselineJson = await File.ReadAllTextAsync(fixturePath);
using var baselineDoc = JsonDocument.Parse(baselineJson);
using var actualDoc = JsonDocument.Parse(normalizedJson);
AssertJsonEquivalent(baselineDoc.RootElement, actualDoc.RootElement);
}
private static string NormalizeDescriptorJson(string json, string sbomFileName)
{
var node = JsonNode.Parse(json)?.AsObject()
?? throw new InvalidOperationException("Failed to parse descriptor JSON for normalization.");
if (node["metadata"] is JsonObject metadata)
{
metadata["sbomPath"] = sbomFileName;
}
return node.ToJsonString(SerializerOptions);
}
private static void AssertJsonEquivalent(JsonElement expected, JsonElement actual)
{
if (expected.ValueKind != actual.ValueKind)
{
throw new Xunit.Sdk.XunitException($"Value kind mismatch. Expected '{expected.ValueKind}' but found '{actual.ValueKind}'.");
}
switch (expected.ValueKind)
{
case JsonValueKind.Object:
var expectedProperties = expected.EnumerateObject().ToDictionary(p => p.Name, p => p.Value, StringComparer.Ordinal);
var actualProperties = actual.EnumerateObject().ToDictionary(p => p.Name, p => p.Value, StringComparer.Ordinal);
Assert.Equal(
expectedProperties.Keys.OrderBy(static name => name).ToArray(),
actualProperties.Keys.OrderBy(static name => name).ToArray());
foreach (var propertyName in expectedProperties.Keys)
{
AssertJsonEquivalent(expectedProperties[propertyName], actualProperties[propertyName]);
}
break;
case JsonValueKind.Array:
var expectedItems = expected.EnumerateArray().ToArray();
var actualItems = actual.EnumerateArray().ToArray();
Assert.Equal(expectedItems.Length, actualItems.Length);
for (var i = 0; i < expectedItems.Length; i++)
{
AssertJsonEquivalent(expectedItems[i], actualItems[i]);
}
break;
default:
Assert.Equal(expected.ToString(), actual.ToString());
break;
}
}
}