Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a SBOM Generation Task #674

Merged
merged 43 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
a4009b1
Experimenting with dotnet
sfoslund Mar 22, 2024
fb128c8
Add comments
sfoslund Apr 3, 2024
a69259a
Merge main with net-sdk-sbom-tool branch
gustavoaca1997 May 24, 2024
40ba546
Make the Sbom.Targets project to build.
gustavoaca1997 May 24, 2024
c31fb95
Add all arguments to GenerateSBOMTask (#1)
vpatakottu May 24, 2024
8ffff9c
Add call to the SBOM API from GenerateSbomTask#Execute (#2)
gustavoaca1997 May 28, 2024
ce039dd
Validate and Sanitize Arguments (#3)
vpatakottu May 29, 2024
5df56c1
Add tests for Generate SBOM Task (#4)
gustavoaca1997 May 31, 2024
4ece5d9
Add unit tests for GenerateSbomTask inputs (#6)
vpatakottu Jun 7, 2024
f7e0383
Add additional unit tests for valid cases (#7)
vpatakottu Jun 12, 2024
245de38
Merging Varshita's branch into our feature branch (#12)
gustavoaca1997 Jun 19, 2024
288626c
Fix ubuntu tests (#16)
gustavoaca1997 Jun 20, 2024
a028861
Update feature branch (#17)
gustavoaca1997 Jun 24, 2024
22ee3b5
Add missing header to AbstractGenerateSbomTaskTests (#598)
gustavoaca1997 Jun 25, 2024
abdb4a0
Create template tool task (#600)
vpatakottu Jun 28, 2024
b04a2f5
Run the Github build also for feature branches.
gustavoaca1997 Jun 29, 2024
84441e4
Implement SBOM CLI ToolTask (#607)
vpatakottu Jul 10, 2024
6a0a5df
Merge main with feature/sbom-targets-task
gustavoaca1997 Jul 11, 2024
69b8642
Update System.Text.Json
gustavoaca1997 Jul 12, 2024
a3e094d
Stop importing the props twice when referencing the Nuget package (#612)
gustavoaca1997 Jul 12, 2024
d758cad
Update with main
gustavoaca1997 Jul 20, 2024
22b1991
Add tests for the MSBuild Full version of the Generate SBOM task (#613)
gustavoaca1997 Jul 24, 2024
31ed7e8
Update NuGet Package Format and Surface Errors (#619)
vpatakottu Jul 30, 2024
d2c4dc6
Add README for Microsoft.Sbom.Targets project (#651)
vpatakottu Aug 3, 2024
4c9354c
Workaround for generating a SBOM manifest at the root level of the Nu…
gustavoaca1997 Aug 9, 2024
5667a00
Use Path.Combine for Unzip and Nupkg paths (#663)
gustavoaca1997 Aug 14, 2024
d0929da
Add E2E tests for Microsoft.Sbom.Targets project (#658)
vpatakottu Aug 15, 2024
6e55535
Remove GenerateSBOMTest project (#673)
gustavoaca1997 Aug 16, 2024
756c822
Add ContinueOnError=ErrorAndContinue to the ZipDirectory, GenerateSBO…
gustavoaca1997 Aug 16, 2024
c74ef7e
User/gustavoca/update with main (#675)
gustavoaca1997 Aug 16, 2024
d36de25
Merge branch 'main' into feature/sbom-targets-task
gustavoaca1997 Aug 16, 2024
a315afe
Update README
gustavoaca1997 Aug 16, 2024
d0fdcfc
Address Feedback (#679)
vpatakottu Aug 21, 2024
fc99905
address more feedback (#682)
vpatakottu Aug 21, 2024
82d5fe5
Pack each project separately (#681)
gustavoaca1997 Aug 23, 2024
65e9d01
Inspect the content of the Nuget package instead of extracting to dis…
gustavoaca1997 Aug 23, 2024
941a14e
Merge branch 'feature/sbom-targets-task' of https://github.com/micros…
gustavoaca1997 Aug 23, 2024
fe6c158
User/gustavoca/dont extract e2e tests (#684)
gustavoaca1997 Aug 23, 2024
2272e04
Update local branch with remote branch
gustavoaca1997 Aug 30, 2024
90e7e3b
Update with main branch
gustavoaca1997 Aug 30, 2024
52329d4
Remove instance of Newtonsoft.Json
gustavoaca1997 Aug 30, 2024
4bf0217
Remove not needed Message
gustavoaca1997 Aug 30, 2024
2faeea6
Revert "Remove instance of Newtonsoft.Json"
gustavoaca1997 Aug 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ on:
push:
branches:
- main
- 'feature/**'
gustavoaca1997 marked this conversation as resolved.
Show resolved Hide resolved
pull_request:
branches:
- main
- 'feature/**'

permissions:
contents: read
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ PublishScripts/

# NuGet Packages
*.nupkg
*.nuspec
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
Expand Down
7 changes: 6 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
<PackageVersion Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
<PackageVersion Include="Microsoft.Build" Version="17.3.2" />
<PackageVersion Include="Microsoft.Build.Framework" Version="17.10.4" />
<PackageVersion Include="Microsoft.Build.Locator" Version="1.7.8" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.10.4" />
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
<PackageVersion Include="MSTest.TestAdapter" Version="3.5.1" />
<PackageVersion Include="MSTest.TestFramework" Version="3.5.1" />
<PackageVersion Include="Microsoft.ComponentDetection.Common" Version="$(ComponentDetectionPackageVersion)" />
Expand All @@ -22,7 +27,7 @@
<PackageVersion Include="Microsoft.ComponentDetection.Orchestrator" Version="$(ComponentDetectionPackageVersion)" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
Expand Down
18 changes: 18 additions & 0 deletions Microsoft.Sbom.sln
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Sbom.Extensions.D
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Sbom.Extensions.DependencyInjection.Tests", "test\Microsoft.Sbom.Extensions.DependencyInjection.Tests\Microsoft.Sbom.Extensions.DependencyInjection.Tests.csproj", "{EE4E2E03-7B4C-46E5-B9D2-89E84A18D787}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Sbom.Targets", "src\Microsoft.Sbom.Targets\Microsoft.Sbom.Targets.csproj", "{E6C3C851-EEA0-466E-BA36-73ED85F13EEA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Sbom.Targets.Tests", "test\Microsoft.Sbom.Targets.Tests\Microsoft.Sbom.Targets.Tests.csproj", "{E31B914C-F24B-4DC8-ACC7-CAEA952563B8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Sbom.Tool.Tests", "test\Microsoft.Sbom.Tool.Tests\Microsoft.Sbom.Tool.Tests.csproj", "{FC5A9799-7C44-4BFA-BA22-55DCAF1A1B9F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Sbom.Targets.E2E.Tests", "test\Microsoft.Sbom.Targets.E2E.Tests\Microsoft.Sbom.Targets.E2E.Tests.csproj", "{3FDE7800-F61F-4C45-93AB-648A4C7979C7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -109,10 +115,22 @@ Global
{EE4E2E03-7B4C-46E5-B9D2-89E84A18D787}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EE4E2E03-7B4C-46E5-B9D2-89E84A18D787}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EE4E2E03-7B4C-46E5-B9D2-89E84A18D787}.Release|Any CPU.Build.0 = Release|Any CPU
{E6C3C851-EEA0-466E-BA36-73ED85F13EEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E6C3C851-EEA0-466E-BA36-73ED85F13EEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E6C3C851-EEA0-466E-BA36-73ED85F13EEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E6C3C851-EEA0-466E-BA36-73ED85F13EEA}.Release|Any CPU.Build.0 = Release|Any CPU
{E31B914C-F24B-4DC8-ACC7-CAEA952563B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E31B914C-F24B-4DC8-ACC7-CAEA952563B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E31B914C-F24B-4DC8-ACC7-CAEA952563B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E31B914C-F24B-4DC8-ACC7-CAEA952563B8}.Release|Any CPU.Build.0 = Release|Any CPU
{FC5A9799-7C44-4BFA-BA22-55DCAF1A1B9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FC5A9799-7C44-4BFA-BA22-55DCAF1A1B9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FC5A9799-7C44-4BFA-BA22-55DCAF1A1B9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC5A9799-7C44-4BFA-BA22-55DCAF1A1B9F}.Release|Any CPU.Build.0 = Release|Any CPU
{3FDE7800-F61F-4C45-93AB-648A4C7979C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3FDE7800-F61F-4C45-93AB-648A4C7979C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3FDE7800-F61F-4C45-93AB-648A4C7979C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3FDE7800-F61F-4C45-93AB-648A4C7979C7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
4 changes: 2 additions & 2 deletions nuget.config
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>
</configuration>
4 changes: 2 additions & 2 deletions src/Microsoft.Sbom.Api/SBOMGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public async Task<SbomGenerationResult> GenerateSbomAsync(

var entityErrors = recorder.Errors.Select(error => error.ToEntityError()).ToList();

return new SbomGenerationResult(isSuccess, entityErrors);
return new SbomGenerationResult(isSuccess, entityErrors, isSuccess ? inputConfiguration.ManifestDirPath.ToString() : null);
}

/// <inheritdoc />
Expand Down Expand Up @@ -120,7 +120,7 @@ public async Task<SbomGenerationResult> GenerateSbomAsync(
// This is the generate workflow
var result = await generationWorkflow.RunAsync();

return new SbomGenerationResult(result, new List<EntityError>());
return new SbomGenerationResult(result, new List<EntityError>(), result ? inputConfiguration.ManifestDirPath.ToString() : null);
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,15 @@ public class SbomGenerationResult
/// </summary>
public IList<EntityError> Errors { get; private set; }

public SbomGenerationResult(bool isSuccessful, IList<EntityError> errors)
/// <summary>
/// Gets the path where the SBOM was generated, if the generation was successful.
/// </summary>
public string? ManifestDirPath { get; private set; }

public SbomGenerationResult(bool isSuccessful, IList<EntityError> errors, string manifestDirPath = null)
{
IsSuccessful = isSuccessful;
Errors = errors ?? new List<EntityError>();
this.ManifestDirPath = manifestDirPath;
}
}
106 changes: 106 additions & 0 deletions src/Microsoft.Sbom.Targets/GenerateSbom.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.Sbom.Targets;

using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using Microsoft.Build.Framework;

/// <summary>
/// This partial class defines and sanitizes the arguments that will be passed
/// into the SBOM API and CLI tool for generation.
/// </summary>
public partial class GenerateSbom
{
/// <summary>
/// Gets or sets the path to the drop directory for which the SBOM will be generated.
/// </summary>
[Required]
public string BuildDropPath { get; set; }

/// <summary>
/// Gets or sets the supplier of the package the SBOM represents.
/// </summary>
[Required]
public string PackageSupplier { get; set; }

/// <summary>
/// Gets or sets the name of the package the SBOM represents.
/// </summary>
[Required]
public string PackageName { get; set; }

/// <summary>
/// Gets or sets the version of the package the SBOM represents.
/// </summary>
[Required]
public string PackageVersion { get; set; }

/// <summary>
/// Gets or sets the base path of the SBOM namespace uri.
/// </summary>
[Required]
public string NamespaceBaseUri { get; set; }

/// <summary>
/// Gets or sets the path to the directory containing build components and package information.
/// For example, path to a .csproj or packages.config file.
/// </summary>
public string BuildComponentPath { get; set; }

/// <summary>
/// Gets or sets a unique URI part that will be appended to NamespaceBaseUri.
/// </summary>
public string NamespaceUriUniquePart { get; set; }

/// <summary>
/// Gets or sets the path to a file containing a list of external SBOMs that will be appended to the
/// SBOM that is being generated.
/// </summary>
public string ExternalDocumentListFile { get; set; }

/// <summary>
/// Indicates whether licensing information will be fetched for detected packages.
/// </summary>
public bool FetchLicenseInformation { get; set; }

/// <summary>
/// Indicates whether to parse licensing and supplier information from a packages metadata file.
/// </summary>
public bool EnablePackageMetadataParsing { get; set; }

/// <summary>
/// Gets or sets the verbosity level for logging output.
/// </summary>
public string Verbosity { get; set; }

/// <summary>
/// Gets or sets a list of names and versions of the manifest format being used.
/// </summary>
public string ManifestInfo { get; set; }

/// <summary>
/// Indicates whether the previously generated SBOM manifest directory should be deleted
/// before generating a new SBOM in the directory specified by ManifestDirPath.
/// Defaults to true.
/// </summary>
public bool DeleteManifestDirIfPresent { get; set; } = true;

/// <summary>
/// Gets or sets the path where the SBOM will be generated.
/// </summary>
public string ManifestDirPath { get; set; }

/// <summary>
/// Gets or sets the path to the SBOM CLI tool
/// </summary>
public string SbomToolPath { get; set; }

/// <summary>
/// Gets or sets the path to the generated SBOM directory.
/// </summary>
[Output]
public string SbomPath { get; set; }
}
126 changes: 126 additions & 0 deletions src/Microsoft.Sbom.Targets/GenerateSbomTask.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.Sbom.Targets;

using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Sbom.Api.Manifest.ManifestConfigHandlers;
using Microsoft.Sbom.Api.Metadata;
using Microsoft.Sbom.Api.Providers;
using Microsoft.Sbom.Api.Providers.ExternalDocumentReferenceProviders;
using Microsoft.Sbom.Api.Providers.FilesProviders;
using Microsoft.Sbom.Api.Providers.PackagesProviders;
using Microsoft.Sbom.Contracts;
using Microsoft.Sbom.Contracts.Entities;
using Microsoft.Sbom.Contracts.Interfaces;
using Microsoft.Sbom.Extensions;
using Microsoft.Sbom.Extensions.DependencyInjection;
using Microsoft.Sbom.Parsers.Spdx22SbomParser;

/// <summary>
/// MSBuild task for generating SBOMs from build output.
/// </summary>
public partial class GenerateSbom : Task
{
private ISBOMGenerator Generator { get; set; }

/// <summary>
/// Constructor for the GenerateSbomTask.
/// </summary>
public GenerateSbom()
{
var host = Host.CreateDefaultBuilder()
.ConfigureServices((host, services) =>
services
.AddSbomTool()
/* Manually adding some dependencies since `AddSbomTool()` does not add them when
* running the MSBuild Task from another project.
*/
.AddSingleton<ISourcesProvider, SBOMPackagesProvider>()
.AddSingleton<ISourcesProvider, CGExternalDocumentReferenceProvider>()
.AddSingleton<ISourcesProvider, DirectoryTraversingFileToJsonProvider>()
.AddSingleton<ISourcesProvider, ExternalDocumentReferenceFileProvider>()
.AddSingleton<ISourcesProvider, ExternalDocumentReferenceProvider>()
.AddSingleton<ISourcesProvider, FileListBasedFileToJsonProvider>()
.AddSingleton<ISourcesProvider, SbomFileBasedFileToJsonProvider>()
.AddSingleton<ISourcesProvider, CGScannedExternalDocumentReferenceFileProvider>()
.AddSingleton<ISourcesProvider, CGScannedPackagesProvider>()
.AddSingleton<IAlgorithmNames, AlgorithmNames>()
.AddSingleton<IManifestGenerator, Generator>()
.AddSingleton<IMetadataProvider, LocalMetadataProvider>()
.AddSingleton<IMetadataProvider, SBOMApiMetadataProvider>()
.AddSingleton<IManifestInterface, Validator>()
.AddSingleton<IManifestConfigHandler, SPDX22ManifestConfigHandler>())
.Build();
this.Generator = host.Services.GetRequiredService<ISBOMGenerator>();
}

/// <inheritdoc/>
public override bool Execute()
{
try
{
// Validate required args and args that take paths as input.
if (!ValidateAndSanitizeRequiredParams() || !ValidateAndSanitizeNamespaceUriUniquePart())
{
return false;
}

// Set other configurations. The GenerateSBOMAsync() already sanitizes and checks for
// a valid namespace URI and generates a random guid for NamespaceUriUniquePart if
// one is not provided.
var sbomMetadata = new SBOMMetadata
{
PackageSupplier = this.PackageSupplier,
PackageName = this.PackageName,
PackageVersion = this.PackageVersion,
};
var runtimeConfiguration = new RuntimeConfiguration
{
NamespaceUriBase = this.NamespaceBaseUri,
NamespaceUriUniquePart = this.NamespaceUriUniquePart,
DeleteManifestDirectoryIfPresent = this.DeleteManifestDirIfPresent,
Verbosity = ValidateAndAssignVerbosity(),
};
#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits
sfoslund marked this conversation as resolved.
Show resolved Hide resolved
var result = System.Threading.Tasks.Task.Run(() => this.Generator.GenerateSbomAsync(
rootPath: this.BuildDropPath,
manifestDirPath: this.ManifestDirPath,
metadata: sbomMetadata,
componentPath: this.BuildComponentPath,
runtimeConfiguration: runtimeConfiguration,
specifications: ValidateAndAssignSpecifications(),
externalDocumentReferenceListFile: this.ExternalDocumentListFile)).GetAwaiter().GetResult();
#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits

SbomPath = !string.IsNullOrWhiteSpace(result.ManifestDirPath) ? Path.GetFullPath(result.ManifestDirPath) : null;
return result.IsSuccessful;
}
catch (Exception e)
{
Log.LogError($"SBOM generation failed: {e.Message}");
return false;
}
}

/// <summary>
/// Check for ManifestInfo and create an SbomSpecification accordingly.
/// </summary>
/// <returns>A list of the parsed manifest info. Null ig the manifest info is null or empty.</returns>
vpatakottu marked this conversation as resolved.
Show resolved Hide resolved
private IList<SbomSpecification> ValidateAndAssignSpecifications()
{
if (!string.IsNullOrWhiteSpace(this.ManifestInfo))
{
return [SbomSpecification.Parse(this.ManifestInfo)];
}

return null;
}
}
Loading
Loading