From d0c21091386f83713d34bb239e060facf5416b6f Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 8 Dec 2021 14:11:33 -0500 Subject: [PATCH 1/3] add cataloging within universal binaries Signed-off-by: Alex Goodman --- internal/constants.go | 2 +- schema/json/generate.go | 1 + schema/json/schema-2.0.1.json | 1025 +++++++++++++++++ syft/pkg/cataloger/golang/exe.go | 89 +- syft/pkg/cataloger/golang/exe_test.go | 43 + syft/pkg/cataloger/golang/parse_go_bin.go | 14 +- .../pkg/cataloger/golang/parse_go_bin_test.go | 13 +- syft/pkg/golang_bin_metadata.go | 5 +- 8 files changed, 1177 insertions(+), 15 deletions(-) create mode 100644 schema/json/schema-2.0.1.json create mode 100644 syft/pkg/cataloger/golang/exe_test.go diff --git a/internal/constants.go b/internal/constants.go index b1625f860ac..3f03950646f 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -6,5 +6,5 @@ const ( // JSONSchemaVersion is the current schema version output by the JSON presenter // 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 = "2.0.0" + JSONSchemaVersion = "2.0.1" ) diff --git a/schema/json/generate.go b/schema/json/generate.go index 1892c0d2384..45cc16cc16c 100644 --- a/schema/json/generate.go +++ b/schema/json/generate.go @@ -35,6 +35,7 @@ type artifactMetadataContainer struct { Python pkg.PythonPackageMetadata Rpm pkg.RpmdbMetadata Cargo pkg.CargoPackageMetadata + Go pkg.GolangBinMetadata } func main() { diff --git a/schema/json/schema-2.0.1.json b/schema/json/schema-2.0.1.json new file mode 100644 index 00000000000..50242ba2ba9 --- /dev/null +++ b/schema/json/schema-2.0.1.json @@ -0,0 +1,1025 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Document", + "definitions": { + "ApkFileRecord": { + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Digest" + } + }, + "additionalProperties": true, + "type": "object" + }, + "ApkMetadata": { + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "license", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ], + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "license": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "type": "string" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/ApkFileRecord" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "CargoPackageMetadata": { + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Classification": { + "required": [ + "class", + "metadata" + ], + "properties": { + "class": { + "type": "string" + }, + "metadata": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Coordinates": { + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Descriptor": { + "required": [ + "name", + "version" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": { + "additionalProperties": true + } + }, + "additionalProperties": true, + "type": "object" + }, + "Digest": { + "required": [ + "algorithm", + "value" + ], + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Distro": { + "required": [ + "name", + "version", + "idLike" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "idLike": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Document": { + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ], + "properties": { + "artifacts": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/File" + }, + "type": "array" + }, + "secrets": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Secrets" + }, + "type": "array" + }, + "source": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Source" + }, + "distro": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Distro" + }, + "descriptor": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Descriptor" + }, + "schema": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Schema" + } + }, + "additionalProperties": true, + "type": "object" + }, + "DpkgFileRecord": { + "required": [ + "path", + "isConfigFile" + ], + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/definitions/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "additionalProperties": true, + "type": "object" + }, + "DpkgMetadata": { + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ], + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/DpkgFileRecord" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "File": { + "required": [ + "id", + "location" + ], + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/definitions/Coordinates" + }, + "metadata": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Digest" + }, + "type": "array" + }, + "classifications": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Classification" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "FileMetadataEntry": { + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType" + ], + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "GemMetadata": { + "required": [ + "name", + "version" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "licenses": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "GolangBinMetadata": { + "required": [ + "goCompiledVersion", + "architecture", + "h1Digest" + ], + "properties": { + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "JavaManifest": { + "properties": { + "main": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "namedSections": { + "patternProperties": { + ".*": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "additionalProperties": true, + "type": "object" + }, + "JavaMetadata": { + "required": [ + "virtualPath" + ], + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/JavaManifest" + }, + "pomProperties": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PomProperties" + }, + "pomProject": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PomProject" + } + }, + "additionalProperties": true, + "type": "object" + }, + "NpmPackageJSONMetadata": { + "required": [ + "author", + "licenses", + "homepage", + "description", + "url" + ], + "properties": { + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "author": { + "type": "string" + }, + "licenses": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Package": { + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl", + "metadataType", + "metadata" + ], + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/Coordinates" + }, + "type": "array" + }, + "licenses": { + "items": { + "type": "string" + }, + "type": "array" + }, + "language": { + "type": "string" + }, + "cpes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/definitions/ApkMetadata" + }, + { + "$ref": "#/definitions/CargoPackageMetadata" + }, + { + "$ref": "#/definitions/DpkgMetadata" + }, + { + "$ref": "#/definitions/GemMetadata" + }, + { + "$ref": "#/definitions/GolangBinMetadata" + }, + { + "$ref": "#/definitions/JavaMetadata" + }, + { + "$ref": "#/definitions/NpmPackageJSONMetadata" + }, + { + "$ref": "#/definitions/PythonPackageMetadata" + }, + { + "$ref": "#/definitions/RpmdbMetadata" + } + ] + } + }, + "additionalProperties": true, + "type": "object" + }, + "PomParent": { + "required": [ + "groupId", + "artifactId", + "version" + ], + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PomProject": { + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ], + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PomProperties": { + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version", + "extraFields" + ], + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PythonFileDigest": { + "required": [ + "algorithm", + "value" + ], + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PythonFileRecord": { + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "PythonPackageMetadata": { + "required": [ + "name", + "version", + "license", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "license": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Relationship": { + "required": [ + "parent", + "child", + "type" + ], + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": { + "additionalProperties": true + } + }, + "additionalProperties": true, + "type": "object" + }, + "RpmdbFileRecord": { + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ], + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/definitions/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "RpmdbMetadata": { + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "license", + "vendor", + "files" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "type": "integer" + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "license": { + "type": "string" + }, + "vendor": { + "type": "string" + }, + "files": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/RpmdbFileRecord" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Schema": { + "required": [ + "version", + "url" + ], + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "SearchResult": { + "required": [ + "classification", + "lineNumber", + "lineOffset", + "seekPosition", + "length" + ], + "properties": { + "classification": { + "type": "string" + }, + "lineNumber": { + "type": "integer" + }, + "lineOffset": { + "type": "integer" + }, + "seekPosition": { + "type": "integer" + }, + "length": { + "type": "integer" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Secrets": { + "required": [ + "location", + "secrets" + ], + "properties": { + "location": { + "$ref": "#/definitions/Coordinates" + }, + "secrets": { + "items": { + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/SearchResult" + }, + "type": "array" + } + }, + "additionalProperties": true, + "type": "object" + }, + "Source": { + "required": [ + "type", + "target" + ], + "properties": { + "type": { + "type": "string" + }, + "target": { + "additionalProperties": true + } + }, + "additionalProperties": true, + "type": "object" + } + } +} diff --git a/syft/pkg/cataloger/golang/exe.go b/syft/pkg/cataloger/golang/exe.go index b4b8bba5ad9..5173c18cb70 100644 --- a/syft/pkg/cataloger/golang/exe.go +++ b/syft/pkg/cataloger/golang/exe.go @@ -14,6 +14,7 @@ import ( "debug/pe" "fmt" "io" + "strings" ) // An exe is a generic interface to an OS executable (ELF, Mach-O, PE, XCOFF). @@ -24,6 +25,8 @@ type exe interface { // ReadData reads and returns up to size byte starting at virtual address addr. ReadData(addr, size uint64) ([]byte, error) + ArchName() string + // DataStart returns the writable data segment start address. DataStart() uint64 } @@ -32,7 +35,7 @@ type exe interface { // we changed this signature from accpeting a string // to a ReadCloser so we could adapt the code to the // stereoscope api. We removed the file open methods. -func openExe(file io.ReadCloser) (exe, error) { +func openExe(file io.ReadCloser) ([]exe, error) { /* f, err := os.Open(file) if err != nil { @@ -57,7 +60,7 @@ func openExe(file io.ReadCloser) (exe, error) { return nil, err } - return &elfExe{file, e}, nil + return []exe{&elfExe{file, e}}, nil } if bytes.HasPrefix(data, []byte("MZ")) { @@ -65,7 +68,7 @@ func openExe(file io.ReadCloser) (exe, error) { if err != nil { return nil, err } - return &peExe{file, e}, nil + return []exe{&peExe{file, e}}, nil } if bytes.HasPrefix(data, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(data[1:], []byte("\xFA\xED\xFE")) { @@ -73,7 +76,19 @@ func openExe(file io.ReadCloser) (exe, error) { if err != nil { return nil, err } - return &machoExe{file, e}, nil + return []exe{&machoExe{file, e}}, nil + } + + if bytes.HasPrefix(data, []byte("\xCA\xFE\xBA\xBE")) || bytes.HasPrefix(data[1:], []byte("\xCA\xFE\xBA\xBF")) { + fatExe, err := macho.NewFatFile(f) + if err != nil { + return nil, err + } + var exes []exe + for _, arch := range fatExe.Arches { + exes = append(exes, &machoExe{file, arch.File}) + } + return exes, nil } return nil, fmt.Errorf("unrecognized executable format") @@ -90,6 +105,14 @@ func (x *elfExe) Close() error { return x.os.Close() } +func (x *elfExe) ArchName() string { + return cleanElfArch(x.f.Machine) +} + +func cleanElfArch(machine elf.Machine) string { + return strings.TrimPrefix(strings.ToLower(machine.String()), "em_") +} + func (x *elfExe) ReadData(addr, size uint64) ([]byte, error) { for _, prog := range x.f.Progs { if prog.Vaddr <= addr && addr <= prog.Vaddr+prog.Filesz-1 { @@ -132,6 +155,56 @@ func (x *peExe) Close() error { return x.os.Close() } +func (x *peExe) ArchName() string { + // from: debug/pe/pe.go + switch x.f.Machine { + case pe.IMAGE_FILE_MACHINE_AM33: + return "amd33" + case pe.IMAGE_FILE_MACHINE_AMD64: + return "amd64" + case pe.IMAGE_FILE_MACHINE_ARM: + return "arm" + case pe.IMAGE_FILE_MACHINE_ARMNT: + return "armnt" + case pe.IMAGE_FILE_MACHINE_ARM64: + return "arm64" + case pe.IMAGE_FILE_MACHINE_EBC: + return "ebc" + case pe.IMAGE_FILE_MACHINE_I386: + return "i386" + case pe.IMAGE_FILE_MACHINE_IA64: + return "ia64" + case pe.IMAGE_FILE_MACHINE_M32R: + return "m32r" + case pe.IMAGE_FILE_MACHINE_MIPS16: + return "mips16" + case pe.IMAGE_FILE_MACHINE_MIPSFPU: + return "mipsfpu" + case pe.IMAGE_FILE_MACHINE_MIPSFPU16: + return "mipsfpu16" + case pe.IMAGE_FILE_MACHINE_POWERPC: + return "ppc" + case pe.IMAGE_FILE_MACHINE_POWERPCFP: + return "ppcfp" + case pe.IMAGE_FILE_MACHINE_R4000: + return "r4000" + case pe.IMAGE_FILE_MACHINE_SH3: + return "sh3" + case pe.IMAGE_FILE_MACHINE_SH3DSP: + return "sh3dsp" + case pe.IMAGE_FILE_MACHINE_SH4: + return "sh4" + case pe.IMAGE_FILE_MACHINE_SH5: + return "sh5" + case pe.IMAGE_FILE_MACHINE_THUMB: + return "thumb" + case pe.IMAGE_FILE_MACHINE_WCEMIPSV2: + return "wcemipsv2" + default: + return fmt.Sprintf("unknown-pe-machine-%d", x.f.Machine) + } +} + func (x *peExe) imageBase() uint64 { switch oh := x.f.OptionalHeader.(type) { case *pe.OptionalHeader32: @@ -193,6 +266,14 @@ func (x *machoExe) Close() error { return x.os.Close() } +func (x *machoExe) ArchName() string { + return cleanMachoArch(x.f.Cpu) +} + +func cleanMachoArch(cpu macho.Cpu) string { + return strings.TrimPrefix(strings.ToLower(cpu.String()), "cpu") +} + func (x *machoExe) ReadData(addr, size uint64) ([]byte, error) { for _, load := range x.f.Loads { seg, ok := load.(*macho.Segment) diff --git a/syft/pkg/cataloger/golang/exe_test.go b/syft/pkg/cataloger/golang/exe_test.go new file mode 100644 index 00000000000..5a5a6d39cd8 --- /dev/null +++ b/syft/pkg/cataloger/golang/exe_test.go @@ -0,0 +1,43 @@ +package golang + +import ( + "debug/elf" + "debug/macho" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_cleanElfArch(t *testing.T) { + tests := []struct { + machine elf.Machine + want string + }{ + { + machine: elf.EM_X86_64, + want: "x86_64", + }, + } + for _, test := range tests { + t.Run(test.machine.String(), func(t *testing.T) { + assert.Equalf(t, test.want, cleanElfArch(test.machine), "cleanElfArch(%v)", test.machine) + }) + } +} + +func Test_cleanMachoArch(t *testing.T) { + tests := []struct { + cpu macho.Cpu + want string + }{ + { + cpu: macho.CpuAmd64, + want: "amd64", + }, + } + for _, test := range tests { + t.Run(test.cpu.String(), func(t *testing.T) { + assert.Equalf(t, test.want, cleanMachoArch(test.cpu), "cleanMachoArch(%v)", test.cpu) + }) + } +} diff --git a/syft/pkg/cataloger/golang/parse_go_bin.go b/syft/pkg/cataloger/golang/parse_go_bin.go index a44b676a2be..f98438d64e6 100644 --- a/syft/pkg/cataloger/golang/parse_go_bin.go +++ b/syft/pkg/cataloger/golang/parse_go_bin.go @@ -16,17 +16,20 @@ const ( func parseGoBin(location source.Location, reader io.ReadCloser) ([]pkg.Package, error) { // Identify if bin was compiled by go - x, err := openExe(reader) + exes, err := openExe(reader) if err != nil { return nil, err } - goVersion, mod := findVers(x) - - return buildGoPkgInfo(location, mod, goVersion), nil + var pkgs []pkg.Package + for _, x := range exes { + goVersion, mod := findVers(x) + pkgs = append(pkgs, buildGoPkgInfo(location, mod, goVersion, x.ArchName())...) + } + return pkgs, nil } -func buildGoPkgInfo(location source.Location, mod, goVersion string) []pkg.Package { +func buildGoPkgInfo(location source.Location, mod, goVersion, arch string) []pkg.Package { pkgsSlice := make([]pkg.Package, 0) scanner := bufio.NewScanner(strings.NewReader(mod)) @@ -52,6 +55,7 @@ func buildGoPkgInfo(location source.Location, mod, goVersion string) []pkg.Packa Metadata: pkg.GolangBinMetadata{ GoCompiledVersion: goVersion, H1Digest: fields[3], + Architecture: arch, }, }) } diff --git a/syft/pkg/cataloger/golang/parse_go_bin_test.go b/syft/pkg/cataloger/golang/parse_go_bin_test.go index 4d460b46edc..52cbe075778 100644 --- a/syft/pkg/cataloger/golang/parse_go_bin_test.go +++ b/syft/pkg/cataloger/golang/parse_go_bin_test.go @@ -8,9 +8,11 @@ import ( "github.com/stretchr/testify/assert" ) -const goCompiledVersion = "1.17" - func TestBuildGoPkgInfo(t *testing.T) { + const ( + goCompiledVersion = "1.17" + archDetails = "amd64" + ) tests := []struct { name string mod string @@ -43,6 +45,7 @@ func TestBuildGoPkgInfo(t *testing.T) { MetadataType: pkg.GolangBinMetadataType, Metadata: pkg.GolangBinMetadata{ GoCompiledVersion: goCompiledVersion, + Architecture: archDetails, H1Digest: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=", }, }, @@ -62,6 +65,7 @@ func TestBuildGoPkgInfo(t *testing.T) { MetadataType: pkg.GolangBinMetadataType, Metadata: pkg.GolangBinMetadata{ GoCompiledVersion: goCompiledVersion, + Architecture: archDetails, H1Digest: "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=", }, }, @@ -92,6 +96,7 @@ func TestBuildGoPkgInfo(t *testing.T) { MetadataType: pkg.GolangBinMetadataType, Metadata: pkg.GolangBinMetadata{ GoCompiledVersion: goCompiledVersion, + Architecture: archDetails, H1Digest: "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k=", }, }, @@ -111,6 +116,7 @@ func TestBuildGoPkgInfo(t *testing.T) { MetadataType: pkg.GolangBinMetadataType, Metadata: pkg.GolangBinMetadata{ GoCompiledVersion: goCompiledVersion, + Architecture: archDetails, H1Digest: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=", }, }, @@ -130,6 +136,7 @@ func TestBuildGoPkgInfo(t *testing.T) { MetadataType: pkg.GolangBinMetadataType, Metadata: pkg.GolangBinMetadata{ GoCompiledVersion: goCompiledVersion, + Architecture: archDetails, H1Digest: "h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=", }, }, @@ -146,7 +153,7 @@ func TestBuildGoPkgInfo(t *testing.T) { FileSystemID: "layer-id", }, } - pkgs := buildGoPkgInfo(location, tt.mod, goCompiledVersion) + pkgs := buildGoPkgInfo(location, tt.mod, goCompiledVersion, archDetails) assert.Equal(t, tt.expected, pkgs) }) } diff --git a/syft/pkg/golang_bin_metadata.go b/syft/pkg/golang_bin_metadata.go index f8d5841ee10..946b5f8769b 100644 --- a/syft/pkg/golang_bin_metadata.go +++ b/syft/pkg/golang_bin_metadata.go @@ -2,6 +2,7 @@ package pkg // GolangBinMetadata represents all captured data for a Golang Binary type GolangBinMetadata struct { - GoCompiledVersion string - H1Digest string + GoCompiledVersion string `json:"goCompiledVersion"` + Architecture string `json:"architecture"` + H1Digest string `json:"h1Digest"` } From 5efa750031af51f5cabeb89fedcb121db1c035b4 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 8 Dec 2021 14:17:56 -0500 Subject: [PATCH 2/3] update json test fixtures Signed-off-by: Alex Goodman --- internal/formats/common/testutils/utils.go | 2 -- .../test-fixtures/snapshot/TestDirectoryPresenter.golden | 4 ++-- .../test-fixtures/snapshot/TestEncodeFullJSONDocument.golden | 4 ++-- .../syftjson/test-fixtures/snapshot/TestImagePresenter.golden | 4 ++-- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/internal/formats/common/testutils/utils.go b/internal/formats/common/testutils/utils.go index 8d1a4df6be9..a5990d4a2a9 100644 --- a/internal/formats/common/testutils/utils.go +++ b/internal/formats/common/testutils/utils.go @@ -60,8 +60,6 @@ func AssertPresenterAgainstGoldenImageSnapshot(t *testing.T, pres presenter.Pres if !bytes.Equal(expected, actual) { dmp := diffmatchpatch.New() diffs := dmp.DiffMain(string(expected), string(actual), true) - t.Logf("len: %d\nexpected: %v", len(expected), expected) - t.Logf("len: %d\nactual: %v", len(actual), actual) t.Errorf("mismatched output:\n%s", dmp.DiffPrettyText(diffs)) } } diff --git a/internal/formats/syftjson/test-fixtures/snapshot/TestDirectoryPresenter.golden b/internal/formats/syftjson/test-fixtures/snapshot/TestDirectoryPresenter.golden index fd99a182423..ec910d63f4b 100644 --- a/internal/formats/syftjson/test-fixtures/snapshot/TestDirectoryPresenter.golden +++ b/internal/formats/syftjson/test-fixtures/snapshot/TestDirectoryPresenter.golden @@ -83,7 +83,7 @@ } }, "schema": { - "version": "2.0.0", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-2.0.0.json" + "version": "2.0.1", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-2.0.1.json" } } diff --git a/internal/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden b/internal/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden index bbfcbf6f7d8..76e131341e5 100644 --- a/internal/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden +++ b/internal/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden @@ -179,7 +179,7 @@ } }, "schema": { - "version": "2.0.0", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-2.0.0.json" + "version": "2.0.1", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-2.0.1.json" } } diff --git a/internal/formats/syftjson/test-fixtures/snapshot/TestImagePresenter.golden b/internal/formats/syftjson/test-fixtures/snapshot/TestImagePresenter.golden index c7c59e8d79d..9431176edc0 100644 --- a/internal/formats/syftjson/test-fixtures/snapshot/TestImagePresenter.golden +++ b/internal/formats/syftjson/test-fixtures/snapshot/TestImagePresenter.golden @@ -104,7 +104,7 @@ } }, "schema": { - "version": "2.0.0", - "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-2.0.0.json" + "version": "2.0.1", + "url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-2.0.1.json" } } From 490afcfbb4396b90ecc2a7768a5e82506b6d7996 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 8 Dec 2021 16:07:08 -0500 Subject: [PATCH 3/3] add comments + correct 32 bit multi arch magic check Signed-off-by: Alex Goodman --- syft/pkg/cataloger/golang/exe.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/syft/pkg/cataloger/golang/exe.go b/syft/pkg/cataloger/golang/exe.go index 5173c18cb70..838d91f0118 100644 --- a/syft/pkg/cataloger/golang/exe.go +++ b/syft/pkg/cataloger/golang/exe.go @@ -25,6 +25,7 @@ type exe interface { // ReadData reads and returns up to size byte starting at virtual address addr. ReadData(addr, size uint64) ([]byte, error) + // ArchName returns a string that represents the CPU architecture of the executable. ArchName() string // DataStart returns the writable data segment start address. @@ -79,7 +80,8 @@ func openExe(file io.ReadCloser) ([]exe, error) { return []exe{&machoExe{file, e}}, nil } - if bytes.HasPrefix(data, []byte("\xCA\xFE\xBA\xBE")) || bytes.HasPrefix(data[1:], []byte("\xCA\xFE\xBA\xBF")) { + // adding macho multi-architecture support (both for 64bit and 32 bit)... this case is not in the stdlib yet + if bytes.HasPrefix(data, []byte("\xCA\xFE\xBA\xBE")) || bytes.HasPrefix(data, []byte("\xCA\xFE\xBA\xBF")) { fatExe, err := macho.NewFatFile(f) if err != nil { return nil, err