diff --git a/pkg/dependency/parser/java/pom/parse.go b/pkg/dependency/parser/java/pom/parse.go index 1add19a4b53b..76ca37f2dd05 100644 --- a/pkg/dependency/parser/java/pom/parse.go +++ b/pkg/dependency/parser/java/pom/parse.go @@ -335,8 +335,20 @@ func (p *Parser) analyze(pom *pom, opts analysisOptions) (analysisResult, error) p.releaseRemoteRepos = lo.Uniq(append(pomReleaseRemoteRepos, p.releaseRemoteRepos...)) p.snapshotRemoteRepos = lo.Uniq(append(pomSnapshotRemoteRepos, p.snapshotRemoteRepos...)) + // We need to forward dependencyManagements from current and root pom to Parent, + // to use them for dependencies in parent. + // For better understanding see the following tests: + // - `dependency from parent uses version from child pom depManagement` + // - `dependency from parent uses version from root pom depManagement` + // + // depManagements from root pom has higher priority than depManagements from current pom. + depManagementForParent := lo.UniqBy(append(opts.depManagement, pom.content.DependencyManagement.Dependencies.Dependency...), + func(dep pomDependency) string { + return dep.Name() + }) + // Parent - parent, err := p.parseParent(pom.filePath, pom.content.Parent) + parent, err := p.parseParent(pom.filePath, pom.content.Parent, depManagementForParent) if err != nil { return analysisResult{}, xerrors.Errorf("parent error: %w", err) } @@ -477,7 +489,7 @@ func excludeDep(exclusions map[string]struct{}, art artifact) bool { return false } -func (p *Parser) parseParent(currentPath string, parent pomParent) (analysisResult, error) { +func (p *Parser) parseParent(currentPath string, parent pomParent, rootDepManagement []pomDependency) (analysisResult, error) { // Pass nil properties so that variables in are not evaluated. target := newArtifact(parent.GroupId, parent.ArtifactId, parent.Version, nil, nil) // if version is property (e.g. ${revision}) - we still need to parse this pom @@ -499,7 +511,9 @@ func (p *Parser) parseParent(currentPath string, parent pomParent) (analysisResu logger.Debug("Parent POM not found", log.Err(err)) } - result, err := p.analyze(parentPOM, analysisOptions{}) + result, err := p.analyze(parentPOM, analysisOptions{ + depManagement: rootDepManagement, + }) if err != nil { return analysisResult{}, xerrors.Errorf("analyze error: %w", err) } diff --git a/pkg/dependency/parser/java/pom/parse_test.go b/pkg/dependency/parser/java/pom/parse_test.go index 934085d5d536..9fa97ffd3672 100644 --- a/pkg/dependency/parser/java/pom/parse_test.go +++ b/pkg/dependency/parser/java/pom/parse_test.go @@ -1499,6 +1499,102 @@ func TestPom_Parse(t *testing.T) { }, }, }, + // [INFO] com.example:root-depManagement-in-parent:jar:1.0.0 + // [INFO] \- org.example:example-dependency:jar:2.0.0:compile + // [INFO] \- org.example:example-api:jar:1.0.1:compile + { + name: "dependency from parent uses version from root pom depManagement", + inputFile: filepath.Join("testdata", "use-root-dep-management-in-parent", "pom.xml"), + local: true, + want: []ftypes.Package{ + { + ID: "com.example:root-depManagement-in-parent:1.0.0", + Name: "com.example:root-depManagement-in-parent", + Version: "1.0.0", + Relationship: ftypes.RelationshipRoot, + }, + { + ID: "org.example:example-dependency:2.0.0", + Name: "org.example:example-dependency", + Version: "2.0.0", + Relationship: ftypes.RelationshipDirect, + Locations: ftypes.Locations{ + { + StartLine: 25, + EndLine: 29, + }, + }, + }, + { + ID: "org.example:example-api:1.0.1", + Name: "org.example:example-api", + Version: "1.0.1", + Relationship: ftypes.RelationshipIndirect, + }, + }, + wantDeps: []ftypes.Dependency{ + { + ID: "com.example:root-depManagement-in-parent:1.0.0", + DependsOn: []string{ + "org.example:example-dependency:2.0.0", + }, + }, + { + ID: "org.example:example-dependency:2.0.0", + DependsOn: []string{ + "org.example:example-api:1.0.1", + }, + }, + }, + }, + // [INFO] com.example:root-depManagement-in-parent:jar:1.0.0 + // [INFO] \- org.example:example-dependency:jar:2.0.0:compile + // [INFO] \- org.example:example-api:jar:2.0.1:compile + { + name: "dependency from parent uses version from child pom depManagement", + inputFile: filepath.Join("testdata", "use-dep-management-from-child-in-parent", "pom.xml"), + local: true, + want: []ftypes.Package{ + { + ID: "com.example:root-depManagement-in-parent:1.0.0", + Name: "com.example:root-depManagement-in-parent", + Version: "1.0.0", + Relationship: ftypes.RelationshipRoot, + }, + { + ID: "org.example:example-dependency:2.0.0", + Name: "org.example:example-dependency", + Version: "2.0.0", + Relationship: ftypes.RelationshipDirect, + Locations: ftypes.Locations{ + { + StartLine: 15, + EndLine: 19, + }, + }, + }, + { + ID: "org.example:example-api:2.0.1", + Name: "org.example:example-api", + Version: "2.0.1", + Relationship: ftypes.RelationshipIndirect, + }, + }, + wantDeps: []ftypes.Dependency{ + { + ID: "com.example:root-depManagement-in-parent:1.0.0", + DependsOn: []string{ + "org.example:example-dependency:2.0.0", + }, + }, + { + ID: "org.example:example-dependency:2.0.0", + DependsOn: []string{ + "org.example:example-api:2.0.1", + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-dependency/2.0.0/example-dependency-2.0.0.pom b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-dependency/2.0.0/example-dependency-2.0.0.pom new file mode 100644 index 000000000000..3f236cdf314e --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-dependency/2.0.0/example-dependency-2.0.0.pom @@ -0,0 +1,26 @@ + + + 4.0.0 + + + org.example + example-parent + 3.0.0 + + + org.example + example-dependency + 2.0.0 + + + + + org.example + example-api + 2.0.1 + + + + + \ No newline at end of file diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-parent/3.0.0/example-parent-3.0.0.pom b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-parent/3.0.0/example-parent-3.0.0.pom new file mode 100644 index 000000000000..917908d53d7b --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-parent/3.0.0/example-parent-3.0.0.pom @@ -0,0 +1,32 @@ + + + + 4.0.0 + + org.example + example-parent + 3.0.0 + pom + + + 3.0.1 + + + + + + org.example + example-api + ${api.version} + + + + + + + org.example + example-api + + + \ No newline at end of file diff --git a/pkg/dependency/parser/java/pom/testdata/use-dep-management-from-child-in-parent/pom.xml b/pkg/dependency/parser/java/pom/testdata/use-dep-management-from-child-in-parent/pom.xml new file mode 100644 index 000000000000..5d062242161d --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/use-dep-management-from-child-in-parent/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + + com.example + root-depManagement-in-parent + 1.0.0 + + + + 1.0.1 + + + + + org.example + example-dependency + 2.0.0 + + + diff --git a/pkg/dependency/parser/java/pom/testdata/use-root-dep-management-in-parent/pom.xml b/pkg/dependency/parser/java/pom/testdata/use-root-dep-management-in-parent/pom.xml new file mode 100644 index 000000000000..685a7fc7caf3 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/use-root-dep-management-in-parent/pom.xml @@ -0,0 +1,31 @@ + + 4.0.0 + + com.example + root-depManagement-in-parent + 1.0.0 + + + + 1.0.1 + + + + + + org.example + example-api + ${api.version} + + + + + + + org.example + example-dependency + 2.0.0 + + +