Skip to content

Commit

Permalink
feat(java): capture licenses from pom.xml (aquasecurity#225)
Browse files Browse the repository at this point in the history
  • Loading branch information
nikpivkin authored and Sq34sy committed Jul 28, 2023
1 parent 89f831d commit 5cab81d
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 21 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@
.idea

/vendor

**/.DS_Store
4 changes: 3 additions & 1 deletion pkg/java/pom/artifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,20 @@ type artifact struct {
GroupID string
ArtifactID string
Version version
License string

Exclusions map[string]struct{}

Module bool
Root bool
}

func newArtifact(groupID, artifactID, version string, props map[string]string) artifact {
func newArtifact(groupID, artifactID, version, license string, props map[string]string) artifact {
return artifact{
GroupID: evaluateVariable(groupID, props, nil),
ArtifactID: evaluateVariable(artifactID, props, nil),
Version: newVersion(evaluateVariable(version, props, nil)),
License: license,
}
}

Expand Down
31 changes: 15 additions & 16 deletions pkg/java/pom/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func (p *parser) parseRoot(root artifact) ([]types.Library, []types.Dependency,
libs []types.Library
deps []types.Dependency
rootDepManagement []pomDependency
uniqArtifacts = map[string]version{}
uniqArtifacts = map[string]artifact{}
)

// Iterate direct and transitive dependencies
Expand Down Expand Up @@ -159,9 +159,8 @@ func (p *parser) parseRoot(root artifact) ([]types.Library, []types.Dependency,
}

// For soft requirements, skip dependency resolution that has already been resolved.
if v, ok := uniqArtifacts[art.Name()]; ok {

if !v.shouldOverride(art.Version) {
if uniqueArt, ok := uniqArtifacts[art.Name()]; ok {
if !uniqueArt.Version.shouldOverride(art.Version) {
continue
}
}
Expand Down Expand Up @@ -199,20 +198,20 @@ func (p *parser) parseRoot(root artifact) ([]types.Library, []types.Dependency,
// Offline mode may be missing some fields.
if !art.IsEmpty() {
// Override the version
uniqArtifacts[art.Name()] = art.Version
uniqArtifacts[art.Name()] = artifact{
Version: art.Version,
License: art.License,
}
}
}

// Convert to []types.Library
for _, lib := range processed {
if !lib.IsEmpty() { // remove the (empty) root

libs = append(libs, types.Library{
ID: id(lib),
Name: lib.Name(),
Version: lib.Version.ver,
})
}
for name, art := range uniqArtifacts {
libs = append(libs, types.Library{
Name: name,
Version: art.Version.String(),
License: art.License,
})
}

return libs, deps, nil
Expand Down Expand Up @@ -386,7 +385,7 @@ func (p *parser) resolveDepManagement(props map[string]string, depManagement []p
// Managed dependencies with a scope of "import" should be processed after other managed dependencies.
// cf. https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#importing-dependencies
for _, imp := range imports {
art := newArtifact(imp.GroupID, imp.ArtifactID, imp.Version, props)
art := newArtifact(imp.GroupID, imp.ArtifactID, imp.Version, "", props)
result, err := p.resolve(art, nil)
if err != nil {
continue
Expand Down Expand Up @@ -437,7 +436,7 @@ func excludeDep(exclusions map[string]struct{}, art artifact) bool {

func (p *parser) parseParent(currentPath string, parent pomParent) (analysisResult, error) {
// Pass nil properties so that variables in <parent> are not evaluated.
target := newArtifact(parent.GroupId, parent.ArtifactId, parent.Version, nil)
target := newArtifact(parent.GroupId, parent.ArtifactId, parent.Version, "", nil)
if target.IsEmpty() {
return analysisResult{}, nil
}
Expand Down
30 changes: 30 additions & 0 deletions pkg/java/pom/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func TestPom_Parse(t *testing.T) {
ID: "com.example.happy:1.0.0",
Name: "com.example:happy",
Version: "1.0.0",
License: "BSD-3-Clause",
},
{
ID: "org.example.example-api:1.7.30",
Expand Down Expand Up @@ -150,6 +151,7 @@ func TestPom_Parse(t *testing.T) {
ID: "com.example.happy:1.0.0",
Name: "com.example:happy",
Version: "1.0.0",
License: "BSD-3-Clause",
},
{
ID: "org.example.example-api:1.7.30",
Expand Down Expand Up @@ -206,6 +208,7 @@ func TestPom_Parse(t *testing.T) {
ID: "com.example.child:1.0.0",
Name: "com.example:child",
Version: "1.0.0",
License: "Apache 2.0",
},
{
ID: "org.example.example-api:1.7.30",
Expand Down Expand Up @@ -287,6 +290,7 @@ func TestPom_Parse(t *testing.T) {
ID: "com.example.child:1.0.0-SNAPSHOT",
Name: "com.example:child",
Version: "1.0.0-SNAPSHOT",
License: "Apache 2.0",
},
{
ID: "org.example.example-api:1.7.30",
Expand Down Expand Up @@ -319,6 +323,7 @@ func TestPom_Parse(t *testing.T) {
ID: "com.example.child:3.0.0",
Name: "com.example:child",
Version: "3.0.0",
License: "Apache 2.0",
},
{
ID: "org.example.example-api:1.7.30",
Expand Down Expand Up @@ -351,6 +356,7 @@ func TestPom_Parse(t *testing.T) {
ID: "com.example.base:4.0.0",
Name: "com.example:base",
Version: "4.0.0",
License: "Apache 2.0",
},
{
ID: "org.example.example-api:1.7.30",
Expand Down Expand Up @@ -392,6 +398,7 @@ func TestPom_Parse(t *testing.T) {
ID: "com.example.child:1.0.0",
Name: "com.example:child",
Version: "1.0.0",
License: "Apache 2.0",
},
{
ID: "org.example.example-api:1.7.30",
Expand Down Expand Up @@ -424,6 +431,7 @@ func TestPom_Parse(t *testing.T) {
ID: "org.example.child:1.0.0",
Name: "org.example:child",
Version: "1.0.0",
License: "Apache 2.0",
},
{
ID: "org.example.example-api:1.7.30",
Expand Down Expand Up @@ -456,6 +464,7 @@ func TestPom_Parse(t *testing.T) {
ID: "com.example.soft:1.0.0",
Name: "com.example:soft",
Version: "1.0.0",
License: "Apache 2.0",
},
{
ID: "org.example.example-api:1.7.30",
Expand Down Expand Up @@ -550,6 +559,7 @@ func TestPom_Parse(t *testing.T) {
ID: "com.example.hard:1.0.0",
Name: "com.example:hard",
Version: "1.0.0",
License: "Apache 2.0",
},
{
ID: "org.example.example-api:1.7.30",
Expand Down Expand Up @@ -596,6 +606,7 @@ func TestPom_Parse(t *testing.T) {
ID: "com.example.hard:1.0.0",
Name: "com.example:hard",
Version: "1.0.0",
License: "Apache 2.0",
},
},
wantDeps: []types.Dependency{
Expand All @@ -614,6 +625,7 @@ func TestPom_Parse(t *testing.T) {
ID: "com.example.import:2.0.0",
Name: "com.example:import",
Version: "2.0.0",
License: "Apache 2.0",
},
{
ID: "org.example.example-api:1.7.30",
Expand Down Expand Up @@ -646,6 +658,7 @@ func TestPom_Parse(t *testing.T) {
ID: "com.example.import:2.0.0",
Name: "com.example:import",
Version: "2.0.0",
License: "Apache 2.0",
},
{
ID: "org.example.example-api:1.7.30",
Expand Down Expand Up @@ -733,11 +746,13 @@ func TestPom_Parse(t *testing.T) {
ID: "com.example.aggregation:1.0.0",
Name: "com.example:aggregation",
Version: "1.0.0",
License: "Apache 2.0",
},
{
ID: "com.example.module:1.1.1",
Name: "com.example:module",
Version: "1.1.1",
License: "Apache 2.0",
},
{
ID: "org.example.example-api:1.7.30",
Expand Down Expand Up @@ -874,6 +889,7 @@ func TestPom_Parse(t *testing.T) {
ID: "com.example.no-parent:1.0-SNAPSHOT",
Name: "com.example:no-parent",
Version: "1.0-SNAPSHOT",
License: "Apache 2.0",
},
{
ID: "org.example.example-api:1.7.30",
Expand Down Expand Up @@ -947,6 +963,7 @@ func TestPom_Parse(t *testing.T) {
ID: "com.example.not-found-dependency:1.0.0",
Name: "com.example:not-found-dependency",
Version: "1.0.0",
License: "Apache 2.0",
},
{
ID: "org.example.example-not-found:999",
Expand All @@ -969,6 +986,19 @@ func TestPom_Parse(t *testing.T) {
{
Name: "com.example:aggregation",
Version: "1.0.0",
License: "Apache 2.0",
},
},
},
{
name: "multiply licenses",
inputFile: filepath.Join("testdata", "multiply-licenses", "pom.xml"),
local: true,
want: []types.Library{
{
Name: "com.example:multiply-licenses",
Version: "1.0.0",
License: "MIT, Apache 2.0",
},
},
},
Expand Down
19 changes: 17 additions & 2 deletions pkg/java/pom/pom.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,17 @@ func (p pom) listProperties(val reflect.Value) map[string]string {
}

func (p pom) artifact() artifact {
return newArtifact(p.content.GroupId, p.content.ArtifactId, p.content.Version, p.content.Properties)
return newArtifact(p.content.GroupId, p.content.ArtifactId, p.content.Version, p.joinLicenses(), p.content.Properties)
}

func (p pom) joinLicenses() string {
var licenses []string
for _, license := range p.content.Licenses.License {
if license.Name != "" {
licenses = append(licenses, license.Name)
}
}
return strings.Join(licenses, ", ")
}

func (p pom) repositories() []string {
Expand All @@ -113,7 +123,12 @@ type pomXML struct {
GroupId string `xml:"groupId"`
ArtifactId string `xml:"artifactId"`
Version string `xml:"version"`
Modules struct {
Licenses struct {
License []struct {
Name string `xml:"name"`
} `xml:"license"`
} `xml:"licenses"`
Modules struct {
Text string `xml:",chardata"`
Module []string `xml:"module"`
} `xml:"modules"`
Expand Down
4 changes: 2 additions & 2 deletions pkg/java/pom/testdata/happy/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

<licenses>
<license>
<name>Apache 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
<name>BSD-3-Clause</name>
<url>https://opensource.org/licenses/BSD-3-Clause</url>
<distribution>repo</distribution>
</license>
</licenses>
Expand Down
23 changes: 23 additions & 0 deletions pkg/java/pom/testdata/multiply-licenses/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>multiply-licenses</artifactId>
<version>1.0.0</version>

<name>multiply-licenses</name>
<description>Example</description>

<licenses>
<license>
<name>MIT</name>
<comments>All source code is under the MIT license.</comments>
</license>
<license>
<name>Apache 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
<distribution>repo</distribution>
</license>
</licenses>
</project>

0 comments on commit 5cab81d

Please sign in to comment.