Skip to content

Commit

Permalink
Merge branch 'main' into sbom-cataloger
Browse files Browse the repository at this point in the history
  • Loading branch information
patrikbeno authored Jun 28, 2022
2 parents 01af6e4 + 0853825 commit 95f3f59
Show file tree
Hide file tree
Showing 103 changed files with 845 additions and 11,978 deletions.
8 changes: 0 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,6 @@

A CLI tool and Go library for generating a Software Bill of Materials (SBOM) from container images and filesystems. Exceptional for vulnerability detection when used with a scanner like [Grype](https://github.com/anchore/grype).

### Join our Virtual OSS Meetup!

You are invited to join us on June 15th, 11AM-Noon PT for our virtual open source meetup.

Hosts Amy Bass from Docker Desktop and Christopher Phillips from Anchore OSS will explore how Docker Extensions for Docker Desktop is supporting open source projects and we’ll have the latest update on Syft: in-toto attestations.

[Register here ->](https://get.anchore.com/anchore-oss-meetup-jun-15-2022/)

### Join our community meetings!

- Calendar: https://calendar.google.com/calendar/u/0/r?cid=Y182OTM4dGt0MjRtajI0NnNzOThiaGtnM29qNEBncm91cC5jYWxlbmRhci5nb29nbGUuY29t
Expand Down
4 changes: 4 additions & 0 deletions syft/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ func CatalogPackages(src *source.Source, cfg cataloger.Config) (*pkg.Catalog, []
return nil, nil, nil, fmt.Errorf("unable to determine cataloger set from scheme=%+v", src.Metadata.Scheme)
}

if cataloger.RequestedAllCatalogers(cfg) {
catalogers = cataloger.AllCatalogers(cfg)
}

catalog, relationships, err := cataloger.Catalog(resolver, release, catalogers...)
if err != nil {
return nil, nil, nil, err
Expand Down
20 changes: 20 additions & 0 deletions syft/pkg/cataloger/cataloger.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
"github.com/anchore/syft/syft/source"
)

const AllCatalogersPattern = "all"

// Cataloger describes behavior for an object to participate in parsing container image or file system
// contents for the purpose of discovering Packages. Each concrete implementation should focus on discovering Packages
// for a specific Package Type or ecosystem.
Expand Down Expand Up @@ -68,6 +70,7 @@ func DirectoryCatalogers(cfg Config) []Cataloger {
deb.NewDpkgdbCataloger(),
rpmdb.NewRpmdbCataloger(),
java.NewJavaCataloger(cfg.Java()),
java.NewJavaPomCataloger(),
apkdb.NewApkdbCataloger(),
golang.NewGoModuleBinaryCataloger(),
golang.NewGoModFileCataloger(),
Expand All @@ -91,21 +94,38 @@ func AllCatalogers(cfg Config) []Cataloger {
deb.NewDpkgdbCataloger(),
rpmdb.NewRpmdbCataloger(),
java.NewJavaCataloger(cfg.Java()),
java.NewJavaPomCataloger(),
apkdb.NewApkdbCataloger(),
golang.NewGoModuleBinaryCataloger(),
golang.NewGoModFileCataloger(),
rust.NewCargoLockCataloger(),
dart.NewPubspecLockCataloger(),
dotnet.NewDotnetDepsCataloger(),
php.NewPHPComposerInstalledCataloger(),
php.NewPHPComposerLockCataloger(),
sbom.NewSBOMCataloger(),
}, cfg.Catalogers)
}

func RequestedAllCatalogers(cfg Config) bool {
for _, enableCatalogerPattern := range cfg.Catalogers {
if enableCatalogerPattern == AllCatalogersPattern {
return true
}
}
return false
}

func filterCatalogers(catalogers []Cataloger, enabledCatalogerPatterns []string) []Cataloger {
// if cataloger is not set, all applicable catalogers are enabled by default
if len(enabledCatalogerPatterns) == 0 {
return catalogers
}
for _, enableCatalogerPattern := range enabledCatalogerPatterns {
if enableCatalogerPattern == AllCatalogersPattern {
return catalogers
}
}
var keepCatalogers []Cataloger
for _, cataloger := range catalogers {
if contains(enabledCatalogerPatterns, cataloger.Name()) {
Expand Down
2 changes: 1 addition & 1 deletion syft/pkg/cataloger/java/archive_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ func pomProjectByParentPath(archivePath, virtualPath string, extractPaths []stri

projectByParentPath := make(map[string]pkg.PomProject)
for filePath, fileContents := range contentsOfMavenProjectFiles {
pomProject, err := parsePomXML(filePath, strings.NewReader(fileContents))
pomProject, err := parsePomXMLProject(filePath, strings.NewReader(fileContents))
if err != nil {
log.Warnf("failed to parse pom.xml virtualPath=%q path=%q: %+v", virtualPath, filePath, err)
continue
Expand Down
75 changes: 60 additions & 15 deletions syft/pkg/cataloger/java/parse_pom_xml.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,79 @@ import (
"io"
"strings"

"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/vifraa/gopom"
"golang.org/x/net/html/charset"
)

const pomXMLGlob = "*pom.xml"
const pomXMLDirGlob = "**/pom.xml"

func parsePomXML(path string, reader io.Reader) (*pkg.PomProject, error) {
var project gopom.Project
func parserPomXML(path string, content io.Reader) ([]*pkg.Package, []artifact.Relationship, error) {
pom, err := decodePomXML(content)
if err != nil {
return nil, nil, err
}

decoder := xml.NewDecoder(reader)
// prevent against warnings for "xml: encoding "iso-8859-1" declared but Decoder.CharsetReader is nil"
decoder.CharsetReader = charset.NewReaderLabel
var pkgs []*pkg.Package
for _, dep := range pom.Dependencies {
p := newPackageFromPom(dep)
if p.Name == "" {
continue
}

if err := decoder.Decode(&project); err != nil {
return nil, fmt.Errorf("unable to unmarshal pom.xml: %w", err)
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{
Path: path,
Parent: pomParent(project.Parent),
GroupID: project.GroupID,
ArtifactID: project.ArtifactID,
Version: project.Version,
Name: project.Name,
Description: cleanDescription(project.Description),
URL: project.URL,
}, nil
Parent: pomParent(p.Parent),
GroupID: p.GroupID,
ArtifactID: p.ArtifactID,
Version: p.Version,
Name: p.Name,
Description: cleanDescription(p.Description),
URL: p.URL,
}
}

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) {
Expand Down
51 changes: 49 additions & 2 deletions syft/pkg/cataloger/java/parse_pom_xml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,54 @@ import (
"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/[email protected]",
},
},
{
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/[email protected]",
},
},
},
},
}

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 {
expected pkg.PomProject
}{
Expand All @@ -37,7 +84,7 @@ func Test_parsePomXML(t *testing.T) {
fixture, err := os.Open(test.expected.Path)
assert.NoError(t, err)

actual, err := parsePomXML(fixture.Name(), fixture)
actual, err := parsePomXMLProject(fixture.Name(), fixture)
assert.NoError(t, err)

assert.Equal(t, &test.expected, actual)
Expand Down
17 changes: 17 additions & 0 deletions syft/pkg/cataloger/java/pom_cataloger.go
Original file line number Diff line number Diff line change
@@ -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 changes: 59 additions & 0 deletions syft/pkg/cataloger/java/test-fixtures/pom/pom.xml
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 95f3f59

Please sign in to comment.