Files
git.stella-ops.org/src/Scanner/__Libraries/StellaOps.Scanner.BuildProvenance/Analyzers/BuildProvenanceChainBuilder.cs
2026-01-22 19:08:46 +02:00

149 lines
4.3 KiB
C#

using System.Collections.Immutable;
using StellaOps.Concelier.SbomIntegration.Models;
using StellaOps.Scanner.BuildProvenance.Models;
namespace StellaOps.Scanner.BuildProvenance.Analyzers;
public sealed class BuildProvenanceChainBuilder
{
private static readonly string[] BuilderIdKeys =
{
"builderId",
"builder",
"builder_id",
"buildService",
"build.service"
};
private static readonly string[] SourceRepoKeys =
{
"sourceRepository",
"sourceRepo",
"repository",
"repo",
"gitUrl",
"git.url"
};
private static readonly string[] SourceCommitKeys =
{
"sourceCommit",
"commit",
"gitCommit",
"git.commit",
"revision"
};
public BuildProvenanceChain Build(ParsedSbom sbom)
{
ArgumentNullException.ThrowIfNull(sbom);
var buildInfo = sbom.BuildInfo;
var formulation = sbom.Formulation;
var environment = buildInfo?.Environment ?? ImmutableDictionary<string, string>.Empty;
var builderId = FindParameter(buildInfo, BuilderIdKeys)
?? buildInfo?.BuildType;
var sourceRepo = FindParameter(buildInfo, SourceRepoKeys);
var sourceCommit = FindParameter(buildInfo, SourceCommitKeys);
var configUri = buildInfo?.ConfigSourceUri ?? buildInfo?.ConfigSourceEntrypoint;
var configDigest = buildInfo?.ConfigSourceDigest;
var inputs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var outputs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
if (formulation is not null)
{
foreach (var component in formulation.Components)
{
if (!string.IsNullOrWhiteSpace(component.BomRef))
{
inputs.Add(component.BomRef!);
}
foreach (var reference in component.ComponentRefs)
{
if (!string.IsNullOrWhiteSpace(reference))
{
inputs.Add(reference);
}
}
}
foreach (var workflow in formulation.Workflows)
{
foreach (var input in workflow.InputRefs)
{
if (!string.IsNullOrWhiteSpace(input))
{
inputs.Add(input);
}
}
foreach (var output in workflow.OutputRefs)
{
if (!string.IsNullOrWhiteSpace(output))
{
outputs.Add(output);
}
}
}
foreach (var task in formulation.Tasks)
{
foreach (var input in task.InputRefs)
{
if (!string.IsNullOrWhiteSpace(input))
{
inputs.Add(input);
}
}
foreach (var output in task.OutputRefs)
{
if (!string.IsNullOrWhiteSpace(output))
{
outputs.Add(output);
}
}
}
}
return new BuildProvenanceChain
{
BuilderId = builderId,
SourceRepository = sourceRepo,
SourceCommit = sourceCommit,
BuildConfigUri = configUri,
BuildConfigDigest = configDigest,
Environment = environment,
Inputs = inputs.Select(reference => new BuildInput { Reference = reference }).ToImmutableArray(),
Outputs = outputs.Select(reference => new BuildOutput { Reference = reference }).ToImmutableArray()
};
}
private static string? FindParameter(ParsedBuildInfo? buildInfo, IEnumerable<string> keys)
{
if (buildInfo?.Parameters is null || buildInfo.Parameters.IsEmpty)
{
return null;
}
foreach (var key in keys)
{
if (string.IsNullOrWhiteSpace(key))
{
continue;
}
if (buildInfo.Parameters.TryGetValue(key, out var value) && !string.IsNullOrWhiteSpace(value))
{
return value.Trim();
}
}
return null;
}
}