From a63e11df3793324f44ec71d3d266f394efcfb661 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Fri, 3 Mar 2023 14:20:44 -0500 Subject: [PATCH 1/7] fix: improved Python binary detection Signed-off-by: Keith Zantow --- .github/workflows/validations.yaml | 7 ++ Makefile | 5 + syft/pkg/cataloger/binary/cataloger.go | 2 +- syft/pkg/cataloger/binary/cataloger_test.go | 30 ++--- syft/pkg/cataloger/binary/classifier.go | 105 +++++++++++++++++- syft/pkg/cataloger/binary/classifier_test.go | 2 +- .../cataloger/binary/default_classifiers.go | 27 ++--- .../cataloger/binary/test-fixtures/.gitignore | 1 + .../cataloger/binary/test-fixtures/Makefile | 21 ++++ .../positive/python-source-3.9/patchlevel.h | 7 -- .../binary/test-fixtures/get-docker-file.sh | 15 +++ 11 files changed, 180 insertions(+), 42 deletions(-) create mode 100644 syft/pkg/cataloger/binary/test-fixtures/.gitignore create mode 100644 syft/pkg/cataloger/binary/test-fixtures/Makefile delete mode 100644 syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-source-3.9/patchlevel.h create mode 100755 syft/pkg/cataloger/binary/test-fixtures/get-docker-file.sh diff --git a/.github/workflows/validations.yaml b/.github/workflows/validations.yaml index dbb376e75e1..d29fa8537da 100644 --- a/.github/workflows/validations.yaml +++ b/.github/workflows/validations.yaml @@ -54,6 +54,13 @@ jobs: path: syft/pkg/cataloger/golang/test-fixtures/archs/binaries key: ${{ runner.os }}-unit-go-binaries-cache-${{ hashFiles( 'syft/pkg/cataloger/golang/test-fixtures/archs/binaries.fingerprint' ) }} + - name: Restore binary cataloger test-fixture cache + id: unit-binary-cataloger-cache + uses: actions/cache@v3 + with: + path: syft/pkg/cataloger/binary/test-fixtures/classifiers/dynamic + key: ${{ runner.os }}-unit-binary-cataloger-cache-${{ hashFiles( 'syft/pkg/cataloger/binary/test-fixtures/cache.fingerprint' ) }} + - name: Run unit tests run: make unit diff --git a/Makefile b/Makefile index 151f687bc12..66a0ae1a2f3 100644 --- a/Makefile +++ b/Makefile @@ -189,6 +189,10 @@ fingerprints: cd test/integration/test-fixtures && \ make cache.fingerprint + # for BINARY test fixtures + cd syft/pkg/cataloger/binary/test-fixtures && \ + make cache.fingerprint + # for JAVA BUILD test fixtures cd syft/pkg/cataloger/java/test-fixtures/java-builds && \ make packages.fingerprint @@ -214,6 +218,7 @@ fixtures: $(call title,Generating test fixtures) cd syft/pkg/cataloger/java/test-fixtures/java-builds && make cd syft/pkg/cataloger/rpm/test-fixtures && make + cd syft/pkg/cataloger/binary/test-fixtures && make .PHONY: show-test-image-cache show-test-image-cache: ## Show all docker and image tar cache diff --git a/syft/pkg/cataloger/binary/cataloger.go b/syft/pkg/cataloger/binary/cataloger.go index 3d610f9bc17..eb17ae5e1d4 100644 --- a/syft/pkg/cataloger/binary/cataloger.go +++ b/syft/pkg/cataloger/binary/cataloger.go @@ -79,7 +79,7 @@ func catalog(resolver source.FileResolver, cls classifier) (packages []pkg.Packa return nil, err } locationReader := source.NewLocationReadCloser(location, reader) - pkgs, err := cls.EvidenceMatcher(cls, locationReader) + pkgs, err := cls.EvidenceMatcher(resolver, cls, locationReader) if err != nil { return nil, err } diff --git a/syft/pkg/cataloger/binary/cataloger_test.go b/syft/pkg/cataloger/binary/cataloger_test.go index d6f6ab15d20..740f9973bd2 100644 --- a/syft/pkg/cataloger/binary/cataloger_test.go +++ b/syft/pkg/cataloger/binary/cataloger_test.go @@ -76,13 +76,12 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Version: "3.8.16", Type: "binary", PURL: "pkg:generic/python@3.8.16", - Locations: locations("dir/python3.8", "python3.8", "libpython3.8.so", "patchlevel.h"), + Locations: locations("dir/python3.8", "python3.8", "libpython3.8.so"), Metadata: pkg.BinaryMetadata{ Matches: []pkg.ClassifierMatch{ match("python-binary", "dir/python3.8"), match("python-binary", "python3.8"), match("python-binary-lib", "libpython3.8.so"), - match("cpython-source", "patchlevel.h"), }, }, }, @@ -314,6 +313,22 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Metadata: metadata("python-binary-lib"), }, }, + { + name: "positive-python-3.11.2-from-shared-lib", + fixtureDir: "test-fixtures/classifiers/dynamic/python-binary-shared-lib-3.11", + expected: pkg.Package{ + Name: "python", + Version: "3.11.2", + PURL: "pkg:generic/python@3.11.2", + Locations: locations("python3", "libpython3.11.so.1.0"), + Metadata: pkg.BinaryMetadata{ + Matches: []pkg.ClassifierMatch{ + match("python-binary"), + match("python-binary-lib"), + }, + }, + }, + }, { name: "positive-python3.6", fixtureDir: "test-fixtures/classifiers/positive/python-binary-3.6", @@ -325,17 +340,6 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Metadata: metadata("python-binary"), }, }, - { - name: "positive-patchlevel.h", - fixtureDir: "test-fixtures/classifiers/positive/python-source-3.9", - expected: pkg.Package{ - Name: "python", - Version: "3.9-aZ5", - PURL: "pkg:generic/python@3.9-aZ5", - Locations: locations("patchlevel.h"), - Metadata: metadata("cpython-source"), - }, - }, { name: "positive-go", fixtureDir: "test-fixtures/classifiers/positive/go-1.14", diff --git a/syft/pkg/cataloger/binary/classifier.go b/syft/pkg/cataloger/binary/classifier.go index 15272e92dce..bd058d5b4f2 100644 --- a/syft/pkg/cataloger/binary/classifier.go +++ b/syft/pkg/cataloger/binary/classifier.go @@ -2,6 +2,9 @@ package binary import ( "bytes" + "debug/elf" + "debug/macho" + "debug/pe" "fmt" "io" "reflect" @@ -10,6 +13,7 @@ import ( "github.com/anchore/packageurl-go" "github.com/anchore/syft/internal" + "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/unionreader" @@ -49,12 +53,12 @@ type classifier struct { } // evidenceMatcher is a function called to catalog Packages that match some sort of evidence -type evidenceMatcher func(classifier classifier, reader source.LocationReadCloser) ([]pkg.Package, error) +type evidenceMatcher func(resolver source.FileResolver, classifier classifier, reader source.LocationReadCloser) ([]pkg.Package, error) func evidenceMatchers(matchers ...evidenceMatcher) evidenceMatcher { - return func(classifier classifier, reader source.LocationReadCloser) ([]pkg.Package, error) { + return func(resolver source.FileResolver, classifier classifier, reader source.LocationReadCloser) ([]pkg.Package, error) { for _, matcher := range matchers { - match, err := matcher(classifier, reader) + match, err := matcher(resolver, classifier, reader) if err != nil { return nil, err } @@ -68,7 +72,7 @@ func evidenceMatchers(matchers ...evidenceMatcher) evidenceMatcher { func fileNameTemplateVersionMatcher(fileNamePattern string, contentTemplate string) evidenceMatcher { pat := regexp.MustCompile(fileNamePattern) - return func(classifier classifier, reader source.LocationReadCloser) ([]pkg.Package, error) { + return func(_ source.FileResolver, classifier classifier, reader source.LocationReadCloser) ([]pkg.Package, error) { if !pat.MatchString(reader.RealPath) { return nil, nil } @@ -103,7 +107,7 @@ func fileNameTemplateVersionMatcher(fileNamePattern string, contentTemplate stri func fileContentsVersionMatcher(pattern string) evidenceMatcher { pat := regexp.MustCompile(pattern) - return func(classifier classifier, reader source.LocationReadCloser) ([]pkg.Package, error) { + return func(_ source.FileResolver, classifier classifier, reader source.LocationReadCloser) ([]pkg.Package, error) { contents, err := getContents(reader) if err != nil { return nil, fmt.Errorf("unable to get read contents for file: %w", err) @@ -114,6 +118,48 @@ func fileContentsVersionMatcher(pattern string) evidenceMatcher { } } +//nolint:gocognit +func sharedLibraryLookup(sharedLibraryPattern string, sharedLibraryMatcher evidenceMatcher) evidenceMatcher { + pat := regexp.MustCompile(sharedLibraryPattern) + return func(resolver source.FileResolver, classifier classifier, reader source.LocationReadCloser) (packages []pkg.Package, _ error) { + libs, err := sharedLibraries(reader) + if err != nil { + return nil, err + } + for _, lib := range libs { + if pat.MatchString(lib) { + locations, err := resolver.FilesByGlob("**/" + lib) + if err != nil { + return nil, err + } + for _, location := range locations { + readCloser, err := resolver.FileContentsByLocation(location) + if err != nil { + return nil, err + } + if readCloser == nil { + log.Debug("unable to get reader for location: %+v", location) + continue + } + locationReader := source.NewLocationReadCloser(location, readCloser) + pkgs, err := sharedLibraryMatcher(resolver, classifier, locationReader) + if err != nil { + return nil, err + } + for _, p := range pkgs { + // set the source binary as the first location + locationSet := source.NewLocationSet(reader.Location) + locationSet.Add(p.Locations.ToSlice()...) + p.Locations = locationSet + packages = append(packages, p) + } + } + } + } + return packages, nil + } +} + func mustPURL(purl string) packageurl.PackageURL { p, err := packageurl.FromString(purl) if err != nil { @@ -195,3 +241,52 @@ func singleCPE(cpeString string) []cpe.CPE { cpe.Must(cpeString), } } + +// sharedLibraries returns a list of all shared libraries found within a binary, currently +// supporting: elf, macho, and windows pe +func sharedLibraries(reader source.LocationReadCloser) ([]string, error) { + contents, err := io.ReadAll(reader) + if err != nil { + return nil, err + } + + r := bytes.NewReader(contents) + + e, err := elf.NewFile(r) + if err != nil { + log.Debug(err) + } + if e != nil { + symbols, err := e.ImportedLibraries() + if err != nil { + log.Debug(err) + } + return symbols, nil + } + + m, err := macho.NewFile(r) + if err != nil { + log.Debug(err) + } + if m != nil { + symbols, err := m.ImportedLibraries() + if err != nil { + log.Debug(err) + } + return symbols, nil + } + + p, err := pe.NewFile(r) + if err != nil { + log.Debug(err) + } + if p != nil { + symbols, err := p.ImportedLibraries() + if err != nil { + log.Debug(err) + } + return symbols, nil + } + + return nil, nil +} diff --git a/syft/pkg/cataloger/binary/classifier_test.go b/syft/pkg/cataloger/binary/classifier_test.go index cc7d2b0d6aa..25636f7ca77 100644 --- a/syft/pkg/cataloger/binary/classifier_test.go +++ b/syft/pkg/cataloger/binary/classifier_test.go @@ -70,7 +70,7 @@ func Test_ClassifierCPEs(t *testing.T) { location := locations[0] readCloser, err := resolver.FileContentsByLocation(location) require.NoError(t, err) - pkgs, err := test.classifier.EvidenceMatcher(test.classifier, source.NewLocationReadCloser(location, readCloser)) + pkgs, err := test.classifier.EvidenceMatcher(resolver, test.classifier, source.NewLocationReadCloser(location, readCloser)) require.NoError(t, err) require.Len(t, pkgs, 1) diff --git a/syft/pkg/cataloger/binary/default_classifiers.go b/syft/pkg/cataloger/binary/default_classifiers.go index da7bd1e206e..50ddc511daf 100644 --- a/syft/pkg/cataloger/binary/default_classifiers.go +++ b/syft/pkg/cataloger/binary/default_classifiers.go @@ -9,9 +9,18 @@ var defaultClassifiers = []classifier{ { Class: "python-binary", FileGlob: "**/python*", - EvidenceMatcher: fileNameTemplateVersionMatcher( - `(.*/|^)python(?P[0-9]+\.[0-9]+)$`, - `(?m)(?P{{ .version }}\.[0-9]+[-_a-zA-Z0-9]*)`), + EvidenceMatcher: evidenceMatchers( + // first, check for version information in the binary + fileNameTemplateVersionMatcher( + `(.*/|^)python(?P[0-9]+\.[0-9]+)$`, + `(?m)(?P{{ .version }}\.[0-9]+[-_a-zA-Z0-9]*)`), + // next try to find version information from libpython shared libraries + sharedLibraryLookup( + `^libpython(?P[0-9]+\.[0-9]+).so.*$`, + fileNameTemplateVersionMatcher( + `(.*/|^)libpython(?P[0-9]+\.[0-9]+).so.*$`, + `(?m)(?P{{ .version }}\.[0-9]+[-_a-zA-Z0-9]*)`)), + ), Package: "python", PURL: mustPURL("pkg:generic/python@version"), CPEs: []cpe.CPE{ @@ -32,18 +41,6 @@ var defaultClassifiers = []classifier{ cpe.Must("cpe:2.3:a:python:python:*:*:*:*:*:*:*:*"), }, }, - { - Class: "cpython-source", - FileGlob: "**/patchlevel.h", - EvidenceMatcher: fileContentsVersionMatcher( - `(?m)#define\s+PY_VERSION\s+"?(?P[0-9\.\-_a-zA-Z]+)"?`), - Package: "python", - PURL: mustPURL("pkg:generic/python@version"), - CPEs: []cpe.CPE{ - cpe.Must("cpe:2.3:a:python_software_foundation:python:*:*:*:*:*:*:*:*"), - cpe.Must("cpe:2.3:a:python:python:*:*:*:*:*:*:*:*"), - }, - }, { Class: "go-binary", FileGlob: "**/go", diff --git a/syft/pkg/cataloger/binary/test-fixtures/.gitignore b/syft/pkg/cataloger/binary/test-fixtures/.gitignore new file mode 100644 index 00000000000..fd45a0e70fc --- /dev/null +++ b/syft/pkg/cataloger/binary/test-fixtures/.gitignore @@ -0,0 +1 @@ +classifiers/dynamic diff --git a/syft/pkg/cataloger/binary/test-fixtures/Makefile b/syft/pkg/cataloger/binary/test-fixtures/Makefile new file mode 100644 index 00000000000..5ea0dc4be39 --- /dev/null +++ b/syft/pkg/cataloger/binary/test-fixtures/Makefile @@ -0,0 +1,21 @@ +.PHONY: all +all: classifiers/dynamic/python-binary-shared-lib-3.11 + +classifiers/dynamic/python-binary-shared-lib-3.11: + $(title,Download python shared library binaries v3.11) + $(eval $@_image := "python:3.11-slim@sha256:0b106e1d2bf485c2a41474bc9cd5103e9eea4e179f40f10741b53b127059221e") + ./get-docker-file.sh $($@_image) \ + /usr/local/bin/python3.11 \ + $@/python3 + ./get-docker-file.sh $($@_image) \ + /usr/local/lib/libpython3.11.so.1.0 \ + $@/libpython3.11.so.1.0 + +.PHONY: clean +clean: + rm -rf classifiers/dynamic + +.PHONY: cache.fingerprint +cache.fingerprint: # for CI + $(title,Install test fixture fingerprint) + @find ./classifiers/dynamic/* -type f -exec md5sum {} + | awk '{print $1}' | sort | tee /dev/stderr | md5sum | tee cache.fingerprint >> cache.fingerprint diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-source-3.9/patchlevel.h b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-source-3.9/patchlevel.h deleted file mode 100644 index c4245addd81..00000000000 --- a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-source-3.9/patchlevel.h +++ /dev/null @@ -1,7 +0,0 @@ -# note: this SHOULD match as python 3.9 - -some source code... - -#define PY_VERSION 3.9-aZ5 - -more source! \ No newline at end of file diff --git a/syft/pkg/cataloger/binary/test-fixtures/get-docker-file.sh b/syft/pkg/cataloger/binary/test-fixtures/get-docker-file.sh new file mode 100755 index 00000000000..d436d79d0b4 --- /dev/null +++ b/syft/pkg/cataloger/binary/test-fixtures/get-docker-file.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -uxe + +CTRID=$(docker create $1) + +function cleanup() { + docker rm "${CTRID}" +} + +trap cleanup EXIT +set +e + +mkdir -p $(dirname $3) + +docker cp ${CTRID}:$2 $3 From 6ad83e99cdafbaa8b9099a4023038a11345b3112 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Fri, 3 Mar 2023 15:17:56 -0500 Subject: [PATCH 2/7] chore: only scan executables for sharedLibraryLookup, fix evidenceMatchers Signed-off-by: Keith Zantow --- syft/pkg/cataloger/binary/classifier.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/syft/pkg/cataloger/binary/classifier.go b/syft/pkg/cataloger/binary/classifier.go index bd058d5b4f2..50f710f74c5 100644 --- a/syft/pkg/cataloger/binary/classifier.go +++ b/syft/pkg/cataloger/binary/classifier.go @@ -7,6 +7,7 @@ import ( "debug/pe" "fmt" "io" + "os" "reflect" "regexp" "text/template" @@ -57,7 +58,14 @@ type evidenceMatcher func(resolver source.FileResolver, classifier classifier, r func evidenceMatchers(matchers ...evidenceMatcher) evidenceMatcher { return func(resolver source.FileResolver, classifier classifier, reader source.LocationReadCloser) ([]pkg.Package, error) { - for _, matcher := range matchers { + for i, matcher := range matchers { + if i > 0 { + readCloser, err := resolver.FileContentsByLocation(reader.Location) + if err != nil { + return nil, err + } + reader = source.NewLocationReadCloser(reader.Location, readCloser) + } match, err := matcher(resolver, classifier, reader) if err != nil { return nil, err @@ -118,10 +126,23 @@ func fileContentsVersionMatcher(pattern string) evidenceMatcher { } } +func isExecutable(mode os.FileMode) bool { + return mode&0111 != 0 +} + //nolint:gocognit func sharedLibraryLookup(sharedLibraryPattern string, sharedLibraryMatcher evidenceMatcher) evidenceMatcher { pat := regexp.MustCompile(sharedLibraryPattern) return func(resolver source.FileResolver, classifier classifier, reader source.LocationReadCloser) (packages []pkg.Package, _ error) { + meta, err := resolver.FileMetadataByLocation(reader.Location) + if err != nil { + return nil, err + } + + if !isExecutable(meta.Mode) { + return nil, nil + } + libs, err := sharedLibraries(reader) if err != nil { return nil, err From 387eb30361059aa089be2cdb27925f7e32e3f6aa Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Sat, 4 Mar 2023 09:07:40 -0500 Subject: [PATCH 3/7] chore: refactor to avoid closed readers Signed-off-by: Keith Zantow --- syft/pkg/cataloger/binary/cataloger.go | 7 +- syft/pkg/cataloger/binary/cataloger_test.go | 5 +- syft/pkg/cataloger/binary/classifier.go | 79 ++++++++++---------- syft/pkg/cataloger/binary/classifier_test.go | 6 +- 4 files changed, 45 insertions(+), 52 deletions(-) diff --git a/syft/pkg/cataloger/binary/cataloger.go b/syft/pkg/cataloger/binary/cataloger.go index eb17ae5e1d4..cf58af15ca7 100644 --- a/syft/pkg/cataloger/binary/cataloger.go +++ b/syft/pkg/cataloger/binary/cataloger.go @@ -74,12 +74,7 @@ func catalog(resolver source.FileResolver, cls classifier) (packages []pkg.Packa return nil, err } for _, location := range locations { - reader, err := resolver.FileContentsByLocation(location) - if err != nil { - return nil, err - } - locationReader := source.NewLocationReadCloser(location, reader) - pkgs, err := cls.EvidenceMatcher(resolver, cls, locationReader) + pkgs, err := cls.EvidenceMatcher(resolver, cls, location) if err != nil { return nil, err } diff --git a/syft/pkg/cataloger/binary/cataloger_test.go b/syft/pkg/cataloger/binary/cataloger_test.go index 740f9973bd2..610d814b825 100644 --- a/syft/pkg/cataloger/binary/cataloger_test.go +++ b/syft/pkg/cataloger/binary/cataloger_test.go @@ -323,8 +323,9 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Locations: locations("python3", "libpython3.11.so.1.0"), Metadata: pkg.BinaryMetadata{ Matches: []pkg.ClassifierMatch{ - match("python-binary"), - match("python-binary-lib"), + match("python-binary", "python3"), + match("python-binary", "libpython3.11.so.1.0"), + match("python-binary-lib", "libpython3.11.so.1.0"), }, }, }, diff --git a/syft/pkg/cataloger/binary/classifier.go b/syft/pkg/cataloger/binary/classifier.go index 50f710f74c5..2d32d97416c 100644 --- a/syft/pkg/cataloger/binary/classifier.go +++ b/syft/pkg/cataloger/binary/classifier.go @@ -54,19 +54,12 @@ type classifier struct { } // evidenceMatcher is a function called to catalog Packages that match some sort of evidence -type evidenceMatcher func(resolver source.FileResolver, classifier classifier, reader source.LocationReadCloser) ([]pkg.Package, error) +type evidenceMatcher func(resolver source.FileResolver, classifier classifier, location source.Location) ([]pkg.Package, error) func evidenceMatchers(matchers ...evidenceMatcher) evidenceMatcher { - return func(resolver source.FileResolver, classifier classifier, reader source.LocationReadCloser) ([]pkg.Package, error) { - for i, matcher := range matchers { - if i > 0 { - readCloser, err := resolver.FileContentsByLocation(reader.Location) - if err != nil { - return nil, err - } - reader = source.NewLocationReadCloser(reader.Location, readCloser) - } - match, err := matcher(resolver, classifier, reader) + return func(resolver source.FileResolver, classifier classifier, location source.Location) ([]pkg.Package, error) { + for _, matcher := range matchers { + match, err := matcher(resolver, classifier, location) if err != nil { return nil, err } @@ -80,12 +73,12 @@ func evidenceMatchers(matchers ...evidenceMatcher) evidenceMatcher { func fileNameTemplateVersionMatcher(fileNamePattern string, contentTemplate string) evidenceMatcher { pat := regexp.MustCompile(fileNamePattern) - return func(_ source.FileResolver, classifier classifier, reader source.LocationReadCloser) ([]pkg.Package, error) { - if !pat.MatchString(reader.RealPath) { + return func(resolver source.FileResolver, classifier classifier, location source.Location) ([]pkg.Package, error) { + if !pat.MatchString(location.RealPath) { return nil, nil } - filepathNamedGroupValues := internal.MatchNamedCaptureGroups(pat, reader.RealPath) + filepathNamedGroupValues := internal.MatchNamedCaptureGroups(pat, location.RealPath) tmpl, err := template.New("").Parse(contentTemplate) if err != nil { @@ -103,26 +96,26 @@ func fileNameTemplateVersionMatcher(fileNamePattern string, contentTemplate stri return nil, fmt.Errorf("unable to compile rendered regex=%q: %w", patternBuf.String(), err) } - contents, err := getContents(reader) + contents, err := getContents(resolver, location) if err != nil { return nil, fmt.Errorf("unable to get read contents for file: %w", err) } matchMetadata := internal.MatchNamedCaptureGroups(tmplPattern, string(contents)) - return singlePackage(classifier, reader, matchMetadata), nil + return singlePackage(classifier, location, matchMetadata), nil } } func fileContentsVersionMatcher(pattern string) evidenceMatcher { pat := regexp.MustCompile(pattern) - return func(_ source.FileResolver, classifier classifier, reader source.LocationReadCloser) ([]pkg.Package, error) { - contents, err := getContents(reader) + return func(resolver source.FileResolver, classifier classifier, location source.Location) ([]pkg.Package, error) { + contents, err := getContents(resolver, location) if err != nil { return nil, fmt.Errorf("unable to get read contents for file: %w", err) } matchMetadata := internal.MatchNamedCaptureGroups(pat, string(contents)) - return singlePackage(classifier, reader, matchMetadata), nil + return singlePackage(classifier, location, matchMetadata), nil } } @@ -133,8 +126,8 @@ func isExecutable(mode os.FileMode) bool { //nolint:gocognit func sharedLibraryLookup(sharedLibraryPattern string, sharedLibraryMatcher evidenceMatcher) evidenceMatcher { pat := regexp.MustCompile(sharedLibraryPattern) - return func(resolver source.FileResolver, classifier classifier, reader source.LocationReadCloser) (packages []pkg.Package, _ error) { - meta, err := resolver.FileMetadataByLocation(reader.Location) + return func(resolver source.FileResolver, classifier classifier, location source.Location) (packages []pkg.Package, _ error) { + meta, err := resolver.FileMetadataByLocation(location) if err != nil { return nil, err } @@ -143,7 +136,7 @@ func sharedLibraryLookup(sharedLibraryPattern string, sharedLibraryMatcher evide return nil, nil } - libs, err := sharedLibraries(reader) + libs, err := sharedLibraries(resolver, location) if err != nil { return nil, err } @@ -153,25 +146,25 @@ func sharedLibraryLookup(sharedLibraryPattern string, sharedLibraryMatcher evide if err != nil { return nil, err } - for _, location := range locations { - readCloser, err := resolver.FileContentsByLocation(location) - if err != nil { - return nil, err - } - if readCloser == nil { - log.Debug("unable to get reader for location: %+v", location) - continue - } - locationReader := source.NewLocationReadCloser(location, readCloser) - pkgs, err := sharedLibraryMatcher(resolver, classifier, locationReader) + for _, libraryLication := range locations { + pkgs, err := sharedLibraryMatcher(resolver, classifier, libraryLication) if err != nil { return nil, err } for _, p := range pkgs { // set the source binary as the first location - locationSet := source.NewLocationSet(reader.Location) + locationSet := source.NewLocationSet(location) locationSet.Add(p.Locations.ToSlice()...) p.Locations = locationSet + meta, _ := p.Metadata.(pkg.BinaryMetadata) + p.Metadata = pkg.BinaryMetadata{ + Matches: append([]pkg.ClassifierMatch{ + { + Classifier: classifier.Class, + Location: location, + }, + }, meta.Matches...), + } packages = append(packages, p) } } @@ -189,7 +182,7 @@ func mustPURL(purl string) packageurl.PackageURL { return p } -func singlePackage(classifier classifier, reader source.LocationReadCloser, matchMetadata map[string]string) []pkg.Package { +func singlePackage(classifier classifier, location source.Location, matchMetadata map[string]string) []pkg.Package { version, ok := matchMetadata["version"] if !ok { return nil @@ -207,7 +200,7 @@ func singlePackage(classifier classifier, reader source.LocationReadCloser, matc p := pkg.Package{ Name: classifier.Package, Version: version, - Locations: source.NewLocationSet(reader.Location), + Locations: source.NewLocationSet(location), Type: pkg.BinaryPkg, CPEs: cpes, FoundBy: catalogerName, @@ -216,7 +209,7 @@ func singlePackage(classifier classifier, reader source.LocationReadCloser, matc Matches: []pkg.ClassifierMatch{ { Classifier: classifier.Class, - Location: reader.Location, + Location: location, }, }, }, @@ -241,7 +234,13 @@ func singlePackage(classifier classifier, reader source.LocationReadCloser, matc return []pkg.Package{p} } -func getContents(reader source.LocationReadCloser) ([]byte, error) { +func getContents(resolver source.FileResolver, location source.Location) ([]byte, error) { + readCloser, err := resolver.FileContentsByLocation(location) + if err != nil { + return nil, err + } + reader := source.NewLocationReadCloser(location, readCloser) + unionReader, err := unionreader.GetUnionReader(reader.ReadCloser) if err != nil { return nil, fmt.Errorf("unable to get union reader for file: %w", err) @@ -265,8 +264,8 @@ func singleCPE(cpeString string) []cpe.CPE { // sharedLibraries returns a list of all shared libraries found within a binary, currently // supporting: elf, macho, and windows pe -func sharedLibraries(reader source.LocationReadCloser) ([]string, error) { - contents, err := io.ReadAll(reader) +func sharedLibraries(resolver source.FileResolver, location source.Location) ([]string, error) { + contents, err := getContents(resolver, location) if err != nil { return nil, err } diff --git a/syft/pkg/cataloger/binary/classifier_test.go b/syft/pkg/cataloger/binary/classifier_test.go index 25636f7ca77..82260a0e633 100644 --- a/syft/pkg/cataloger/binary/classifier_test.go +++ b/syft/pkg/cataloger/binary/classifier_test.go @@ -67,10 +67,8 @@ func Test_ClassifierCPEs(t *testing.T) { locations, err := resolver.FilesByPath(test.fixture) require.NoError(t, err) require.Len(t, locations, 1) - location := locations[0] - readCloser, err := resolver.FileContentsByLocation(location) - require.NoError(t, err) - pkgs, err := test.classifier.EvidenceMatcher(resolver, test.classifier, source.NewLocationReadCloser(location, readCloser)) + + pkgs, err := test.classifier.EvidenceMatcher(resolver, test.classifier, locations[0]) require.NoError(t, err) require.Len(t, pkgs, 1) From 73c025275e070e589957b4c2d57729961237d7db Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Sat, 4 Mar 2023 09:19:04 -0500 Subject: [PATCH 4/7] chore: reduce nesting Signed-off-by: Keith Zantow --- syft/pkg/cataloger/binary/classifier.go | 46 +++++++++++++------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/syft/pkg/cataloger/binary/classifier.go b/syft/pkg/cataloger/binary/classifier.go index 2d32d97416c..2863f8adbd0 100644 --- a/syft/pkg/cataloger/binary/classifier.go +++ b/syft/pkg/cataloger/binary/classifier.go @@ -141,32 +141,34 @@ func sharedLibraryLookup(sharedLibraryPattern string, sharedLibraryMatcher evide return nil, err } for _, lib := range libs { - if pat.MatchString(lib) { - locations, err := resolver.FilesByGlob("**/" + lib) + if !pat.MatchString(lib) { + continue + } + + locations, err := resolver.FilesByGlob("**/" + lib) + if err != nil { + return nil, err + } + for _, libraryLication := range locations { + pkgs, err := sharedLibraryMatcher(resolver, classifier, libraryLication) if err != nil { return nil, err } - for _, libraryLication := range locations { - pkgs, err := sharedLibraryMatcher(resolver, classifier, libraryLication) - if err != nil { - return nil, err - } - for _, p := range pkgs { - // set the source binary as the first location - locationSet := source.NewLocationSet(location) - locationSet.Add(p.Locations.ToSlice()...) - p.Locations = locationSet - meta, _ := p.Metadata.(pkg.BinaryMetadata) - p.Metadata = pkg.BinaryMetadata{ - Matches: append([]pkg.ClassifierMatch{ - { - Classifier: classifier.Class, - Location: location, - }, - }, meta.Matches...), - } - packages = append(packages, p) + for _, p := range pkgs { + // set the source binary as the first location + locationSet := source.NewLocationSet(location) + locationSet.Add(p.Locations.ToSlice()...) + p.Locations = locationSet + meta, _ := p.Metadata.(pkg.BinaryMetadata) + p.Metadata = pkg.BinaryMetadata{ + Matches: append([]pkg.ClassifierMatch{ + { + Classifier: classifier.Class, + Location: location, + }, + }, meta.Matches...), } + packages = append(packages, p) } } } From 73eb9b28aed891020ce1b73058059c2636d55aa0 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Sat, 4 Mar 2023 09:21:39 -0500 Subject: [PATCH 5/7] chore: slight cleanup Signed-off-by: Keith Zantow --- syft/pkg/cataloger/binary/classifier.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/syft/pkg/cataloger/binary/classifier.go b/syft/pkg/cataloger/binary/classifier.go index 2863f8adbd0..1a842e1b881 100644 --- a/syft/pkg/cataloger/binary/classifier.go +++ b/syft/pkg/cataloger/binary/classifier.go @@ -237,13 +237,12 @@ func singlePackage(classifier classifier, location source.Location, matchMetadat } func getContents(resolver source.FileResolver, location source.Location) ([]byte, error) { - readCloser, err := resolver.FileContentsByLocation(location) + reader, err := resolver.FileContentsByLocation(location) if err != nil { return nil, err } - reader := source.NewLocationReadCloser(location, readCloser) - unionReader, err := unionreader.GetUnionReader(reader.ReadCloser) + unionReader, err := unionreader.GetUnionReader(reader) if err != nil { return nil, fmt.Errorf("unable to get union reader for file: %w", err) } From 1ce466908c795bba863c85ccb256af9a12e2caf4 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Tue, 7 Mar 2023 10:02:59 -0500 Subject: [PATCH 6/7] fix: add more tests and update python version regexes Signed-off-by: Keith Zantow --- syft/pkg/cataloger/binary/cataloger_test.go | 124 ++++++++++++------ .../cataloger/binary/default_classifiers.go | 36 ++--- .../cataloger/binary/test-fixtures/Makefile | 25 +++- .../positive/python-binary-3.6/python3.6 | Bin 66 -> 69 bytes .../positive/python-duplicates/dir/python3.8 | Bin 7 -> 70 bytes .../python-duplicates/libpython3.8.so | Bin 7 -> 122 bytes .../positive/python-duplicates/python3.8 | Bin 7 -> 70 bytes .../{get-docker-file.sh => get-image-file.sh} | 0 8 files changed, 127 insertions(+), 58 deletions(-) rename syft/pkg/cataloger/binary/test-fixtures/{get-docker-file.sh => get-image-file.sh} (100%) diff --git a/syft/pkg/cataloger/binary/cataloger_test.go b/syft/pkg/cataloger/binary/cataloger_test.go index 610d814b825..3270f5d2f35 100644 --- a/syft/pkg/cataloger/binary/cataloger_test.go +++ b/syft/pkg/cataloger/binary/cataloger_test.go @@ -4,8 +4,11 @@ import ( "errors" "fmt" "io" + "strings" "testing" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -68,24 +71,6 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Metadata: metadata("postgresql-binary"), }, }, - { - name: "positive-python-duplicates", - fixtureDir: "test-fixtures/classifiers/positive/python-duplicates", - expected: pkg.Package{ - Name: "python", - Version: "3.8.16", - Type: "binary", - PURL: "pkg:generic/python@3.8.16", - Locations: locations("dir/python3.8", "python3.8", "libpython3.8.so"), - Metadata: pkg.BinaryMetadata{ - Matches: []pkg.ClassifierMatch{ - match("python-binary", "dir/python3.8"), - match("python-binary", "python3.8"), - match("python-binary-lib", "libpython3.8.so"), - }, - }, - }, - }, { name: "positive-traefik-2.9.6", fixtureDir: "test-fixtures/classifiers/positive/traefik-2.9.6", @@ -330,17 +315,67 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { }, }, }, + { + name: "positive-python-3.9-from-shared-redhat-lib", + fixtureDir: "test-fixtures/classifiers/dynamic/python-binary-shared-lib-redhat-3.9", + expected: pkg.Package{ + Name: "python", + Version: "3.9.13", + PURL: "pkg:generic/python@3.9.13", + Locations: locations("python3.9", "libpython3.9.so.1.0"), + Metadata: pkg.BinaryMetadata{ + Matches: []pkg.ClassifierMatch{ + match("python-binary", "python3.9"), + match("python-binary", "libpython3.9.so.1.0"), + match("python-binary-lib", "libpython3.9.so.1.0"), + }, + }, + }, + }, + { + name: "positive-python-binary-with-version-3.9", + fixtureDir: "test-fixtures/classifiers/dynamic/python-binary-with-version-3.9", + expected: pkg.Package{ + Name: "python", + Version: "3.9.2", + PURL: "pkg:generic/python@3.9.2", + Locations: locations("python3.9"), + Metadata: pkg.BinaryMetadata{ + Matches: []pkg.ClassifierMatch{ + match("python-binary", "python3.9"), + }, + }, + }, + }, { name: "positive-python3.6", fixtureDir: "test-fixtures/classifiers/positive/python-binary-3.6", expected: pkg.Package{ Name: "python", - Version: "3.6.3a-vZ9", - PURL: "pkg:generic/python@3.6.3a-vZ9", + Version: "3.6.3", + PURL: "pkg:generic/python@3.6.3", Locations: locations("python3.6"), Metadata: metadata("python-binary"), }, }, + { + name: "positive-python-duplicates", + fixtureDir: "test-fixtures/classifiers/positive/python-duplicates", + expected: pkg.Package{ + Name: "python", + Version: "3.8.16", + Type: "binary", + PURL: "pkg:generic/python@3.8.16", + Locations: locations("dir/python3.8", "python3.8", "libpython3.8.so"), + Metadata: pkg.BinaryMetadata{ + Matches: []pkg.ClassifierMatch{ + match("python-binary", "dir/python3.8"), + match("python-binary", "python3.8"), + match("python-binary-lib", "libpython3.8.so"), + }, + }, + }, + }, { name: "positive-go", fixtureDir: "test-fixtures/classifiers/positive/go-1.14", @@ -496,17 +531,6 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { require.NoError(t, err) for _, p := range packages { - expectedLocations := test.expected.Locations.ToSlice() - gotLocations := p.Locations.ToSlice() - require.Len(t, gotLocations, len(expectedLocations)) - - for i, expectedLocation := range expectedLocations { - gotLocation := gotLocations[i] - if expectedLocation.RealPath != gotLocation.RealPath { - t.Fatalf("locations do not match; expected: %v got: %v", expectedLocations, gotLocations) - } - } - assertPackagesAreEqual(t, test.expected, p) } }) @@ -616,6 +640,21 @@ func match(classifier string, paths ...string) pkg.ClassifierMatch { } func assertPackagesAreEqual(t *testing.T, expected pkg.Package, p pkg.Package) { + var failMessages []string + expectedLocations := expected.Locations.ToSlice() + gotLocations := p.Locations.ToSlice() + + if len(expectedLocations) != len(gotLocations) { + failMessages = append(failMessages, "locations are not equal length") + } else { + for i, expectedLocation := range expectedLocations { + gotLocation := gotLocations[i] + if expectedLocation.RealPath != gotLocation.RealPath { + failMessages = append(failMessages, fmt.Sprintf("locations do not match; expected: %v got: %v", expectedLocation.RealPath, gotLocation.RealPath)) + } + } + } + m1 := expected.Metadata.(pkg.BinaryMetadata).Matches m2 := p.Metadata.(pkg.BinaryMetadata).Matches matches := true @@ -638,17 +677,26 @@ func assertPackagesAreEqual(t *testing.T, expected pkg.Package, p pkg.Package) { } else { matches = false } + + if !matches { + failMessages = append(failMessages, "classifier matches not equal") + } if expected.Name != p.Name || expected.Version != p.Version || - expected.PURL != p.PURL || - !matches { - assert.Failf(t, "packages not equal", "%v != %v", stringifyPkg(expected), stringifyPkg(p)) + expected.PURL != p.PURL { + failMessages = append(failMessages, "packages do not match") } -} -func stringifyPkg(p pkg.Package) string { - matches := p.Metadata.(pkg.BinaryMetadata).Matches - return fmt.Sprintf("(name=%s, version=%s, purl=%s, matches=%+v)", p.Name, p.Version, p.PURL, matches) + if len(failMessages) > 0 { + assert.Failf(t, strings.Join(failMessages, "; "), "diff: %s", + cmp.Diff(expected, p, + cmp.Transformer("Locations", func(l source.LocationSet) []source.Location { + return l.ToSlice() + }), + cmpopts.IgnoreUnexported(pkg.Package{}, source.Location{}), + cmpopts.IgnoreFields(pkg.Package{}, "CPEs", "FoundBy", "MetadataType", "Type"), + )) + } } type panicyResolver struct { diff --git a/syft/pkg/cataloger/binary/default_classifiers.go b/syft/pkg/cataloger/binary/default_classifiers.go index 50ddc511daf..01c707eeb6f 100644 --- a/syft/pkg/cataloger/binary/default_classifiers.go +++ b/syft/pkg/cataloger/binary/default_classifiers.go @@ -10,16 +10,14 @@ var defaultClassifiers = []classifier{ Class: "python-binary", FileGlob: "**/python*", EvidenceMatcher: evidenceMatchers( - // first, check for version information in the binary - fileNameTemplateVersionMatcher( - `(.*/|^)python(?P[0-9]+\.[0-9]+)$`, - `(?m)(?P{{ .version }}\.[0-9]+[-_a-zA-Z0-9]*)`), - // next try to find version information from libpython shared libraries + // try to find version information from libpython shared libraries sharedLibraryLookup( - `^libpython(?P[0-9]+\.[0-9]+).so.*$`, - fileNameTemplateVersionMatcher( - `(.*/|^)libpython(?P[0-9]+\.[0-9]+).so.*$`, - `(?m)(?P{{ .version }}\.[0-9]+[-_a-zA-Z0-9]*)`)), + `^libpython[0-9]+(?:\.[0-9]+)+\.so.*$`, + libpythonMatcher), + // check for version information in the binary + fileNameTemplateVersionMatcher( + `(?:.*/|^)python(?P[0-9]+(?:\.[0-9]+)+)$`, + pythonVersionTemplate), ), Package: "python", PURL: mustPURL("pkg:generic/python@version"), @@ -29,13 +27,11 @@ var defaultClassifiers = []classifier{ }, }, { - Class: "python-binary-lib", - FileGlob: "**/libpython*.so*", - EvidenceMatcher: fileNameTemplateVersionMatcher( - `(.*/|^)libpython(?P[0-9]+\.[0-9]+).so.*$`, - `(?m)(?P{{ .version }}\.[0-9]+[-_a-zA-Z0-9]*)`), - Package: "python", - PURL: mustPURL("pkg:generic/python@version"), + Class: "python-binary-lib", + FileGlob: "**/libpython*.so*", + EvidenceMatcher: libpythonMatcher, + Package: "python", + PURL: mustPURL("pkg:generic/python@version"), CPEs: []cpe.CPE{ cpe.Must("cpe:2.3:a:python_software_foundation:python:*:*:*:*:*:*:*:*"), cpe.Must("cpe:2.3:a:python:python:*:*:*:*:*:*:*:*"), @@ -225,3 +221,11 @@ var defaultClassifiers = []classifier{ CPEs: singleCPE("cpe:2.3:a:rust-lang:rust:*:*:*:*:*:*:*:*"), }, } + +// in both binaries and shared libraries, the version pattern is [NUL]3.11.2[NUL] +var pythonVersionTemplate = `(?m)\x00(?P{{ .version }}[-._a-zA-Z0-9]*)\x00` + +var libpythonMatcher = fileNameTemplateVersionMatcher( + `(?:.*/|^)libpython(?P[0-9]+(?:\.[0-9]+)+)\.so.*$`, + pythonVersionTemplate, +) diff --git a/syft/pkg/cataloger/binary/test-fixtures/Makefile b/syft/pkg/cataloger/binary/test-fixtures/Makefile index 5ea0dc4be39..a23faaf887e 100644 --- a/syft/pkg/cataloger/binary/test-fixtures/Makefile +++ b/syft/pkg/cataloger/binary/test-fixtures/Makefile @@ -1,16 +1,33 @@ .PHONY: all -all: classifiers/dynamic/python-binary-shared-lib-3.11 +all: \ + classifiers/dynamic/python-binary-shared-lib-3.11 \ + classifiers/dynamic/python-binary-shared-lib-redhat-3.9 \ + classifiers/dynamic/python-binary-with-version-3.9 classifiers/dynamic/python-binary-shared-lib-3.11: - $(title,Download python shared library binaries v3.11) $(eval $@_image := "python:3.11-slim@sha256:0b106e1d2bf485c2a41474bc9cd5103e9eea4e179f40f10741b53b127059221e") - ./get-docker-file.sh $($@_image) \ + ./get-image-file.sh $($@_image) \ /usr/local/bin/python3.11 \ $@/python3 - ./get-docker-file.sh $($@_image) \ + ./get-image-file.sh $($@_image) \ /usr/local/lib/libpython3.11.so.1.0 \ $@/libpython3.11.so.1.0 +classifiers/dynamic/python-binary-shared-lib-redhat-3.9: + $(eval $@_image := "registry.access.redhat.com/ubi8/python-39@sha256:f3cf958b96ce016b63e3e163e488f52e42891304dafef5a0811563f22e3cbad0") + ./get-image-file.sh $($@_image) \ + /usr/bin/python3.9 \ + $@/python3.9 + ./get-image-file.sh $($@_image) \ + /usr/lib64/libpython3.9.so.1.0 \ + $@/libpython3.9.so.1.0 + +classifiers/dynamic/python-binary-with-version-3.9: + $(eval $@_image := "python:3.9.16-bullseye@sha256:93fb93c461a2e47a2176706fad1f39eaacd5dd40e19c0b018699a28c03eb2e2a") + ./get-image-file.sh $($@_image) \ + /usr/bin/python3.9 \ + $@/python3.9 + .PHONY: clean clean: rm -rf classifiers/dynamic diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-binary-3.6/python3.6 b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-binary-3.6/python3.6 index fecd79a46d9440a07d17655ac7aa79554a42749f..e58b8228f8030a4d8d56c76c3d47b034d150d2dc 100644 GIT binary patch literal 69 zcmXS7D5xyS$j>v@vt$T}4Ds;y^YnN1bM|-f^mAu0)-%&HX0RzJN=?hGu+z`UOwxy_ Rj?jx>2mtF-P%Y+S007lR6v+Sp literal 66 zcmY#Z$jdKDwNfa_$ShU}_V5q&aZ$)kEJ@B#NGw(;s4U6I&r>kgGvngQ%g-!M1+w&v Q6Lrg?EERL}i&DX20KDoINB{r; diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-duplicates/dir/python3.8 b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-duplicates/dir/python3.8 index eee6392d5c718de042804ec9f0b3d600fb56a496..9bca09aa819dacd0828b82ad4b0eccc55407dbfe 100644 GIT binary patch literal 70 zcmXS7D5xyS$j>v@vt$T}4Ds;y^YnN1bM|-f^mAu0*0azvG-I$SC`wJstgzG1$xPCR SD38#KU RRE2=Z5D$O9aK|7&E&$|XC_exI literal 7 OcmXrkv(Pg%;{pH$Sphi! diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-duplicates/python3.8 b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/python-duplicates/python3.8 index eee6392d5c718de042804ec9f0b3d600fb56a496..9bca09aa819dacd0828b82ad4b0eccc55407dbfe 100644 GIT binary patch literal 70 zcmXS7D5xyS$j>v@vt$T}4Ds;y^YnN1bM|-f^mAu0*0azvG-I$SC`wJstgzG1$xPCR SD38#KU Date: Tue, 7 Mar 2023 10:37:32 -0500 Subject: [PATCH 7/7] chore: address PR feedback Signed-off-by: Keith Zantow --- syft/pkg/cataloger/binary/classifier.go | 35 +++++-------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/syft/pkg/cataloger/binary/classifier.go b/syft/pkg/cataloger/binary/classifier.go index 1a842e1b881..13b42f9926c 100644 --- a/syft/pkg/cataloger/binary/classifier.go +++ b/syft/pkg/cataloger/binary/classifier.go @@ -7,7 +7,6 @@ import ( "debug/pe" "fmt" "io" - "os" "reflect" "regexp" "text/template" @@ -119,23 +118,10 @@ func fileContentsVersionMatcher(pattern string) evidenceMatcher { } } -func isExecutable(mode os.FileMode) bool { - return mode&0111 != 0 -} - //nolint:gocognit func sharedLibraryLookup(sharedLibraryPattern string, sharedLibraryMatcher evidenceMatcher) evidenceMatcher { pat := regexp.MustCompile(sharedLibraryPattern) return func(resolver source.FileResolver, classifier classifier, location source.Location) (packages []pkg.Package, _ error) { - meta, err := resolver.FileMetadataByLocation(location) - if err != nil { - return nil, err - } - - if !isExecutable(meta.Mode) { - return nil, nil - } - libs, err := sharedLibraries(resolver, location) if err != nil { return nil, err @@ -273,38 +259,29 @@ func sharedLibraries(resolver source.FileResolver, location source.Location) ([] r := bytes.NewReader(contents) - e, err := elf.NewFile(r) - if err != nil { - log.Debug(err) - } + e, _ := elf.NewFile(r) if e != nil { symbols, err := e.ImportedLibraries() if err != nil { - log.Debug(err) + log.Debugf("unable to read elf binary at: %s -- %s", location.RealPath, err) } return symbols, nil } - m, err := macho.NewFile(r) - if err != nil { - log.Debug(err) - } + m, _ := macho.NewFile(r) if m != nil { symbols, err := m.ImportedLibraries() if err != nil { - log.Debug(err) + log.Debugf("unable to read macho binary at: %s -- %s", location.RealPath, err) } return symbols, nil } - p, err := pe.NewFile(r) - if err != nil { - log.Debug(err) - } + p, _ := pe.NewFile(r) if p != nil { symbols, err := p.ImportedLibraries() if err != nil { - log.Debug(err) + log.Debugf("unable to read pe binary at: %s -- %s", location.RealPath, err) } return symbols, nil }