diff --git a/src/Microsoft.Sbom.Api/Config/Args/GenerationArgs.cs b/src/Microsoft.Sbom.Api/Config/Args/GenerationArgs.cs index 3d87b243..914e213f 100644 --- a/src/Microsoft.Sbom.Api/Config/Args/GenerationArgs.cs +++ b/src/Microsoft.Sbom.Api/Config/Args/GenerationArgs.cs @@ -116,6 +116,15 @@ 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; } + /// + /// 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. + /// + [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. ")] + public int? LicenseInformationTimeout { get; set; } + /// /// If set to true, we will attempt to parse license and supplier info from the packages metadata file. /// diff --git a/src/Microsoft.Sbom.Api/Executors/ComponentDetectionBaseWalker.cs b/src/Microsoft.Sbom.Api/Executors/ComponentDetectionBaseWalker.cs index d4240c50..e0e1ca5f 100644 --- a/src/Microsoft.Sbom.Api/Executors/ComponentDetectionBaseWalker.cs +++ b/src/Microsoft.Sbom.Api/Executors/ComponentDetectionBaseWalker.cs @@ -137,7 +137,15 @@ async Task Scan(string path) { licenseInformationRetrieved = true; - var apiResponses = await licenseInformationFetcher.FetchLicenseInformationAsync(listOfComponentsForApi); + List apiResponses; + if (configuration.LicenseInformationTimeout is null) + { + apiResponses = await licenseInformationFetcher.FetchLicenseInformationAsync(listOfComponentsForApi); + } + else + { + apiResponses = await licenseInformationFetcher.FetchLicenseInformationAsync(listOfComponentsForApi, configuration.LicenseInformationTimeout.Value); + } foreach (var response in apiResponses) { diff --git a/src/Microsoft.Sbom.Api/Executors/ILicenseInformationFetcher.cs b/src/Microsoft.Sbom.Api/Executors/ILicenseInformationFetcher.cs index b5e7a719..c0f28f93 100644 --- a/src/Microsoft.Sbom.Api/Executors/ILicenseInformationFetcher.cs +++ b/src/Microsoft.Sbom.Api/Executors/ILicenseInformationFetcher.cs @@ -17,12 +17,16 @@ public interface ILicenseInformationFetcher /// List ConvertComponentsToListForApi(IEnumerable scannedComponents); + /// > + Task> FetchLicenseInformationAsync(List listOfComponentsForApi); + /// /// Calls the ClearlyDefined API to get the license information for the list of components. /// /// A list of strings formatted into a list of strings that can be used to call the batch ClearlyDefined API. + /// Timeout in seconds to use when making web requests /// - Task> FetchLicenseInformationAsync(List listOfComponentsForApi); + Task> FetchLicenseInformationAsync(List listOfComponentsForApi, int timeout); /// /// Gets the dictionary of licenses that were fetched from the ClearlyDefined API. diff --git a/src/Microsoft.Sbom.Api/Executors/ILicenseInformationService.cs b/src/Microsoft.Sbom.Api/Executors/ILicenseInformationService.cs index 675c4613..2b09a65c 100644 --- a/src/Microsoft.Sbom.Api/Executors/ILicenseInformationService.cs +++ b/src/Microsoft.Sbom.Api/Executors/ILicenseInformationService.cs @@ -9,4 +9,6 @@ namespace Microsoft.Sbom.Api.Executors; public interface ILicenseInformationService { public Task> FetchLicenseInformationFromAPI(List listOfComponentsForApi); + + public Task> FetchLicenseInformationFromAPI(List listOfComponentsForApi, int timeout); } diff --git a/src/Microsoft.Sbom.Api/Executors/LicenseInformationFetcher.cs b/src/Microsoft.Sbom.Api/Executors/LicenseInformationFetcher.cs index 47dc6f7d..59ed5a70 100644 --- a/src/Microsoft.Sbom.Api/Executors/LicenseInformationFetcher.cs +++ b/src/Microsoft.Sbom.Api/Executors/LicenseInformationFetcher.cs @@ -84,7 +84,12 @@ public List ConvertComponentsToListForApi(IEnumerable public async Task> FetchLicenseInformationAsync(List listOfComponentsForApi) { - return await licenseInformationService.FetchLicenseInformationFromAPI(listOfComponentsForApi); + return await FetchLicenseInformationAsync(listOfComponentsForApi, 30); + } + + public async Task> FetchLicenseInformationAsync(List listOfComponentsForApi, int timeout) + { + return await licenseInformationService.FetchLicenseInformationFromAPI(listOfComponentsForApi, timeout); } // 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. diff --git a/src/Microsoft.Sbom.Api/Executors/LicenseInformationService.cs b/src/Microsoft.Sbom.Api/Executors/LicenseInformationService.cs index c2a2a150..ee435f34 100644 --- a/src/Microsoft.Sbom.Api/Executors/LicenseInformationService.cs +++ b/src/Microsoft.Sbom.Api/Executors/LicenseInformationService.cs @@ -20,7 +20,6 @@ public class LicenseInformationService : ILicenseInformationService 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) { @@ -30,6 +29,11 @@ public LicenseInformationService(ILogger log, IRecorder recorder, HttpClient htt } public async Task> FetchLicenseInformationFromAPI(List listOfComponentsForApi) + { + return await FetchLicenseInformationFromAPI(listOfComponentsForApi, 30); + } + + public async Task> FetchLicenseInformationFromAPI(List listOfComponentsForApi, int timeout) { var batchSize = 500; var responses = new List(); @@ -38,7 +42,7 @@ public async Task> FetchLicenseInformationFromAPI(List 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); + httpClient.Timeout = TimeSpan.FromSeconds(timeout); for (var i = 0; i < listOfComponentsForApi.Count; i += batchSize) { diff --git a/src/Microsoft.Sbom.Common/Config/Configuration.cs b/src/Microsoft.Sbom.Common/Config/Configuration.cs index b7cfd621..3dd94ab1 100644 --- a/src/Microsoft.Sbom.Common/Config/Configuration.cs +++ b/src/Microsoft.Sbom.Common/Config/Configuration.cs @@ -47,6 +47,7 @@ public class Configuration : IConfiguration private static readonly AsyncLocal> generationTimestamp = new(); private static readonly AsyncLocal> followSymlinks = new(); private static readonly AsyncLocal> fetchLicenseInformation = new(); + private static readonly AsyncLocal> licenseInformationTimeout = new(); private static readonly AsyncLocal> enablePackageMetadataParsing = new(); private static readonly AsyncLocal> deleteManifestDirIfPresent = new(); private static readonly AsyncLocal> failIfNoPackages = new(); @@ -309,6 +310,14 @@ public ConfigurationSetting FetchLicenseInformation set => fetchLicenseInformation.Value = value; } + /// + [DefaultValue(30)] + public ConfigurationSetting LicenseInformationTimeout + { + get => licenseInformationTimeout.Value; + set => licenseInformationTimeout.Value = value; + } + /// [DefaultValue(false)] public ConfigurationSetting EnablePackageMetadataParsing diff --git a/src/Microsoft.Sbom.Common/Config/IConfiguration.cs b/src/Microsoft.Sbom.Common/Config/IConfiguration.cs index b2ecdc35..1da7b892 100644 --- a/src/Microsoft.Sbom.Common/Config/IConfiguration.cs +++ b/src/Microsoft.Sbom.Common/Config/IConfiguration.cs @@ -194,6 +194,12 @@ public interface IConfiguration /// ConfigurationSetting FetchLicenseInformation { get; set; } + /// + /// 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. + /// + ConfigurationSetting LicenseInformationTimeout { get; set; } + /// /// 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. /// diff --git a/src/Microsoft.Sbom.Common/Config/InputConfiguration.cs b/src/Microsoft.Sbom.Common/Config/InputConfiguration.cs index f0102229..384c0c83 100644 --- a/src/Microsoft.Sbom.Common/Config/InputConfiguration.cs +++ b/src/Microsoft.Sbom.Common/Config/InputConfiguration.cs @@ -141,6 +141,10 @@ public class InputConfiguration : IConfiguration [DefaultValue(false)] public ConfigurationSetting FetchLicenseInformation { get; set; } + /// + [DefaultValue(30)] + public ConfigurationSetting LicenseInformationTimeout { get; set; } + [DefaultValue(false)] public ConfigurationSetting EnablePackageMetadataParsing { get; set; }