diff --git a/.editorconfig b/.editorconfig
index 7f6cb2ac..48c5b234 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -811,6 +811,10 @@ dotnet_diagnostic.CA1852.severity = suggestion
# CA1854: Prefer 'TryGetValue' over 'ContainsKey' and 'Item' when accessing dictionary items
dotnet_diagnostic.CA1854.severity = suggestion
+# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1859
+# CA1859: Use culture-aware string operations
+dotnet_diagnostic.CA1859.severity = suggestion
+
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1860
# CA1860: Avoid using 'Enumerable.Any()' extension method.
dotnet_diagnostic.CA1860.severity = suggestion
@@ -819,18 +823,6 @@ dotnet_diagnostic.CA1860.severity = suggestion
# CA1861: Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly
dotnet_diagnostic.CA1861.severity = suggestion
-# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1862
-# CA1862: Prefer using 'string.Equals(string, StringComparison)' to perform a case-insensitive comparison
-dotnet_diagnostic.CA1862.severity = suggestion
-
-# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1865
-# CA1865: Use 'string.StartsWith(char)' instead of 'string.StartsWith(string)' when you have a string with a single char
-dotnet_diagnostic.CA1865.severity = suggestion
-
-# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1869
-# CA1869: Avoid creating a new 'JsonSerializerOptions' instance for every serialization operation. Cache and reuse instances instead.
-dotnet_diagnostic.CA1869.severity = suggestion
-
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2000
# CA2000: Dispose objects before losing scope
dotnet_diagnostic.CA2000.severity = suggestion
@@ -1043,14 +1035,6 @@ dotnet_diagnostic.IDE0220.severity = suggestion
# IDE0251: Member can be made 'readonly'
dotnet_diagnostic.IDE0251.severity = suggestion
-# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0290
-# IDE0290: Use primary constructor
-dotnet_diagnostic.IDE0290.severity = suggestion
-
-# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0300
-# IDE0290: Collection initialization can be simplified
-dotnet_diagnostic.IDE0300.severity = suggestion
-
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide1006
# IDE1006: Naming rule violation: These words must begin with upper case characters: ...
dotnet_diagnostic.IDE1006.severity = suggestion
diff --git a/Directory.Build.props b/Directory.Build.props
index 5c17dd66..64e721e3 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -11,7 +11,6 @@
AllEnabledByDefault
true
$(NoWarn);CA1014;CS8002
- true
diff --git a/docs/sbom-tool-api-reference.md b/docs/sbom-tool-api-reference.md
index 880753aa..998a7c97 100644
--- a/docs/sbom-tool-api-reference.md
+++ b/docs/sbom-tool-api-reference.md
@@ -262,3 +262,81 @@ var result = await generator.GenerateSBOMAsync(rootPath: scanPath,
* The `packages` parameter contains a list of `SBOMPackage` objects.
* The `metadata` and `runtimeConfiguration` parameters accept the [`SBOMMetadata`](#sbommetadata) and [`RuntimeConfiguration`](#runtimeconfiguration) objects (respectively).
* If users want the API to generate the output SBOM in a different folder other the default location, they need to provide the path in the `manifestDirPath` parameter. Users will find the SBOM file under the `_manifest` directory at the user-specified path.
+
+## SBOM Validation
+
+Now that you have generated the SBOM file, you can validate it using the `SBOMValidator` class. Setup for this will be very similar to the `SBOMGenerator` class. Here is an example:
+
+```C#
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Sbom.Extensions.DependencyInjection;
+
+namespace SBOMApiExample;
+class Program
+{
+ public static async Task Main(string[] args)
+ {
+ await Host.CreateDefaultBuilder(args)
+ .ConfigureServices((host, services) =>
+ {
+ services
+ .AddHostedService()
+ .AddSbomTool();
+ })
+ .RunConsoleAsync(x => x.SuppressStatusMessages = true);
+ }
+}
+```
+
+After the Host is set up, you can inject the `ISBOMValidator` interface into your service and use it to validate the SBOM file. Here is an example:
+Note that the only arguments required are the `buildDropPath`, the `outputPath`, and the `SbomSpecification`. The `buildDropPath` is the path to the directory containing the _manifest directory. The `outPath` is the path to the file where the validation output will be written. The only `SbomSpecification` currently supported is `SPDX 2.2`.
+All other arguments are optional.
+
+```C#
+using Microsoft.Extensions.Hosting;
+using Microsoft.Sbom.Contracts;
+
+namespace SBOMApiExample
+{
+ public class ValidationService : IHostedService
+ {
+ private readonly ISBOMValidator sbomValidator;
+ private readonly IHostApplicationLifetime hostApplicationLifetime;
+
+ public ValidationService(
+ ISBOMValidator sbomValidator,
+ IHostApplicationLifetime hostApplicationLifetime)
+ {
+ this.sbomValidator = sbomValidator;
+ this.hostApplicationLifetime = hostApplicationLifetime;
+ }
+
+ public async Task StartAsync(CancellationToken cancellationToken)
+ {
+ string buildDropPath = "C:/repos/samplePath";
+ string outputPath = "C:/temp/ValidationOutput.json";
+ IList spdx22Specification = new List()
+ {
+ new SbomSpecification("SPDX","2.2")
+ };
+
+ RuntimeConfiguration configuration = new RuntimeConfiguration()
+ {
+ Verbosity = System.Diagnostics.Tracing.EventLevel.Information,
+ };
+
+ var result = await sbomValidator.ValidateSbomAsync(buildDropPath, outputPath, spdx22Specification, runtimeConfiguration: configuration);
+
+
+ hostApplicationLifetime.StopApplication();
+ }
+ public Task StopAsync(CancellationToken cancellationToken)
+ {
+ return Task.CompletedTask;
+ }
+
+ }
+}
+
+```
diff --git a/global.json b/global.json
index 42fe8224..95bdf7f0 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,7 @@
{
"sdk": {
- "version": "8.0.100-rc.1.23463.5",
- "rollForward": "latestMajor"
+ "version": "7.0.400",
+ "rollForward": "latestMajor",
+ "allowPrerelease": false
}
}
diff --git a/src/Microsoft.Sbom.Api/SBOMValidator.cs b/src/Microsoft.Sbom.Api/SBOMValidator.cs
index 29f8f215..7184e219 100644
--- a/src/Microsoft.Sbom.Api/SBOMValidator.cs
+++ b/src/Microsoft.Sbom.Api/SBOMValidator.cs
@@ -1,11 +1,20 @@
-// Copyright (c) Microsoft. All rights reserved.
+// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
+using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
+using Microsoft.Sbom.Api.Config;
+using Microsoft.Sbom.Api.Config.Extensions;
using Microsoft.Sbom.Api.Output.Telemetry;
using Microsoft.Sbom.Api.Workflows;
+using Microsoft.Sbom.Common.Config;
+using Microsoft.Sbom.Common.Config.Validators;
+using Microsoft.Sbom.Contracts;
+using Microsoft.Sbom.Contracts.Enums;
+using PowerArgs;
namespace Microsoft.Sbom.Api;
@@ -13,13 +22,17 @@ public class SbomValidator : ISBOMValidator
{
private readonly IWorkflow sbomParserBasedValidationWorkflow;
private readonly IRecorder recorder;
+ private readonly IEnumerable configValidators;
public SbomValidator(
IWorkflow sbomParserBasedValidationWorkflow,
- IRecorder recorder)
+ IRecorder recorder,
+ IEnumerable configValidators,
+ ConfigSanitizer configSanitizer)
{
this.sbomParserBasedValidationWorkflow = sbomParserBasedValidationWorkflow ?? throw new ArgumentNullException(nameof(sbomParserBasedValidationWorkflow));
this.recorder = recorder ?? throw new ArgumentNullException(nameof(recorder));
+ this.configValidators = configValidators;
}
public async Task ValidateSbomAsync()
@@ -31,4 +44,58 @@ public async Task ValidateSbomAsync()
return isSuccess;
}
+
+ public async Task ValidateSbomAsync(
+ string buildDropPath,
+ string outputPath,
+ IList specifications,
+ string manifestDirPath = null,
+ bool validateSignature = false,
+ bool ignoreMissing = false,
+ string rootPathFilter = null,
+ RuntimeConfiguration runtimeConfiguration = null,
+ AlgorithmName algorithmName = null)
+ {
+ // If the API user does not specify a manifest directory path, we will default to the build drop path.
+ if (string.IsNullOrWhiteSpace(manifestDirPath))
+ {
+ manifestDirPath = $"{buildDropPath}\\_manifest";
+ }
+
+ var inputConfig = ApiConfigurationBuilder.GetConfiguration(
+ buildDropPath,
+ outputPath,
+ specifications,
+ algorithmName,
+ manifestDirPath,
+ validateSignature,
+ ignoreMissing,
+ rootPathFilter,
+ runtimeConfiguration);
+
+ inputConfig = ValidateConfig(inputConfig);
+
+ inputConfig.ToConfiguration();
+
+ var isSuccess = await sbomParserBasedValidationWorkflow.RunAsync();
+ await recorder.FinalizeAndLogTelemetryAsync();
+
+ var entityErrors = recorder.Errors.Select(error => error.ToEntityError()).ToList();
+
+ return isSuccess;
+ }
+
+ private InputConfiguration ValidateConfig(InputConfiguration config)
+ {
+ foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(config))
+ {
+ configValidators.ForEach(v =>
+ {
+ v.CurrentAction = config.ManifestToolAction;
+ v.Validate(property.DisplayName, property.GetValue(config), property.Attributes);
+ });
+ }
+
+ return config;
+ }
}
diff --git a/src/Microsoft.Sbom.Contracts/ISBOMValidator.cs b/src/Microsoft.Sbom.Contracts/ISBOMValidator.cs
index 028cee77..8130054d 100644
--- a/src/Microsoft.Sbom.Contracts/ISBOMValidator.cs
+++ b/src/Microsoft.Sbom.Contracts/ISBOMValidator.cs
@@ -1,9 +1,11 @@
-// Copyright (c) Microsoft. All rights reserved.
+// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System.Collections.Generic;
using System.Threading.Tasks;
+using Microsoft.Sbom.Contracts.Enums;
-namespace Microsoft.Sbom;
+namespace Microsoft.Sbom.Contracts;
///
/// Provides an interface to validate a SBOM.
@@ -15,4 +17,27 @@ public interface ISBOMValidator
/// and writes JSON output to the outputPath file location.
///
Task ValidateSbomAsync();
+
+ ///
+ /// Validates all the files in a given SBOM with the files present in the build drop path
+ /// and writes JSON output to the outputPath file location.
+ /// The path to the root of the drop."
+ /// The path to a writeable location where the output json should be written.
+ /// The list of specifications to use for validation.
+ /// The path to the directory that contains the _manifest folder. If null then buildDropPath will be used
+ /// If true, validate the signature of the SBOM.
+ /// The root path filter to use for validation.
+ /// The runtime configuration to use for validation.
+ /// The algorithm to use for hashing.
+ ///
+ Task ValidateSbomAsync(
+ string buildDropPath,
+ string outputPath,
+ IList specifications,
+ string manifestDirPath = null,
+ bool validateSignature = false,
+ bool ignoreMissing = false,
+ string rootPathFilter = null,
+ RuntimeConfiguration runtimeConfiguration = null,
+ AlgorithmName algorithmName = null);
}
diff --git a/src/Microsoft.Sbom.Extensions.DependencyInjection/ServiceCollectionExtensions.cs b/src/Microsoft.Sbom.Extensions.DependencyInjection/ServiceCollectionExtensions.cs
index 348b67c4..21a33dc5 100644
--- a/src/Microsoft.Sbom.Extensions.DependencyInjection/ServiceCollectionExtensions.cs
+++ b/src/Microsoft.Sbom.Extensions.DependencyInjection/ServiceCollectionExtensions.cs
@@ -170,6 +170,7 @@ public static IServiceCollection AddSbomTool(this IServiceCollection services, L
typeof(IManifestInterface)))
.AsImplementedInterfaces())
.AddScoped()
+ .AddScoped()
.AddSingleton(x =>
{
var fileSystemUtils = x.GetRequiredService();