-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support parsing NuGet
packages.lock.json
lockfiles (#165)
- Loading branch information
Showing
17 changed files
with
374 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"version": 0, | ||
"dependencies": {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"version": 1, | ||
"dependencies": {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
this is not json! |
13 changes: 13 additions & 0 deletions
13
pkg/lockfile/fixtures/nuget/one-framework-one-package.v1.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"version": 1, | ||
"dependencies": { | ||
"net6.0": { | ||
"Test.Core": { | ||
"type": "Direct", | ||
"requested": "[6.0.5, )", | ||
"resolved": "6.0.5", | ||
"contentHash": "FwdQVtpj34xt8vKyFUUeNIS+obWlEnSrSW7y1ivRVts/ZsrUsKyOd0bZehgFhWdnB/NBsa9DCWvNFMTO0XDFcg==" | ||
} | ||
} | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
pkg/lockfile/fixtures/nuget/one-framework-two-packages.v1.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
{ | ||
"version": 1, | ||
"dependencies": { | ||
"net6.0": { | ||
"Test.Core": { | ||
"type": "Direct", | ||
"requested": "[6.0.5, )", | ||
"resolved": "6.0.5", | ||
"contentHash": "FwdQVtpj34xt8vKyFUUeNIS+obWlEnSrSW7y1ivRVts/ZsrUsKyOd0bZehgFhWdnB/NBsa9DCWvNFMTO0XDFcg==" | ||
}, | ||
"Test.System": { | ||
"type": "Direct", | ||
"requested": "[0.13.0-beta4, )", | ||
"resolved": "0.13.0-beta4", | ||
"contentHash": "5r9yBPe7XOnb4zAQYzyvlt85dpuIJQkPJYEns5hpfv/JbC4uBHVqnrzqiPlTiaWEcXFgmDjjh0ihVB0vvChuCQ==", | ||
"dependencies": { | ||
"Test.Core": "6.0.0" | ||
} | ||
} | ||
} | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
pkg/lockfile/fixtures/nuget/two-frameworks-different-packages.v1.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"version": 1, | ||
"dependencies": { | ||
"net6.0": { | ||
"Test.Core": { | ||
"type": "Direct", | ||
"requested": "[6.0.5, )", | ||
"resolved": "6.0.5", | ||
"contentHash": "FwdQVtpj34xt8vKyFUUeNIS+obWlEnSrSW7y1ivRVts/ZsrUsKyOd0bZehgFhWdnB/NBsa9DCWvNFMTO0XDFcg==" | ||
} | ||
}, | ||
"net7.0": { | ||
"Test.System": { | ||
"type": "Direct", | ||
"requested": "[0.13.0-beta4, )", | ||
"resolved": "0.13.0-beta4", | ||
"contentHash": "5r9yBPe7XOnb4zAQYzyvlt85dpuIJQkPJYEns5hpfv/JbC4uBHVqnrzqiPlTiaWEcXFgmDjjh0ihVB0vvChuCQ==" | ||
} | ||
} | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
pkg/lockfile/fixtures/nuget/two-frameworks-duplicate-packages.v1.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"version": 1, | ||
"dependencies": { | ||
"net6.0": { | ||
"Test.Core": { | ||
"type": "Direct", | ||
"requested": "[6.0.5, )", | ||
"resolved": "6.0.5", | ||
"contentHash": "FwdQVtpj34xt8vKyFUUeNIS+obWlEnSrSW7y1ivRVts/ZsrUsKyOd0bZehgFhWdnB/NBsa9DCWvNFMTO0XDFcg==" | ||
} | ||
}, | ||
"net7.0": { | ||
"Test.Core": { | ||
"type": "Direct", | ||
"requested": "[6.0.5, )", | ||
"resolved": "6.0.5", | ||
"contentHash": "FwdQVtpj34xt8vKyFUUeNIS+obWlEnSrSW7y1ivRVts/ZsrUsKyOd0bZehgFhWdnB/NBsa9DCWvNFMTO0XDFcg==" | ||
} | ||
} | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
pkg/lockfile/fixtures/nuget/two-frameworks-mixed-packages.v1.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
{ | ||
"version": 1, | ||
"dependencies": { | ||
"net6.0": { | ||
"Test.Core": { | ||
"type": "Direct", | ||
"requested": "[6.0.5, )", | ||
"resolved": "6.0.5", | ||
"contentHash": "FwdQVtpj34xt8vKyFUUeNIS+obWlEnSrSW7y1ivRVts/ZsrUsKyOd0bZehgFhWdnB/NBsa9DCWvNFMTO0XDFcg==" | ||
}, | ||
"Test.System": { | ||
"type": "Direct", | ||
"requested": "[0.13.0-beta4, )", | ||
"resolved": "0.13.0-beta4", | ||
"contentHash": "5r9yBPe7XOnb4zAQYzyvlt85dpuIJQkPJYEns5hpfv/JbC4uBHVqnrzqiPlTiaWEcXFgmDjjh0ihVB0vvChuCQ==", | ||
"dependencies": { | ||
"Test.Core": "6.0.0" | ||
} | ||
} | ||
}, | ||
"net7.0": { | ||
"Test.Core": { | ||
"type": "Direct", | ||
"requested": "[6.0.5, )", | ||
"resolved": "6.0.5", | ||
"contentHash": "FwdQVtpj34xt8vKyFUUeNIS+obWlEnSrSW7y1ivRVts/ZsrUsKyOd0bZehgFhWdnB/NBsa9DCWvNFMTO0XDFcg==" | ||
}, | ||
"Test.System": { | ||
"type": "Direct", | ||
"requested": "[2.15.0, )", | ||
"resolved": "2.15.0", | ||
"contentHash": "t85dpuIJQkPJYEns5hpfv5r9yBPe7XOnb4zAQYzyvl/cXFgmDjjh0ihVB0vvChuCQJbC4uBHVqnrzqiPlTiaWE==", | ||
"dependencies": { | ||
"Test.Core": "6.0.0" | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
package lockfile_test | ||
|
||
import ( | ||
"github.com/g-rath/osv-detector/pkg/lockfile" | ||
"testing" | ||
) | ||
|
||
func TestParseNuGetLock_v1_FileDoesNotExist(t *testing.T) { | ||
t.Parallel() | ||
|
||
packages, err := lockfile.ParseNuGetLock("fixtures/nuget/does-not-exist") | ||
|
||
expectErrContaining(t, err, "could not read") | ||
expectPackages(t, packages, []lockfile.PackageDetails{}) | ||
} | ||
|
||
func TestParseNuGetLock_v1_InvalidJson(t *testing.T) { | ||
t.Parallel() | ||
|
||
packages, err := lockfile.ParseNuGetLock("fixtures/nuget/not-json.txt") | ||
|
||
expectErrContaining(t, err, "could not parse") | ||
expectPackages(t, packages, []lockfile.PackageDetails{}) | ||
} | ||
|
||
func TestParseNuGetLock_v1_NoPackages(t *testing.T) { | ||
t.Parallel() | ||
|
||
packages, err := lockfile.ParseNuGetLock("fixtures/nuget/empty.v1.json") | ||
|
||
if err != nil { | ||
t.Errorf("Got unexpected error: %v", err) | ||
} | ||
|
||
expectPackages(t, packages, []lockfile.PackageDetails{}) | ||
} | ||
|
||
func TestParseNuGetLock_v1_OneFramework_OnePackage(t *testing.T) { | ||
t.Parallel() | ||
|
||
packages, err := lockfile.ParseNuGetLock("fixtures/nuget/one-framework-one-package.v1.json") | ||
|
||
if err != nil { | ||
t.Errorf("Got unexpected error: %v", err) | ||
} | ||
|
||
expectPackages(t, packages, []lockfile.PackageDetails{ | ||
{ | ||
Name: "Test.Core", | ||
Version: "6.0.5", | ||
Ecosystem: lockfile.NuGetEcosystem, | ||
CompareAs: lockfile.NuGetEcosystem, | ||
}, | ||
}) | ||
} | ||
|
||
func TestParseNuGetLock_v1_OneFramework_TwoPackages(t *testing.T) { | ||
t.Parallel() | ||
|
||
packages, err := lockfile.ParseNuGetLock("fixtures/nuget/one-framework-two-packages.v1.json") | ||
|
||
if err != nil { | ||
t.Errorf("Got unexpected error: %v", err) | ||
} | ||
|
||
expectPackages(t, packages, []lockfile.PackageDetails{ | ||
{ | ||
Name: "Test.Core", | ||
Version: "6.0.5", | ||
Ecosystem: lockfile.NuGetEcosystem, | ||
CompareAs: lockfile.NuGetEcosystem, | ||
}, | ||
{ | ||
Name: "Test.System", | ||
Version: "0.13.0-beta4", | ||
Ecosystem: lockfile.NuGetEcosystem, | ||
CompareAs: lockfile.NuGetEcosystem, | ||
}, | ||
}) | ||
} | ||
|
||
func TestParseNuGetLock_v1_TwoFrameworks_MixedPackages(t *testing.T) { | ||
t.Parallel() | ||
|
||
packages, err := lockfile.ParseNuGetLock("fixtures/nuget/two-frameworks-mixed-packages.v1.json") | ||
|
||
if err != nil { | ||
t.Errorf("Got unexpected error: %v", err) | ||
} | ||
|
||
expectPackages(t, packages, []lockfile.PackageDetails{ | ||
{ | ||
Name: "Test.Core", | ||
Version: "6.0.5", | ||
Ecosystem: lockfile.NuGetEcosystem, | ||
CompareAs: lockfile.NuGetEcosystem, | ||
}, | ||
{ | ||
Name: "Test.System", | ||
Version: "0.13.0-beta4", | ||
Ecosystem: lockfile.NuGetEcosystem, | ||
CompareAs: lockfile.NuGetEcosystem, | ||
}, | ||
{ | ||
Name: "Test.System", | ||
Version: "2.15.0", | ||
Ecosystem: lockfile.NuGetEcosystem, | ||
CompareAs: lockfile.NuGetEcosystem, | ||
}, | ||
}) | ||
} | ||
|
||
func TestParseNuGetLock_v1_TwoFrameworks_DifferentPackages(t *testing.T) { | ||
t.Parallel() | ||
|
||
packages, err := lockfile.ParseNuGetLock("fixtures/nuget/two-frameworks-different-packages.v1.json") | ||
|
||
if err != nil { | ||
t.Errorf("Got unexpected error: %v", err) | ||
} | ||
|
||
expectPackages(t, packages, []lockfile.PackageDetails{ | ||
{ | ||
Name: "Test.Core", | ||
Version: "6.0.5", | ||
Ecosystem: lockfile.NuGetEcosystem, | ||
CompareAs: lockfile.NuGetEcosystem, | ||
}, | ||
{ | ||
Name: "Test.System", | ||
Version: "0.13.0-beta4", | ||
Ecosystem: lockfile.NuGetEcosystem, | ||
CompareAs: lockfile.NuGetEcosystem, | ||
}, | ||
}) | ||
} | ||
|
||
func TestParseNuGetLock_v1_TwoFrameworks_DuplicatePackages(t *testing.T) { | ||
t.Parallel() | ||
|
||
packages, err := lockfile.ParseNuGetLock("fixtures/nuget/two-frameworks-duplicate-packages.v1.json") | ||
|
||
if err != nil { | ||
t.Errorf("Got unexpected error: %v", err) | ||
} | ||
|
||
expectPackages(t, packages, []lockfile.PackageDetails{ | ||
{ | ||
Name: "Test.Core", | ||
Version: "6.0.5", | ||
Ecosystem: lockfile.NuGetEcosystem, | ||
CompareAs: lockfile.NuGetEcosystem, | ||
}, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package lockfile | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"os" | ||
) | ||
|
||
type NuGetLockPackage struct { | ||
Resolved string `json:"resolved"` | ||
} | ||
|
||
// NuGetLockfile contains the required dependency information as defined in | ||
// https://github.com/NuGet/NuGet.Client/blob/6.5.0.136/src/NuGet.Core/NuGet.ProjectModel/ProjectLockFile/PackagesLockFileFormat.cs | ||
type NuGetLockfile struct { | ||
Version int `json:"version"` | ||
Dependencies map[string]map[string]NuGetLockPackage `json:"dependencies"` | ||
} | ||
|
||
const NuGetEcosystem Ecosystem = "NuGet" | ||
|
||
func parseNuGetLockDependencies(dependencies map[string]NuGetLockPackage) map[string]PackageDetails { | ||
details := map[string]PackageDetails{} | ||
|
||
for name, dependency := range dependencies { | ||
details[name+"@"+dependency.Resolved] = PackageDetails{ | ||
Name: name, | ||
Version: dependency.Resolved, | ||
Ecosystem: NuGetEcosystem, | ||
CompareAs: NuGetEcosystem, | ||
} | ||
} | ||
|
||
return details | ||
} | ||
|
||
func parseNuGetLock(lockfile NuGetLockfile) ([]PackageDetails, error) { | ||
details := map[string]PackageDetails{} | ||
|
||
// go through the dependencies for each framework, e.g. `net6.0` and parse | ||
// its dependencies, there might be different or duplicate dependencies | ||
// between frameworks | ||
for _, dependencies := range lockfile.Dependencies { | ||
details = mergePkgDetailsMap(details, parseNuGetLockDependencies(dependencies)) | ||
} | ||
|
||
return pkgDetailsMapToSlice(details), nil | ||
} | ||
|
||
var ErrNuGetUnsupportedLockfileVersion = errors.New("unsupported lockfile version") | ||
|
||
func ParseNuGetLock(pathToLockfile string) ([]PackageDetails, error) { | ||
var parsedLockfile *NuGetLockfile | ||
|
||
lockfileContents, err := os.ReadFile(pathToLockfile) | ||
|
||
if err != nil { | ||
return []PackageDetails{}, fmt.Errorf("could not read %s: %w", pathToLockfile, err) | ||
} | ||
|
||
err = json.Unmarshal(lockfileContents, &parsedLockfile) | ||
|
||
if err != nil { | ||
return []PackageDetails{}, fmt.Errorf("could not parse %s: %w", pathToLockfile, err) | ||
} | ||
|
||
if parsedLockfile.Version != 1 { | ||
return []PackageDetails{}, fmt.Errorf("could not parse %s: %w", pathToLockfile, ErrNuGetUnsupportedLockfileVersion) | ||
} | ||
|
||
return parseNuGetLock(*parsedLockfile) | ||
} |
Oops, something went wrong.