Skip to content

Commit

Permalink
Update SBOMValidator API / Revert sdk update (#418)
Browse files Browse the repository at this point in the history
* Revert "chore: bump dotnet sdk to 8 (#406)"

This reverts commit 2714d38.

* Add method to SBOMValidator / Revert sdk update

* Add documentation showcasing new changes.

* Add missing quote.

---------

Co-authored-by: Sebastian Gomez <[email protected]>
  • Loading branch information
sebasgomez238 and sebasgomez238 authored Oct 6, 2023
1 parent d2627c0 commit 2e304ea
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 27 deletions.
24 changes: 4 additions & 20 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>$(NoWarn);CA1014;CS8002</NoWarn>
<SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
</PropertyGroup>

<PropertyGroup Label="Package">
Expand Down
78 changes: 78 additions & 0 deletions docs/sbom-tool-api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<ValidationService>()
.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<SbomSpecification> spdx22Specification = new List<SbomSpecification>()
{
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;
}

}
}

```
5 changes: 3 additions & 2 deletions global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"sdk": {
"version": "8.0.100-rc.1.23463.5",
"rollForward": "latestMajor"
"version": "7.0.400",
"rollForward": "latestMajor",
"allowPrerelease": false
}
}
71 changes: 69 additions & 2 deletions src/Microsoft.Sbom.Api/SBOMValidator.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,38 @@
// 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;

public class SbomValidator : ISBOMValidator
{
private readonly IWorkflow<SbomParserBasedValidationWorkflow> sbomParserBasedValidationWorkflow;
private readonly IRecorder recorder;
private readonly IEnumerable<ConfigValidator> configValidators;

public SbomValidator(
IWorkflow<SbomParserBasedValidationWorkflow> sbomParserBasedValidationWorkflow,
IRecorder recorder)
IRecorder recorder,
IEnumerable<ConfigValidator> 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<bool> ValidateSbomAsync()
Expand All @@ -31,4 +44,58 @@ public async Task<bool> ValidateSbomAsync()

return isSuccess;
}

public async Task<bool> ValidateSbomAsync(
string buildDropPath,
string outputPath,
IList<SbomSpecification> 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;
}
}
29 changes: 27 additions & 2 deletions src/Microsoft.Sbom.Contracts/ISBOMValidator.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Provides an interface to validate a SBOM.
Expand All @@ -15,4 +17,27 @@ public interface ISBOMValidator
/// and writes JSON output to the outputPath file location.
/// </summary>
Task<bool> ValidateSbomAsync();

/// <summary>
/// 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.
/// <param name="buildDropPath">The path to the root of the drop.</param>"
/// <param name="outputPath">The path to a writeable location where the output json should be written.</param>
/// <param name="specifications">The list of specifications to use for validation.</param>
/// <param name="manifestDirPath"/>The path to the directory that contains the _manifest folder. If null then buildDropPath will be used</param>
/// <param name="validateSignature">If true, validate the signature of the SBOM.</param>
/// <param name="rootPathFilter">The root path filter to use for validation.</param>
/// <param name="runtimeConfiguration">The runtime configuration to use for validation.</param>
/// <param name="algorithmName">The algorithm to use for hashing.</param>
/// </summary>
Task<bool> ValidateSbomAsync(
string buildDropPath,
string outputPath,
IList<SbomSpecification> specifications,
string manifestDirPath = null,
bool validateSignature = false,
bool ignoreMissing = false,
string rootPathFilter = null,
RuntimeConfiguration runtimeConfiguration = null,
AlgorithmName algorithmName = null);
}
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ public static IServiceCollection AddSbomTool(this IServiceCollection services, L
typeof(IManifestInterface)))
.AsImplementedInterfaces())
.AddScoped<ISBOMGenerator, SbomGenerator>()
.AddScoped<ISBOMValidator, SbomValidator>()
.AddSingleton(x =>
{
var fileSystemUtils = x.GetRequiredService<IFileSystemUtils>();
Expand Down

0 comments on commit 2e304ea

Please sign in to comment.