| File: RestoreCommand\VulnerabilityInformationProvider.cs | Web Access |
| Project: src\nuget-client\src\NuGet.Core\NuGet.Commands\NuGet.Commands.csproj (NuGet.Commands) |
// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Threading; using System.Threading.Tasks; using NuGet.Common; using NuGet.Configuration; using NuGet.Protocol; using NuGet.Protocol.Core.Types; using NuGet.Protocol.Model; namespace NuGet.Commands { internal sealed class VulnerabilityInformationProvider : IVulnerabilityInformationProvider, IDisposable { private readonly SemaphoreSlim _semaphore = new(initialCount: 1, maxCount: 1); private readonly SourceRepository _source; private readonly ILogger _logger; private GetVulnerabilityInfoResult? _cachedResult; public VulnerabilityInformationProvider(SourceRepository source, ILogger logger, bool isAuditSource) { _source = source; _logger = logger; IsAuditSource = isAuditSource; } public async Task<GetVulnerabilityInfoResult?> GetVulnerabilityInformationAsync(CancellationToken cancellationToken) { if (_cachedResult is not null) { return _cachedResult; } await _semaphore.WaitAsync(cancellationToken); try { if (_cachedResult is not null) { return _cachedResult; } _cachedResult = await GetVulnerabilityInfoAsync(cancellationToken); return _cachedResult; } finally { _semaphore.Release(); } } public bool IsAuditSource { get; } public string SourceName => _source.PackageSource.Name; public PackageSource PackageSource => _source.PackageSource; private async Task<GetVulnerabilityInfoResult?> GetVulnerabilityInfoAsync(CancellationToken cancellationToken) { try { IVulnerabilityInfoResource? vulnerabilityInfoResource = await _source.GetResourceAsync<IVulnerabilityInfoResource>(cancellationToken); if (vulnerabilityInfoResource is null) { return null; } // Don't re-use a SourceCacheContext from whatever triggered this, because installing packages // (and a few other scenarios, look at everything that calls SourceCacheContext.MaxAge's setter) // will ignore the http cache, because it wants to allow newly published packages to be installable. // However, in VS, when a new project template installs packages, we don't want to force a re-download // of the vulnerability data. Firstly, from a customer point of view, it's not necessary. The reason // we bust the cache for package install doesn't apply to vulnerability data. Secondly, it triggers // regressions in VS's performance tests. using SourceCacheContext cacheContext = new(); GetVulnerabilityInfoResult result = await vulnerabilityInfoResource.GetVulnerabilityInfoAsync(cacheContext, _logger, cancellationToken); return result; } catch (OperationCanceledException) { throw; } catch (Exception e) { GetVulnerabilityInfoResult result = new(knownVulnerabilities: null, exceptions: new(e)); return result; } } public void Dispose() { _semaphore.Dispose(); } } }