Skip to content

Commit

Permalink
Merging Varshita's branch into our feature branch (#12)
Browse files Browse the repository at this point in the history
* setting up imports

* Add reference to local Nuget package, for testing purposes

* rename targets and props, export them to the build folder

* Fix test project

* Fix Targets file to include props

* Manually adding the Sources Providers that support ProviderType.Packages

* Manually add the missing classes for SBOM generation

* Add MSBuild properties to our Props file (#8)

* Use MSBuild/.NET props for default values of the Generate SBOM task.

* Remove hardcoded path from the Targets

* Add default value to props file for SbomGenerationManifestDirPath

* Add final ManifestDirPath to SbomGenerationResult.

* Fix typo

* Change Summary comment for ManifestDirPath

* include sbom files in user's nuget packages (#11)

Co-authored-by: vpatakottu <vpatakottu@microsoft.com>

* Make the task target .net 8 and .net 6 (#13)

* Remove unrooted checks (#14)

* Downgrade Microsoft.Extensions.Hosting back to 7.0.1

* Remove LocalNuget configuration

* Stop tracking nuspec file

* Remove unnecessary comments.

* Remove reference to Microsoft.Sbom.Targets Nuget

* Apply suggestions from the linter and PR comments.

---------

Co-authored-by: vpatakottu <vpatakottu@microsoft.com>
Co-authored-by: vpatakottu <47004464+vpatakottu@users.noreply.github.com>
  • Loading branch information
3 people authored Jun 19, 2024
1 parent f7e0383 commit 245de38
Show file tree
Hide file tree
Showing 17 changed files with 226 additions and 184 deletions.
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
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@
<PackageVersion Include="System.Threading.Tasks.Dataflow" Version="4.11.1" />
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
</ItemGroup>
</Project>
</Project>
2 changes: 1 addition & 1 deletion Microsoft.Sbom.sln
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Sbom.Extensions.D
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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Sbom.Targets.Tests", "test\Microsoft.Sbom.Targets.Tests\Microsoft.Sbom.Targets.Tests.csproj", "{E31B914C-F24B-4DC8-ACC7-CAEA952563B8}"
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
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
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>
25 changes: 25 additions & 0 deletions src/GenerateSBOMTest/GenerateSBOMTest.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.34929.205
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenerateSBOMTest", "GenerateSBOMTest\GenerateSBOMTest.csproj", "{42BFEE5C-290D-4E99-9247-6AE26FA57227}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{42BFEE5C-290D-4E99-9247-6AE26FA57227}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42BFEE5C-290D-4E99-9247-6AE26FA57227}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42BFEE5C-290D-4E99-9247-6AE26FA57227}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42BFEE5C-290D-4E99-9247-6AE26FA57227}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {424A9B69-FF02-48BE-8D4C-B7D85BCFF76C}
EndGlobalSection
EndGlobal
19 changes: 19 additions & 0 deletions src/GenerateSBOMTest/GenerateSBOMTest/GenerateSBOMTest.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<!--<Import Project="D:\sbom-tool\src\Microsoft.Sbom.Targets\SbomExecution.props" />
<Import Project="D:\sbom-tool\src\Microsoft.Sbom.Targets\SbomExecution.targets" />
<Import Project="C:\Users\gustavoca\Repos\github\sbom-tool\src\Microsoft.Sbom.Targets\SbomExecution.props" />
<Import Project="C:\Users\gustavoca\Repos\github\sbom-tool\src\Microsoft.Sbom.Targets\SbomExecution.targets" />-->

<ItemGroup>
<PackageReference Include="Microsoft.Sbom.Targets" />
</ItemGroup>

</Project>
3 changes: 3 additions & 0 deletions src/GenerateSBOMTest/GenerateSBOMTest/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
Console.WriteLine("Hello, World!");
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;
}
}
107 changes: 53 additions & 54 deletions src/Microsoft.Sbom.Targets/GenerateSbomTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,26 @@ namespace Microsoft.Sbom.Targets;
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 class GenerateSbomTask : Task
{
// TODO it is possible we will want to expose additional arguments, either as required or optional.
// Will need to get SDK team/ windows team input on which arguments are necessary.

/// <summary>
/// The path to the drop directory for which the SBOM will be generated
/// The path to the drop directory for which the SBOM will be generated.
/// </summary>
[Required]
public string BuildDropPath { get; set; }
Expand Down Expand Up @@ -49,7 +59,7 @@ public class GenerateSbomTask : Task
[Required]
public string NamespaceBaseUri { get; set; }

/// <summary>
/// <summary>
/// The path to the directory containing build components and package information.
/// For example, path to a .csproj or packages.config file.
/// </summary>
Expand Down Expand Up @@ -97,27 +107,52 @@ public class GenerateSbomTask : Task
/// </summary>
public string ManifestDirPath { get; set; }

/// <summary>
/// The path to the generated SBOM directory.
/// </summary>
[Output]
public string SbomPath { get; set; }

private ISBOMGenerator Generator { get; set; }

/// <summary>
/// Constructor for the GenerateSbomTask.
/// </summary>
public GenerateSbomTask()
{
var host = Host.CreateDefaultBuilder()
.ConfigureServices((host, services) =>
services
.AddSbomTool())
.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() || !ValidateRootedPaths() || !ValidateAndSanitizeNamespaceUriUniquePart())
if (!ValidateAndSanitizeRequiredParams() || !ValidateAndSanitizeNamespaceUriUniquePart())
{
return false;
}
Expand All @@ -136,7 +171,7 @@ public override bool Execute()
NamespaceUriBase = this.NamespaceBaseUri,
NamespaceUriUniquePart = this.NamespaceUriUniquePart,
DeleteManifestDirectoryIfPresent = this.DeleteManifestDirIfPresent,
Verbosity = ValidateAndAssignVerbosity()
Verbosity = ValidateAndAssignVerbosity(),
};
#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits
var result = System.Threading.Tasks.Task.Run(() => this.Generator.GenerateSbomAsync(
Expand All @@ -149,7 +184,7 @@ public override bool Execute()
externalDocumentReferenceListFile: this.ExternalDocumentListFile)).GetAwaiter().GetResult();
#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits

