audit work, fixed StellaOps.sln warnings/errors, fixed tests, sprints work, new advisories
This commit is contained in:
@@ -40,7 +40,29 @@ public sealed record SpdxCompositionOptions
|
||||
|
||||
public SpdxLicenseListVersion LicenseListVersion { get; init; } = SpdxLicenseListVersion.V3_21;
|
||||
|
||||
public ImmutableArray<string> ProfileConformance { get; init; } = ImmutableArray.Create("core", "software");
|
||||
/// <summary>
|
||||
/// Gets or sets the SPDX 3.0.1 profile type. Defaults to Software.
|
||||
/// </summary>
|
||||
public Spdx3ProfileType ProfileType { get; init; } = Spdx3ProfileType.Software;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an explicit profile conformance override.
|
||||
/// If not set (default or empty), the conformance is derived from ProfileType.
|
||||
/// </summary>
|
||||
public ImmutableArray<string> ProfileConformance { get; init; } = ImmutableArray<string>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the effective profile conformance based on ProfileType if ProfileConformance is not explicitly set.
|
||||
/// </summary>
|
||||
public ImmutableArray<string> GetEffectiveProfileConformance()
|
||||
{
|
||||
if (!ProfileConformance.IsDefaultOrEmpty && ProfileConformance.Length > 0)
|
||||
{
|
||||
return ProfileConformance;
|
||||
}
|
||||
|
||||
return ProfileType.GetProfileConformance().ToImmutableArray();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SpdxComposer : ISpdxComposer
|
||||
@@ -139,12 +161,12 @@ public sealed class SpdxComposer : ISpdxComposer
|
||||
var packages = new List<SpdxPackage>();
|
||||
var packageIdMap = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
|
||||
var rootPackage = BuildRootPackage(request.Image, idBuilder);
|
||||
var rootPackage = BuildRootPackage(request.Image, idBuilder, options);
|
||||
packages.Add(rootPackage);
|
||||
|
||||
foreach (var component in graph.Components)
|
||||
{
|
||||
var package = BuildComponentPackage(component, idBuilder, licenseList);
|
||||
var package = BuildComponentPackage(component, idBuilder, licenseList, options);
|
||||
packages.Add(package);
|
||||
packageIdMap[component.Identity.Key] = package.SpdxId;
|
||||
}
|
||||
@@ -175,7 +197,7 @@ public sealed class SpdxComposer : ISpdxComposer
|
||||
Sbom = sbom,
|
||||
Elements = packages.Cast<SpdxElement>().ToImmutableArray(),
|
||||
Relationships = relationships,
|
||||
ProfileConformance = options.ProfileConformance
|
||||
ProfileConformance = options.GetEffectiveProfileConformance()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -261,17 +283,23 @@ public sealed class SpdxComposer : ISpdxComposer
|
||||
.ToImmutableArray();
|
||||
}
|
||||
|
||||
private static SpdxPackage BuildRootPackage(ImageArtifactDescriptor image, SpdxIdBuilder idBuilder)
|
||||
private static SpdxPackage BuildRootPackage(
|
||||
ImageArtifactDescriptor image,
|
||||
SpdxIdBuilder idBuilder,
|
||||
SpdxCompositionOptions options)
|
||||
{
|
||||
var digest = image.ImageDigest;
|
||||
var digestParts = digest.Split(':', 2, StringSplitOptions.TrimEntries);
|
||||
var digestValue = digestParts.Length == 2 ? digestParts[1] : digest;
|
||||
|
||||
var checksums = ImmutableArray.Create(new SpdxChecksum
|
||||
{
|
||||
Algorithm = digestParts.Length == 2 ? digestParts[0].ToUpperInvariant() : "SHA256",
|
||||
Value = digestValue
|
||||
});
|
||||
// Lite profile omits checksums
|
||||
var checksums = options.ProfileType.IncludeChecksums()
|
||||
? ImmutableArray.Create(new SpdxChecksum
|
||||
{
|
||||
Algorithm = digestParts.Length == 2 ? digestParts[0].ToUpperInvariant() : "SHA256",
|
||||
Value = digestValue
|
||||
})
|
||||
: ImmutableArray<SpdxChecksum>.Empty;
|
||||
|
||||
return new SpdxPackage
|
||||
{
|
||||
@@ -288,13 +316,17 @@ public sealed class SpdxComposer : ISpdxComposer
|
||||
private static SpdxPackage BuildComponentPackage(
|
||||
AggregatedComponent component,
|
||||
SpdxIdBuilder idBuilder,
|
||||
SpdxLicenseList licenseList)
|
||||
SpdxLicenseList licenseList,
|
||||
SpdxCompositionOptions options)
|
||||
{
|
||||
var packageUrl = !string.IsNullOrWhiteSpace(component.Identity.Purl)
|
||||
? component.Identity.Purl
|
||||
: (component.Identity.Key.StartsWith("pkg:", StringComparison.Ordinal) ? component.Identity.Key : null);
|
||||
|
||||
var declared = BuildLicenseExpression(component.Metadata?.Licenses, licenseList);
|
||||
// Lite profile omits detailed licensing
|
||||
var declared = options.ProfileType.IncludeDetailedLicensing()
|
||||
? BuildLicenseExpression(component.Metadata?.Licenses, licenseList)
|
||||
: null;
|
||||
|
||||
return new SpdxPackage
|
||||
{
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// <copyright file="Spdx3ProfileType.cs" company="StellaOps">
|
||||
// Copyright (c) StellaOps. Licensed under the AGPL-3.0-or-later.
|
||||
// </copyright>
|
||||
|
||||
namespace StellaOps.Scanner.Emit.Spdx;
|
||||
|
||||
/// <summary>
|
||||
/// SPDX 3.0.1 profile types for SBOM generation.
|
||||
/// </summary>
|
||||
public enum Spdx3ProfileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Full Software profile with all available fields.
|
||||
/// Includes detailed licensing, checksums, external refs, etc.
|
||||
/// </summary>
|
||||
Software,
|
||||
|
||||
/// <summary>
|
||||
/// Lite profile with minimal required fields.
|
||||
/// Optimized for CI/CD and performance-sensitive use cases.
|
||||
/// Includes: spdxId, name, packageVersion, packageUrl or downloadLocation.
|
||||
/// </summary>
|
||||
Lite,
|
||||
|
||||
/// <summary>
|
||||
/// Build profile with provenance and build environment data.
|
||||
/// Suitable for attestation integration.
|
||||
/// </summary>
|
||||
Build,
|
||||
|
||||
/// <summary>
|
||||
/// Security profile with vulnerability and VEX data.
|
||||
/// Suitable for security analysis and VexLens integration.
|
||||
/// </summary>
|
||||
Security
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="Spdx3ProfileType"/>.
|
||||
/// </summary>
|
||||
public static class Spdx3ProfileTypeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the profile conformance URIs for this profile type.
|
||||
/// </summary>
|
||||
public static string[] GetProfileConformance(this Spdx3ProfileType profileType) => profileType switch
|
||||
{
|
||||
Spdx3ProfileType.Software => ["core", "software"],
|
||||
Spdx3ProfileType.Lite => ["core", "software", "lite"],
|
||||
Spdx3ProfileType.Build => ["core", "software", "build"],
|
||||
Spdx3ProfileType.Security => ["core", "software", "security"],
|
||||
_ => ["core", "software"]
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this profile should include detailed licensing.
|
||||
/// </summary>
|
||||
public static bool IncludeDetailedLicensing(this Spdx3ProfileType profileType) =>
|
||||
profileType is Spdx3ProfileType.Software;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this profile should include checksums.
|
||||
/// </summary>
|
||||
public static bool IncludeChecksums(this Spdx3ProfileType profileType) =>
|
||||
profileType is Spdx3ProfileType.Software or Spdx3ProfileType.Build or Spdx3ProfileType.Security;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this profile should include external references.
|
||||
/// </summary>
|
||||
public static bool IncludeExternalRefs(this Spdx3ProfileType profileType) =>
|
||||
profileType is not Spdx3ProfileType.Lite;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this profile should include annotations and comments.
|
||||
/// </summary>
|
||||
public static bool IncludeAnnotations(this Spdx3ProfileType profileType) =>
|
||||
profileType is Spdx3ProfileType.Software;
|
||||
}
|
||||
Reference in New Issue
Block a user