save work
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using StellaOps.Scanner.Analyzers.Native.Index;
|
||||
using StellaOps.Scanner.Core.Contracts;
|
||||
|
||||
namespace StellaOps.Scanner.Emit.Native;
|
||||
|
||||
@@ -17,7 +20,143 @@ public sealed record NativeComponentEmitResult(
|
||||
string? Version,
|
||||
NativeBinaryMetadata Metadata,
|
||||
bool IndexMatch,
|
||||
BuildIdLookupResult? LookupResult);
|
||||
BuildIdLookupResult? LookupResult)
|
||||
{
|
||||
public ComponentRecord ToComponentRecord(string layerDigest)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(layerDigest);
|
||||
ArgumentNullException.ThrowIfNull(Metadata);
|
||||
|
||||
var fileName = string.IsNullOrWhiteSpace(Name)
|
||||
? Path.GetFileName(Metadata.FilePath)
|
||||
: Name.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(fileName))
|
||||
{
|
||||
fileName = Purl;
|
||||
}
|
||||
|
||||
var properties = new SortedDictionary<string, string>(StringComparer.Ordinal)
|
||||
{
|
||||
["stellaops:binary.format"] = Metadata.Format,
|
||||
["stellaops:binary.indexMatch"] = IndexMatch ? "true" : "false",
|
||||
};
|
||||
|
||||
AddIfNotEmpty(properties, "stellaops:binary.architecture", Metadata.Architecture);
|
||||
AddIfNotEmpty(properties, "stellaops:binary.platform", Metadata.Platform);
|
||||
AddIfNotEmpty(properties, "stellaops:binary.filePath", Metadata.FilePath);
|
||||
AddIfNotEmpty(properties, "stellaops:binary.fileDigest", Metadata.FileDigest);
|
||||
|
||||
if (Metadata.FileSize > 0)
|
||||
{
|
||||
properties["stellaops:binary.fileSizeBytes"] = Metadata.FileSize.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (Metadata.LayerIndex >= 0)
|
||||
{
|
||||
properties["stellaops:binary.layerIndex"] = Metadata.LayerIndex.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (Metadata.Is64Bit)
|
||||
{
|
||||
properties["stellaops:binary.is64Bit"] = "true";
|
||||
}
|
||||
|
||||
if (Metadata.IsSigned)
|
||||
{
|
||||
properties["stellaops:binary.isSigned"] = "true";
|
||||
}
|
||||
|
||||
AddIfNotEmpty(properties, "stellaops:binary.signatureDetails", Metadata.SignatureDetails);
|
||||
AddIfNotEmpty(properties, "stellaops:binary.productVersion", Metadata.ProductVersion);
|
||||
AddIfNotEmpty(properties, "stellaops:binary.fileVersion", Metadata.FileVersion);
|
||||
AddIfNotEmpty(properties, "stellaops:binary.companyName", Metadata.CompanyName);
|
||||
|
||||
AddDictionary(properties, "stellaops:binary.hardeningFlags", Metadata.HardeningFlags);
|
||||
AddList(properties, "stellaops:binary.imports", Metadata.Imports);
|
||||
AddList(properties, "stellaops:binary.exports", Metadata.Exports);
|
||||
|
||||
if (LookupResult is not null)
|
||||
{
|
||||
AddIfNotEmpty(properties, "stellaops:binary.index.sourceDistro", LookupResult.SourceDistro);
|
||||
properties["stellaops:binary.index.confidence"] = LookupResult.Confidence.ToString();
|
||||
}
|
||||
|
||||
var componentMetadata = new ComponentMetadata
|
||||
{
|
||||
BuildId = Metadata.BuildId,
|
||||
Properties = properties.Count == 0 ? null : properties,
|
||||
};
|
||||
|
||||
return new ComponentRecord
|
||||
{
|
||||
Identity = ComponentIdentity.Create(
|
||||
key: Purl,
|
||||
name: fileName,
|
||||
version: Version,
|
||||
purl: Purl,
|
||||
componentType: "file"),
|
||||
LayerDigest = layerDigest,
|
||||
Evidence = ImmutableArray.Create(ComponentEvidence.FromPath(Metadata.FilePath)),
|
||||
Dependencies = ImmutableArray<string>.Empty,
|
||||
Metadata = componentMetadata,
|
||||
Usage = ComponentUsage.Unused,
|
||||
};
|
||||
}
|
||||
|
||||
private static void AddIfNotEmpty(IDictionary<string, string> properties, string key, string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
properties[key] = value.Trim();
|
||||
}
|
||||
|
||||
private static void AddDictionary(IDictionary<string, string> properties, string key, IReadOnlyDictionary<string, string>? dictionary)
|
||||
{
|
||||
if (dictionary is null || dictionary.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var entries = dictionary
|
||||
.Where(pair => !string.IsNullOrWhiteSpace(pair.Key) && !string.IsNullOrWhiteSpace(pair.Value))
|
||||
.OrderBy(pair => pair.Key, StringComparer.Ordinal)
|
||||
.Select(pair => $"{pair.Key}={pair.Value}")
|
||||
.ToArray();
|
||||
|
||||
if (entries.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
properties[key] = string.Join(",", entries);
|
||||
}
|
||||
|
||||
private static void AddList(IDictionary<string, string> properties, string key, IReadOnlyList<string>? items)
|
||||
{
|
||||
if (items is null || items.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var normalized = items
|
||||
.Where(static item => !string.IsNullOrWhiteSpace(item))
|
||||
.Select(static item => item.Trim())
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.OrderBy(static item => item, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
|
||||
if (normalized.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
properties[key] = string.Join(",", normalized);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for emitting native binary components for SBOM generation.
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using StellaOps.Scanner.Analyzers.Native.Index;
|
||||
using StellaOps.Scanner.Core.Contracts;
|
||||
|
||||
namespace StellaOps.Scanner.Emit.Native;
|
||||
|
||||
@@ -183,7 +184,15 @@ public sealed record LayerComponentMapping(
|
||||
IReadOnlyList<NativeComponentEmitResult> Components,
|
||||
int TotalCount,
|
||||
int ResolvedCount,
|
||||
int UnresolvedCount);
|
||||
int UnresolvedCount)
|
||||
{
|
||||
public LayerComponentFragment ToFragment()
|
||||
{
|
||||
return LayerComponentFragment.Create(
|
||||
LayerDigest,
|
||||
Components.Select(component => component.ToComponentRecord(LayerDigest)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result of mapping an entire container image to SBOM components.
|
||||
|
||||
5
src/Scanner/__Libraries/StellaOps.Scanner.Emit/TASKS.md
Normal file
5
src/Scanner/__Libraries/StellaOps.Scanner.Emit/TASKS.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Scanner Emit Local Tasks
|
||||
|
||||
| Task ID | Sprint | Status | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| `BSE-009` | `docs/implplan/SPRINT_3500_0012_0001_binary_sbom_emission.md` | DONE | Added end-to-end integration test coverage for native binary SBOM emission (emit → fragments → CycloneDX). |
|
||||
Reference in New Issue
Block a user