mirror of
https://github.com/anchore/syft.git
synced 2026-02-12 10:36:45 +01:00
add pom.xml cataloger (#1055)
Co-authored-by: Christopher Phillips <christopher.phillips@anchore.com>
This commit is contained in:
parent
3f6afd572a
commit
1d14f22e45
@ -66,6 +66,7 @@ func DirectoryCatalogers(cfg Config) []Cataloger {
|
|||||||
deb.NewDpkgdbCataloger(),
|
deb.NewDpkgdbCataloger(),
|
||||||
rpmdb.NewRpmdbCataloger(),
|
rpmdb.NewRpmdbCataloger(),
|
||||||
java.NewJavaCataloger(cfg.Java()),
|
java.NewJavaCataloger(cfg.Java()),
|
||||||
|
java.NewJavaPomCataloger(),
|
||||||
apkdb.NewApkdbCataloger(),
|
apkdb.NewApkdbCataloger(),
|
||||||
golang.NewGoModuleBinaryCataloger(),
|
golang.NewGoModuleBinaryCataloger(),
|
||||||
golang.NewGoModFileCataloger(),
|
golang.NewGoModFileCataloger(),
|
||||||
@ -88,6 +89,7 @@ func AllCatalogers(cfg Config) []Cataloger {
|
|||||||
deb.NewDpkgdbCataloger(),
|
deb.NewDpkgdbCataloger(),
|
||||||
rpmdb.NewRpmdbCataloger(),
|
rpmdb.NewRpmdbCataloger(),
|
||||||
java.NewJavaCataloger(cfg.Java()),
|
java.NewJavaCataloger(cfg.Java()),
|
||||||
|
java.NewJavaPomCataloger(),
|
||||||
apkdb.NewApkdbCataloger(),
|
apkdb.NewApkdbCataloger(),
|
||||||
golang.NewGoModuleBinaryCataloger(),
|
golang.NewGoModuleBinaryCataloger(),
|
||||||
golang.NewGoModFileCataloger(),
|
golang.NewGoModFileCataloger(),
|
||||||
|
|||||||
@ -335,7 +335,7 @@ func pomProjectByParentPath(archivePath, virtualPath string, extractPaths []stri
|
|||||||
|
|
||||||
projectByParentPath := make(map[string]pkg.PomProject)
|
projectByParentPath := make(map[string]pkg.PomProject)
|
||||||
for filePath, fileContents := range contentsOfMavenProjectFiles {
|
for filePath, fileContents := range contentsOfMavenProjectFiles {
|
||||||
pomProject, err := parsePomXML(filePath, strings.NewReader(fileContents))
|
pomProject, err := parsePomXMLProject(filePath, strings.NewReader(fileContents))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("failed to parse pom.xml virtualPath=%q path=%q: %+v", virtualPath, filePath, err)
|
log.Warnf("failed to parse pom.xml virtualPath=%q path=%q: %+v", virtualPath, filePath, err)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@ -6,34 +6,79 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/vifraa/gopom"
|
"github.com/vifraa/gopom"
|
||||||
"golang.org/x/net/html/charset"
|
"golang.org/x/net/html/charset"
|
||||||
)
|
)
|
||||||
|
|
||||||
const pomXMLGlob = "*pom.xml"
|
const pomXMLGlob = "*pom.xml"
|
||||||
|
const pomXMLDirGlob = "**/pom.xml"
|
||||||
|
|
||||||
func parsePomXML(path string, reader io.Reader) (*pkg.PomProject, error) {
|
func parserPomXML(path string, content io.Reader) ([]*pkg.Package, []artifact.Relationship, error) {
|
||||||
var project gopom.Project
|
pom, err := decodePomXML(content)
|
||||||
|
if err != nil {
|
||||||
decoder := xml.NewDecoder(reader)
|
return nil, nil, err
|
||||||
// prevent against warnings for "xml: encoding "iso-8859-1" declared but Decoder.CharsetReader is nil"
|
|
||||||
decoder.CharsetReader = charset.NewReaderLabel
|
|
||||||
|
|
||||||
if err := decoder.Decode(&project); err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to unmarshal pom.xml: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var pkgs []*pkg.Package
|
||||||
|
for _, dep := range pom.Dependencies {
|
||||||
|
p := newPackageFromPom(dep)
|
||||||
|
if p.Name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgs = append(pkgs, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkgs, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePomXMLProject(path string, reader io.Reader) (*pkg.PomProject, error) {
|
||||||
|
project, err := decodePomXML(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newPomProject(path, project), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPomProject(path string, p gopom.Project) *pkg.PomProject {
|
||||||
return &pkg.PomProject{
|
return &pkg.PomProject{
|
||||||
Path: path,
|
Path: path,
|
||||||
Parent: pomParent(project.Parent),
|
Parent: pomParent(p.Parent),
|
||||||
GroupID: project.GroupID,
|
GroupID: p.GroupID,
|
||||||
ArtifactID: project.ArtifactID,
|
ArtifactID: p.ArtifactID,
|
||||||
Version: project.Version,
|
Version: p.Version,
|
||||||
Name: project.Name,
|
Name: p.Name,
|
||||||
Description: cleanDescription(project.Description),
|
Description: cleanDescription(p.Description),
|
||||||
URL: project.URL,
|
URL: p.URL,
|
||||||
}, nil
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPackageFromPom(dep gopom.Dependency) *pkg.Package {
|
||||||
|
p := &pkg.Package{
|
||||||
|
Name: dep.ArtifactID,
|
||||||
|
Version: dep.Version,
|
||||||
|
Language: pkg.Java,
|
||||||
|
Type: pkg.JavaPkg, // TODO: should we differentiate between packages from jar/war/zip versus packages from a pom.xml that were not installed yet?
|
||||||
|
MetadataType: pkg.JavaMetadataType,
|
||||||
|
FoundBy: javaPomCataloger,
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Metadata = pkg.JavaMetadata{PURL: packageURL(*p)}
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodePomXML(content io.Reader) (project gopom.Project, err error) {
|
||||||
|
decoder := xml.NewDecoder(content)
|
||||||
|
// prevent against warnings for "xml: encoding "iso-8859-1" declared but Decoder.CharsetReader is nil"
|
||||||
|
decoder.CharsetReader = charset.NewReaderLabel
|
||||||
|
if err := decoder.Decode(&project); err != nil {
|
||||||
|
return project, fmt.Errorf("unable to unmarshal pom.xml: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return project, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func pomParent(parent gopom.Parent) (result *pkg.PomParent) {
|
func pomParent(parent gopom.Parent) (result *pkg.PomParent) {
|
||||||
|
|||||||
@ -10,7 +10,54 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_parsePomXML(t *testing.T) {
|
func Test_parserPomXML(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input string
|
||||||
|
expected []*pkg.Package
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
input: "test-fixtures/pom/pom.xml",
|
||||||
|
expected: []*pkg.Package{
|
||||||
|
{
|
||||||
|
Name: "joda-time",
|
||||||
|
Version: "2.9.2",
|
||||||
|
FoundBy: javaPomCataloger,
|
||||||
|
Language: pkg.Java,
|
||||||
|
Type: pkg.JavaPkg,
|
||||||
|
MetadataType: pkg.JavaMetadataType,
|
||||||
|
Metadata: pkg.JavaMetadata{
|
||||||
|
PURL: "pkg:maven/joda-time/joda-time@2.9.2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "junit",
|
||||||
|
Version: "4.12",
|
||||||
|
FoundBy: "java-pom-cataloger",
|
||||||
|
Language: pkg.Java,
|
||||||
|
Type: pkg.JavaPkg,
|
||||||
|
MetadataType: pkg.JavaMetadataType,
|
||||||
|
Metadata: pkg.JavaMetadata{
|
||||||
|
PURL: "pkg:maven/junit/junit@4.12",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.input, func(t *testing.T) {
|
||||||
|
fixture, err := os.Open(test.input)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
actual, relationships, err := parserPomXML(fixture.Name(), fixture)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Nil(t, relationships)
|
||||||
|
assert.Equal(t, test.expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_parsePomXMLProject(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
expected pkg.PomProject
|
expected pkg.PomProject
|
||||||
}{
|
}{
|
||||||
@ -37,7 +84,7 @@ func Test_parsePomXML(t *testing.T) {
|
|||||||
fixture, err := os.Open(test.expected.Path)
|
fixture, err := os.Open(test.expected.Path)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
actual, err := parsePomXML(fixture.Name(), fixture)
|
actual, err := parsePomXMLProject(fixture.Name(), fixture)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, &test.expected, actual)
|
assert.Equal(t, &test.expected, actual)
|
||||||
|
|||||||
17
syft/pkg/cataloger/java/pom_cataloger.go
Normal file
17
syft/pkg/cataloger/java/pom_cataloger.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package java
|
||||||
|
|
||||||
|
import "github.com/anchore/syft/syft/pkg/cataloger/common"
|
||||||
|
|
||||||
|
const javaPomCataloger = "java-pom-cataloger"
|
||||||
|
|
||||||
|
// NewJavaPomCataloger returns a cataloger capable of parsing
|
||||||
|
// dependencies from a pom.xml file.
|
||||||
|
// Pom files list dependencies that maybe not be locally installed yet.
|
||||||
|
func NewJavaPomCataloger() *common.GenericCataloger {
|
||||||
|
globParsers := make(map[string]common.ParserFn)
|
||||||
|
|
||||||
|
// java project files
|
||||||
|
globParsers[pomXMLDirGlob] = parserPomXML
|
||||||
|
|
||||||
|
return common.NewGenericCataloger(nil, globParsers, javaPomCataloger)
|
||||||
|
}
|
||||||
59
syft/pkg/cataloger/java/test-fixtures/pom/pom.xml
Normal file
59
syft/pkg/cataloger/java/test-fixtures/pom/pom.xml
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>org.anchore</groupId>
|
||||||
|
<artifactId>example-java-app-maven</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<version>0.1.0</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- tag::joda[] -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>joda-time</groupId>
|
||||||
|
<artifactId>joda-time</artifactId>
|
||||||
|
<version>2.9.2</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- end::joda[] -->
|
||||||
|
<!-- tag::junit[] -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.12</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- end::junit[] -->
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>2.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<transformers>
|
||||||
|
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
|
<mainClass>hello.HelloWorld</mainClass>
|
||||||
|
</transformer>
|
||||||
|
</transformers>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
@ -60,6 +60,15 @@ var imageOnlyTestCases = []testCase{
|
|||||||
"libc-utils": "0.7.2-r0",
|
"libc-utils": "0.7.2-r0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "find java packages excluding pom.xml", // image scans can not include packages that have yet to be installed
|
||||||
|
pkgType: pkg.JavaPkg,
|
||||||
|
pkgLanguage: pkg.Java,
|
||||||
|
pkgInfo: map[string]string{
|
||||||
|
"example-java-app-maven": "0.1.0",
|
||||||
|
"joda-time": "2.9.2",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var dirOnlyTestCases = []testCase{
|
var dirOnlyTestCases = []testCase{
|
||||||
@ -218,6 +227,17 @@ var dirOnlyTestCases = []testCase{
|
|||||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
|
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "find java packages including pom.xml", // directory scans can include packages that have yet to be installed
|
||||||
|
pkgType: pkg.JavaPkg,
|
||||||
|
pkgLanguage: pkg.Java,
|
||||||
|
duplicates: 1, // joda-time is included in both pom.xml AND the .jar collection
|
||||||
|
pkgInfo: map[string]string{
|
||||||
|
"example-java-app-maven": "0.1.0",
|
||||||
|
"joda-time": "2.9.2",
|
||||||
|
"junit": "4.12",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var commonTestCases = []testCase{
|
var commonTestCases = []testCase{
|
||||||
@ -244,15 +264,7 @@ var commonTestCases = []testCase{
|
|||||||
"netbase": "5.4",
|
"netbase": "5.4",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "find java packages",
|
|
||||||
pkgType: pkg.JavaPkg,
|
|
||||||
pkgLanguage: pkg.Java,
|
|
||||||
pkgInfo: map[string]string{
|
|
||||||
"example-java-app-maven": "0.1.0",
|
|
||||||
"joda-time": "2.9.2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "find jenkins plugins",
|
name: "find jenkins plugins",
|
||||||
pkgType: pkg.JenkinsPluginPkg,
|
pkgType: pkg.JenkinsPluginPkg,
|
||||||
|
|||||||
@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>org.anchore</groupId>
|
||||||
|
<artifactId>example-java-app-maven</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<version>0.1.0</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- tag::joda[] -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>joda-time</groupId>
|
||||||
|
<artifactId>joda-time</artifactId>
|
||||||
|
<version>2.9.2</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- end::joda[] -->
|
||||||
|
<!-- tag::junit[] -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.12</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- end::junit[] -->
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>2.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<transformers>
|
||||||
|
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
|
<mainClass>hello.HelloWorld</mainClass>
|
||||||
|
</transformer>
|
||||||
|
</transformers>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
Loading…
x
Reference in New Issue
Block a user