From 2675891110a351a3673318b173a3063339d21756 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 28 Oct 2020 17:14:16 -0400 Subject: [PATCH] remove parent java package from json && add java manifest section parsing Signed-off-by: Alex Goodman --- schema/json/schema.json | 369 +++++----------------- syft/cataloger/java/archive_filename.go | 12 +- syft/cataloger/java/java_manifest.go | 29 +- syft/cataloger/java/java_manifest_test.go | 17 +- syft/pkg/java_metadata.go | 4 +- 5 files changed, 126 insertions(+), 305 deletions(-) diff --git a/schema/json/schema.json b/schema/json/schema.json index 2e62406e8..b38874381 100644 --- a/schema/json/schema.json +++ b/schema/json/schema.json @@ -125,116 +125,109 @@ "type": "string" }, "manifest": { - "anyOf": [ - { - "type": "null" - }, - { + "properties": { + "extraFields": { "properties": { - "extraFields": { - "properties": { - "Archiver-Version": { - "type": "string" - }, - "Build-Jdk": { - "type": "string" - }, - "Built-By": { - "type": "string" - }, - "Created-By": { - "type": "string" - }, - "Extension-Name": { - "type": "string" - }, - "Group-Id": { - "type": "string" - }, - "Hudson-Version": { - "type": "string" - }, - "Jenkins-Version": { - "type": "string" - }, - "Long-Name": { - "type": "string" - }, - "Main-Class": { - "type": "string" - }, - "Minimum-Java-Version": { - "type": "string" - }, - "Plugin-Dependencies": { - "type": "string" - }, - "Plugin-Developers": { - "type": "string" - }, - "Plugin-License-Name": { - "type": "string" - }, - "Plugin-License-Url": { - "type": "string" - }, - "Plugin-ScmUrl": { - "type": "string" - }, - "Plugin-Version": { - "type": "string" - }, - "Short-Name": { - "type": "string" - } - }, - "required": [ - "Archiver-Version", - "Build-Jdk", - "Built-By", - "Created-By" - ], - "type": "object" - }, - "implementationTitle": { + "Archiver-Version": { "type": "string" }, - "implementationVendor": { + "Build-Jdk": { "type": "string" }, - "implementationVersion": { + "Built-By": { "type": "string" }, - "manifestVersion": { + "Created-By": { "type": "string" }, - "name": { + "Extension-Name": { "type": "string" }, - "specificationTitle": { + "Group-Id": { "type": "string" }, - "specificationVendor": { + "Hudson-Version": { "type": "string" }, - "specificationVersion": { + "Jenkins-Version": { + "type": "string" + }, + "Long-Name": { + "type": "string" + }, + "Main-Class": { + "type": "string" + }, + "Minimum-Java-Version": { + "type": "string" + }, + "Plugin-Dependencies": { + "type": "string" + }, + "Plugin-Developers": { + "type": "string" + }, + "Plugin-License-Name": { + "type": "string" + }, + "Plugin-License-Url": { + "type": "string" + }, + "Plugin-ScmUrl": { + "type": "string" + }, + "Plugin-Version": { + "type": "string" + }, + "Short-Name": { "type": "string" } }, "required": [ - "extraFields", - "implementationTitle", - "implementationVendor", - "implementationVersion", - "manifestVersion", - "name", - "specificationTitle", - "specificationVendor", - "specificationVersion" + "Archiver-Version", + "Build-Jdk", + "Built-By", + "Created-By" ], "type": "object" + }, + "implementationTitle": { + "type": "string" + }, + "implementationVendor": { + "type": "string" + }, + "implementationVersion": { + "type": "string" + }, + "manifestVersion": { + "type": "string" + }, + "name": { + "type": "string" + }, + "specificationTitle": { + "type": "string" + }, + "specificationVendor": { + "type": "string" + }, + "specificationVersion": { + "type": "string" } - ] + }, + "required": [ + "extraFields", + "implementationTitle", + "implementationVendor", + "implementationVersion", + "manifestVersion", + "name", + "specificationTitle", + "specificationVendor", + "specificationVersion" + ], + "type": "object" }, "name": { "type": "string" @@ -245,206 +238,6 @@ "package": { "type": "string" }, - "parentPackage": { - "anyOf": [ - { - "type": "null" - }, - { - "properties": { - "foundBy": { - "type": "string" - }, - "language": { - "type": "integer" - }, - "licenses": { - "type": "null" - }, - "manifest": { - "type": "string" - }, - "metadata": { - "properties": { - "manifest": { - "properties": { - "extraFields": { - "properties": { - "Archiver-Version": { - "type": "string" - }, - "Build-Jdk": { - "type": "string" - }, - "Built-By": { - "type": "string" - }, - "Created-By": { - "type": "string" - }, - "Extension-Name": { - "type": "string" - }, - "Group-Id": { - "type": "string" - }, - "Hudson-Version": { - "type": "string" - }, - "Jenkins-Version": { - "type": "string" - }, - "Long-Name": { - "type": "string" - }, - "Main-Class": { - "type": "string" - }, - "Minimum-Java-Version": { - "type": "string" - }, - "Plugin-Dependencies": { - "type": "string" - }, - "Plugin-Developers": { - "type": "string" - }, - "Plugin-License-Name": { - "type": "string" - }, - "Plugin-License-Url": { - "type": "string" - }, - "Plugin-ScmUrl": { - "type": "string" - }, - "Plugin-Version": { - "type": "string" - }, - "Short-Name": { - "type": "string" - } - }, - "required": [ - "Archiver-Version", - "Build-Jdk", - "Built-By", - "Created-By" - ], - "type": "object" - }, - "implementationTitle": { - "type": "string" - }, - "implementationVendor": { - "type": "string" - }, - "implementationVersion": { - "type": "string" - }, - "manifestVersion": { - "type": "string" - }, - "name": { - "type": "string" - }, - "specificationTitle": { - "type": "string" - }, - "specificationVendor": { - "type": "string" - }, - "specificationVersion": { - "type": "string" - } - }, - "required": [ - "extraFields", - "implementationTitle", - "implementationVendor", - "implementationVersion", - "manifestVersion", - "name", - "specificationTitle", - "specificationVendor", - "specificationVersion" - ], - "type": "object" - }, - "parentPackage": { - "type": "null" - }, - "pomProperties": { - "properties": { - "artifactId": { - "type": "string" - }, - "extraFields": { - "type": "null" - }, - "groupId": { - "type": "string" - }, - "name": { - "type": "string" - }, - "path": { - "type": "string" - }, - "version": { - "type": "string" - } - }, - "required": [ - "artifactId", - "extraFields", - "groupId", - "name", - "path", - "version" - ], - "type": "object" - }, - "virtualPath": { - "type": "string" - } - }, - "required": [ - "manifest", - "parentPackage", - "pomProperties", - "virtualPath" - ], - "type": "object" - }, - "metadataType": { - "type": "string" - }, - "sources": { - "type": "null" - }, - "type": { - "type": "string" - }, - "version": { - "type": "string" - } - }, - "required": [ - "foundBy", - "language", - "licenses", - "manifest", - "metadata", - "metadataType", - "sources", - "type", - "version" - ], - "type": "object" - } - ] - }, "platform": { "type": "string" }, diff --git a/syft/cataloger/java/archive_filename.go b/syft/cataloger/java/archive_filename.go index e64690584..0f50d99c5 100644 --- a/syft/cataloger/java/archive_filename.go +++ b/syft/cataloger/java/archive_filename.go @@ -70,10 +70,14 @@ func (a archiveFilename) version() string { } func (a archiveFilename) name() string { - // there should be only one name, if there is more or less then something is wrong - if len(a.fields) != 1 { - return "" + for _, fieldSet := range a.fields { + if name, ok := fieldSet["name"]; ok { + // return the first name + return name + } } - return a.fields[0]["name"] + // derive the name from the archive name (no path or extension) + basename := filepath.Base(a.raw) + return strings.TrimSuffix(basename, filepath.Ext(basename)) } diff --git a/syft/cataloger/java/java_manifest.go b/syft/cataloger/java/java_manifest.go index 5afec3f27..5d4c1a6b7 100644 --- a/syft/cataloger/java/java_manifest.go +++ b/syft/cataloger/java/java_manifest.go @@ -12,17 +12,29 @@ import ( const manifestPath = "META-INF/MANIFEST.MF" +// nolint:funlen func parseJavaManifest(reader io.Reader) (*pkg.JavaManifest, error) { var manifest pkg.JavaManifest - manifestMap := make(map[string]string) + sections := []map[string]string{ + make(map[string]string), + } + currentSection := 0 scanner := bufio.NewScanner(reader) var lastKey string for scanner.Scan() { line := scanner.Text() - // ignore empty lines + // empty lines denote section separators if strings.TrimSpace(line) == "" { + currentSection++ + // we don't want to allocate a new section map that wont necessarily be used, do that once there is + // a non-empty line to process + + // do not process line continuations after this + lastKey = "" continue + } else if currentSection >= len(sections) { + sections = append(sections, make(map[string]string)) } if line[0] == ' ' { @@ -30,7 +42,7 @@ func parseJavaManifest(reader io.Reader) (*pkg.JavaManifest, error) { if lastKey == "" { return nil, fmt.Errorf("found continuation with no previous key (%s)", line) } - manifestMap[lastKey] += strings.TrimSpace(line) + sections[currentSection][lastKey] += strings.TrimSpace(line) } else { // this is a new key-value pair idx := strings.Index(line, ":") @@ -40,9 +52,9 @@ func parseJavaManifest(reader io.Reader) (*pkg.JavaManifest, error) { key := strings.TrimSpace(line[0:idx]) value := strings.TrimSpace(line[idx+1:]) - manifestMap[key] = value + sections[currentSection][key] = value - // keep track of key for future continuations + // keep track of key for potential future continuations lastKey = key } } @@ -51,10 +63,15 @@ func parseJavaManifest(reader io.Reader) (*pkg.JavaManifest, error) { return nil, fmt.Errorf("unable to read java manifest: %w", err) } - if err := mapstructure.Decode(manifestMap, &manifest); err != nil { + if err := mapstructure.Decode(sections[0], &manifest); err != nil { return nil, fmt.Errorf("unable to parse java manifest: %w", err) } + // append on extra sections + if len(sections) > 1 { + manifest.Sections = sections[1:] + } + // clean select fields if strings.Trim(manifest.ImplVersion, " ") != "" { // transform versions with dates attached to just versions (e.g. "1.3 2244 October 5 2008" --> "1.3") diff --git a/syft/cataloger/java/java_manifest_test.go b/syft/cataloger/java/java_manifest_test.go index 5f30d840f..ad80b4871 100644 --- a/syft/cataloger/java/java_manifest_test.go +++ b/syft/cataloger/java/java_manifest_test.go @@ -2,10 +2,11 @@ package java import ( "encoding/json" - "github.com/anchore/syft/syft/pkg" - "github.com/go-test/deep" "os" "testing" + + "github.com/anchore/syft/syft/pkg" + "github.com/go-test/deep" ) func TestParseJavaManifest(t *testing.T) { @@ -38,10 +39,16 @@ func TestParseJavaManifest(t *testing.T) { ManifestVersion: "1.0", Extra: map[string]string{ "Archiver-Version": "Plexus Archiver", - "Build-Jdk": "14.0.1", - "Built-By": "?", "Created-By": "Apache Maven 3.6.3", - "Main-Class": "hello.HelloWorld", + }, + Sections: []map[string]string{ + { + "Built-By": "?", + }, + { + "Build-Jdk": "14.0.1", + "Main-Class": "hello.HelloWorld", + }, }, }, }, diff --git a/syft/pkg/java_metadata.go b/syft/pkg/java_metadata.go index 810234d2f..ad2a66079 100644 --- a/syft/pkg/java_metadata.go +++ b/syft/pkg/java_metadata.go @@ -7,7 +7,7 @@ type JavaMetadata struct { VirtualPath string `json:"virtualPath"` Manifest *JavaManifest `mapstructure:"Manifest" json:"manifest,omitempty"` PomProperties *PomProperties `mapstructure:"PomProperties" json:"pomProperties,omitempty"` - Parent *Package `json:"parentPackage,omitempty"` // TODO: should this be included in the json output? + Parent *Package `json:"-"` } // PomProperties represents the fields of interest extracted from a Java archive's pom.xml file. @@ -31,7 +31,7 @@ type JavaManifest struct { ImplVersion string `mapstructure:"Implementation-Version" json:"implementationVersion"` ImplVendor string `mapstructure:"Implementation-Vendor" json:"implementationVendor"` Extra map[string]string `mapstructure:",remain" json:"extraFields"` - Sections []map[string]string `json:"sections"` + Sections []map[string]string `json:"sections,omitempty"` } func (m JavaMetadata) PackageURL() string {