108 lines
3.5 KiB
C#
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;
|
|
}
|
|
}
|