diff --git a/docs/docs/vulnerability/detection/language.md b/docs/docs/vulnerability/detection/language.md index c8737d65a25..81c9c4b1288 100644 --- a/docs/docs/vulnerability/detection/language.md +++ b/docs/docs/vulnerability/detection/language.md @@ -2,28 +2,29 @@ `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 | -| | .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 | +| 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 | +| | *gradle.lockfile | - | - | ✅ | ✅ | excluded | +| Go | Binaries built by Go[^6] | ✅ | ✅ | - | - | excluded | +| | go.mod[^7] | - | - | ✅ | ✅ | included | +| Rust | Cargo.lock | ✅ | ✅ | ✅ | ✅ | included | | | Binaries built with [cargo-auditable](https://github.com/rust-secure-code/cargo-auditable) | ✅ | ✅ | - | - | excluded The path of these files does not matter. diff --git a/go.mod b/go.mod index 7a3203c1d3d..580eea4f998 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/NYTimes/gziphandler v1.1.1 github.com/alicebob/miniredis/v2 v2.22.0 github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 - github.com/aquasecurity/go-dep-parser v0.0.0-20220824115155-600849a106bd + github.com/aquasecurity/go-dep-parser v0.0.0-20220830123424-46cde9383d60 github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798 github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 diff --git a/go.sum b/go.sum index e3c8d89f1ac..af62f096d43 100644 --- a/go.sum +++ b/go.sum @@ -206,8 +206,8 @@ github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 h1:2a30 github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986/go.mod h1:NT+jyeCzXk6vXR5MTkdn4z64TgGfE5HMLC8qfj5unl8= github.com/aquasecurity/defsec v0.71.9 h1:eo244v1RQzziClY9xXyVftPibE0fddXbTtkvH52/slU= github.com/aquasecurity/defsec v0.71.9/go.mod h1:2jYgkIi3UFbkrbtpnr3Cu49JZ3MGuLMJAhyh63jV1I4= -github.com/aquasecurity/go-dep-parser v0.0.0-20220824115155-600849a106bd h1:jgYzIraCXhabEhYOmQRb73/YlyWGaGOktA5ZsZ7+6RE= -github.com/aquasecurity/go-dep-parser v0.0.0-20220824115155-600849a106bd/go.mod h1:6G1Y5nht5TL9kr1SzmrdE8PrmbNXo9nHx3qFR3qURg0= +github.com/aquasecurity/go-dep-parser v0.0.0-20220830123424-46cde9383d60 h1:lBkhapZtunGpC8yu2fjGvGXUNbB2pNgmn5XPuHrPxnw= +github.com/aquasecurity/go-dep-parser v0.0.0-20220830123424-46cde9383d60/go.mod h1:6G1Y5nht5TL9kr1SzmrdE8PrmbNXo9nHx3qFR3qURg0= github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce h1:QgBRgJvtEOBtUXilDb1MLi1p1MWoyFDXAu5DEUl5nwM= github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce/go.mod h1:HXgVzOPvXhVGLJs4ZKO817idqr/xhwsTcj17CLYY74s= github.com/aquasecurity/go-mock-aws v0.0.0-20220726154943-99847deb62b0 h1:tihCUjLWkF0b1SAjAKcFltUs3SpsqGrLtI+Frye0D10= diff --git a/integration/fs_test.go b/integration/fs_test.go index 7eb38944124..b4552c30d5a 100644 --- a/integration/fs_test.go +++ b/integration/fs_test.go @@ -73,6 +73,14 @@ func TestFilesystem(t *testing.T) { }, golden: "testdata/pom.json.golden", }, + { + name: "gradle", + args: args{ + securityChecks: "vuln", + input: "testdata/fixtures/fs/gradle", + }, + golden: "testdata/gradle.json.golden", + }, { name: "dockerfile", args: args{ diff --git a/integration/testdata/fixtures/fs/gradle/gradle.lockfile b/integration/testdata/fixtures/fs/gradle/gradle.lockfile new file mode 100644 index 00000000000..b6ba3d4b8b2 --- /dev/null +++ b/integration/testdata/fixtures/fs/gradle/gradle.lockfile @@ -0,0 +1,5 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +com.fasterxml.jackson.core:jackson-databind:2.9.1=compileClasspath, runtimeClasspath +empty= \ No newline at end of file diff --git a/integration/testdata/gradle.json.golden b/integration/testdata/gradle.json.golden new file mode 100644 index 00000000000..52a97f26b5e --- /dev/null +++ b/integration/testdata/gradle.json.golden @@ -0,0 +1,126 @@ +{ + "SchemaVersion": 2, + "ArtifactName": "testdata/fixtures/fs/gradle", + "ArtifactType": "filesystem", + "Metadata": { + "ImageConfig": { + "architecture": "", + "created": "0001-01-01T00:00:00Z", + "os": "", + "rootfs": { + "type": "", + "diff_ids": null + }, + "config": {} + } + }, + "Results": [ + { + "Target": "gradle.lockfile", + "Class": "lang-pkgs", + "Type": "gradle", + "Vulnerabilities": [ + { + "VulnerabilityID": "CVE-2020-9548", + "PkgName": "com.fasterxml.jackson.core:jackson-databind", + "InstalledVersion": "2.9.1", + "FixedVersion": "2.9.10.4", + "Layer": {}, + "SeveritySource": "ghsa", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2020-9548", + "DataSource": { + "ID": "ghsa", + "Name": "GitHub Security Advisory Maven", + "URL": "https://github.com/advisories?query=type%3Areviewed+ecosystem%3Amaven" + }, + "Title": "jackson-databind: Serialization gadgets in anteros-core", + "Description": "FasterXML jackson-databind 2.x before 2.9.10.4 mishandles the interaction between serialization gadgets and typing, related to br.com.anteros.dbcp.AnterosDBCPConfig (aka anteros-core).", + "Severity": "CRITICAL", + "CweIDs": [ + "CWE-502" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:N/AC:M/Au:N/C:P/I:P/A:P", + "V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", + "V2Score": 6.8, + "V3Score": 9.8 + }, + "redhat": { + "V3Vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H", + "V3Score": 8.1 + } + }, + "References": [ + "https://access.redhat.com/security/cve/CVE-2020-9548", + "https://github.com/FasterXML/jackson-databind/issues/2634", + "https://github.com/advisories/GHSA-p43x-xfjf-5jhr", + "https://lists.apache.org/thread.html/r35d30db00440ef63b791c4b7f7acb036e14d4a23afa2a249cb66c0fd@%3Cissues.zookeeper.apache.org%3E", + "https://lists.apache.org/thread.html/r9464a40d25c3ba1a55622db72f113eb494a889656962d098c70c5bb1@%3Cdev.zookeeper.apache.org%3E", + "https://lists.apache.org/thread.html/r98c9b6e4c9e17792e2cd1ec3e4aa20b61a791939046d3f10888176bb@%3Cissues.zookeeper.apache.org%3E", + "https://lists.apache.org/thread.html/rb6fecb5e96a6d61e175ff49f33f2713798dd05cf03067c169d195596@%3Cissues.zookeeper.apache.org%3E", + "https://lists.apache.org/thread.html/rd5a4457be4623038c3989294429bc063eec433a2e55995d81591e2ca@%3Cissues.zookeeper.apache.org%3E", + "https://lists.apache.org/thread.html/rdd49ab9565bec436a896bc00c4b9fc9dce1598e106c318524fbdfec6@%3Cissues.zookeeper.apache.org%3E", + "https://lists.apache.org/thread.html/rdd4df698d5d8e635144d2994922bf0842e933809eae259521f3b5097@%3Cissues.zookeeper.apache.org%3E", + "https://lists.apache.org/thread.html/rf1bbc0ea4a9f014cf94df9a12a6477d24a27f52741dbc87f2fd52ff2@%3Cissues.geode.apache.org%3E", + "https://lists.debian.org/debian-lts-announce/2020/03/msg00008.html", + "https://medium.com/@cowtowncoder/on-jackson-cves-dont-panic-here-is-what-you-need-to-know-54cd0d6e8062", + "https://nvd.nist.gov/vuln/detail/CVE-2020-9548", + "https://security.netapp.com/advisory/ntap-20200904-0006/", + "https://www.oracle.com/security-alerts/cpujan2021.html", + "https://www.oracle.com/security-alerts/cpujul2020.html", + "https://www.oracle.com/security-alerts/cpuoct2020.html", + "https://www.oracle.com/security-alerts/cpuoct2021.html" + ], + "PublishedDate": "2020-03-02T04:15:00Z", + "LastModifiedDate": "2021-12-02T21:23:00Z" + }, + { + "VulnerabilityID": "CVE-2021-20190", + "PkgName": "com.fasterxml.jackson.core:jackson-databind", + "InstalledVersion": "2.9.1", + "FixedVersion": "2.9.10.7", + "Layer": {}, + "SeveritySource": "nvd", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2021-20190", + "DataSource": { + "ID": "glad", + "Name": "GitLab Advisory Database Community", + "URL": "https://gitlab.com/gitlab-org/advisories-community" + }, + "Title": "jackson-databind: mishandles the interaction between serialization gadgets and typing, related to javax.swing", + "Description": "A flaw was found in jackson-databind before 2.9.10.7. FasterXML mishandles the interaction between serialization gadgets and typing. The highest threat from this vulnerability is to data confidentiality and integrity as well as system availability.", + "Severity": "HIGH", + "CweIDs": [ + "CWE-502" + ], + "CVSS": { + "nvd": { + "V2Vector": "AV:N/AC:M/Au:N/C:P/I:P/A:C", + "V3Vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H", + "V2Score": 8.3, + "V3Score": 8.1 + }, + "redhat": { + "V3Vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H", + "V3Score": 8.1 + } + }, + "References": [ + "https://access.redhat.com/security/cve/CVE-2021-20190", + "https://bugzilla.redhat.com/show_bug.cgi?id=1916633", + "https://github.com/FasterXML/jackson-databind/commit/7dbf51bf78d157098074a20bd9da39bd48c18e4a", + "https://github.com/FasterXML/jackson-databind/issues/2854", + "https://github.com/advisories/GHSA-5949-rw7g-wx7w", + "https://lists.apache.org/thread.html/r380e9257bacb8551ee6fcf2c59890ae9477b2c78e553fa9ea08e9d9a@%3Ccommits.nifi.apache.org%3E", + "https://lists.debian.org/debian-lts-announce/2021/04/msg00025.html", + "https://nvd.nist.gov/vuln/detail/CVE-2021-20190", + "https://security.netapp.com/advisory/ntap-20210219-0008/" + ], + "PublishedDate": "2021-01-19T17:15:00Z", + "LastModifiedDate": "2021-07-20T23:15:00Z" + } + ] + } + ] +} diff --git a/pkg/detector/library/driver.go b/pkg/detector/library/driver.go index 5f1e5652fb2..3cf5439b176 100644 --- a/pkg/detector/library/driver.go +++ b/pkg/detector/library/driver.go @@ -37,7 +37,7 @@ func NewDriver(libType string) (Driver, error) { case ftypes.GoBinary, ftypes.GoModule: ecosystem = vulnerability.Go comparer = compare.GenericComparer{} - case ftypes.Jar, ftypes.Pom: + case ftypes.Jar, ftypes.Pom, ftypes.Gradle: ecosystem = vulnerability.Maven comparer = maven.Comparer{} case ftypes.Npm, ftypes.Yarn, ftypes.Pnpm, ftypes.NodePkg, ftypes.JavaScript: diff --git a/pkg/fanal/analyzer/all/import.go b/pkg/fanal/analyzer/all/import.go index 3fd1b007865..dd3ee0fadee 100644 --- a/pkg/fanal/analyzer/all/import.go +++ b/pkg/fanal/analyzer/all/import.go @@ -7,6 +7,7 @@ import ( _ "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" + _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/java/gradle" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/java/jar" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/java/pom" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/nodejs/npm" diff --git a/pkg/fanal/analyzer/const.go b/pkg/fanal/analyzer/const.go index 8ebdbf311be..33a46cea361 100644 --- a/pkg/fanal/analyzer/const.go +++ b/pkg/fanal/analyzer/const.go @@ -47,8 +47,9 @@ const ( TypeComposer Type = "composer" // Java - TypeJar Type = "jar" - TypePom Type = "pom" + TypeJar Type = "jar" + TypePom Type = "pom" + TypeGradleLock Type = "gradle-lockfile" // Node.js TypeNpmPkgLock Type = "npm" @@ -113,7 +114,7 @@ var ( // TypeLanguages has all language analyzers TypeLanguages = []Type{ - TypeBundler, TypeGemSpec, TypeCargo, TypeComposer, TypeJar, TypePom, + TypeBundler, TypeGemSpec, TypeCargo, TypeComposer, TypeJar, TypePom, TypeGradleLock, TypeNpmPkgLock, TypeNodePkg, TypeYarn, TypePnpm, TypeNuget, TypeDotNetDeps, TypePythonPkg, TypePip, TypePipenv, TypePoetry, TypeGoBinary, TypeGoMod, TypeRustBinary, } @@ -121,7 +122,7 @@ var ( // TypeLockfiles has all lock file analyzers TypeLockfiles = []Type{ TypeBundler, TypeNpmPkgLock, TypeYarn, - TypePnpm, TypePip, TypePipenv, TypePoetry, TypeGoMod, TypePom, + TypePnpm, TypePip, TypePipenv, TypePoetry, TypeGoMod, TypePom, TypeGradleLock, } // TypeIndividualPkgs has all analyzers for individual packages diff --git a/pkg/fanal/analyzer/language/java/gradle/lockfile.go b/pkg/fanal/analyzer/language/java/gradle/lockfile.go new file mode 100644 index 00000000000..dfecac331e5 --- /dev/null +++ b/pkg/fanal/analyzer/language/java/gradle/lockfile.go @@ -0,0 +1,47 @@ +package gradle + +import ( + "context" + "os" + "strings" + + "github.com/aquasecurity/go-dep-parser/pkg/gradle/lockfile" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language" + "github.com/aquasecurity/trivy/pkg/fanal/types" + + "golang.org/x/xerrors" +) + +func init() { + analyzer.RegisterAnalyzer(&gradleLockAnalyzer{}) +} + +const ( + version = 1 + fileNameSuffix = "gradle.lockfile" +) + +// gradleLockAnalyzer analyzes '*gradle.lockfile' +type gradleLockAnalyzer struct{} + +func (a gradleLockAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) { + p := lockfile.NewParser() + res, err := language.Analyze(types.Gradle, input.FilePath, input.Content, p) + if err != nil { + return nil, xerrors.Errorf("%s parse error: %w", input.FilePath, err) + } + return res, nil +} + +func (a gradleLockAnalyzer) Required(_ string, fileInfo os.FileInfo) bool { + return strings.HasSuffix(fileInfo.Name(), fileNameSuffix) +} + +func (a gradleLockAnalyzer) Type() analyzer.Type { + return analyzer.TypeGradleLock +} + +func (a gradleLockAnalyzer) Version() int { + return version +} diff --git a/pkg/fanal/analyzer/language/java/gradle/lockfile_test.go b/pkg/fanal/analyzer/language/java/gradle/lockfile_test.go new file mode 100644 index 00000000000..2e3eafcff4f --- /dev/null +++ b/pkg/fanal/analyzer/language/java/gradle/lockfile_test.go @@ -0,0 +1,106 @@ +package gradle + +import ( + "os" + "path/filepath" + "testing" + + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_gradleLockAnalyzer_Analyze(t *testing.T) { + tests := []struct { + name string + inputFile string + want *analyzer.AnalysisResult + }{ + { + name: "happy path", + inputFile: "testdata/happy.lockfile", + want: &analyzer.AnalysisResult{ + Applications: []types.Application{ + { + Type: types.Gradle, + FilePath: "testdata/happy.lockfile", + Libraries: []types.Package{ + { + Name: "com.example:example", + Version: "0.0.1", + }, + }, + }, + }, + }, + }, + { + name: "empty file", + inputFile: "testdata/empty.lockfile", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f, err := os.Open(tt.inputFile) + require.NoError(t, err) + defer func() { + err = f.Close() + assert.NoError(t, err) + }() + + a := gradleLockAnalyzer{} + got, err := a.Analyze(nil, analyzer.AnalysisInput{ + FilePath: tt.inputFile, + Content: f, + }) + + assert.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} + +func Test_nugetLibraryAnalyzer_Required(t *testing.T) { + tests := []struct { + name string + filePath string + want bool + }{ + { + name: "default name", + filePath: "test/gradle.lockfile", + want: true, + }, + { + name: "name with prefix", + filePath: "test/settings-gradle.lockfile", + want: true, + }, + { + name: "txt", + filePath: "test/test.txt", + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := os.MkdirAll(filepath.Dir(tt.filePath), 0700) + assert.NoError(t, err) + _, err = os.Create(tt.filePath) + assert.NoError(t, err) + defer func() { + err = os.RemoveAll(filepath.Dir(tt.filePath)) + assert.NoError(t, err) + }() + + fileInfo, err := os.Stat(tt.filePath) + assert.NoError(t, err) + + a := gradleLockAnalyzer{} + got := a.Required("", fileInfo) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/pkg/fanal/analyzer/language/java/gradle/testdata/empty.lockfile b/pkg/fanal/analyzer/language/java/gradle/testdata/empty.lockfile new file mode 100644 index 00000000000..138b79c580c --- /dev/null +++ b/pkg/fanal/analyzer/language/java/gradle/testdata/empty.lockfile @@ -0,0 +1,4 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +empty= \ No newline at end of file diff --git a/pkg/fanal/analyzer/language/java/gradle/testdata/happy.lockfile b/pkg/fanal/analyzer/language/java/gradle/testdata/happy.lockfile new file mode 100644 index 00000000000..3b965af3166 --- /dev/null +++ b/pkg/fanal/analyzer/language/java/gradle/testdata/happy.lockfile @@ -0,0 +1,5 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +com.example:example:0.0.1=classpath +empty= \ No newline at end of file diff --git a/pkg/fanal/types/const.go b/pkg/fanal/types/const.go index 6594fd936fc..0280c5ce381 100644 --- a/pkg/fanal/types/const.go +++ b/pkg/fanal/types/const.go @@ -23,6 +23,7 @@ const ( Pnpm = "pnpm" Jar = "jar" Pom = "pom" + Gradle = "gradle" GoBinary = "gobinary" GoModule = "gomod" JavaScript = "javascript"