-
Notifications
You must be signed in to change notification settings - Fork 695
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
Enable AuditSource during dotnet list package command #6206
base: dev
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good work!
I particularly like test calling GetReportDataAsync
and asserting on the object, rather than a test that tries to parse console output.
There are some changes needed to make dothet list package work more similarly to restore with regards to audit sources. And considering audit sources introduces multiple behaviours, it means multiple tests are needed to test all the scenarios.
I also made some suggestions about code readability/maintainability, which while subjective, I hope that I've justified sufficiently well to convince you that it's changes we should make, not just an opinion I'm trying to force onto others.
@@ -17,6 +17,7 @@ internal class ListPackageArgs | |||
public ILogger Logger { get; } | |||
public string Path { get; } | |||
public List<PackageSource> PackageSources { get; } | |||
public List<PackageSource> AuditSources { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pattern that most NuGet.CommandLine and NuGet.CommandLine.XPlat commands use is that these *Args
classes hold the inputs, and business logic goes in the *Runner
classes.
Since no CLI argument/option was added to dotnet list package
for audit sources (should there be?), I feel like this AuditSources
property doesn't belong here.
I had a quick look, and I see that ListCommand
reads nuget.config and gets the sources at the same time as it's parsing the other CLI arguments and options into the ListPakcageArgs
class. But I think this is not good software design. Following the Single Responsibility Principal, I think easier to understand code if ListPackageArgs
is only parses CLI commands and arguments and puts them in the ListPackageArgs
class, and then ListCommandRunner
has all business logic regarding whether CLI sources are added to nuget.config sources, or used as a replacement instead, as well all the other work that list package does.
Logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||
CancellationToken = cancellationToken; | ||
ArgumentText = GetReportParameters(); | ||
} | ||
|
||
public ListPackageArgs( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My comment isn't as relevant given my other suggestion that AuditSources
shouldnt be defined in this class (unless a CLI option is added), but this class isn't public, so it's not part of the public API, and therefore we can modify the existing constructor rather than adding a new overload.
If this was already understood, and this overload was added instead to avoid needing to modify existing tests that call the existing constructor, a trade-off needs to be made between ease of finishing your current work (this PR) vs ongoing tech debt in having more methods, risks of bugs because tests are using the production class differently to how production code is being used.
There will be times where adding an overload is the better choice, and other times when making the effort to update tests is the better choice to reduce risk. I haven't looked in detail at how many tests create instances of this class, but my gut feeling is that I would expect it not to be too difficult to update all tests, so we probably don't need a constructor overload.
@@ -176,6 +179,18 @@ private static void WarnForHttpSources(ListPackageArgs listPackageArgs, ListPack | |||
} | |||
} | |||
|
|||
foreach (PackageSource packageSource in listPackageArgs.AuditSources) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would have liked the PR description to have addressed my question, but from https://learn.microsoft.com/en-us/nuget/concepts/auditing-packages#audit-sources
If auditSources is not defined or is cleared without adding any sources, then packageSources will be used and warning NU1905 is suppressed.
So, for restore, when nuget.config defines one or more audit sources, then vulnerability information is not retrieved from the package sources, only from the audit sources.
This PR does not appear to follow this behaviour for dotnet list package
.
In theory, if a package source lists a package as vulnerable, but the audit source does not, this will cause dotnet list package --vulnerable
to have different results to dotnet restore
.
If it's intentional that dotnet list package --vulnerable
will work differently, I'm curious why
@@ -176,6 +179,18 @@ private static void WarnForHttpSources(ListPackageArgs listPackageArgs, ListPack | |||
} | |||
} | |||
|
|||
foreach (PackageSource packageSource in listPackageArgs.AuditSources) | |||
{ | |||
if (packageSource.IsHttp && !packageSource.IsHttps && !packageSource.AllowInsecureConnections) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no else
block, so if any sources defined in nuget.config are not used because it's using insecure http, it will be silently ignored. I believe all or other commands report an error, so this should as well.
foreach (var source in listPackageArgs.AuditSources) | ||
{ | ||
var repository = Repository.Factory.GetCoreV3(source); | ||
var vulnerabilityProvider = new VulnerabilityInfoResourceV3Provider(); | ||
var result = await vulnerabilityProvider.TryCreate(repository, listPackageArgs.CancellationToken); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Package sources can provide the VulnerabilityInfoResource
as well as audit sources, and that's actually how audit during restore works. An issue was created for this before auditSources
was implemented, so maybe some people would consider it scope creep because the issue to add support for auditSources didn't explicitly call it out, but it's very related to the work currently being done, so I feel like it's a good time to implement it at the same time:
Implementing this will give dotnet list package --vulnerable
a significant performance boost when there are many packages to check, because NuGet can download the database once and then do a whole lot of lookups, instead of making an HTTP request to every source for every package id.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing test: when a package source has vulnerabilitiy data in addition to an audit source
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing test: when an audit source has a server-side error. For example, the vulnerability index lists two files, one of which does not exist.
Note that I designed IVulnerabilityInfoResource
to be easily mockable, see some of the restore tests. In theory it's not necessary to use a mock HTTP server to test, you can create a SourceRepository
that overrides the default IVulnerabilityInfoResource
, where you return a hard-coded result with an exception and/or vulnerability data. You just need to be able to inject the mock SourceRepository
into the production code. For example, make it possible for tests to inject their own _sourceRepositoryCache
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically this is probably a missing test in list package's original implementation, but I can't find a test that validates expected behaviour when two package/audit sources list the same vulnerability. Does it get de-duplicated, or does the same advisory get reported to the customer multiple times?
}"; | ||
if (!Directory.Exists(projectFolder)) | ||
{ | ||
Directory.CreateDirectory(projectFolder); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Directory.CreateDirectory
does not throw if the path already exists, and considering SimpleTestPathContext
creates a random directory for the test to run in, we know that this directory does not exist.
This test is very long, making it hard to review for correctness, so little things like removing unnecessary if blocks can help shorten it a bit.
Assert.Equal(1, result.Item2.Projects.First().TargetFrameworkPackages.First().TopLevelPackages.Count); | ||
Assert.Equal(1, result.Item2.Projects.First().TargetFrameworkPackages.First().TopLevelPackages.First().Vulnerabilities.Count); | ||
Assert.Equal(severity, result.Item2.Projects[0].TargetFrameworkPackages[0].TopLevelPackages.First().Vulnerabilities.First().Severity); | ||
Assert.Equal(advisoryUrl, result.Item2.Projects[0].TargetFrameworkPackages[0].TopLevelPackages.First().Vulnerabilities.First().AdvisoryUrl.ToString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consider using FluentAssertions when writing new tests. Personally, I think their exception messages are better than XUnit's Assert methods.
Bug
Fixes: NuGet/Home#13767
Description
This PR updates
dotnet list package --vulnerable
to use user configured<AuditSources>
.Currently, the command only looks into
<PackageSources>
to load vulnerability data. However, with the introduction of NuGet Audit, other commands now support<AuditSources>
to specify vulnerability data sources. This PR makes suredotnet list package --vulnerable
is also up to date and supports<AuditSources>
In order to do a manual test, I specified a package that has only one vulnerability data source. That source is only specified as an Audit source. This is what running
dotnet list package --vulnerable
results in before and after this PRBefore
After
PR Checklist
dotnet list package --vulnerable
uses AuditSources Home#14021