Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for gradle.lockfile #2759

Merged
merged 10 commits into from Sep 1, 2022
45 changes: 23 additions & 22 deletions docs/docs/vulnerability/detection/language.md
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -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-20220819065825-29e1e04fb7ae
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
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Expand Up @@ -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-20220819065825-29e1e04fb7ae h1:1WdRZrDTkXHC5deeJhatiP3IUHHqdIo/dZlagTtlU8g=
github.com/aquasecurity/go-dep-parser v0.0.0-20220819065825-29e1e04fb7ae/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=
Expand Down
2 changes: 1 addition & 1 deletion pkg/detector/library/driver.go
Expand Up @@ -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.GradleLock:
ecosystem = vulnerability.Maven
comparer = maven.Comparer{}
case ftypes.Npm, ftypes.Yarn, ftypes.Pnpm, ftypes.NodePkg, ftypes.JavaScript:
Expand Down
1 change: 1 addition & 0 deletions pkg/fanal/analyzer/all/import.go
Expand Up @@ -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"
Expand Down
9 changes: 5 additions & 4 deletions pkg/fanal/analyzer/const.go
Expand Up @@ -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"
Expand Down Expand Up @@ -113,15 +114,15 @@ 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,
}

// 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
Expand Down
47 changes: 47 additions & 0 deletions 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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see on GitHub, that user have lock files with another names. is it ok?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is suffix of filename.
If I understand correctly - we need to check only this suffix.

return strings.HasSuffix(filepath.Base(filePath), fileNameSuffix)

)

// 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.GradleLock, 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
}
106 changes: 106 additions & 0 deletions 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.GradleLock,
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)
})
}
}
@@ -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=
@@ -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=
1 change: 1 addition & 0 deletions pkg/fanal/types/const.go
Expand Up @@ -23,6 +23,7 @@ const (
Pnpm = "pnpm"
Jar = "jar"
Pom = "pom"
GradleLock = "gradle-lock"
Copy link
Collaborator

@knqyf263 knqyf263 Aug 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Gradle is enough. Other languages don't mention if it is a lock file.

Suggested change
GradleLock = "gradle-lock"
Gradle = "gradle"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

GoBinary = "gobinary"
GoModule = "gomod"
JavaScript = "javascript"
Expand Down