Files
git.stella-ops.org/src/Policy/StellaOps.Policy.Engine/Services/LicenseComplianceService.cs
2026-01-22 19:08:46 +02:00

108 lines
3.5 KiB
C#

using System.Collections.Immutable;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Policy.Engine.Evaluation;
using StellaOps.Policy.Engine.Options;
using StellaOps.Policy.Licensing;
namespace StellaOps.Policy.Engine.Services;
internal sealed class LicenseComplianceService
{
private readonly ILicenseComplianceEvaluator _evaluator;
private readonly ILicensePolicyLoader _policyLoader;
private readonly LicenseComplianceOptions _options;
private readonly ILogger<LicenseComplianceService> _logger;
private readonly Lazy<LicensePolicy> _policy;
public LicenseComplianceService(
ILicenseComplianceEvaluator evaluator,
ILicensePolicyLoader policyLoader,
IOptions<LicenseComplianceOptions> options,
ILogger<LicenseComplianceService> logger)
{
_evaluator = evaluator ?? throw new ArgumentNullException(nameof(evaluator));
_policyLoader = policyLoader ?? throw new ArgumentNullException(nameof(policyLoader));
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_policy = new Lazy<LicensePolicy>(ResolvePolicy);
}
public async Task<LicenseComplianceReport?> EvaluateAsync(
PolicyEvaluationSbom sbom,
CancellationToken ct)
{
if (!_options.Enabled)
{
return null;
}
var components = sbom.Components
.Select(MapComponent)
.ToList();
try
{
var report = await _evaluator.EvaluateAsync(components, _policy.Value, ct)
.ConfigureAwait(false);
return report;
}
catch (Exception ex)
{
_logger.LogWarning(ex, "License compliance evaluation failed; proceeding without report.");
return null;
}
}
private LicensePolicy ResolvePolicy()
{
if (_options.Policy is not null)
{
return _options.Policy;
}
if (!string.IsNullOrWhiteSpace(_options.PolicyPath))
{
return _policyLoader.Load(_options.PolicyPath);
}
return LicensePolicyDefaults.Default;
}
private static LicenseComponent MapComponent(PolicyEvaluationComponent component)
{
var expression = GetMetadata(component, "license_expression")
?? GetMetadata(component, "licenseexpression")
?? GetMetadata(component, "spdx_license_expression")
?? GetMetadata(component, "license")
?? GetMetadata(component, "licenses");
var licenses = ImmutableArray<string>.Empty;
if (!string.IsNullOrWhiteSpace(expression) && expression.Contains(','))
{
licenses = expression
.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(value => value.Trim())
.Where(value => !string.IsNullOrWhiteSpace(value))
.ToImmutableArray();
expression = null;
}
return new LicenseComponent
{
Name = component.Name,
Version = component.Version,
Purl = component.Purl,
LicenseExpression = expression,
Licenses = licenses,
Metadata = component.Metadata
};
}
private static string? GetMetadata(PolicyEvaluationComponent component, string key)
{
return component.Metadata.TryGetValue(key, out var value) ? value : null;
}
}