Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/nuget/Microsoft.Extensions.Depend…
Browse files Browse the repository at this point in the history
…encyModel-9.0.0
  • Loading branch information
DaveTryon authored Dec 30, 2024
2 parents 5530f60 + af78e0c commit e7d1d58
Show file tree
Hide file tree
Showing 63 changed files with 2,828 additions and 84 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
fetch-depth: 0

- name: Setup .NET
uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 # v4.1.0
uses: actions/setup-dotnet@87b7050bc53ea08284295505d98d2aa94301e852 # v4.2.0
with:
dotnet-version: |
8.0.x
Expand All @@ -43,6 +43,6 @@ jobs:
run: dotnet test --collect:"XPlat Code Coverage"

- name: Upload code coverage
uses: codecov/codecov-action@015f24e6818733317a2da2edd6290ab26238649a # v5.0.7
uses: codecov/codecov-action@1e68e06f1dbfde0e4cefc87efeba9e4643565303 # v5.1.2
with:
token: ${{ secrets.CODECOV_TOKEN }}
6 changes: 3 additions & 3 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Initialize CodeQL
uses: github/codeql-action/init@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
uses: github/codeql-action/init@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0

- name: Autobuild
uses: github/codeql-action/autobuild@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
uses: github/codeql-action/autobuild@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
uses: github/codeql-action/analyze@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0
2 changes: 1 addition & 1 deletion .github/workflows/gen-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Setup .NET
uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 # v4.1.0
uses: actions/setup-dotnet@87b7050bc53ea08284295505d98d2aa94301e852 # v4.2.0

