Add support for Version 2 of Nuget's packages.lock.json in SCA
<!--- Please read this! Before opening a new issue, make sure to search for keywords in the issues filtered by the "regression" or "type::bug" label: - https://gitlab.com/gitlab-org/gitlab/issues?label_name%5B%5D=regression - https://gitlab.com/gitlab-org/gitlab/issues?label_name%5B%5D=type::bug and verify the issue you're about to submit isn't a duplicate. ---> ### Summary When using the Gemnasium dependency scanner with a NuGet `packages.lock.json` in Version 2, the scanner fails with the error ``` [FATA] [Gemnasium] [2023-03-22T03:06:13Z] ▶ scanning file /builds/.../packages.lock.json: parsing file /builds/.../packages.lock.json: wrong file format version ``` Currently only packages.lock.json files in Version 1 are supported ([see documentation](https://docs.gitlab.com/ee/user/application_security/dependency_scanning/index.html#obtaining-dependency-information-by-parsing-lockfiles)). However, NuGet generates version 2 files if the new central package management is enabled. ([NuGet source for reference](https://github.com/NuGet/NuGet.Client/blob/f99e9b890eb66a8b4876aa6f93852717683e1fe4/src/NuGet.Core/NuGet.Commands/PackagesLockFileBuilder.cs#L119-L129)) ### Steps to reproduce 1) Have the the .NET7 SDK installed 2) In an empty directory, run `dotnet new console -f net7.0` 3) Create the file `Directory.Packages.props` with the following contents: ```xml <Project> <PropertyGroup> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> <EnablePackageVersionOverride>false</EnablePackageVersionOverride> </PropertyGroup> </Project> ``` 4) Add any NuGet to the project, eg. EF Core via `dotnet add package Microsoft.EntityFrameworkCore` 5) Create a `packages.lock.json` via `dotnet restore --use-lock-file` The generated packages.lock.json is in version 2 (as seen at the very top in the json) and thus can't be used with the gemnasium dependency scanner, currently. ### What is the current *bug* behavior? Gemnasium dependency scanner is not able to use the packages.lock.json file in version 2, even though v2 is backwards compatible to v1. ### What is the expected *correct* behavior? Gemnasium dependency scanner should be able to use the packages.lock.json in the versions 1 and 2. ### Workaround Replace the version 2 with 1 in the `packages.lock.json` ``` find . -maxdepth 2 -type f -name packages.lock.json -print0 | xargs -0 sed -i 's/"version": 2,/"version": 1,/' ``` ### Output of checks <!-- If you are reporting a bug on GitLab.com, uncomment below --> This bug happens on GitLab.com ### Proposal Make the existing nuget parser capable of parsing nuget `v2` lock files. Lock files in `v2` are backwards compatible with `v1`. ### Implementation Plan Since there is a backwards compatibility the solution is straight forward: - [x] Update the [nuget parser version check](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/scanner/parser/nuget/nuget.go#L57-60) so that it allows `v2` <details> <summary> patch </summary> ```diff diff --git a/scanner/parser/nuget/nuget.go b/scanner/parser/nuget/nuget.go index 10e5754..7a46e20 100644 --- a/scanner/parser/nuget/nuget.go +++ b/scanner/parser/nuget/nuget.go @@ -43,7 +43,17 @@ type DependencyInfo struct { // ContentHash string `json:"contentHash"` } -const supportedFileFormatVersion = 1 +// Returns whether the given lockfiel version can be parsed +func isSupportedVersion(version int) error { + supportedFileFormatVersion := []int{1, 2} + for _, supportedVersion := range supportedFileFormatVersion { + if version == supportedVersion { + return nil + } + } + + return parser.ErrWrongFileFormatVersion +} // Parse scans a NuGet lock file and returns a list of packages func Parse(r io.Reader, opts parser.Options) ([]parser.Package, []parser.Dependency, error) { @@ -55,8 +65,8 @@ func Parse(r io.Reader, opts parser.Options) ([]parser.Package, []parser.Depende } // check format version - if document.Version != supportedFileFormatVersion { - return nil, nil, parser.ErrWrongFileFormatVersion + if err := isSupportedVersion(document.Version); err != nil { + return nil, nil, err } // collect targets like ".NETCoreApp,Version=v5.0" ``` </details> - [x] Update the [nuget parser unit tests](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/scanner/parser/nuget/nuget_test.go) with another case for v2. - [x] Update the [dependency scanning documentation](https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#obtaining-dependency-information-by-parsing-lockfiles) to indicate that nuget v2 lock files are supported. ### Documentation As stated in the implementation plan we need to update the [dependency scanning documentation](https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#obtaining-dependency-information-by-parsing-lockfiles) ### Availability & Testing Our plan is to extend the unit tests to test against a v2 nuget lock file. Since the v2 file is exactly the same as the v1 except the version number, is not worth adding a separate rspec test.
issue