SbomPath = "path/to/sbom";
SbomPath = !string.IsNullOrWhiteSpace(result.ManifestDirPath) ? Path.GetFullPath(result.ManifestDirPath) : null;
return result.IsSuccessful;
}
catch (Exception e)
Expand All @@ -169,7 +204,7 @@ private string Remove_Spaces_Tabs_Newlines(string value)
/// Ensure all required arguments are non-null/empty,
/// and do not contain whitespaces, tabs, or newline characters.
/// </summary>
/// <returns></returns>
/// <returns>True if the required parameters are valid. False otherwise.</returns>
private bool ValidateAndSanitizeRequiredParams()
{
if (string.IsNullOrWhiteSpace(this.BuildDropPath))
Expand Down Expand Up @@ -211,43 +246,6 @@ private bool ValidateAndSanitizeRequiredParams()
return true;
}

/// <summary>
/// Ensure all arguments that accept paths are rooted.
/// </summary>
/// <returns></returns>
private bool ValidateRootedPaths()
{
if (!Path.IsPathRooted(this.BuildDropPath))
{
Log.LogError($"SBOM generation failed: Unrooted path detected. Please specify a full path for {nameof(this.BuildDropPath)}. " +
$"Current value is {this.BuildDropPath}");
return false;
}

if (!string.IsNullOrWhiteSpace(this.BuildComponentPath) && !Path.IsPathRooted(this.BuildComponentPath))
{
Log.LogError($"SBOM generation failed: Unrooted path detected. Please specify a full path for {nameof(this.BuildComponentPath)}. " +
$"Current value is {this.BuildComponentPath}");
return false;
}

if (!string.IsNullOrWhiteSpace(this.ManifestDirPath) && !Path.IsPathRooted(this.ManifestDirPath))
{
Log.LogError($"SBOM generation failed: Unrooted path detected. Please specify a full path for {nameof(this.ManifestDirPath)}. " +
$"Current value is {this.ManifestDirPath}");
return false;
}

if (!string.IsNullOrWhiteSpace(this.ExternalDocumentListFile) && !Path.IsPathRooted(this.ExternalDocumentListFile))
{
Log.LogError($"SBOM generation failed: Unrooted path detected. Please specify a full path for {nameof(this.ExternalDocumentListFile)}. " +
$"Current value is {this.ExternalDocumentListFile}");
return false;
}

return true;
}

/// <summary>
/// Checks the user's input for Verbosity and assigns the
/// associated EventLevel value for logging.
Expand All @@ -260,7 +258,8 @@ private EventLevel ValidateAndAssignVerbosity()
return EventLevel.LogAlways;
}

if (Enum.TryParse(this.Verbosity, true, out EventLevel eventLevel)) {
if (Enum.TryParse(this.Verbosity, true, out EventLevel eventLevel))
{
return eventLevel;
}

Expand All @@ -269,28 +268,28 @@ private EventLevel ValidateAndAssignVerbosity()
}

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

return null;
}

/// <summary>
/// Ensure a valid NamespaceUriUniquePart is provided
/// Ensure a valid NamespaceUriUniquePart is provided.
/// </summary>
/// <returns></returns>
/// <returns>True if the Namespace URI unique part is valid. False otherwise.</returns>
private bool ValidateAndSanitizeNamespaceUriUniquePart()
{
// Ensure the NamespaceUriUniquePart is valid if provided.
if (!string.IsNullOrWhiteSpace(this.NamespaceUriUniquePart)
&& (!Guid.TryParse(this.NamespaceUriUniquePart, out var guidResult)
&& (!Guid.TryParse(this.NamespaceUriUniquePart, out _)
|| this.NamespaceUriUniquePart.Equals(Guid.Empty.ToString())))
{
Log.LogError($"SBOM generation failed: NamespaceUriUniquePart '{this.NamespaceUriUniquePart}' must be a valid unique GUID.");
Expand Down
Loading

0 comments on commit 245de38

Please sign in to comment.