mirror of
https://github.com/anchore/syft.git
synced 2026-11-02 15:36:41 +01:00
feat: report unknowns in sbom (#2998)
Signed-off-by: Keith Zantow <kzantow@gmail.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
co-authored by
Alex Goodman
parent
4d7ed9f749
commit
ccbee94b87
@@ -6,8 +6,10 @@ package binary
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -59,12 +61,14 @@ func (c cataloger) Name() string {
|
||||
func (c cataloger) Catalog(_ context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
var packages []pkg.Package
|
||||
var relationships []artifact.Relationship
|
||||
var errs error
|
||||
|
||||
for _, cls := range c.classifiers {
|
||||
log.WithFields("classifier", cls.Class).Trace("cataloging binaries")
|
||||
newPkgs, err := catalog(resolver, cls)
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "classifier", cls.Class).Warn("unable to catalog binary package: %w", err)
|
||||
log.WithFields("error", err, "classifier", cls.Class).Debugf("unable to catalog binary package: %v", err)
|
||||
errs = unknown.Join(errs, fmt.Errorf("%s: %w", cls.Class, err))
|
||||
continue
|
||||
}
|
||||
newPackages:
|
||||
@@ -82,7 +86,7 @@ func (c cataloger) Catalog(_ context.Context, resolver file.Resolver) ([]pkg.Pac
|
||||
}
|
||||
}
|
||||
|
||||
return packages, relationships, nil
|
||||
return packages, relationships, errs
|
||||
}
|
||||
|
||||
// mergePackages merges information from the extra package into the target package
|
||||
@@ -98,6 +102,7 @@ func mergePackages(target *pkg.Package, extra *pkg.Package) {
|
||||
}
|
||||
|
||||
func catalog(resolver file.Resolver, cls Classifier) (packages []pkg.Package, err error) {
|
||||
var errs error
|
||||
locations, err := resolver.FilesByGlob(cls.FileGlob)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -105,11 +110,12 @@ func catalog(resolver file.Resolver, cls Classifier) (packages []pkg.Package, er
|
||||
for _, location := range locations {
|
||||
pkgs, err := cls.EvidenceMatcher(cls, matcherContext{resolver: resolver, location: location})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
errs = unknown.Append(errs, location, err)
|
||||
continue
|
||||
}
|
||||
packages = append(packages, pkgs...)
|
||||
}
|
||||
return packages, nil
|
||||
return packages, errs
|
||||
}
|
||||
|
||||
// packagesMatch returns true if the binary packages "match" based on basic criteria
|
||||
|
||||
@@ -1668,7 +1668,7 @@ func Test_Cataloger_ResilientToErrors(t *testing.T) {
|
||||
|
||||
resolver := &panicyResolver{}
|
||||
_, _, err := c.Catalog(context.Background(), resolver)
|
||||
assert.NoError(t, err)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, resolver.searchCalled)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/mimetype"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/internal/unionreader"
|
||||
@@ -52,6 +53,7 @@ func (c *elfPackageCataloger) Name() string {
|
||||
}
|
||||
|
||||
func (c *elfPackageCataloger) Catalog(_ context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
var errs error
|
||||
locations, err := resolver.FilesByMIMEType(mimetype.ExecutableMIMETypeSet.List()...)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to get binary files by mime type: %w", err)
|
||||
@@ -62,7 +64,8 @@ func (c *elfPackageCataloger) Catalog(_ context.Context, resolver file.Resolver)
|
||||
for _, location := range locations {
|
||||
notes, key, err := parseElfPackageNotes(resolver, location, c)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
errs = unknown.Append(errs, location, err)
|
||||
continue
|
||||
}
|
||||
if notes == nil {
|
||||
continue
|
||||
@@ -87,7 +90,7 @@ func (c *elfPackageCataloger) Catalog(_ context.Context, resolver file.Resolver)
|
||||
// why not return relationships? We have an executable cataloger that will note the dynamic libraries imported by
|
||||
// each binary. After all files and packages are processed there is a final task that creates package-to-package
|
||||
// and package-to-file relationships based on the dynamic libraries imported by each binary.
|
||||
return pkgs, nil, nil
|
||||
return pkgs, nil, errs
|
||||
}
|
||||
|
||||
func parseElfPackageNotes(resolver file.Resolver, location file.Location, c *elfPackageCataloger) (*elfBinaryPackageNotes, elfPackageKey, error) {
|
||||
@@ -104,7 +107,7 @@ func parseElfPackageNotes(resolver file.Resolver, location file.Location, c *elf
|
||||
|
||||
if err != nil {
|
||||
log.WithFields("file", location.Path(), "error", err).Trace("unable to parse ELF notes")
|
||||
return nil, elfPackageKey{}, nil
|
||||
return nil, elfPackageKey{}, err
|
||||
}
|
||||
|
||||
if notes == nil {
|
||||
@@ -173,7 +176,7 @@ func getELFNotes(r file.LocationReadCloser) (*elfBinaryPackageNotes, error) {
|
||||
if len(notes) > headerSize {
|
||||
var metadata elfBinaryPackageNotes
|
||||
newPayload := bytes.TrimRight(notes[headerSize:], "\x00")
|
||||
if err := json.Unmarshal(newPayload, &metadata); err == nil {
|
||||
if err = json.Unmarshal(newPayload, &metadata); err == nil {
|
||||
return &metadata, nil
|
||||
}
|
||||
log.WithFields("file", r.Location.Path(), "error", err).Trace("unable to unmarshal ELF package notes as JSON")
|
||||
|
||||
@@ -3,6 +3,8 @@ package binary
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
@@ -14,6 +16,7 @@ func Test_ELF_Package_Cataloger(t *testing.T) {
|
||||
name string
|
||||
fixture string
|
||||
expected []pkg.Package
|
||||
wantErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "go case",
|
||||
@@ -63,6 +66,7 @@ func Test_ELF_Package_Cataloger(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: require.Error,
|
||||
},
|
||||
{
|
||||
name: "fedora 64 bit binaries",
|
||||
@@ -116,6 +120,7 @@ func Test_ELF_Package_Cataloger(t *testing.T) {
|
||||
WithImageResolver(t, v.fixture).
|
||||
IgnoreLocationLayer(). // this fixture can be rebuilt, thus the layer ID will change
|
||||
Expects(v.expected, nil).
|
||||
WithErrorAssertion(v.wantErr).
|
||||
TestCataloger(t, NewELFPackageCataloger())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@ RUN dnf update -y; \
|
||||
dnf clean all
|
||||
RUN mkdir -p /usr/local/bin/elftests/elfbinwithnestedlib
|
||||
RUN mkdir -p /usr/local/bin/elftests/elfbinwithsisterlib
|
||||
RUN mkdir -p /usr/local/bin/elftests/elfbinwithcorrupt
|
||||
|
||||
COPY ./elfbinwithnestedlib /usr/local/bin/elftests/elfbinwithnestedlib
|
||||
COPY ./elfbinwithsisterlib /usr/local/bin/elftests/elfbinwithsisterlib
|
||||
COPY ./elfbinwithcorrupt /usr/local/bin/elftests/elfbinwithcorrupt
|
||||
|
||||
ENV LD_LIBRARY_PATH=/usr/local/bin/elftests/elfbinwithnestedlib/bin/lib
|
||||
|
||||
@@ -16,6 +18,8 @@ RUN make
|
||||
|
||||
WORKDIR /usr/local/bin/elftests/elfbinwithsisterlib
|
||||
RUN make
|
||||
WORKDIR /usr/local/bin/elftests/elfbinwithcorrupt
|
||||
RUN make
|
||||
|
||||
# let's make the test image smaller, since we only require the built binaries and supporting libraries
|
||||
FROM busybox:1.36.1-musl
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
#include <iostream>
|
||||
#include "hello_world.h"
|
||||
|
||||
void print_hello_world() {
|
||||
std::cout << "Hello, World!" << std::endl;
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
|
||||
#ifndef HELLO_WORLD_H
|
||||
#define HELLO_WORLD_H
|
||||
|
||||
// Function declaration for printing "Hello, World!" to stdout
|
||||
void print_hello_world();
|
||||
|
||||
#endif // HELLO_WORLD_H
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
LDFLAGS := -L/lib64 -lstdc++
|
||||
|
||||
SRC_DIR := ./
|
||||
BUILD_DIR := ../build
|
||||
BIN_DIR := ../bin
|
||||
LIB_DIR := $(BIN_DIR)/lib
|
||||
|
||||
LIB_NAME := hello_world
|
||||
LIB_SRC := $(SRC_DIR)/hello_world.cpp
|
||||
LIB_OBJ := $(BUILD_DIR)/$(LIB_NAME).o
|
||||
LIB_SO := $(LIB_DIR)/lib$(LIB_NAME).so
|
||||
|
||||
EXECUTABLE := elfbinwithnestedlib
|
||||
EXEC_SRC := $(SRC_DIR)/testbin.cpp
|
||||
EXEC_OBJ := $(BUILD_DIR)/$(EXECUTABLE).o
|
||||
|
||||
|
||||
|
||||
all: testfixture
|
||||
|
||||
$(LIB_SO): $(LIB_OBJ) | $(LIB_DIR)
|
||||
$(CC) -shared -o $@ $<
|
||||
echo '{ corrupt json "system": "syftsys","name": "libhello_world.so","version": "0.01","pure:0.01"}' | objcopy --add-section .note.package=/dev/stdin --set-section-flags .note.package=noload,readonly $@
|
||||
|
||||
$(LIB_OBJ): $(LIB_SRC) | $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) -fPIC -c $< -o $@
|
||||
|
||||
$(EXEC_OBJ): $(EXEC_SRC) | $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BIN_DIR):
|
||||
mkdir -p $(BIN_DIR)
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $(BUILD_DIR)
|
||||
$(LIB_DIR):
|
||||
mkdir -p $(LIB_DIR)
|
||||
|
||||
$(BIN_DIR)/$(EXECUTABLE): $(EXEC_OBJ) $(LIB_SO) | $(BIN_DIR)
|
||||
$(CC) $(CFLAGS) -o $@ $^ -L$(LIB_DIR) -l$(LIB_NAME) $(LDFLAGS)
|
||||
echo '{corrupt json ..._syfttestfixture:0.01"}' | objcopy --add-section .note.package=/dev/stdin --set-section-flags .note.package=noload,readonly $@
|
||||
|
||||
testfixture: $(BIN_DIR)/$(EXECUTABLE)
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR) $(LIB_DIR) $(BIN_DIR) $(EXECUTABLE)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
#include "hello_world.h"
|
||||
|
||||
int main() {
|
||||
// Call the function from the shared library
|
||||
print_hello_world();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
CC = g++
|
||||
CFLAGS = -std=c++17 -Wall -Wextra -pedantic
|
||||
BUILD_DIR := ./build
|
||||
BIN_DIR := ./bin
|
||||
LIB_DIR := $(BIN_DIR)/lib
|
||||
|
||||
|
||||
|
||||
all: testfixtures
|
||||
|
||||
testfixtures:
|
||||
$(MAKE) -C elfsrc
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR) $(BIN_DIR)
|
||||
|
||||
.PHONY: all clean testfixtures
|
||||
|
||||
Reference in New Issue
Block a user