From a6685b1bad2907496b83f1ca789e1db8cc06af9d Mon Sep 17 00:00:00 2001 From: afdesk Date: Mon, 11 Jul 2022 16:32:38 +0600 Subject: [PATCH] feat(dotnet): add support for .Net core .deps.json files (#2487) Co-authored-by: DmitriyLewen Co-authored-by: knqyf263 --- docs/docs/vulnerability/detection/language.md | 41 ++++---- pkg/detector/library/driver.go | 2 +- pkg/fanal/analyzer/all/import.go | 1 + pkg/fanal/analyzer/const.go | 3 +- .../analyzer/language/dotnet/deps/deps.go | 47 +++++++++ .../language/dotnet/deps/deps_test.go | 95 +++++++++++++++++++ .../deps/testdata/datacollector.deps.json | 21 ++++ .../language/dotnet/deps/testdata/invalid.txt | 1 + pkg/fanal/types/const.go | 1 + 9 files changed, 190 insertions(+), 22 deletions(-) create mode 100644 pkg/fanal/analyzer/language/dotnet/deps/deps.go create mode 100644 pkg/fanal/analyzer/language/dotnet/deps/deps_test.go create mode 100644 pkg/fanal/analyzer/language/dotnet/deps/testdata/datacollector.deps.json create mode 100644 pkg/fanal/analyzer/language/dotnet/deps/testdata/invalid.txt diff --git a/docs/docs/vulnerability/detection/language.md b/docs/docs/vulnerability/detection/language.md index 45bd39d12c17..bcfcfe8f26fd 100644 --- a/docs/docs/vulnerability/detection/language.md +++ b/docs/docs/vulnerability/detection/language.md @@ -2,27 +2,28 @@ `Trivy` automatically detects the following files in the container and scans vulnerabilities in the application dependencies. -| Language | File | Image[^8] | Rootfs[^9] | Filesystem[^10] | Repository[^11] | Dev dependencies | -| -------- | ------------------------ | :-------: | :--------: | :-------------: | :-------------: | ---------------- | -| Ruby | Gemfile.lock | - | - | ✅ | ✅ | included | -| | gemspec | ✅ | ✅ | - | - | included | -| Python | Pipfile.lock | - | - | ✅ | ✅ | excluded | -| | poetry.lock | - | - | ✅ | ✅ | included | -| | requirements.txt | - | - | ✅ | ✅ | included | -| | egg package[^1] | ✅ | ✅ | - | - | excluded | -| | wheel package[^2] | ✅ | ✅ | - | - | excluded | -| PHP | composer.lock | ✅ | ✅ | ✅ | ✅ | excluded | -| Node.js | package-lock.json | - | - | ✅ | ✅ | excluded | -| | yarn.lock | - | - | ✅ | ✅ | included | -| | pnpm-lock.yaml | - | - | ✅ | ✅ | excluded | -| | package.json | ✅ | ✅ | - | - | excluded | -| .NET | packages.lock.json | ✅ | ✅ | ✅ | ✅ | included | -| | packages.config | ✅ | ✅ | ✅ | ✅ | excluded | -| Java | JAR/WAR/PAR/EAR[^3][^4] | ✅ | ✅ | - | - | included | -| | pom.xml[^5] | - | - | ✅ | ✅ | excluded | +| Language | File | Image[^8] | Rootfs[^9] | Filesystem[^10] | Repository[^11] | Dev dependencies | +| -------- |-------------------------| :-------: | :--------: | :-------------: | :-------------: | ---------------- | +| Ruby | Gemfile.lock | - | - | ✅ | ✅ | included | +| | gemspec | ✅ | ✅ | - | - | included | +| Python | Pipfile.lock | - | - | ✅ | ✅ | excluded | +| | poetry.lock | - | - | ✅ | ✅ | included | +| | requirements.txt | - | - | ✅ | ✅ | included | +| | egg package[^1] | ✅ | ✅ | - | - | excluded | +| | wheel package[^2] | ✅ | ✅ | - | - | excluded | +| PHP | composer.lock | ✅ | ✅ | ✅ | ✅ | excluded | +| Node.js | package-lock.json | - | - | ✅ | ✅ | excluded | +| | yarn.lock | - | - | ✅ | ✅ | included | +| | pnpm-lock.yaml | - | - | ✅ | ✅ | excluded | +| | package.json | ✅ | ✅ | - | - | excluded | +| .NET | packages.lock.json | ✅ | ✅ | ✅ | ✅ | included | +| | packages.config | ✅ | ✅ | ✅ | ✅ | excluded | +| | .deps.json | ✅ | ✅ | ✅ | ✅ | excluded | +| Java | JAR/WAR/PAR/EAR[^3][^4] | ✅ | ✅ | - | - | included | +| | pom.xml[^5] | - | - | ✅ | ✅ | excluded | | Go | Binaries built by Go[^6] | ✅ | ✅ | - | - | excluded | -| | go.mod[^7] | - | - | ✅ | ✅ | included | -| Rust | Cargo.lock | ✅ | ✅ | ✅ | ✅ | included | +| | go.mod[^7] | - | - | ✅ | ✅ | included | +| Rust | Cargo.lock | ✅ | ✅ | ✅ | ✅ | included | The path of these files does not matter. diff --git a/pkg/detector/library/driver.go b/pkg/detector/library/driver.go index 03826fbb40f9..561b83d22e83 100644 --- a/pkg/detector/library/driver.go +++ b/pkg/detector/library/driver.go @@ -43,7 +43,7 @@ func NewDriver(libType string) (Driver, error) { case ftypes.Npm, ftypes.Yarn, ftypes.Pnpm, ftypes.NodePkg, ftypes.JavaScript: ecosystem = vulnerability.Npm comparer = npm.Comparer{} - case ftypes.NuGet: + case ftypes.NuGet, ftypes.DotNetCore: ecosystem = vulnerability.NuGet comparer = compare.GenericComparer{} case ftypes.Pipenv, ftypes.Poetry, ftypes.Pip, ftypes.PythonPkg: diff --git a/pkg/fanal/analyzer/all/import.go b/pkg/fanal/analyzer/all/import.go index fa36fb6c144e..27a8d442f216 100644 --- a/pkg/fanal/analyzer/all/import.go +++ b/pkg/fanal/analyzer/all/import.go @@ -3,6 +3,7 @@ package all import ( _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/buildinfo" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/command/apk" + _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/dotnet/deps" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/dotnet/nuget" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/golang/binary" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/golang/mod" diff --git a/pkg/fanal/analyzer/const.go b/pkg/fanal/analyzer/const.go index 0f33598c439d..3476210d5d25 100644 --- a/pkg/fanal/analyzer/const.go +++ b/pkg/fanal/analyzer/const.go @@ -56,7 +56,8 @@ const ( TypePnpm Type = "pnpm" // .NET - TypeNuget Type = "nuget" + TypeNuget Type = "nuget" + TypeDotNetDeps Type = "dotnet-deps" // Python TypePythonPkg Type = "python-pkg" diff --git a/pkg/fanal/analyzer/language/dotnet/deps/deps.go b/pkg/fanal/analyzer/language/dotnet/deps/deps.go new file mode 100644 index 000000000000..4fb4a6260cb3 --- /dev/null +++ b/pkg/fanal/analyzer/language/dotnet/deps/deps.go @@ -0,0 +1,47 @@ +package deps + +import ( + "context" + "os" + "strings" + + "golang.org/x/xerrors" + + core "github.com/aquasecurity/go-dep-parser/pkg/dotnet/core_deps" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language" + "github.com/aquasecurity/trivy/pkg/fanal/types" +) + +func init() { + analyzer.RegisterAnalyzer(&depsLibraryAnalyzer{}) +} + +const ( + version = 1 + depsExtension = ".deps.json" +) + +type depsLibraryAnalyzer struct{} + +func (a depsLibraryAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) { + parser := core.NewParser() + res, err := language.Analyze(types.DotNetCore, input.FilePath, input.Content, parser) + if err != nil { + return nil, xerrors.Errorf(".Net Core dependencies analysis error: %w", err) + } + + return res, nil +} + +func (a depsLibraryAnalyzer) Required(filePath string, _ os.FileInfo) bool { + return strings.HasSuffix(filePath, depsExtension) +} + +func (a depsLibraryAnalyzer) Type() analyzer.Type { + return analyzer.TypeDotNetDeps +} + +func (a depsLibraryAnalyzer) Version() int { + return version +} diff --git a/pkg/fanal/analyzer/language/dotnet/deps/deps_test.go b/pkg/fanal/analyzer/language/dotnet/deps/deps_test.go new file mode 100644 index 000000000000..5b70810f93bb --- /dev/null +++ b/pkg/fanal/analyzer/language/dotnet/deps/deps_test.go @@ -0,0 +1,95 @@ +package deps + +import ( + "context" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/fanal/types" +) + +func Test_depsLibraryAnalyzer_Analyze(t *testing.T) { + tests := []struct { + name string + inputFile string + want *analyzer.AnalysisResult + wantErr string + }{ + { + name: "happy path", + inputFile: "testdata/datacollector.deps.json", + want: &analyzer.AnalysisResult{ + Applications: []types.Application{ + { + Type: types.DotNetCore, + FilePath: "testdata/datacollector.deps.json", + Libraries: []types.Package{ + { + Name: "Newtonsoft.Json", + Version: "9.0.1", + }, + }, + }, + }, + }, + }, + { + name: "sad path", + inputFile: "testdata/invalid.txt", + wantErr: ".Net Core dependencies analysis error", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f, err := os.Open(tt.inputFile) + require.NoError(t, err) + defer f.Close() + + a := depsLibraryAnalyzer{} + ctx := context.Background() + got, err := a.Analyze(ctx, analyzer.AnalysisInput{ + FilePath: tt.inputFile, + Content: f, + }) + + if tt.wantErr != "" { + require.NotNil(t, err) + assert.Contains(t, err.Error(), tt.wantErr) + return + } + + assert.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} + +func Test_depsLibraryAnalyzer_Required(t *testing.T) { + tests := []struct { + name string + filePath string + want bool + }{ + { + name: "config", + filePath: "test/datacollector.deps.json", + want: true, + }, + { + name: "zip", + filePath: "test.zip", + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := depsLibraryAnalyzer{} + got := a.Required(tt.filePath, nil) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/pkg/fanal/analyzer/language/dotnet/deps/testdata/datacollector.deps.json b/pkg/fanal/analyzer/language/dotnet/deps/testdata/datacollector.deps.json new file mode 100644 index 000000000000..08699fee4a7a --- /dev/null +++ b/pkg/fanal/analyzer/language/dotnet/deps/testdata/datacollector.deps.json @@ -0,0 +1,21 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v2.1", + "signature": "" + }, + "compilationOptions": {}, + "libraries": { + "Newtonsoft.Json/9.0.1": { + "type": "package", + "serviceable": true, + "sha512": "sha512-U82mHQSKaIk+lpSVCbWYKNavmNH1i5xrExDEquU1i6I5pV6UMOqRnJRSlKO3cMPfcpp0RgDY+8jUXHdQ4IfXvw==", + "path": "newtonsoft.json/9.0.1", + "hashPath": "newtonsoft.json.9.0.1.nupkg.sha512" + }, + "Microsoft.VisualStudio.TestPlatform.Common/17.2.0-release-20220408-11": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/pkg/fanal/analyzer/language/dotnet/deps/testdata/invalid.txt b/pkg/fanal/analyzer/language/dotnet/deps/testdata/invalid.txt new file mode 100644 index 000000000000..9daeafb9864c --- /dev/null +++ b/pkg/fanal/analyzer/language/dotnet/deps/testdata/invalid.txt @@ -0,0 +1 @@ +test diff --git a/pkg/fanal/types/const.go b/pkg/fanal/types/const.go index 1c1592872523..bfd61826af76 100644 --- a/pkg/fanal/types/const.go +++ b/pkg/fanal/types/const.go @@ -13,6 +13,7 @@ const ( Composer = "composer" Npm = "npm" NuGet = "nuget" + DotNetCore = "dotnet-core" Pip = "pip" Pipenv = "pipenv" Poetry = "poetry"