translate maps to sequences in pkg metadata (#2553)

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman 2024-02-01 11:58:10 -05:00 committed by GitHub
parent fef0e54c0f
commit 6107e5e2ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 3153 additions and 543 deletions

View File

@ -3,5 +3,5 @@ package internal
const (
// JSONSchemaVersion is the current schema version output by the JSON encoder
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
JSONSchemaVersion = "14.0.0"
JSONSchemaVersion = "15.0.0"
)

File diff suppressed because it is too large Load Diff

View File

@ -68,6 +68,10 @@ func findMetadataDefinitionNames(paths ...string) ([]string, error) {
// any definition that is used within another struct should not be considered a top-level metadata definition
names.Remove(usedNames.List()...)
// remove known exceptions, that is, types exported in the pkg Package that are not used
// in a metadata type but are not metadata types themselves.
names.Remove("Licenses", "KeyValue")
strNames := names.List()
sort.Strings(strNames)
@ -114,7 +118,11 @@ func findMetadataDefinitionNamesInFile(path string) ([]string, []string, error)
structType := extractStructType(spec.Type)
if structType == nil {
continue
// maybe this is a slice of structs? This is useful (say type KeyValues is []KeyValue)
structType = extractSliceOfStructType(spec.Type)
if structType == nil {
continue
}
}
metadataDefinitions = append(metadataDefinitions, name)
@ -124,6 +132,34 @@ func findMetadataDefinitionNamesInFile(path string) ([]string, []string, error)
return metadataDefinitions, usedTypeNames, nil
}
func extractSliceOfStructType(exp ast.Expr) *ast.StructType {
var structType *ast.StructType
switch ty := exp.(type) {
case *ast.ArrayType:
// this is a standard definition:
// type FooMetadata []BarMetadata
structType = extractStructType(ty.Elt)
case *ast.Ident:
if ty.Obj == nil {
return nil
}
// this might be a type created from another type:
// type FooMetadata BarMetadata
// ... but we need to check that the other type definition is a struct type
typeSpec, ok := ty.Obj.Decl.(*ast.TypeSpec)
if !ok {
return nil
}
nestedStructType, ok := typeSpec.Type.(*ast.StructType)
if !ok {
return nil
}
structType = nestedStructType
}
return structType
}
func extractStructType(exp ast.Expr) *ast.StructType {
var structType *ast.StructType
switch ty := exp.(type) {

View File

@ -87,8 +87,8 @@ func parseConanlock(_ context.Context, _ file.Resolver, _ *generic.Environment,
return pkgs, relationships, nil
}
func parseOptions(options string) map[string]string {
o := make(map[string]string)
func parseOptions(options string) []pkg.KeyValue {
o := make([]pkg.KeyValue, 0)
if len(options) == 0 {
return nil
}
@ -97,7 +97,10 @@ func parseOptions(options string) map[string]string {
for _, kvp := range kvps {
kv := strings.Split(kvp, "=")
if len(kv) == 2 {
o[kv[0]] = kv[1]
o = append(o, pkg.KeyValue{
Key: kv[0],
Value: kv[1],
})
}
}

View File

@ -21,82 +21,82 @@ func TestParseConanlock(t *testing.T) {
Type: pkg.ConanPkg,
Metadata: pkg.ConanLockEntry{
Ref: "mfast/1.2.2@my_user/my_channel#c6f6387c9b99780f0ee05e25f99d0f39",
Options: map[string]string{
"fPIC": "True",
"shared": "False",
"with_sqlite3": "False",
"boost:addr2line_location": "/usr/bin/addr2line",
"boost:asio_no_deprecated": "False",
"boost:buildid": "None",
"boost:bzip2": "True",
"boost:debug_level": "0",
"boost:diagnostic_definitions": "False",
"boost:error_code_header_only": "False",
"boost:extra_b2_flags": "None",
"boost:fPIC": "True",
"boost:filesystem_no_deprecated": "False",
"boost:header_only": "False",
"boost:i18n_backend": "deprecated",
"boost:i18n_backend_iconv": "libc",
"boost:i18n_backend_icu": "False",
"boost:layout": "system",
"boost:lzma": "False",
"boost:magic_autolink": "False",
"boost:multithreading": "True",
"boost:namespace": "boost",
"boost:namespace_alias": "False",
"boost:numa": "True",
"boost:pch": "True",
"boost:python_executable": "None",
"boost:python_version": "None",
"boost:segmented_stacks": "False",
"boost:shared": "False",
"boost:system_no_deprecated": "False",
"boost:system_use_utf8": "False",
"boost:visibility": "hidden",
"boost:with_stacktrace_backtrace": "True",
"boost:without_atomic": "False",
"boost:without_chrono": "False",
"boost:without_container": "False",
"boost:without_context": "False",
"boost:without_contract": "False",
"boost:without_coroutine": "False",
"boost:without_date_time": "False",
"boost:without_exception": "False",
"boost:without_fiber": "False",
"boost:without_filesystem": "False",
"boost:without_graph": "False",
"boost:without_graph_parallel": "True",
"boost:without_iostreams": "False",
"boost:without_json": "False",
"boost:without_locale": "False",
"boost:without_log": "False",
"boost:without_math": "False",
"boost:without_mpi": "True",
"boost:without_nowide": "False",
"boost:without_program_options": "False",
"boost:without_python": "True",
"boost:without_random": "False",
"boost:without_regex": "False",
"boost:without_serialization": "False",
"boost:without_stacktrace": "False",
"boost:without_system": "False",
"boost:without_test": "False",
"boost:without_thread": "False",
"boost:without_timer": "False",
"boost:without_type_erasure": "False",
"boost:without_wave": "False",
"boost:zlib": "True",
"boost:zstd": "False",
"bzip2:build_executable": "True",
"bzip2:fPIC": "True",
"bzip2:shared": "False",
"libbacktrace:fPIC": "True",
"libbacktrace:shared": "False",
"tinyxml2:fPIC": "True",
"tinyxml2:shared": "False",
"zlib:fPIC": "True",
"zlib:shared": "False",
Options: pkg.KeyValues{
{Key: "fPIC", Value: "True"},
{Key: "shared", Value: "False"},
{Key: "with_sqlite3", Value: "False"},
{Key: "boost:addr2line_location", Value: "/usr/bin/addr2line"},
{Key: "boost:asio_no_deprecated", Value: "False"},
{Key: "boost:buildid", Value: "None"},
{Key: "boost:bzip2", Value: "True"},
{Key: "boost:debug_level", Value: "0"},
{Key: "boost:diagnostic_definitions", Value: "False"},
{Key: "boost:error_code_header_only", Value: "False"},
{Key: "boost:extra_b2_flags", Value: "None"},
{Key: "boost:fPIC", Value: "True"},
{Key: "boost:filesystem_no_deprecated", Value: "False"},
{Key: "boost:header_only", Value: "False"},
{Key: "boost:i18n_backend", Value: "deprecated"},
{Key: "boost:i18n_backend_iconv", Value: "libc"},
{Key: "boost:i18n_backend_icu", Value: "False"},
{Key: "boost:layout", Value: "system"},
{Key: "boost:lzma", Value: "False"},
{Key: "boost:magic_autolink", Value: "False"},
{Key: "boost:multithreading", Value: "True"},
{Key: "boost:namespace", Value: "boost"},
{Key: "boost:namespace_alias", Value: "False"},
{Key: "boost:numa", Value: "True"},
{Key: "boost:pch", Value: "True"},
{Key: "boost:python_executable", Value: "None"},
{Key: "boost:python_version", Value: "None"},
{Key: "boost:segmented_stacks", Value: "False"},
{Key: "boost:shared", Value: "False"},
{Key: "boost:system_no_deprecated", Value: "False"},
{Key: "boost:system_use_utf8", Value: "False"},
{Key: "boost:visibility", Value: "hidden"},
{Key: "boost:with_stacktrace_backtrace", Value: "True"},
{Key: "boost:without_atomic", Value: "False"},
{Key: "boost:without_chrono", Value: "False"},
{Key: "boost:without_container", Value: "False"},
{Key: "boost:without_context", Value: "False"},
{Key: "boost:without_contract", Value: "False"},
{Key: "boost:without_coroutine", Value: "False"},
{Key: "boost:without_date_time", Value: "False"},
{Key: "boost:without_exception", Value: "False"},
{Key: "boost:without_fiber", Value: "False"},
{Key: "boost:without_filesystem", Value: "False"},
{Key: "boost:without_graph", Value: "False"},
{Key: "boost:without_graph_parallel", Value: "True"},
{Key: "boost:without_iostreams", Value: "False"},
{Key: "boost:without_json", Value: "False"},
{Key: "boost:without_locale", Value: "False"},
{Key: "boost:without_log", Value: "False"},
{Key: "boost:without_math", Value: "False"},
{Key: "boost:without_mpi", Value: "True"},
{Key: "boost:without_nowide", Value: "False"},
{Key: "boost:without_program_options", Value: "False"},
{Key: "boost:without_python", Value: "True"},
{Key: "boost:without_random", Value: "False"},
{Key: "boost:without_regex", Value: "False"},
{Key: "boost:without_serialization", Value: "False"},
{Key: "boost:without_stacktrace", Value: "False"},
{Key: "boost:without_system", Value: "False"},
{Key: "boost:without_test", Value: "False"},
{Key: "boost:without_thread", Value: "False"},
{Key: "boost:without_timer", Value: "False"},
{Key: "boost:without_type_erasure", Value: "False"},
{Key: "boost:without_wave", Value: "False"},
{Key: "boost:zlib", Value: "True"},
{Key: "boost:zstd", Value: "False"},
{Key: "bzip2:build_executable", Value: "True"},
{Key: "bzip2:fPIC", Value: "True"},
{Key: "bzip2:shared", Value: "False"},
{Key: "libbacktrace:fPIC", Value: "True"},
{Key: "libbacktrace:shared", Value: "False"},
{Key: "tinyxml2:fPIC", Value: "True"},
{Key: "tinyxml2:shared", Value: "False"},
{Key: "zlib:fPIC", Value: "True"},
{Key: "zlib:shared", Value: "False"},
},
Context: "host",
PackageID: "9d1f076b471417647c2022a78d5e2c1f834289ac",
@ -112,77 +112,77 @@ func TestParseConanlock(t *testing.T) {
Type: pkg.ConanPkg,
Metadata: pkg.ConanLockEntry{
Ref: "boost/1.75.0#a9c318f067216f900900e044e7af4ab1",
Options: map[string]string{
"addr2line_location": "/usr/bin/addr2line",
"asio_no_deprecated": "False",
"buildid": "None",
"bzip2": "True",
"debug_level": "0",
"diagnostic_definitions": "False",
"error_code_header_only": "False",
"extra_b2_flags": "None",
"fPIC": "True",
"filesystem_no_deprecated": "False",
"header_only": "False",
"i18n_backend": "deprecated",
"i18n_backend_iconv": "libc",
"i18n_backend_icu": "False",
"layout": "system",
"lzma": "False",
"magic_autolink": "False",
"multithreading": "True",
"namespace": "boost",
"namespace_alias": "False",
"numa": "True",
"pch": "True",
"python_executable": "None",
"python_version": "None",
"segmented_stacks": "False",
"shared": "False",
"system_no_deprecated": "False",
"system_use_utf8": "False",
"visibility": "hidden",
"with_stacktrace_backtrace": "True",
"without_atomic": "False",
"without_chrono": "False",
"without_container": "False",
"without_context": "False",
"without_contract": "False",
"without_coroutine": "False",
"without_date_time": "False",
"without_exception": "False",
"without_fiber": "False",
"without_filesystem": "False",
"without_graph": "False",
"without_graph_parallel": "True",
"without_iostreams": "False",
"without_json": "False",
"without_locale": "False",
"without_log": "False",
"without_math": "False",
"without_mpi": "True",
"without_nowide": "False",
"without_program_options": "False",
"without_python": "True",
"without_random": "False",
"without_regex": "False",
"without_serialization": "False",
"without_stacktrace": "False",
"without_system": "False",
"without_test": "False",
"without_thread": "False",
"without_timer": "False",
"without_type_erasure": "False",
"without_wave": "False",
"zlib": "True",
"zstd": "False",
"bzip2:build_executable": "True",
"bzip2:fPIC": "True",
"bzip2:shared": "False",
"libbacktrace:fPIC": "True",
"libbacktrace:shared": "False",
"zlib:fPIC": "True",
"zlib:shared": "False",
Options: pkg.KeyValues{
{Key: "addr2line_location", Value: "/usr/bin/addr2line"},
{Key: "asio_no_deprecated", Value: "False"},
{Key: "buildid", Value: "None"},
{Key: "bzip2", Value: "True"},
{Key: "debug_level", Value: "0"},
{Key: "diagnostic_definitions", Value: "False"},
{Key: "error_code_header_only", Value: "False"},
{Key: "extra_b2_flags", Value: "None"},
{Key: "fPIC", Value: "True"},
{Key: "filesystem_no_deprecated", Value: "False"},
{Key: "header_only", Value: "False"},
{Key: "i18n_backend", Value: "deprecated"},
{Key: "i18n_backend_iconv", Value: "libc"},
{Key: "i18n_backend_icu", Value: "False"},
{Key: "layout", Value: "system"},
{Key: "lzma", Value: "False"},
{Key: "magic_autolink", Value: "False"},
{Key: "multithreading", Value: "True"},
{Key: "namespace", Value: "boost"},
{Key: "namespace_alias", Value: "False"},
{Key: "numa", Value: "True"},
{Key: "pch", Value: "True"},
{Key: "python_executable", Value: "None"},
{Key: "python_version", Value: "None"},
{Key: "segmented_stacks", Value: "False"},
{Key: "shared", Value: "False"},
{Key: "system_no_deprecated", Value: "False"},
{Key: "system_use_utf8", Value: "False"},
{Key: "visibility", Value: "hidden"},
{Key: "with_stacktrace_backtrace", Value: "True"},
{Key: "without_atomic", Value: "False"},
{Key: "without_chrono", Value: "False"},
{Key: "without_container", Value: "False"},
{Key: "without_context", Value: "False"},
{Key: "without_contract", Value: "False"},
{Key: "without_coroutine", Value: "False"},
{Key: "without_date_time", Value: "False"},
{Key: "without_exception", Value: "False"},
{Key: "without_fiber", Value: "False"},
{Key: "without_filesystem", Value: "False"},
{Key: "without_graph", Value: "False"},
{Key: "without_graph_parallel", Value: "True"},
{Key: "without_iostreams", Value: "False"},
{Key: "without_json", Value: "False"},
{Key: "without_locale", Value: "False"},
{Key: "without_log", Value: "False"},
{Key: "without_math", Value: "False"},
{Key: "without_mpi", Value: "True"},
{Key: "without_nowide", Value: "False"},
{Key: "without_program_options", Value: "False"},
{Key: "without_python", Value: "True"},
{Key: "without_random", Value: "False"},
{Key: "without_regex", Value: "False"},
{Key: "without_serialization", Value: "False"},
{Key: "without_stacktrace", Value: "False"},
{Key: "without_system", Value: "False"},
{Key: "without_test", Value: "False"},
{Key: "without_thread", Value: "False"},
{Key: "without_timer", Value: "False"},
{Key: "without_type_erasure", Value: "False"},
{Key: "without_wave", Value: "False"},
{Key: "zlib", Value: "True"},
{Key: "zstd", Value: "False"},
{Key: "bzip2:build_executable", Value: "True"},
{Key: "bzip2:fPIC", Value: "True"},
{Key: "bzip2:shared", Value: "False"},
{Key: "libbacktrace:fPIC", Value: "True"},
{Key: "libbacktrace:shared", Value: "False"},
{Key: "zlib:fPIC", Value: "True"},
{Key: "zlib:shared", Value: "False"},
},
Context: "host",
PackageID: "dc8aedd23a0f0a773a5fcdcfe1ae3e89c4205978",
@ -198,9 +198,15 @@ func TestParseConanlock(t *testing.T) {
Type: pkg.ConanPkg,
Metadata: pkg.ConanLockEntry{
Ref: "zlib/1.2.12#c67ce17f2e96b972d42393ce50a76a1a",
Options: map[string]string{
"fPIC": "True",
"shared": "False",
Options: pkg.KeyValues{
{
Key: "fPIC",
Value: "True",
},
{
Key: "shared",
Value: "False",
},
},
Context: "host",
PackageID: "dfbe50feef7f3c6223a476cd5aeadb687084a646",
@ -216,10 +222,19 @@ func TestParseConanlock(t *testing.T) {
Type: pkg.ConanPkg,
Metadata: pkg.ConanLockEntry{
Ref: "bzip2/1.0.8#62a8031289639043797cf53fa876d0ef",
Options: map[string]string{
"build_executable": "True",
"fPIC": "True",
"shared": "False",
Options: []pkg.KeyValue{
{
Key: "build_executable",
Value: "True",
},
{
Key: "fPIC",
Value: "True",
},
{
Key: "shared",
Value: "False",
},
},
Context: "host",
PackageID: "c32092bf4d4bb47cf962af898e02823f499b017e",
@ -235,9 +250,15 @@ func TestParseConanlock(t *testing.T) {
Type: pkg.ConanPkg,
Metadata: pkg.ConanLockEntry{
Ref: "libbacktrace/cci.20210118#76e40b760e0bcd602d46db56b22820ab",
Options: map[string]string{
"fPIC": "True",
"shared": "False",
Options: []pkg.KeyValue{
{
Key: "fPIC",
Value: "True",
},
{
Key: "shared",
Value: "False",
},
},
Context: "host",
PackageID: "dfbe50feef7f3c6223a476cd5aeadb687084a646",
@ -253,9 +274,15 @@ func TestParseConanlock(t *testing.T) {
Type: pkg.ConanPkg,
Metadata: pkg.ConanLockEntry{
Ref: "tinyxml2/9.0.0#9f13a36ebfc222cd55fe531a0a8d94d1",
Options: map[string]string{
"fPIC": "True",
"shared": "False",
Options: []pkg.KeyValue{
{
Key: "fPIC",
Value: "True",
},
{
Key: "shared",
Value: "False",
},
},
Context: "host",
// intentionally remove to test missing PackageID and Prev

View File

@ -10,7 +10,7 @@ import (
"github.com/anchore/syft/syft/pkg"
)
func (c *goBinaryCataloger) newGoBinaryPackage(resolver file.Resolver, dep *debug.Module, mainModule, goVersion, architecture string, buildSettings map[string]string, cryptoSettings []string, locations ...file.Location) pkg.Package {
func (c *goBinaryCataloger) newGoBinaryPackage(resolver file.Resolver, dep *debug.Module, mainModule, goVersion, architecture string, buildSettings pkg.KeyValues, cryptoSettings []string, locations ...file.Location) pkg.Package {
if dep.Replace != nil {
dep = dep.Replace
}

View File

@ -84,8 +84,8 @@ func (c *goBinaryCataloger) makeGoMainPackage(resolver file.Resolver, mod *exten
return main
}
version, hasVersion := gbs["vcs.revision"]
timestamp, hasTimestamp := gbs["vcs.time"]
version, hasVersion := gbs.Get("vcs.revision")
timestamp, hasTimestamp := gbs.Get("vcs.time")
var ldflags string
if metadata, ok := main.Metadata.(pkg.GolangBinaryBuildinfoEntry); ok {
@ -95,7 +95,7 @@ func (c *goBinaryCataloger) makeGoMainPackage(resolver file.Resolver, mod *exten
// there is a matching vcs tag to match that could be referenced. This assumption could
// be incorrect in terms of the go.mod contents, but is not incorrect in terms of the logical
// version of the package.
ldflags = metadata.BuildSettings["-ldflags"]
ldflags, _ = metadata.BuildSettings.Get("-ldflags")
}
majorVersion, fullVersion := extractVersionFromLDFlags(ldflags)
@ -207,10 +207,13 @@ func getGOARCHFromBin(r io.ReaderAt) (string, error) {
return arch, nil
}
func getBuildSettings(settings []debug.BuildSetting) map[string]string {
m := make(map[string]string)
func getBuildSettings(settings []debug.BuildSetting) pkg.KeyValues {
m := make(pkg.KeyValues, 0)
for _, s := range settings {
m[s.Key] = s.Value
m = append(m, pkg.KeyValue{
Key: s.Key,
Value: s.Value,
})
}
return m
}

View File

@ -124,10 +124,20 @@ func TestBuildGoPkgInfo(t *testing.T) {
goCompiledVersion = "1.18"
archDetails = "amd64"
)
defaultBuildSettings := map[string]string{
"GOARCH": "amd64",
"GOOS": "darwin",
"GOAMD64": "v1",
defaultBuildSettings := []pkg.KeyValue{
{
Key: "GOARCH",
Value: "amd64",
},
{
Key: "GOOS",
Value: "darwin",
},
{
Key: "GOAMD64",
Value: "v1",
},
}
unmodifiedMain := pkg.Package{
@ -275,10 +285,19 @@ func TestBuildGoPkgInfo(t *testing.T) {
GoCompiledVersion: goCompiledVersion,
Architecture: archDetails,
H1Digest: "",
BuildSettings: map[string]string{
"GOAMD64": "v1",
"GOARCH": "amd64",
"GOOS": "darwin",
BuildSettings: []pkg.KeyValue{
{
Key: "GOARCH",
Value: archDetails,
},
{
Key: "GOOS",
Value: "darwin",
},
{
Key: "GOAMD64",
Value: "v1",
},
},
MainModule: "github.com/a/b/c",
GoCryptoSettings: []string{"boringcrypto + fips"},
@ -339,13 +358,31 @@ func TestBuildGoPkgInfo(t *testing.T) {
Metadata: pkg.GolangBinaryBuildinfoEntry{
GoCompiledVersion: goCompiledVersion,
Architecture: archDetails,
BuildSettings: map[string]string{
"GOARCH": archDetails,
"GOOS": "darwin",
"GOAMD64": "v1",
"vcs.revision": "41bc6bb410352845f22766e27dd48ba93aa825a4",
"vcs.time": "2022-10-14T19:54:57Z",
"-ldflags": `build -ldflags="-w -s -extldflags '-static' -X blah=foobar`,
BuildSettings: []pkg.KeyValue{
{
Key: "GOARCH",
Value: archDetails,
},
{
Key: "GOOS",
Value: "darwin",
},
{
Key: "GOAMD64",
Value: "v1",
},
{
Key: "vcs.revision",
Value: "41bc6bb410352845f22766e27dd48ba93aa825a4",
},
{
Key: "vcs.time",
Value: "2022-10-14T19:54:57Z",
},
{
Key: "-ldflags",
Value: `build -ldflags="-w -s -extldflags '-static' -X blah=foobar`,
},
},
MainModule: "github.com/anchore/syft",
},
@ -388,13 +425,31 @@ func TestBuildGoPkgInfo(t *testing.T) {
Metadata: pkg.GolangBinaryBuildinfoEntry{
GoCompiledVersion: goCompiledVersion,
Architecture: archDetails,
BuildSettings: map[string]string{
"GOARCH": archDetails,
"GOOS": "darwin",
"GOAMD64": "v1",
"vcs.revision": "41bc6bb410352845f22766e27dd48ba93aa825a4",
"vcs.time": "2022-10-14T19:54:57Z",
"-ldflags": `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`,
BuildSettings: []pkg.KeyValue{
{
Key: "GOARCH",
Value: archDetails,
},
{
Key: "GOOS",
Value: "darwin",
},
{
Key: "GOAMD64",
Value: "v1",
},
{
Key: "vcs.revision",
Value: "41bc6bb410352845f22766e27dd48ba93aa825a4",
},
{
Key: "vcs.time",
Value: "2022-10-14T19:54:57Z",
},
{
Key: "-ldflags",
Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`,
},
},
MainModule: "github.com/anchore/syft",
},
@ -435,11 +490,23 @@ func TestBuildGoPkgInfo(t *testing.T) {
Metadata: pkg.GolangBinaryBuildinfoEntry{
GoCompiledVersion: goCompiledVersion,
Architecture: archDetails,
BuildSettings: map[string]string{
"GOARCH": archDetails,
"GOOS": "darwin",
"GOAMD64": "v1",
"-ldflags": `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`,
BuildSettings: []pkg.KeyValue{
{
Key: "GOARCH",
Value: archDetails,
},
{
Key: "GOOS",
Value: "darwin",
},
{
Key: "GOAMD64",
Value: "v1",
},
{
Key: "-ldflags",
Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`,
},
},
MainModule: "github.com/anchore/syft",
},
@ -480,11 +547,23 @@ func TestBuildGoPkgInfo(t *testing.T) {
Metadata: pkg.GolangBinaryBuildinfoEntry{
GoCompiledVersion: goCompiledVersion,
Architecture: archDetails,
BuildSettings: map[string]string{
"GOARCH": archDetails,
"GOOS": "darwin",
"GOAMD64": "v1",
"-ldflags": `build -ldflags="-w -s -extldflags '-static' -X main.version=0.79.0`,
BuildSettings: []pkg.KeyValue{
{
Key: "GOARCH",
Value: archDetails,
},
{
Key: "GOOS",
Value: "darwin",
},
{
Key: "GOAMD64",
Value: "v1",
},
{
Key: "-ldflags",
Value: `build -ldflags="-w -s -extldflags '-static' -X main.version=0.79.0`,
},
},
MainModule: "github.com/anchore/syft",
},
@ -525,11 +604,23 @@ func TestBuildGoPkgInfo(t *testing.T) {
Metadata: pkg.GolangBinaryBuildinfoEntry{
GoCompiledVersion: goCompiledVersion,
Architecture: archDetails,
BuildSettings: map[string]string{
"GOARCH": archDetails,
"GOOS": "darwin",
"GOAMD64": "v1",
"-ldflags": `build -ldflags="-w -s -extldflags '-static' -X main.Version=0.79.0`,
BuildSettings: []pkg.KeyValue{
{
Key: "GOARCH",
Value: archDetails,
},
{
Key: "GOOS",
Value: "darwin",
},
{
Key: "GOAMD64",
Value: "v1",
},
{
Key: "-ldflags",
Value: `build -ldflags="-w -s -extldflags '-static' -X main.Version=0.79.0`,
},
},
MainModule: "github.com/anchore/syft",
},
@ -571,12 +662,27 @@ func TestBuildGoPkgInfo(t *testing.T) {
Metadata: pkg.GolangBinaryBuildinfoEntry{
GoCompiledVersion: goCompiledVersion,
Architecture: archDetails,
BuildSettings: map[string]string{
"GOARCH": archDetails,
"GOOS": "darwin",
"GOAMD64": "v1",
"vcs.revision": "41bc6bb410352845f22766e27dd48ba93aa825a4",
"vcs.time": "2022-10-14T19:54:57Z",
BuildSettings: []pkg.KeyValue{
{
Key: "GOARCH",
Value: archDetails,
},
{
Key: "GOOS",
Value: "darwin",
},
{
Key: "GOAMD64",
Value: "v1",
},
{
Key: "vcs.revision",
Value: "41bc6bb410352845f22766e27dd48ba93aa825a4",
},
{
Key: "vcs.time",
Value: "2022-10-14T19:54:57Z",
},
},
MainModule: "github.com/anchore/syft",
},

View File

@ -13,6 +13,17 @@ import (
"github.com/anchore/syft/syft/pkg"
)
func keyValues(m map[string]string) []pkg.KeyValue {
var kvs []pkg.KeyValue
for k, v := range m {
kvs = append(kvs, pkg.KeyValue{
Key: k,
Value: v,
})
}
return kvs
}
func TestGeneratePackageCPEs(t *testing.T) {
tests := []struct {
name string
@ -201,7 +212,7 @@ func TestGeneratePackageCPEs(t *testing.T) {
Type: pkg.JavaPkg,
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
Main: map[string]string{
Main: keyValues(map[string]string{
"Ant-Version": "Apache Ant 1.6.5",
"Built-By": "tatu",
"Created-By": "1.4.2_03-b02 (Sun Microsystems Inc.)",
@ -212,7 +223,7 @@ func TestGeneratePackageCPEs(t *testing.T) {
"Specification-Title": "StAX 1.0 API",
"Specification-Vendor": "http://jcp.org/en/jsr/detail?id=173",
"Specification-Version": "1.0",
},
}),
},
},
},
@ -253,7 +264,7 @@ func TestGeneratePackageCPEs(t *testing.T) {
Metadata: pkg.JavaArchive{
VirtualPath: "/opt/jboss/keycloak/modules/system/layers/base/org/apache/cxf/impl/main/cxf-rt-bindings-xml-3.3.10.jar",
Manifest: &pkg.JavaManifest{
Main: map[string]string{
Main: keyValues(map[string]string{
"Automatic-Module-Name": "org.apache.cxf.binding.xml",
"Bnd-LastModified": "1615836524860",
"Build-Jdk": "1.8.0_261",
@ -278,7 +289,7 @@ func TestGeneratePackageCPEs(t *testing.T) {
"Specification-Vendor": "The Apache Software Foundation",
"Specification-Version": "3.3.10",
"Tool": "Bnd-4.2.0.201903051501",
},
}),
},
PomProperties: &pkg.JavaPomProperties{
Path: "META-INF/maven/org.apache.cxf/cxf-rt-bindings-xml/pom.properties",
@ -558,7 +569,7 @@ func TestGeneratePackageCPEs(t *testing.T) {
FoundBy: "java-cataloger",
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
Main: map[string]string{
Main: keyValues(map[string]string{
"Extension-Name": "handlebars",
"Group-Id": "org.jenkins-ci.ui",
"Hudson-Version": "2.204",
@ -566,7 +577,7 @@ func TestGeneratePackageCPEs(t *testing.T) {
"Implementation-Version": "3.0.8",
"Plugin-Version": "3.0.8",
"Short-Name": "handlebars",
},
}),
},
PomProperties: &pkg.JavaPomProperties{
GroupID: "org.jenkins-ci.ui",
@ -594,10 +605,10 @@ func TestGeneratePackageCPEs(t *testing.T) {
Language: pkg.Java,
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
Main: map[string]string{
Main: keyValues(map[string]string{
"Extension-Name": "active-directory",
"Group-Id": "org.jenkins-ci.plugins",
},
}),
},
PomProperties: &pkg.JavaPomProperties{
GroupID: "org.jenkins-ci.plugins",

View File

@ -66,7 +66,7 @@ func vendorsFromJavaManifestNames(p pkg.Package) fieldCandidateSet {
for _, name := range javaManifestNameFields {
if metadata.Manifest.Main != nil {
if value, exists := metadata.Manifest.Main[name]; exists {
if value, exists := metadata.Manifest.Main.Get(name); exists {
if !startsWithTopLevelDomain(value) {
vendors.add(fieldCandidate{
value: normalizePersonName(value),
@ -75,12 +75,12 @@ func vendorsFromJavaManifestNames(p pkg.Package) fieldCandidateSet {
}
}
}
if metadata.Manifest.NamedSections != nil {
for _, section := range metadata.Manifest.NamedSections {
if metadata.Manifest.Sections != nil {
for _, section := range metadata.Manifest.Sections {
if section == nil {
continue
}
if value, exists := section[name]; exists {
if value, exists := section.Get(name); exists {
if !startsWithTopLevelDomain(value) {
vendors.add(fieldCandidate{
value: normalizePersonName(value),
@ -275,13 +275,13 @@ func GetManifestFieldGroupIDs(manifest *pkg.JavaManifest, fields []string) (grou
}
for _, name := range fields {
if value, exists := manifest.Main[name]; exists {
if value, exists := manifest.Main.Get(name); exists {
if startsWithTopLevelDomain(value) {
groupIDs = append(groupIDs, cleanGroupID(value))
}
}
for _, section := range manifest.NamedSections {
if value, exists := section[name]; exists {
for _, section := range manifest.Sections {
if value, exists := section.Get(name); exists {
if startsWithTopLevelDomain(value) {
groupIDs = append(groupIDs, cleanGroupID(value))
}

View File

@ -188,8 +188,11 @@ func Test_groupIDsFromJavaPackage(t *testing.T) {
pkg: pkg.Package{
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Extension-Name": "io.jenkins-ci.plugin.thing",
Main: pkg.KeyValues{
{
Key: "Extension-Name",
Value: "io.jenkins-ci.plugin.thing",
},
},
},
},
@ -201,9 +204,16 @@ func Test_groupIDsFromJavaPackage(t *testing.T) {
pkg: pkg.Package{
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
NamedSections: map[string]map[string]string{
"section": {
"Extension-Name": "io.jenkins-ci.plugin.thing",
Sections: []pkg.KeyValues{
{
{
Key: "Name",
Value: "section",
},
{
Key: "Extension-Name",
Value: "io.jenkins-ci.plugin.thing",
},
},
},
},
@ -216,20 +226,20 @@ func Test_groupIDsFromJavaPackage(t *testing.T) {
pkg: pkg.Package{
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
Main: map[string]string{
Main: []pkg.KeyValue{
// positive cases
// tier 1
"Extension-Name": "io.jenkins-ci.plugin.1",
"Specification-Vendor": "io.jenkins-ci.plugin.2",
"Implementation-Vendor": "io.jenkins-ci.plugin.3",
"Bundle-SymbolicName": "io.jenkins-ci.plugin.4",
"Implementation-Vendor-Id": "io.jenkins-ci.plugin.5",
"Implementation-Title": "io.jenkins-ci.plugin.6",
"Bundle-Activator": "io.jenkins-ci.plugin.7",
{Key: "Extension-Name", Value: "io.jenkins-ci.plugin.1"},
{Key: "Specification-Vendor", Value: "io.jenkins-ci.plugin.2"},
{Key: "Implementation-Vendor", Value: "io.jenkins-ci.plugin.3"},
{Key: "Bundle-SymbolicName", Value: "io.jenkins-ci.plugin.4"},
{Key: "Implementation-Vendor-Id", Value: "io.jenkins-ci.plugin.5"},
{Key: "Implementation-Title", Value: "io.jenkins-ci.plugin.6"},
{Key: "Bundle-Activator", Value: "io.jenkins-ci.plugin.7"},
// tier 2
"Automatic-Module-Name": "io.jenkins-ci.plugin.8",
"Main-Class": "io.jenkins-ci.plugin.9",
"Package": "io.jenkins-ci.plugin.10",
{Key: "Automatic-Module-Name", Value: "io.jenkins-ci.plugin.8"},
{Key: "Main-Class", Value: "io.jenkins-ci.plugin.9"},
{Key: "Package", Value: "io.jenkins-ci.plugin.10"},
},
},
},
@ -249,11 +259,11 @@ func Test_groupIDsFromJavaPackage(t *testing.T) {
pkg: pkg.Package{
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
Main: map[string]string{
Main: []pkg.KeyValue{
// positive cases
"Automatic-Module-Name": "io.jenkins-ci.plugin.8",
"Main-Class": "io.jenkins-ci.plugin.9",
"Package": "io.jenkins-ci.plugin.10",
{Key: "Automatic-Module-Name", Value: "io.jenkins-ci.plugin.8"},
{Key: "Main-Class", Value: "io.jenkins-ci.plugin.9"},
{Key: "Package", Value: "io.jenkins-ci.plugin.10"},
},
},
},
@ -269,10 +279,10 @@ func Test_groupIDsFromJavaPackage(t *testing.T) {
pkg: pkg.Package{
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
Main: map[string]string{
Main: []pkg.KeyValue{
// negative cases
"Extension-Name": "not.a-group.id",
"bogus": "io.jenkins-ci.plugin.please-dont-find-me",
{Key: "Extension-Name", Value: "not.a-group.id"},
{Key: "bogus", Value: "io.jenkins-ci.plugin.please-dont-find-me"},
},
},
},
@ -284,21 +294,55 @@ func Test_groupIDsFromJavaPackage(t *testing.T) {
pkg: pkg.Package{
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
NamedSections: map[string]map[string]string{
"section": {
Sections: []pkg.KeyValues{
{
{
Key: "Name",
Value: "section",
},
// positive cases
// tier 1
"Extension-Name": "io.jenkins-ci.plugin.1",
"Specification-Vendor": "io.jenkins-ci.plugin.2",
"Implementation-Vendor": "io.jenkins-ci.plugin.3",
"Bundle-SymbolicName": "io.jenkins-ci.plugin.4",
"Implementation-Vendor-Id": "io.jenkins-ci.plugin.5",
"Implementation-Title": "io.jenkins-ci.plugin.6",
"Bundle-Activator": "io.jenkins-ci.plugin.7",
{
Key: "Extension-Name",
Value: "io.jenkins-ci.plugin.1",
},
{
Key: "Specification-Vendor",
Value: "io.jenkins-ci.plugin.2",
},
{
Key: "Implementation-Vendor",
Value: "io.jenkins-ci.plugin.3",
},
{
Key: "Bundle-SymbolicName",
Value: "io.jenkins-ci.plugin.4",
},
{
Key: "Implementation-Vendor-Id",
Value: "io.jenkins-ci.plugin.5",
},
{
Key: "Implementation-Title",
Value: "io.jenkins-ci.plugin.6",
},
{
Key: "Bundle-Activator",
Value: "io.jenkins-ci.plugin.7",
},
// tier 2
"Automatic-Module-Name": "io.jenkins-ci.plugin.8",
"Main-Class": "io.jenkins-ci.plugin.9",
"Package": "io.jenkins-ci.plugin.10",
{
Key: "Automatic-Module-Name",
Value: "io.jenkins-ci.plugin.8",
},
{
Key: "Main-Class",
Value: "io.jenkins-ci.plugin.9",
},
{
Key: "Package",
Value: "io.jenkins-ci.plugin.10",
},
},
},
},
@ -319,11 +363,20 @@ func Test_groupIDsFromJavaPackage(t *testing.T) {
pkg: pkg.Package{
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
NamedSections: map[string]map[string]string{
"section": {
// negative cases
"Extension-Name": "not.a-group.id",
"bogus": "io.jenkins-ci.plugin.please-dont-find-me",
Sections: []pkg.KeyValues{
{
{
Key: "Name",
Value: "section",
},
{
Key: "Extension-Name",
Value: "not.a-group.id",
},
{
Key: "bogus",
Value: "io.jenkins-ci.plugin.please-dont-find-me",
},
},
},
},
@ -403,11 +456,21 @@ func Test_vendorsFromJavaManifestNames(t *testing.T) {
pkg: pkg.Package{
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
NamedSections: map[string]map[string]string{
"section": {
Sections: []pkg.KeyValues{
{
{
Key: "Name",
Value: "section",
},
// positive cases
"Specification-Vendor": "Alex Goodman",
"Implementation-Vendor": "William Goodman",
{
Key: "Specification-Vendor",
Value: "Alex Goodman",
},
{
Key: "Implementation-Vendor",
Value: "William Goodman",
},
},
},
},
@ -420,11 +483,22 @@ func Test_vendorsFromJavaManifestNames(t *testing.T) {
pkg: pkg.Package{
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
NamedSections: map[string]map[string]string{
"section": {
Sections: []pkg.KeyValues{
{
{
Key: "Name",
Value: "section",
},
// negative cases
"Specification-Vendor": "io.jenkins-ci.plugin.thing",
"Implementation-Vendor-ID": "William Goodman",
{
Key: "Specification-Vendor",
Value: "io.jenkins-ci.plugin.thing",
},
{
Key: "Implementation-Vendor-ID",
Value: "William Goodman",
},
},
},
},
@ -459,8 +533,11 @@ func Test_groupIDsFromJavaManifest(t *testing.T) {
{
name: "spring-foo",
manifest: pkg.JavaManifest{
Main: map[string]string{
"Implementation-Vendor": "org.foo",
Main: []pkg.KeyValue{
{
Key: "Implementation-Vendor",
Value: "org.foo",
},
},
},
expected: []string{"org.foo"},

View File

@ -14,6 +14,7 @@ import (
"syscall"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/gookit/color"
"github.com/scylladb/go-set/strset"
@ -197,31 +198,31 @@ func TestParseJar(t *testing.T) {
Metadata: pkg.JavaArchive{
VirtualPath: "test-fixtures/java-builds/packages/example-jenkins-plugin.hpi",
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Manifest-Version": "1.0",
"Specification-Title": "Example Jenkins Plugin",
"Specification-Version": "1.0",
"Implementation-Title": "Example Jenkins Plugin",
"Implementation-Version": "1.0-SNAPSHOT",
Main: pkg.KeyValues{
{Key: "Manifest-Version", Value: "1.0"},
{Key: "Created-By", Value: "Maven Archiver 3.6.0"},
{Key: "Build-Jdk-Spec", Value: "18"},
{Key: "Specification-Title", Value: "Example Jenkins Plugin"},
{Key: "Specification-Version", Value: "1.0"},
{Key: "Implementation-Title", Value: "Example Jenkins Plugin"},
{Key: "Implementation-Version", Value: "1.0-SNAPSHOT"},
{Key: "Group-Id", Value: "io.jenkins.plugins"},
{Key: "Short-Name", Value: "example-jenkins-plugin"},
{Key: "Long-Name", Value: "Example Jenkins Plugin"},
{Key: "Hudson-Version", Value: "2.204"},
{Key: "Jenkins-Version", Value: "2.204"},
{Key: "Plugin-Dependencies", Value: "structs:1.20"},
{Key: "Plugin-Developers", Value: ""},
{Key: "Plugin-License-Name", Value: "MIT License"},
{Key: "Plugin-License-Url", Value: "https://opensource.org/licenses/MIT"},
{Key: "Plugin-ScmUrl", Value: "https://github.com/jenkinsci/plugin-pom/example-jenkins-plugin"},
// extra fields...
//"Archiver-Version": "Plexus Archiver",
"Plugin-License-Url": "https://opensource.org/licenses/MIT",
"Plugin-License-Name": "MIT License",
"Created-By": "Maven Archiver 3.6.0",
//"Built-By": "?",
//"Build-Jdk": "14.0.1",
"Build-Jdk-Spec": "18",
"Jenkins-Version": "2.204",
//"Minimum-Java-Version": "1.8",
"Plugin-Developers": "",
"Plugin-ScmUrl": "https://github.com/jenkinsci/plugin-pom/example-jenkins-plugin",
//"Extension-Name": "example-jenkins-plugin",
"Short-Name": "example-jenkins-plugin",
"Group-Id": "io.jenkins.plugins",
"Plugin-Dependencies": "structs:1.20",
//"Plugin-Version": "1.0-SNAPSHOT (private-07/09/2020 13:30-?)",
"Hudson-Version": "2.204",
"Long-Name": "Example Jenkins Plugin",
//{Key: "Minimum-Java-Version", Value: "1.8"},
//{Key: "Archiver-Version", Value: "Plexus Archiver"},
//{Key: "Built-By", Value: "?"},
//{Key: "Build-Jdk", Value: "14.0.1"},
//{Key: "Extension-Name", Value: "example-jenkins-plugin"},
//{Key: "Plugin-Version", Value: "1.0-SNAPSHOT (private-07/09/2020 13:30-?)"},
},
},
PomProperties: &pkg.JavaPomProperties{
@ -255,9 +256,15 @@ func TestParseJar(t *testing.T) {
Metadata: pkg.JavaArchive{
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar",
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Manifest-Version": "1.0",
"Main-Class": "hello.HelloWorld",
Main: []pkg.KeyValue{
{
Key: "Manifest-Version",
Value: "1.0",
},
{
Key: "Main-Class",
Value: "hello.HelloWorld",
},
},
},
},
@ -326,14 +333,32 @@ func TestParseJar(t *testing.T) {
Metadata: pkg.JavaArchive{
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar",
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Manifest-Version": "1.0",
Main: []pkg.KeyValue{
{
Key: "Manifest-Version",
Value: "1.0",
},
// extra fields...
"Archiver-Version": "Plexus Archiver",
"Created-By": "Apache Maven 3.8.6",
//"Built-By": "?",
//"Build-Jdk": "14.0.1",
"Main-Class": "hello.HelloWorld",
{
Key: "Archiver-Version",
Value: "Plexus Archiver",
},
{
Key: "Created-By",
Value: "Apache Maven 3.8.6",
},
//{
// Key: "Built-By",
// Value: "?",
//},
//{
// Key: "Build-Jdk",
// Value: "14.0.1",
//},
{
Key: "Main-Class",
Value: "hello.HelloWorld",
},
},
},
PomProperties: &pkg.JavaPomProperties{
@ -455,7 +480,14 @@ func TestParseJar(t *testing.T) {
// ignore select fields (only works for the main section)
for _, field := range test.ignoreExtras {
if metadata.Manifest != nil && metadata.Manifest.Main != nil {
delete(metadata.Manifest.Main, field)
newMain := make(pkg.KeyValues, 0)
for i, kv := range metadata.Manifest.Main {
if kv.Key == field {
continue
}
newMain = append(newMain, metadata.Manifest.Main[i])
}
metadata.Manifest.Main = newMain
}
}
@ -1158,32 +1190,32 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
Metadata: pkg.JavaArchive{
VirtualPath: "test-fixtures/jar-metadata/cache/jackson-core-2.15.2.jar",
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Build-Jdk-Spec": "1.8",
"Bundle-Description": "Core Jackson processing abstractions",
"Bundle-DocURL": "https://github.com/FasterXML/jackson-core",
"Bundle-License": "https://www.apache.org/licenses/LICENSE-2.0.txt",
"Bundle-ManifestVersion": "2",
"Bundle-Name": "Jackson-core",
"Bundle-SymbolicName": "com.fasterxml.jackson.core.jackson-core",
"Bundle-Vendor": "FasterXML",
"Bundle-Version": "2.15.2",
"Created-By": "Apache Maven Bundle Plugin 5.1.8",
"Export-Package": "com.fasterxml.jackson.core;version...snip",
"Implementation-Title": "Jackson-core",
"Implementation-Vendor": "FasterXML",
"Implementation-Vendor-Id": "com.fasterxml.jackson.core",
"Implementation-Version": "2.15.2",
"Import-Package": "com.fasterxml.jackson.core;version=...snip",
"Manifest-Version": "1.0",
"Multi-Release": "true",
"Require-Capability": `osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"`,
"Specification-Title": "Jackson-core",
"Specification-Vendor": "FasterXML",
"Specification-Version": "2.15.2",
"Tool": "Bnd-6.3.1.202206071316",
"X-Compile-Source-JDK": "1.8",
"X-Compile-Target-JDK": "1.8",
Main: pkg.KeyValues{
{Key: "Manifest-Version", Value: "1.0"},
{Key: "Bundle-License", Value: "https://www.apache.org/licenses/LICENSE-2.0.txt"},
{Key: "Bundle-SymbolicName", Value: "com.fasterxml.jackson.core.jackson-core"},
{Key: "Implementation-Vendor-Id", Value: "com.fasterxml.jackson.core"},
{Key: "Specification-Title", Value: "Jackson-core"},
{Key: "Bundle-DocURL", Value: "https://github.com/FasterXML/jackson-core"},
{Key: "Import-Package", Value: "com.fasterxml.jackson.core;version=...snip"},
{Key: "Require-Capability", Value: `osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"`},
{Key: "Export-Package", Value: "com.fasterxml.jackson.core;version...snip"},
{Key: "Bundle-Name", Value: "Jackson-core"},
{Key: "Multi-Release", Value: "true"},
{Key: "Build-Jdk-Spec", Value: "1.8"},
{Key: "Bundle-Description", Value: "Core Jackson processing abstractions"},
{Key: "Implementation-Title", Value: "Jackson-core"},
{Key: "Implementation-Version", Value: "2.15.2"},
{Key: "Bundle-ManifestVersion", Value: "2"},
{Key: "Specification-Vendor", Value: "FasterXML"},
{Key: "Bundle-Vendor", Value: "FasterXML"},
{Key: "Tool", Value: "Bnd-6.3.1.202206071316"},
{Key: "Implementation-Vendor", Value: "FasterXML"},
{Key: "Bundle-Version", Value: "2.15.2"},
{Key: "X-Compile-Target-JDK", Value: "1.8"},
{Key: "X-Compile-Source-JDK", Value: "1.8"},
{Key: "Created-By", Value: "Apache Maven Bundle Plugin 5.1.8"},
{Key: "Specification-Version", Value: "2.15.2"},
},
},
// not under test
@ -1212,32 +1244,32 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
Metadata: pkg.JavaArchive{
VirtualPath: "test-fixtures/jar-metadata/cache/com.fasterxml.jackson.core.jackson-core-2.15.2.jar",
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Build-Jdk-Spec": "1.8",
"Bundle-Description": "Core Jackson processing abstractions",
"Bundle-DocURL": "https://github.com/FasterXML/jackson-core",
"Bundle-License": "https://www.apache.org/licenses/LICENSE-2.0.txt",
"Bundle-ManifestVersion": "2",
"Bundle-Name": "Jackson-core",
"Bundle-SymbolicName": "com.fasterxml.jackson.core.jackson-core",
"Bundle-Vendor": "FasterXML",
"Bundle-Version": "2.15.2",
"Created-By": "Apache Maven Bundle Plugin 5.1.8",
"Export-Package": "com.fasterxml.jackson.core;version...snip",
"Implementation-Title": "Jackson-core",
"Implementation-Vendor": "FasterXML",
"Implementation-Vendor-Id": "com.fasterxml.jackson.core",
"Implementation-Version": "2.15.2",
"Import-Package": "com.fasterxml.jackson.core;version=...snip",
"Manifest-Version": "1.0",
"Multi-Release": "true",
"Require-Capability": `osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"`,
"Specification-Title": "Jackson-core",
"Specification-Vendor": "FasterXML",
"Specification-Version": "2.15.2",
"Tool": "Bnd-6.3.1.202206071316",
"X-Compile-Source-JDK": "1.8",
"X-Compile-Target-JDK": "1.8",
Main: pkg.KeyValues{
{Key: "Manifest-Version", Value: "1.0"},
{Key: "Bundle-License", Value: "https://www.apache.org/licenses/LICENSE-2.0.txt"},
{Key: "Bundle-SymbolicName", Value: "com.fasterxml.jackson.core.jackson-core"},
{Key: "Implementation-Vendor-Id", Value: "com.fasterxml.jackson.core"},
{Key: "Specification-Title", Value: "Jackson-core"},
{Key: "Bundle-DocURL", Value: "https://github.com/FasterXML/jackson-core"},
{Key: "Import-Package", Value: "com.fasterxml.jackson.core;version=...snip"},
{Key: "Require-Capability", Value: `osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"`},
{Key: "Export-Package", Value: "com.fasterxml.jackson.core;version...snip"},
{Key: "Bundle-Name", Value: "Jackson-core"},
{Key: "Multi-Release", Value: "true"},
{Key: "Build-Jdk-Spec", Value: "1.8"},
{Key: "Bundle-Description", Value: "Core Jackson processing abstractions"},
{Key: "Implementation-Title", Value: "Jackson-core"},
{Key: "Implementation-Version", Value: "2.15.2"},
{Key: "Bundle-ManifestVersion", Value: "2"},
{Key: "Specification-Vendor", Value: "FasterXML"},
{Key: "Bundle-Vendor", Value: "FasterXML"},
{Key: "Tool", Value: "Bnd-6.3.1.202206071316"},
{Key: "Implementation-Vendor", Value: "FasterXML"},
{Key: "Bundle-Version", Value: "2.15.2"},
{Key: "X-Compile-Target-JDK", Value: "1.8"},
{Key: "X-Compile-Source-JDK", Value: "1.8"},
{Key: "Created-By", Value: "Apache Maven Bundle Plugin 5.1.8"},
{Key: "Specification-Version", Value: "2.15.2"},
},
},
// not under test
@ -1261,11 +1293,23 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
Metadata: pkg.JavaArchive{
VirtualPath: "test-fixtures/jar-metadata/cache/api-all-2.0.0-sources.jar",
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Build-Jdk": "1.8.0_191",
"Built-By": "elecharny",
"Created-By": "Apache Maven 3.6.0",
"Manifest-Version": "1.0",
Main: []pkg.KeyValue{
{
Key: "Manifest-Version",
Value: "1.0",
},
{
Key: "Built-By",
Value: "elecharny",
},
{
Key: "Created-By",
Value: "Apache Maven 3.6.0",
},
{
Key: "Build-Jdk",
Value: "1.8.0_191",
},
},
},
PomProperties: &pkg.JavaPomProperties{
@ -1314,10 +1358,25 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
if tt.assignParent {
assignParent(&tt.expectedPkgs[0], tt.expectedPkgs[1:]...)
}
for i := range tt.expectedPkgs {
tt.expectedPkgs[i].SetID()
}
pkgtest.NewCatalogTester().
FromFile(t, generateJavaMetadataJarFixture(t, tt.fixtureName)).
Expects(tt.expectedPkgs, tt.expectedRelationships).
WithCompareOptions(cmpopts.IgnoreFields(pkg.JavaArchive{}, "ArchiveDigests")).
WithCompareOptions(
cmpopts.IgnoreFields(pkg.JavaArchive{}, "ArchiveDigests"),
cmp.Comparer(func(x, y pkg.KeyValue) bool {
if x.Key != y.Key {
return false
}
if x.Value != y.Value {
return false
}
return true
}),
).
TestParser(t, gap.parseJavaArchive)
})
}

View File

@ -24,8 +24,11 @@ func Test_packageURL(t *testing.T) {
Metadata: pkg.JavaArchive{
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar",
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Manifest-Version": "1.0",
Main: []pkg.KeyValue{
{
Key: "Manifest-Version",
Value: "1.0",
},
},
},
PomProperties: &pkg.JavaPomProperties{
@ -49,8 +52,11 @@ func Test_packageURL(t *testing.T) {
Metadata: pkg.JavaArchive{
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar",
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Manifest-Version": "1.0",
Main: []pkg.KeyValue{
{
Key: "Manifest-Version",
Value: "1.0",
},
},
},
PomProperties: &pkg.JavaPomProperties{
@ -74,8 +80,11 @@ func Test_packageURL(t *testing.T) {
Metadata: pkg.JavaArchive{
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar",
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Manifest-Version": "1.0",
Main: []pkg.KeyValue{
{
Key: "Manifest-Version",
Value: "1.0",
},
},
},
PomProperties: &pkg.JavaPomProperties{
@ -101,8 +110,11 @@ func Test_packageURL(t *testing.T) {
Metadata: pkg.JavaArchive{
VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar",
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Manifest-Version": "1.0",
Main: []pkg.KeyValue{
{
Key: "Manifest-Version",
Value: "1.0",
},
},
},
PomProperties: &pkg.JavaPomProperties{
@ -163,8 +175,11 @@ func Test_groupIDFromJavaMetadata(t *testing.T) {
name: "java manifest",
metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Implementation-Vendor": "org.anchore",
Main: []pkg.KeyValue{
{
Key: "Implementation-Vendor",
Value: "org.anchore",
},
},
},
},

View File

@ -4,7 +4,6 @@ import (
"bufio"
"fmt"
"io"
"strconv"
"strings"
"unicode"
@ -20,7 +19,7 @@ const manifestGlob = "/META-INF/MANIFEST.MF"
//nolint:funlen
func parseJavaManifest(path string, reader io.Reader) (*pkg.JavaManifest, error) {
var manifest pkg.JavaManifest
var sections []map[string]string
sections := make([]pkg.KeyValues, 0)
currentSection := func() int {
return len(sections) - 1
@ -51,7 +50,9 @@ func parseJavaManifest(path string, reader io.Reader) (*pkg.JavaManifest, error)
continue
}
sections[currentSection()][lastKey] += strings.TrimSpace(line)
lastSection := sections[currentSection()]
sections[currentSection()][len(lastSection)-1].Value += strings.TrimSpace(line)
continue
}
@ -73,10 +74,13 @@ func parseJavaManifest(path string, reader io.Reader) (*pkg.JavaManifest, error)
if lastKey == "" {
// we're entering a new section
sections = append(sections, make(map[string]string))
sections = append(sections, make(pkg.KeyValues, 0))
}
sections[currentSection()][key] = value
sections[currentSection()] = append(sections[currentSection()], pkg.KeyValue{
Key: key,
Value: value,
})
// keep track of key for potential future continuations
lastKey = key
@ -89,20 +93,7 @@ func parseJavaManifest(path string, reader io.Reader) (*pkg.JavaManifest, error)
if len(sections) > 0 {
manifest.Main = sections[0]
if len(sections) > 1 {
manifest.NamedSections = make(map[string]map[string]string)
for i, s := range sections[1:] {
name, ok := s["Name"]
if !ok {
// per the manifest spec (https://docs.oracle.com/en/java/javase/11/docs/specs/jar/jar.html#jar-manifest)
// this should never happen. If it does, we want to know about it, but not necessarily stop
// cataloging entirely... for this reason we only log.
log.Debugf("java manifest section found without a name: %s", path)
name = strconv.Itoa(i)
} else {
delete(s, "Name")
}
manifest.NamedSections[name] = s
}
manifest.Sections = sections[1:]
}
}
@ -127,12 +118,12 @@ func extractNameFromApacheMavenBundlePlugin(manifest *pkg.JavaManifest) string {
// The computed symbolic name is also stored in the $(maven-symbolicname) property in case you want to add attributes or directives to it.
//
if manifest != nil {
if strings.Contains(manifest.Main["Created-By"], "Apache Maven Bundle Plugin") {
if symbolicName := manifest.Main["Bundle-SymbolicName"]; symbolicName != "" {
if strings.Contains(manifest.Main.MustGet("Created-By"), "Apache Maven Bundle Plugin") {
if symbolicName := manifest.Main.MustGet("Bundle-SymbolicName"); symbolicName != "" {
// It is possible that `Bundle-SymbolicName` is just the groupID (like in the case of
// https://repo1.maven.org/maven2/com/google/oauth-client/google-oauth-client/1.25.0/google-oauth-client-1.25.0.jar),
// so if `Implementation-Vendor-Id` is equal to `Bundle-SymbolicName`, bail on this logic
if vendorID := manifest.Main["Implementation-Vendor-Id"]; vendorID != "" && vendorID == symbolicName {
if vendorID := manifest.Main.MustGet("Implementation-Vendor-Id"); vendorID != "" && vendorID == symbolicName {
return ""
}
@ -212,21 +203,21 @@ func selectName(manifest *pkg.JavaManifest, filenameObj archiveFilename) string
// remaining fields in the manifest is a bit of a free-for-all depending on the build tooling used and package maintainer preferences
if manifest != nil {
switch {
case manifest.Main["Name"] != "":
case manifest.Main.MustGet("Name") != "":
// Manifest original spec...
return manifest.Main["Name"]
case manifest.Main["Bundle-Name"] != "":
return manifest.Main.MustGet("Name")
case manifest.Main.MustGet("Bundle-Name") != "":
// BND tooling... TODO: this does not seem accurate (I don't see a reference in the BND tooling docs for this)
return manifest.Main["Bundle-Name"]
case manifest.Main["Short-Name"] != "":
return manifest.Main.MustGet("Bundle-Name")
case manifest.Main.MustGet("Short-Name") != "":
// Jenkins...
return manifest.Main["Short-Name"]
case manifest.Main["Extension-Name"] != "":
return manifest.Main.MustGet("Short-Name")
case manifest.Main.MustGet("Extension-Name") != "":
// Jenkins...
return manifest.Main["Extension-Name"]
case manifest.Main["Implementation-Title"] != "":
return manifest.Main.MustGet("Extension-Name")
case manifest.Main.MustGet("Implementation-Title") != "":
// last ditch effort...
return manifest.Main["Implementation-Title"]
return manifest.Main.MustGet("Implementation-Title")
}
}
return ""
@ -278,12 +269,12 @@ func selectLicenses(manifest *pkg.JavaManifest) []string {
}
func fieldValueFromManifest(manifest pkg.JavaManifest, fieldName string) string {
if value := manifest.Main[fieldName]; value != "" {
if value := manifest.Main.MustGet(fieldName); value != "" {
return value
}
for _, section := range manifest.NamedSections {
if value := section[fieldName]; value != "" {
for _, section := range manifest.Sections {
if value := section.MustGet(fieldName); value != "" {
return value
}
}

View File

@ -19,41 +19,63 @@ func TestParseJavaManifest(t *testing.T) {
{
fixture: "test-fixtures/manifest/small",
expected: pkg.JavaManifest{
Main: map[string]string{
"Manifest-Version": "1.0",
Main: pkg.KeyValues{
{Key: "Manifest-Version", Value: "1.0"},
},
},
},
{
fixture: "test-fixtures/manifest/standard-info",
expected: pkg.JavaManifest{
Main: map[string]string{
"Name": "the-best-name",
"Manifest-Version": "1.0",
"Specification-Title": "the-spec-title",
"Specification-Version": "the-spec-version",
"Specification-Vendor": "the-spec-vendor",
"Implementation-Title": "the-impl-title",
"Implementation-Version": "the-impl-version",
"Implementation-Vendor": "the-impl-vendor",
Main: pkg.KeyValues{
{Key: "Manifest-Version", Value: "1.0"},
{Key: "Name", Value: "the-best-name"},
{Key: "Specification-Title", Value: "the-spec-title"},
{Key: "Specification-Vendor", Value: "the-spec-vendor"},
{Key: "Specification-Version", Value: "the-spec-version"},
{Key: "Implementation-Title", Value: "the-impl-title"},
{Key: "Implementation-Vendor", Value: "the-impl-vendor"},
{Key: "Implementation-Version", Value: "the-impl-version"},
},
},
},
{
fixture: "test-fixtures/manifest/extra-info",
expected: pkg.JavaManifest{
Main: map[string]string{
"Manifest-Version": "1.0",
"Archiver-Version": "Plexus Archiver",
"Created-By": "Apache Maven 3.6.3",
},
NamedSections: map[string]map[string]string{
"thing-1": {
"Built-By": "?",
Main: []pkg.KeyValue{
{
Key: "Manifest-Version",
Value: "1.0",
},
"1": {
"Build-Jdk": "14.0.1",
"Main-Class": "hello.HelloWorld",
{
Key: "Archiver-Version",
Value: "Plexus Archiver",
},
{
Key: "Created-By",
Value: "Apache Maven 3.6.3",
},
},
Sections: []pkg.KeyValues{
{
{
Key: "Name",
Value: "thing-1",
},
{
Key: "Built-By",
Value: "?",
},
},
{
{
Key: "Build-Jdk",
Value: "14.0.1",
},
{
Key: "Main-Class",
Value: "hello.HelloWorld",
},
},
},
},
@ -61,23 +83,34 @@ func TestParseJavaManifest(t *testing.T) {
{
fixture: "test-fixtures/manifest/extra-empty-lines",
expected: pkg.JavaManifest{
Main: map[string]string{
"Manifest-Version": "1.0",
"Archiver-Version": "Plexus Archiver",
"Created-By": "Apache Maven 3.6.3",
Main: pkg.KeyValues{
{
Key: "Manifest-Version",
Value: "1.0",
},
{
Key: "Archiver-Version",
Value: "Plexus Archiver",
},
{
Key: "Created-By",
Value: "Apache Maven 3.6.3",
},
},
NamedSections: map[string]map[string]string{
"thing-1": {
"Built-By": "?",
Sections: []pkg.KeyValues{
{
{Key: "Name", Value: "thing-1"},
{Key: "Built-By", Value: "?"},
},
"thing-2": {
"Built-By": "someone!",
{
{Key: "Name", Value: "thing-2"},
{Key: "Built-By", Value: "someone!"},
},
"2": {
"Other": "things",
{
{Key: "Other", Value: "things"},
},
"3": {
"Last": "item",
{
{Key: "Last", Value: "item"},
},
},
},
@ -85,9 +118,15 @@ func TestParseJavaManifest(t *testing.T) {
{
fixture: "test-fixtures/manifest/continuation",
expected: pkg.JavaManifest{
Main: map[string]string{
"Manifest-Version": "1.0",
"Plugin-ScmUrl": "https://github.com/jenkinsci/plugin-pom/example-jenkins-plugin",
Main: pkg.KeyValues{
{
Key: "Manifest-Version",
Value: "1.0",
},
{
Key: "Plugin-ScmUrl",
Value: "https://github.com/jenkinsci/plugin-pom/example-jenkins-plugin",
},
},
},
},
@ -95,9 +134,15 @@ func TestParseJavaManifest(t *testing.T) {
// regression test, we should always keep the full version
fixture: "test-fixtures/manifest/version-with-date",
expected: pkg.JavaManifest{
Main: map[string]string{
"Manifest-Version": "1.0",
"Implementation-Version": "1.3 2244 October 5 2005",
Main: []pkg.KeyValue{
{
Key: "Manifest-Version",
Value: "1.0",
},
{
Key: "Implementation-Version",
Value: "1.3 2244 October 5 2005",
},
},
},
},
@ -106,9 +151,15 @@ func TestParseJavaManifest(t *testing.T) {
// https://github.com/anchore/syft/issues/2179
fixture: "test-fixtures/manifest/leading-space",
expected: pkg.JavaManifest{
Main: map[string]string{
"Key-keykeykey": "initialconfig:com$ # aka not empty line",
"should": "parse",
Main: []pkg.KeyValue{
{
Key: "Key-keykeykey",
Value: "initialconfig:com$ # aka not empty line",
},
{
Key: "should",
Value: "parse",
},
},
},
},
@ -154,8 +205,11 @@ func TestSelectName(t *testing.T) {
desc: "Get name from Implementation-Title",
archive: archiveFilename{},
manifest: pkg.JavaManifest{
Main: map[string]string{
"Implementation-Title": "maven-wrapper",
Main: []pkg.KeyValue{
{
Key: "Implementation-Title",
Value: "maven-wrapper",
},
},
},
expected: "maven-wrapper",
@ -163,9 +217,15 @@ func TestSelectName(t *testing.T) {
{
desc: "Implementation-Title does not override name from filename",
manifest: pkg.JavaManifest{
Main: map[string]string{
"Name": "foo",
"Implementation-Title": "maven-wrapper",
Main: []pkg.KeyValue{
{
Key: "Name",
Value: "foo",
},
{
Key: "Implementation-Title",
Value: "maven-wrapper",
},
},
},
archive: newJavaArchiveFilename("/something/omg.jar"),
@ -174,11 +234,11 @@ func TestSelectName(t *testing.T) {
{
desc: "Use the artifact ID baked by the Apache Maven Bundle Plugin",
manifest: pkg.JavaManifest{
Main: map[string]string{
"Created-By": "Apache Maven Bundle Plugin",
"Bundle-SymbolicName": "com.atlassian.gadgets.atlassian-gadgets-api",
"Name": "foo",
"Implementation-Title": "maven-wrapper",
Main: pkg.KeyValues{
{Key: "Created-By", Value: "Apache Maven Bundle Plugin"},
{Key: "Bundle-SymbolicName", Value: "com.atlassian.gadgets.atlassian-gadgets-api"},
{Key: "Name", Value: "foo"},
{Key: "Implementation-Title", Value: "maven-wrapper"},
},
},
archive: newJavaArchiveFilename("/something/omg.jar"),
@ -188,11 +248,11 @@ func TestSelectName(t *testing.T) {
// example: pkg:maven/org.apache.servicemix.bundles/org.apache.servicemix.bundles.spring-beans@5.3.26_1
desc: "Apache Maven Bundle Plugin might bake a version in the created-by field",
manifest: pkg.JavaManifest{
Main: map[string]string{
"Created-By": "Apache Maven Bundle Plugin 5.1.6",
"Bundle-SymbolicName": "com.atlassian.gadgets.atlassian-gadgets-api",
"Name": "foo",
"Implementation-Title": "maven-wrapper",
Main: pkg.KeyValues{
{Key: "Created-By", Value: "Apache Maven Bundle Plugin 5.1.6"},
{Key: "Bundle-SymbolicName", Value: "com.atlassian.gadgets.atlassian-gadgets-api"},
{Key: "Name", Value: "foo"},
{Key: "Implementation-Title", Value: "maven-wrapper"},
},
},
archive: newJavaArchiveFilename("/something/omg.jar"),
@ -201,9 +261,15 @@ func TestSelectName(t *testing.T) {
{
desc: "Filename looks like a groupid + artifact id",
manifest: pkg.JavaManifest{
Main: map[string]string{
"Name": "foo",
"Implementation-Title": "maven-wrapper",
Main: []pkg.KeyValue{
{
Key: "Name",
Value: "foo",
},
{
Key: "Implementation-Title",
Value: "maven-wrapper",
},
},
},
archive: newJavaArchiveFilename("/something/com.atlassian.gadgets.atlassian-gadgets-api.jar"),
@ -228,8 +294,11 @@ func TestSelectName(t *testing.T) {
{
desc: "Skip stripping groupId prefix from archive filename for org.eclipse",
manifest: pkg.JavaManifest{
Main: map[string]string{
"Automatic-Module-Name": "org.eclipse.ant.core",
Main: []pkg.KeyValue{
{
Key: "Automatic-Module-Name",
Value: "org.eclipse.ant.core",
},
},
},
archive: newJavaArchiveFilename("/something/org.eclipse.ant.core-3.7.0.jar"),
@ -239,21 +308,21 @@ func TestSelectName(t *testing.T) {
// example: pkg:maven/com.google.oauth-client/google-oauth-client@1.25.0
desc: "skip Apache Maven Bundle Plugin logic if symbolic name is same as vendor id",
manifest: pkg.JavaManifest{
Main: map[string]string{
"Bundle-DocURL": "http://www.google.com/",
"Bundle-License": "http://www.apache.org/licenses/LICENSE-2.0.txt",
"Bundle-ManifestVersion": "2",
"Bundle-Name": "Google OAuth Client Library for Java",
"Bundle-RequiredExecutionEnvironment": "JavaSE-1.6",
"Bundle-SymbolicName": "com.google.oauth-client",
"Bundle-Vendor": "Google",
"Bundle-Version": "1.25.0",
"Created-By": "Apache Maven Bundle Plugin",
"Export-Package": "com.google.api.client.auth.openidconnect;uses:=\"com.google.api.client.auth.oauth2,com.google.api.client.json,com.google.api.client.json.webtoken,com.google.api.client.util\";version=\"1.25.0\",com.google.api.client.auth.oauth;uses:=\"com.google.api.client.http,com.google.api.client.util\";version=\"1.25.0\",com.google.api.client.auth.oauth2;uses:=\"com.google.api.client.http,com.google.api.client.json,com.google.api.client.util,com.google.api.client.util.store\";version=\"1.25.0\"",
"Implementation-Title": "Google OAuth Client Library for Java",
"Implementation-Vendor": "Google",
"Implementation-Vendor-Id": "com.google.oauth-client",
"Implementation-Version": "1.25.0",
Main: pkg.KeyValues{
{Key: "Bundle-DocURL", Value: "http://www.google.com/"},
{Key: "Bundle-License", Value: "http://www.apache.org/licenses/LICENSE-2.0.txt"},
{Key: "Bundle-ManifestVersion", Value: "2"},
{Key: "Bundle-Name", Value: "Google OAuth Client Library for Java"},
{Key: "Bundle-RequiredExecutionEnvironment", Value: "JavaSE-1.6"},
{Key: "Bundle-SymbolicName", Value: "com.google.oauth-client"},
{Key: "Bundle-Vendor", Value: "Google"},
{Key: "Bundle-Version", Value: "1.25.0"},
{Key: "Created-By", Value: "Apache Maven Bundle Plugin"},
{Key: "Export-Package", Value: "com.google.api.client.auth.openidconnect;uses:=\"com.google.api.client.auth.oauth2,com.google.api.client.json,com.google.api.client.json.webtoken,com.google.api.client.util\";version=\"1.25.0\",com.google.api.client.auth.oauth;uses:=\"com.google.api.client.http,com.google.api.client.util\";version=\"1.25.0\",com.google.api.client.auth.oauth2;uses:=\"com.google.api.client.http,com.google.api.client.json,com.google.api.client.util,com.google.api.client.util.store\";version=\"1.25.0\""},
{Key: "Implementation-Title", Value: "Google OAuth Client Library for Java"},
{Key: "Implementation-Vendor", Value: "Google"},
{Key: "Implementation-Vendor-Id", Value: "com.google.oauth-client"},
{Key: "Implementation-Version", Value: "1.25.0"},
},
},
archive: newJavaArchiveFilename("/something/google-oauth-client-1.25.0.jar"),
@ -283,8 +352,11 @@ func TestSelectVersion(t *testing.T) {
name: "Get name from Implementation-Version",
archive: archiveFilename{},
manifest: pkg.JavaManifest{
Main: map[string]string{
"Implementation-Version": "1.8.2",
Main: []pkg.KeyValue{
{
Key: "Implementation-Version",
Value: "1.8.2",
},
},
},
expected: "1.8.2",
@ -292,9 +364,15 @@ func TestSelectVersion(t *testing.T) {
{
name: "Implementation-Version takes precedence over Specification-Version",
manifest: pkg.JavaManifest{
Main: map[string]string{
"Implementation-Version": "1.8.2",
"Specification-Version": "1.0",
Main: []pkg.KeyValue{
{
Key: "Implementation-Version",
Value: "1.8.2",
},
{
Key: "Specification-Version",
Value: "1.0",
},
},
},
expected: "1.8.2",
@ -302,14 +380,15 @@ func TestSelectVersion(t *testing.T) {
{
name: "Implementation-Version found outside the main section",
manifest: pkg.JavaManifest{
Main: map[string]string{
"Manifest-Version": "1.0",
"Ant-Version": "Apache Ant 1.8.2",
"Created-By": "1.5.0_22-b03 (Sun Microsystems Inc.)",
Main: pkg.KeyValues{
{Key: "Manifest-Version", Value: "1.0"},
{Key: "Ant-Version", Value: "Apache Ant 1.8.2"},
{Key: "Created-By", Value: "1.5.0_22-b03 (Sun Microsystems Inc.)"},
},
NamedSections: map[string]map[string]string{
"org/apache/tools/ant/taskdefs/optional/": {
"Implementation-Version": "1.8.2",
Sections: []pkg.KeyValues{
{
{Key: "Name", Value: "org/apache/tools/ant/taskdefs/optional/"},
{Key: "Implementation-Version", Value: "1.8.2"},
},
},
},
@ -318,34 +397,53 @@ func TestSelectVersion(t *testing.T) {
{
name: "Implementation-Version takes precedence over Specification-Version in subsequent section",
manifest: pkg.JavaManifest{
Main: map[string]string{
"Manifest-Version": "1.0",
"Ant-Version": "Apache Ant 1.8.2",
"Created-By": "1.5.0_22-b03 (Sun Microsystems Inc.)",
"Specification-Version": "2.0",
Main: pkg.KeyValues{
{Key: "Manifest-Version", Value: "1.0"},
{Key: "Ant-Version", Value: "Apache Ant 1.8.2"},
{Key: "Created-By", Value: "1.5.0_22-b03 (Sun Microsystems Inc.)"},
{Key: "Specification-Version", Value: "2.0"},
},
NamedSections: map[string]map[string]string{
"org/apache/tools/ant/taskdefs/optional/": {
"Specification-Version": "1.8",
Sections: []pkg.KeyValues{
{
{Key: "Name", Value: "org/apache/tools/ant/taskdefs/optional/"},
{Key: "Specification-Version", Value: "1.8"},
},
"some-other-section": {
"Implementation-Version": "1.8.2",
{
{Key: "Name", Value: "some-other-section"},
{Key: "Implementation-Version", Value: "1.8.2"},
},
},
},
expected: "1.8.2",
},
{
name: "Implementation-Version takes precedence over Specification-Version in subsequent section",
manifest: pkg.JavaManifest{
Main: map[string]string{
"Manifest-Version": "1.0",
"Ant-Version": "Apache Ant 1.8.2",
"Created-By": "1.5.0_22-b03 (Sun Microsystems Inc.)",
Main: []pkg.KeyValue{
{
Key: "Manifest-Version",
Value: "1.0",
},
{
Key: "Ant-Version",
Value: "Apache Ant 1.8.2",
},
{
Key: "Created-By",
Value: "1.5.0_22-b03 (Sun Microsystems Inc.)",
},
},
NamedSections: map[string]map[string]string{
"some-other-section": {
"Bundle-Version": "1.11.28",
Sections: []pkg.KeyValues{
{
{
Key: "Name",
Value: "some-other-section",
},
{
Key: "Bundle-Version",
Value: "1.11.28",
},
},
},
},

View File

@ -2,15 +2,15 @@ package pkg
// ConanLockEntry represents a single "node" entry from a conan.lock file.
type ConanLockEntry struct {
Ref string `json:"ref"`
PackageID string `json:"package_id,omitempty"`
Prev string `json:"prev,omitempty"`
Requires []string `json:"requires,omitempty"`
BuildRequires []string `json:"build_requires,omitempty"`
PythonRequires []string `json:"py_requires,omitempty"`
Options map[string]string `json:"options,omitempty"`
Path string `json:"path,omitempty"`
Context string `json:"context,omitempty"`
Ref string `json:"ref"`
PackageID string `json:"package_id,omitempty"`
Prev string `json:"prev,omitempty"`
Requires []string `json:"requires,omitempty"`
BuildRequires []string `json:"build_requires,omitempty"`
PythonRequires []string `json:"py_requires,omitempty"`
Options KeyValues `json:"options,omitempty"`
Path string `json:"path,omitempty"`
Context string `json:"context,omitempty"`
}
// ConanfileEntry represents a single "Requires" entry from a conanfile.txt.

View File

@ -2,12 +2,12 @@ package pkg
// GolangBinaryBuildinfoEntry represents all captured data for a Golang binary
type GolangBinaryBuildinfoEntry struct {
BuildSettings map[string]string `json:"goBuildSettings,omitempty" cyclonedx:"goBuildSettings"`
GoCompiledVersion string `json:"goCompiledVersion" cyclonedx:"goCompiledVersion"`
Architecture string `json:"architecture" cyclonedx:"architecture"`
H1Digest string `json:"h1Digest,omitempty" cyclonedx:"h1Digest"`
MainModule string `json:"mainModule,omitempty" cyclonedx:"mainModule"`
GoCryptoSettings []string `json:"goCryptoSettings,omitempty" cyclonedx:"goCryptoSettings"`
BuildSettings KeyValues `json:"goBuildSettings,omitempty" cyclonedx:"goBuildSettings"`
GoCompiledVersion string `json:"goCompiledVersion" cyclonedx:"goCompiledVersion"`
Architecture string `json:"architecture" cyclonedx:"architecture"`
H1Digest string `json:"h1Digest,omitempty" cyclonedx:"h1Digest"`
MainModule string `json:"mainModule,omitempty" cyclonedx:"mainModule"`
GoCryptoSettings []string `json:"goCryptoSettings,omitempty" cyclonedx:"goCryptoSettings"`
}
// GolangModuleEntry represents all captured data for a Golang source scan with go.mod/go.sum

View File

@ -66,6 +66,15 @@ func (p JavaPomProperties) PkgTypeIndicated() Type {
// JavaManifest represents the fields of interest extracted from a Java archive's META-INF/MANIFEST.MF file.
type JavaManifest struct {
Main map[string]string `json:"main,omitempty"`
NamedSections map[string]map[string]string `json:"namedSections,omitempty"`
Main KeyValues `json:"main,omitempty"`
Sections []KeyValues `json:"sections,omitempty"`
}
func (m JavaManifest) Section(name string) KeyValues {
for _, section := range m.Sections {
if sectionName, ok := section.Get("Name"); ok && sectionName == name {
return section
}
}
return nil
}

28
syft/pkg/key_value.go Normal file
View File

@ -0,0 +1,28 @@
package pkg
type KeyValue struct {
Key string `json:"key"`
Value string `json:"value"`
}
type KeyValues []KeyValue
func (k KeyValues) Get(key string) (string, bool) {
for _, kv := range k {
if kv.Key == key {
return kv.Value, true
}
}
return "", false
}
func (k KeyValues) MustGet(key string) string {
for _, kv := range k {
if kv.Key == key {
return kv.Value
}
}
return ""
}