- name: Generate docs
run: |
Expand Down
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
<PackageVersion Include="NuGet.ProjectModel" Version="6.11.1" />
<PackageVersion Include="packageurl-dotnet" Version="1.1.0" />
<PackageVersion Include="PowerArgs" Version="3.6.0" />
<PackageVersion Include="Scrutor" Version="5.0.2" />
<PackageVersion Include="Scrutor" Version="5.1.0" />
<PackageVersion Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
Expand Down
12 changes: 12 additions & 0 deletions Microsoft.Sbom.sln
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Sbom.Tool.Tests",
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
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Sbom.Parsers.Spdx30SbomParser", "src\Microsoft.Sbom.Parsers.Spdx30SbomParser\Microsoft.Sbom.Parsers.Spdx30SbomParser.csproj", "{476B9C87-293F-4BF7-B39A-EB732083409F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Sbom.Parsers.Spdx30SbomParser.Tests", "test\Microsoft.Sbom.Parsers.Spdx30SbomParser.Tests\Microsoft.Sbom.Parsers.Spdx30SbomParser.Tests.csproj", "{E3FE33BB-FAB2-4F60-B930-BEB736AACE25}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -131,6 +135,14 @@ Global
{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
{476B9C87-293F-4BF7-B39A-EB732083409F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{476B9C87-293F-4BF7-B39A-EB732083409F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{476B9C87-293F-4BF7-B39A-EB732083409F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{476B9C87-293F-4BF7-B39A-EB732083409F}.Release|Any CPU.Build.0 = Release|Any CPU
{E3FE33BB-FAB2-4F60-B930-BEB736AACE25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E3FE33BB-FAB2-4F60-B930-BEB736AACE25}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E3FE33BB-FAB2-4F60-B930-BEB736AACE25}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E3FE33BB-FAB2-4F60-B930-BEB736AACE25}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
3 changes: 3 additions & 0 deletions docs/sbom-tool-arguments.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ Actions
DeleteManifestDirIfPresent (-D) If set to true, we will delete any previous manifest directories that are already present in the ManifestDirPath without asking the user for confirmation. The new
manifest directory will then be created at this location and the generated SBOM will be stored there.
FetchLicenseInformation (-li) If set to true, we will attempt to fetch license information of packages detected in the SBOM from the ClearlyDefinedApi.
LicenseInformationTimeoutInSeconds (-lto) Specifies the timeout in seconds for fetching the license information. Defaults to 30 seconds. Has no effect if
FetchLicenseInformation (-li) argument is false or not provided. Valid values are from 1 to 86400. Negative values use the default
value and Values exceeding the maximum are truncated to the maximum.
EnablePackageMetadataParsing (-pm) If set to true, we will attempt to parse license and supplier info from the packages metadata file (RubyGems, NuGet, Maven, Npm).
Verbosity (-V) Display this amount of detail in the logging output.
Verbose
Expand Down
5 changes: 5 additions & 0 deletions pipelines/sbom-tool-main-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ extends:
inputs:
arguments: '-c $(BuildConfiguration)'

- task: DotNetCoreCLI@2
displayName: Run tests
inputs:
command: 'test'

- task: UseDotNet@2
displayName: 'Install .NET 6 SDK'
inputs:
Expand Down
10 changes: 10 additions & 0 deletions src/Microsoft.Sbom.Api/Config/Args/GenerationArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@ public class GenerationArgs : GenerationAndValidationCommonArgs
[ArgDescription("If set to true, we will attempt to fetch license information of packages detected in the SBOM from the ClearlyDefinedApi.")]
public bool? FetchLicenseInformation { get; set; }

/// <summary>
/// Specifies the timeout in seconds for fetching the license information. Defaults to 30 seconds. Has no effect if
/// FetchLicenseInformation (li) argument is false or not provided. A negative value corresponds to an infinite timeout.
/// </summary>
[ArgShortcut("lto")]
[ArgDescription("Specifies the timeout in seconds for fetching the license information. Defaults to 30 seconds. " +
"Has no effect if the FetchLicenseInformation (li) argument is false or not provided. A negative value corresponds" +
"to an infinite timeout.")]
public int? LicenseInformationTimeoutInSeconds { get; set; }

/// <summary>
/// If set to true, we will attempt to parse license and supplier info from the packages metadata file.
/// </summary>
Expand Down
25 changes: 25 additions & 0 deletions src/Microsoft.Sbom.Api/Config/ConfigSanitizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.Sbom.Api.Executors;
using Microsoft.Sbom.Api.Hashing;
using Microsoft.Sbom.Api.Utils;
using Microsoft.Sbom.Common;
Expand Down Expand Up @@ -81,6 +82,30 @@ public IConfiguration SanitizeConfig(IConfiguration configuration)
// Set default package supplier if not provided in configuration.
configuration.PackageSupplier = GetPackageSupplierFromAssembly(configuration, logger);

// Prevent null value for LicenseInformationTimeoutInSeconds.
// Values of (0, Constants.MaxLicenseFetchTimeoutInSeconds] are allowed. Negative values are replaced with the default, and
// the higher values are truncated to the maximum of Common.Constants.MaxLicenseFetchTimeoutInSeconds
if (configuration.LicenseInformationTimeoutInSeconds is null)
{
configuration.LicenseInformationTimeoutInSeconds = new(Common.Constants.DefaultLicenseFetchTimeoutInSeconds, SettingSource.Default);
}
else if (configuration.LicenseInformationTimeoutInSeconds.Value <= 0)
{
logger.Warning($"Negative and Zero Values not allowed for timeout. Using the default {Common.Constants.DefaultLicenseFetchTimeoutInSeconds} seconds instead.");
configuration.LicenseInformationTimeoutInSeconds.Value = Common.Constants.DefaultLicenseFetchTimeoutInSeconds;
}
else if (configuration.LicenseInformationTimeoutInSeconds.Value > Common.Constants.MaxLicenseFetchTimeoutInSeconds)
{
logger.Warning($"Specified timeout exceeds maximum allowed. Truncating the timeout to {Common.Constants.MaxLicenseFetchTimeoutInSeconds} seconds.");
configuration.LicenseInformationTimeoutInSeconds.Value = Common.Constants.MaxLicenseFetchTimeoutInSeconds;
}

// Check if arg -lto is specified but -li is not
if (configuration.FetchLicenseInformation?.Value != true && !configuration.LicenseInformationTimeoutInSeconds.IsDefaultSource)
{
logger.Warning("A license fetching timeout is specified (argument -lto), but this has no effect when FetchLicenseInfo is unspecified or false (argument -li)");
}

// Replace backslashes in directory paths with the OS-sepcific directory separator character.
PathUtils.ConvertToOSSpecificPathSeparators(configuration);

Expand Down
16 changes: 15 additions & 1 deletion src/Microsoft.Sbom.Api/Executors/ComponentDetectionBaseWalker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,21 @@ async Task Scan(string path)
{
licenseInformationRetrieved = true;

var apiResponses = await licenseInformationFetcher.FetchLicenseInformationAsync(listOfComponentsForApi);
List<string> apiResponses;
var licenseInformationFetcher2 = licenseInformationFetcher as ILicenseInformationFetcher2;
if (licenseInformationFetcher2 is null && (bool)!configuration.LicenseInformationTimeoutInSeconds?.IsDefaultSource)
{
log.Warning("Timeout value is specified, but ILicenseInformationFetcher2 is not implemented for the licenseInformationFetcher");
}

if (licenseInformationFetcher2 is null || configuration.LicenseInformationTimeoutInSeconds is null)
{
apiResponses = await licenseInformationFetcher.FetchLicenseInformationAsync(listOfComponentsForApi);
}
else
{
apiResponses = await licenseInformationFetcher2.FetchLicenseInformationAsync(listOfComponentsForApi, configuration.LicenseInformationTimeoutInSeconds.Value);
}

foreach (var response in apiResponses)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public interface ILicenseInformationFetcher
List<string> ConvertComponentsToListForApi(IEnumerable<ScannedComponent> scannedComponents);

/// <summary>
/// Calls the ClearlyDefined API to get the license information for the list of components.
/// Calls the ClearlyDefined API to get the license information for the list of components. Uses a default timeout specified in implementation
/// </summary>
/// <param name="listOfComponentsForApi"> A list of strings formatted into a list of strings that can be used to call the batch ClearlyDefined API.</param>
/// <returns></returns>
Expand Down
20 changes: 20 additions & 0 deletions src/Microsoft.Sbom.Api/Executors/ILicenseInformationFetcher2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.ComponentDetection.Contracts.BcdeModels;

namespace Microsoft.Sbom.Api.Executors;

public interface ILicenseInformationFetcher2: ILicenseInformationFetcher
{
/// <summary>
/// Calls the ClearlyDefined API to get the license information for the list of components.
/// </summary>
/// <param name="listOfComponentsForApi"> A list of strings formatted into a list of strings that can be used to call the batch ClearlyDefined API.</param>
/// <param name="timeoutInSeconds">Timeout in seconds to use when making web requests</param>
/// <returns></returns>
Task<List<string>> FetchLicenseInformationAsync(List<string> listOfComponentsForApi, int timeoutInSeconds);
}
12 changes: 12 additions & 0 deletions src/Microsoft.Sbom.Api/Executors/ILicenseInformationService2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// 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;

namespace Microsoft.Sbom.Api.Executors;

public interface ILicenseInformationService2 : ILicenseInformationService
{
public Task<List<string>> FetchLicenseInformationFromAPI(List<string> listOfComponentsForApi, int timeoutInSeconds);
}
16 changes: 15 additions & 1 deletion src/Microsoft.Sbom.Api/Executors/LicenseInformationFetcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

namespace Microsoft.Sbom.Api.Executors;

public class LicenseInformationFetcher : ILicenseInformationFetcher
public class LicenseInformationFetcher : ILicenseInformationFetcher2
{
private readonly ILogger log;
private readonly IRecorder recorder;
Expand Down Expand Up @@ -87,6 +87,20 @@ public async Task<List<string>> FetchLicenseInformationAsync(List<string> listOf
return await licenseInformationService.FetchLicenseInformationFromAPI(listOfComponentsForApi);
}

public async Task<List<string>> FetchLicenseInformationAsync(List<string> listOfComponentsForApi, int timeoutInSeconds)
{
var licenseInformationService2 = licenseInformationService as ILicenseInformationService2;
if (licenseInformationService2 is null)
{
log.Warning("Timeout is specified in License Fetcher, but licenseInformationService does not implement ILicenseInformationService2");
return await licenseInformationService.FetchLicenseInformationFromAPI(listOfComponentsForApi);
}
else
{
return await licenseInformationService2.FetchLicenseInformationFromAPI(listOfComponentsForApi, timeoutInSeconds);
}
}

// Will attempt to extract license information from a clearlyDefined batch API response. Will always return a dictionary which may be empty depending on the response.
public Dictionary<string, string> ConvertClearlyDefinedApiResponseToList(string httpResponseContent)
{
Expand Down
13 changes: 10 additions & 3 deletions src/Microsoft.Sbom.Api/Executors/LicenseInformationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@

namespace Microsoft.Sbom.Api.Executors;

public class LicenseInformationService : ILicenseInformationService
public class LicenseInformationService : ILicenseInformationService2
{
private readonly ILogger log;
private readonly IRecorder recorder;
private readonly HttpClient httpClient;
private const int ClientTimeoutSeconds = 30;

public LicenseInformationService(ILogger log, IRecorder recorder, HttpClient httpClient)
{
Expand All @@ -30,6 +29,11 @@ public LicenseInformationService(ILogger log, IRecorder recorder, HttpClient htt
}

public async Task<List<string>> FetchLicenseInformationFromAPI(List<string> listOfComponentsForApi)
{
return await FetchLicenseInformationFromAPI(listOfComponentsForApi, Common.Constants.MaxLicenseFetchTimeoutInSeconds);
}

public async Task<List<string>> FetchLicenseInformationFromAPI(List<string> listOfComponentsForApi, int timeoutInSeconds)
{
var batchSize = 500;
var responses = new List<HttpResponseMessage>();
Expand All @@ -38,7 +42,10 @@ public async Task<List<string>> FetchLicenseInformationFromAPI(List<string> list
var uri = new Uri("https://api.clearlydefined.io/definitions?expand=-files");

httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.Timeout = TimeSpan.FromSeconds(ClientTimeoutSeconds);
if (timeoutInSeconds > 0)
{
httpClient.Timeout = TimeSpan.FromSeconds(timeoutInSeconds);
} // The else cases should be sanitized in Config Sanitizer, and even if not, it'll just use httpClient's default timeout

for (var i = 0; i < listOfComponentsForApi.Count; i += batchSize)
{
Expand Down
9 changes: 9 additions & 0 deletions src/Microsoft.Sbom.Common/Config/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class Configuration : IConfiguration
private static readonly AsyncLocal<ConfigurationSetting<string>> generationTimestamp = new();
private static readonly AsyncLocal<ConfigurationSetting<bool>> followSymlinks = new();
private static readonly AsyncLocal<ConfigurationSetting<bool>> fetchLicenseInformation = new();
private static readonly AsyncLocal<ConfigurationSetting<int>> licenseInformationTimeout = new();
private static readonly AsyncLocal<ConfigurationSetting<bool>> enablePackageMetadataParsing = new();
private static readonly AsyncLocal<ConfigurationSetting<bool>> deleteManifestDirIfPresent = new();
private static readonly AsyncLocal<ConfigurationSetting<bool>> failIfNoPackages = new();
Expand Down Expand Up @@ -309,6 +310,14 @@ public ConfigurationSetting<bool> FetchLicenseInformation
set => fetchLicenseInformation.Value = value;
}

/// <inheritdoc cref="IConfiguration.LicenseInformationTimeoutInSeconds" />
[DefaultValue(Constants.DefaultLicenseFetchTimeoutInSeconds)]
public ConfigurationSetting<int> LicenseInformationTimeoutInSeconds
{
get => licenseInformationTimeout.Value;
set => licenseInformationTimeout.Value = value;
}

/// <inheritdoc cref="IConfiguration.EnablePackageMetadataParsing" />
[DefaultValue(false)]
public ConfigurationSetting<bool> EnablePackageMetadataParsing
Expand Down
7 changes: 7 additions & 0 deletions src/Microsoft.Sbom.Common/Config/IConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,13 @@ public interface IConfiguration
/// </summary>
ConfigurationSetting<bool> FetchLicenseInformation { get; set; }

/// <summary>
/// Specifies the timeout in seconds for fetching the license information. Defaults to <see cref="Constants.DefaultLicenseFetchTimeoutInSeconds"/>.
/// Has no effect if FetchLicenseInformation (li) argument is false or not provided. Negative values are set to the default and values exceeding the
/// maximum are truncated to <see cref="Constants.MaxLicenseFetchTimeoutInSeconds"/>
/// </summary>
ConfigurationSetting<int> LicenseInformationTimeoutInSeconds { get; set; }

/// <summary>
/// If set to true, we will attempt to locate and parse package metadata files for additional information to include in the SBOM such as .nuspec/.pom files in the local package cache.
/// </summary>
Expand Down
4 changes: 4 additions & 0 deletions src/Microsoft.Sbom.Common/Config/InputConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ public class InputConfiguration : IConfiguration
[DefaultValue(false)]
public ConfigurationSetting<bool> FetchLicenseInformation { get; set; }

/// <inheritdoc cref="IConfiguration.LicenseInformationTimeoutInSeconds" />
[DefaultValue(Constants.DefaultLicenseFetchTimeoutInSeconds)]
public ConfigurationSetting<int> LicenseInformationTimeoutInSeconds { get; set; }

[DefaultValue(false)]
public ConfigurationSetting<bool> EnablePackageMetadataParsing { get; set; }

Expand Down
3 changes: 3 additions & 0 deletions src/Microsoft.Sbom.Common/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ public static class Constants
public const int DefaultParallelism = 8;
public const int MaxParallelism = 48;

public const int DefaultLicenseFetchTimeoutInSeconds = 30;
public const int MaxLicenseFetchTimeoutInSeconds = 86400;

public const LogEventLevel DefaultLogLevel = LogEventLevel.Warning;
}
Loading

0 comments on commit e7d1d58

Please sign in to comment.