From 8af5215bc5fecb58a86e35c8e85fa6279be34235 Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Fri, 13 Sep 2024 14:46:26 +0600 Subject: [PATCH 1/9] test: add unit test --- pkg/dependency/parser/java/pom/parse_test.go | 24 +++++++++++++++++++ .../pom/testdata/dep-without-version/pom.xml | 24 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 pkg/dependency/parser/java/pom/testdata/dep-without-version/pom.xml diff --git a/pkg/dependency/parser/java/pom/parse_test.go b/pkg/dependency/parser/java/pom/parse_test.go index 934085d5d536..fed0a7d7f924 100644 --- a/pkg/dependency/parser/java/pom/parse_test.go +++ b/pkg/dependency/parser/java/pom/parse_test.go @@ -1499,6 +1499,30 @@ func TestPom_Parse(t *testing.T) { }, }, }, + { + name: "dependency without version", + inputFile: filepath.Join("testdata", "dep-without-version", "pom.xml"), + local: true, + want: []ftypes.Package{ + { + ID: "com.example:dep-without-version:1.0.0", + Name: "com.example:dep-without-version", + Version: "1.0.0", + Relationship: ftypes.RelationshipRoot, + }, + { + ID: "org.example:example-api", + Name: "org.example:example-api", + Relationship: ftypes.RelationshipDirect, + Locations: ftypes.Locations{ + { + StartLine: 19, + EndLine: 22, + }, + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/dependency/parser/java/pom/testdata/dep-without-version/pom.xml b/pkg/dependency/parser/java/pom/testdata/dep-without-version/pom.xml new file mode 100644 index 000000000000..d7da70dd4735 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/dep-without-version/pom.xml @@ -0,0 +1,24 @@ + + 4.0.0 + + no-parent + Parent not found + + + com.example + wrong-parent + 1.0.0 + + + com.example + dep-without-version + 1.0.0 + + + + org.example + example-api + + + From ad79c37e8b0c356dd8f0d5a382eccdcafaec0322 Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Mon, 16 Sep 2024 11:32:26 +0600 Subject: [PATCH 2/9] refactor: save deps without version --- pkg/dependency/parser/java/pom/artifact.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/dependency/parser/java/pom/artifact.go b/pkg/dependency/parser/java/pom/artifact.go index b2e97efb229b..3b31fb834e73 100644 --- a/pkg/dependency/parser/java/pom/artifact.go +++ b/pkg/dependency/parser/java/pom/artifact.go @@ -42,7 +42,7 @@ func newArtifact(groupID, artifactID, version string, licenses []string, props m } func (a artifact) IsEmpty() bool { - return a.GroupID == "" || a.ArtifactID == "" || a.Version.String() == "" + return a.GroupID == "" || a.ArtifactID == "" } func (a artifact) Equal(o artifact) bool { From 73f29fa312943e1e404a0e96975c12779cc5387e Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Tue, 17 Sep 2024 10:47:57 +0600 Subject: [PATCH 3/9] test: update tests --- pkg/dependency/parser/java/pom/parse_test.go | 11 +++++++++++ pkg/fanal/analyzer/language/java/pom/pom_test.go | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/pkg/dependency/parser/java/pom/parse_test.go b/pkg/dependency/parser/java/pom/parse_test.go index fed0a7d7f924..db001ac6d19f 100644 --- a/pkg/dependency/parser/java/pom/parse_test.go +++ b/pkg/dependency/parser/java/pom/parse_test.go @@ -809,6 +809,17 @@ func TestPom_Parse(t *testing.T) { Licenses: []string{"Apache 2.0"}, Relationship: ftypes.RelationshipRoot, }, + { + ID: "org.example:example-api", + Name: "org.example:example-api", + Relationship: ftypes.RelationshipDirect, + Locations: []ftypes.Location{ + { + StartLine: 28, + EndLine: 32, + }, + }, + }, }, }, { diff --git a/pkg/fanal/analyzer/language/java/pom/pom_test.go b/pkg/fanal/analyzer/language/java/pom/pom_test.go index b3aaf43a82cf..ea147a4d1141 100644 --- a/pkg/fanal/analyzer/language/java/pom/pom_test.go +++ b/pkg/fanal/analyzer/language/java/pom/pom_test.go @@ -147,6 +147,17 @@ func Test_pomAnalyzer_Analyze(t *testing.T) { Licenses: []string{"Apache-2.0"}, Relationship: types.RelationshipRoot, }, + { + ID: "org.example:example-api", + Name: "org.example:example-api", + Relationship: types.RelationshipDirect, + Locations: []types.Location{ + { + StartLine: 21, + EndLine: 25, + }, + }, + }, }, }, }, From 9c782ef00b47fcf1f1d0acdd6afbe15d38d48955 Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Tue, 17 Sep 2024 11:06:27 +0600 Subject: [PATCH 4/9] docs: add info about empty versions --- docs/docs/coverage/language/java.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/docs/coverage/language/java.md b/docs/docs/coverage/language/java.md index 67cd8c135b9d..0ee39bbd3155 100644 --- a/docs/docs/coverage/language/java.md +++ b/docs/docs/coverage/language/java.md @@ -69,6 +69,16 @@ The vulnerability database will be downloaded anyway. !!! Warning Trivy may skip some dependencies (that were not found on your local machine) when the `--offline-scan` flag is passed. +### empty dependency version +There are cases when Trivy cannot determine the version of dependencies: + +- Unable to determine the version from the parent because the parent is not reachable; +- The dependency uses a [hard requirement][version-requirement] with more than one version. + +In these cases, Trivy uses an empty version for the dependency. + +!!! Warning + Trivy doesn't detect child dependencies for dependencies without a version. ### maven-invoker-plugin Typically, the integration tests directory (`**/[src|target]/it/*/pom.xml`) of [maven-invoker-plugin][maven-invoker-plugin] doesn't contain actual `pom.xml` files and should be skipped to avoid noise. @@ -120,3 +130,4 @@ Make sure that you have cache[^8] directory to find licenses from `*.pom` depend [maven-pom-repos]: https://maven.apache.org/settings.html#repositories [sbt-dependency-lock]: https://stringbean.github.io/sbt-dependency-lock [detection-priority]: ../../scanner/vulnerability.md#detection-priority +[version-requirement]: https://maven.apache.org/pom.html#dependency-version-requirement-specification From 9929396e1772f489554196f5c117a7d2a475c38f Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Tue, 17 Sep 2024 11:18:46 +0600 Subject: [PATCH 5/9] refactor: add warning about empty version --- pkg/dependency/parser/java/pom/artifact.go | 17 +++++++++++++++-- pkg/dependency/parser/java/pom/parse_test.go | 2 ++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/pkg/dependency/parser/java/pom/artifact.go b/pkg/dependency/parser/java/pom/artifact.go index 3b31fb834e73..e965dd1a2935 100644 --- a/pkg/dependency/parser/java/pom/artifact.go +++ b/pkg/dependency/parser/java/pom/artifact.go @@ -6,7 +6,9 @@ import ( "regexp" "slices" "strings" + "sync" + "github.com/aquasecurity/trivy/pkg/version/doc" "github.com/samber/lo" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" @@ -14,7 +16,12 @@ import ( ) var ( - varRegexp = regexp.MustCompile(`\${(\S+?)}`) + varRegexp = regexp.MustCompile(`\${(\S+?)}`) + emptyVersionWarn = sync.OnceFunc(func() { + log.WithPrefix("pom").Warn("Dependency version is empty. Child dependencies will not be found.", + // e.g. https://aquasecurity.github.io/trivy/latest/docs/coverage/language/java/#empty-dependency-version + log.String("details", doc.URL("/docs/coverage/language/java/", "empty-dependency-version"))) + }) ) type artifact struct { @@ -42,7 +49,13 @@ func newArtifact(groupID, artifactID, version string, licenses []string, props m } func (a artifact) IsEmpty() bool { - return a.GroupID == "" || a.ArtifactID == "" + if a.GroupID == "" || a.ArtifactID == "" { + return true + } + if a.Version.String() == "" { + emptyVersionWarn() + } + return false } func (a artifact) Equal(o artifact) bool { diff --git a/pkg/dependency/parser/java/pom/parse_test.go b/pkg/dependency/parser/java/pom/parse_test.go index db001ac6d19f..6329c3a67bb8 100644 --- a/pkg/dependency/parser/java/pom/parse_test.go +++ b/pkg/dependency/parser/java/pom/parse_test.go @@ -7,6 +7,7 @@ import ( "path/filepath" "testing" + "github.com/aquasecurity/trivy/pkg/log" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -1537,6 +1538,7 @@ func TestPom_Parse(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + log.InitLogger(true, false) f, err := os.Open(tt.inputFile) require.NoError(t, err) defer f.Close() From 57bf8326b654e13e7e2661c0fe6bfb78f67a9dc8 Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Tue, 17 Sep 2024 11:32:48 +0600 Subject: [PATCH 6/9] fix: linter errors --- pkg/dependency/parser/java/pom/artifact.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/dependency/parser/java/pom/artifact.go b/pkg/dependency/parser/java/pom/artifact.go index e965dd1a2935..341fa941cd8f 100644 --- a/pkg/dependency/parser/java/pom/artifact.go +++ b/pkg/dependency/parser/java/pom/artifact.go @@ -8,11 +8,11 @@ import ( "strings" "sync" - "github.com/aquasecurity/trivy/pkg/version/doc" "github.com/samber/lo" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/version/doc" ) var ( From 69f5bd506769f42b96a655601accbf9d766e919b Mon Sep 17 00:00:00 2001 From: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Date: Wed, 18 Sep 2024 17:13:14 +0600 Subject: [PATCH 7/9] refactor: update comment Co-authored-by: Teppei Fukuda --- pkg/dependency/parser/java/pom/artifact.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/dependency/parser/java/pom/artifact.go b/pkg/dependency/parser/java/pom/artifact.go index 341fa941cd8f..4dc5311377e2 100644 --- a/pkg/dependency/parser/java/pom/artifact.go +++ b/pkg/dependency/parser/java/pom/artifact.go @@ -18,7 +18,7 @@ import ( var ( varRegexp = regexp.MustCompile(`\${(\S+?)}`) emptyVersionWarn = sync.OnceFunc(func() { - log.WithPrefix("pom").Warn("Dependency version is empty. Child dependencies will not be found.", + log.WithPrefix("pom").Warn("Dependency version cannot be determined. Child dependencies will not be found.", // e.g. https://aquasecurity.github.io/trivy/latest/docs/coverage/language/java/#empty-dependency-version log.String("details", doc.URL("/docs/coverage/language/java/", "empty-dependency-version"))) }) From 9424f139dea73a39b22012de613dff9085efc8f5 Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Mon, 23 Sep 2024 09:37:29 +0600 Subject: [PATCH 8/9] refactor: add debug log for each dependency without version --- pkg/dependency/parser/java/pom/artifact.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/dependency/parser/java/pom/artifact.go b/pkg/dependency/parser/java/pom/artifact.go index 4dc5311377e2..73ebe6b21407 100644 --- a/pkg/dependency/parser/java/pom/artifact.go +++ b/pkg/dependency/parser/java/pom/artifact.go @@ -54,6 +54,10 @@ func (a artifact) IsEmpty() bool { } if a.Version.String() == "" { emptyVersionWarn() + log.WithPrefix("pom").Debug("Dependency version cannot be determined.", + log.String("GroupID", a.GroupID), + log.String("ArtifactID", a.ArtifactID), + ) } return false } From 97c9c1a73a447cf75dcc00f645669e9ef1bdc7a4 Mon Sep 17 00:00:00 2001 From: DmitriyLewen Date: Mon, 23 Sep 2024 09:55:05 +0600 Subject: [PATCH 9/9] refactor: don't resolve dependencies without version --- pkg/dependency/parser/java/pom/parse.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/dependency/parser/java/pom/parse.go b/pkg/dependency/parser/java/pom/parse.go index 76ca37f2dd05..542abcac3c8a 100644 --- a/pkg/dependency/parser/java/pom/parse.go +++ b/pkg/dependency/parser/java/pom/parse.go @@ -292,6 +292,14 @@ func (p *Parser) resolve(art artifact, rootDepManagement []pomDependency) (analy return *result, nil } + // We can't resolve a dependency without a version. + // So let's just keep this dependency. + if art.Version.String() == "" { + return analysisResult{ + artifact: art, + }, nil + } + p.logger.Debug("Resolving...", log.String("group_id", art.GroupID), log.String("artifact_id", art.ArtifactID), log.String("version", art.Version.String())) pomContent, err := p.tryRepository(art.GroupID, art.ArtifactID, art.Version.String())