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