mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23:15 +01:00
chore: prevent file resolver from bubbling errors in binary cataloger (#3410)
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> Signed-off-by: Keith Zantow <kzantow@gmail.com> Co-authored-by: Keith Zantow <kzantow@gmail.com>
This commit is contained in:
parent
eb56f2e4bb
commit
8a41d77250
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -57,14 +58,14 @@ func (p *Executor) Execute(ctx context.Context, resolver file.Resolver, s sbomsy
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := runTaskSafely(ctx, tsk, resolver, s)
|
err := runTaskSafely(ctx, tsk, resolver, s)
|
||||||
unknowns, err := unknown.ExtractCoordinateErrors(err)
|
unknowns, remainingErrors := unknown.ExtractCoordinateErrors(err)
|
||||||
if len(unknowns) > 0 {
|
if len(unknowns) > 0 {
|
||||||
appendUnknowns(s, tsk.Name(), unknowns)
|
appendUnknowns(s, tsk.Name(), unknowns)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if remainingErrors != nil {
|
||||||
withLock(func() {
|
withLock(func() {
|
||||||
errs = multierror.Append(errs, fmt.Errorf("failed to run task: %w", err))
|
errs = multierror.Append(errs, fmt.Errorf("failed to run task: %w", remainingErrors))
|
||||||
prog.SetError(err)
|
prog.SetError(remainingErrors)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
prog.Increment()
|
prog.Increment()
|
||||||
@ -84,7 +85,13 @@ func appendUnknowns(builder sbomsync.Builder, taskName string, unknowns []unknow
|
|||||||
if sb.Artifacts.Unknowns == nil {
|
if sb.Artifacts.Unknowns == nil {
|
||||||
sb.Artifacts.Unknowns = map[file.Coordinates][]string{}
|
sb.Artifacts.Unknowns = map[file.Coordinates][]string{}
|
||||||
}
|
}
|
||||||
sb.Artifacts.Unknowns[u.Coordinates] = append(sb.Artifacts.Unknowns[u.Coordinates], formatUnknown(u.Reason.Error(), taskName))
|
unknownText := formatUnknown(u.Reason.Error(), taskName)
|
||||||
|
existing := sb.Artifacts.Unknowns[u.Coordinates]
|
||||||
|
// don't include duplicate unknowns
|
||||||
|
if slices.Contains(existing, unknownText) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sb.Artifacts.Unknowns[u.Coordinates] = append(existing, unknownText)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
33
internal/unknown/path_error.go
Normal file
33
internal/unknown/path_error.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package unknown
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pathErrorRegex = regexp.MustCompile(`.*path="([^"]+)".*`)
|
||||||
|
|
||||||
|
// ProcessPathErrors replaces "path" errors returned from the file.Resolver into unknowns,
|
||||||
|
// and warn logs non-unknown errors, returning only the unknown errors
|
||||||
|
func ProcessPathErrors(err error) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
errText := err.Error()
|
||||||
|
if pathErrorRegex.MatchString(errText) {
|
||||||
|
foundPath := pathErrorRegex.ReplaceAllString(err.Error(), "$1")
|
||||||
|
if foundPath != "" {
|
||||||
|
return New(file.NewLocation(foundPath), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unknowns, remainingErrors := ExtractCoordinateErrors(err)
|
||||||
|
log.Warn(remainingErrors)
|
||||||
|
|
||||||
|
var out []error
|
||||||
|
for _, u := range unknowns {
|
||||||
|
out = append(out, &u)
|
||||||
|
}
|
||||||
|
return Join(out...)
|
||||||
|
}
|
||||||
56
internal/unknown/path_error_test.go
Normal file
56
internal/unknown/path_error_test.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package unknown
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_ProcessPathErrors(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
errorText string
|
||||||
|
expected error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
errorText: `prefix path="/var/lib/thing" suffix`,
|
||||||
|
expected: &CoordinateError{
|
||||||
|
Coordinates: file.Coordinates{
|
||||||
|
RealPath: "/var/lib/thing",
|
||||||
|
},
|
||||||
|
Reason: fmt.Errorf(`prefix path="/var/lib/thing" suffix`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
errorText: `prefix path="/var/lib/thing"`,
|
||||||
|
expected: &CoordinateError{
|
||||||
|
Coordinates: file.Coordinates{
|
||||||
|
RealPath: "/var/lib/thing",
|
||||||
|
},
|
||||||
|
Reason: fmt.Errorf(`prefix path="/var/lib/thing"`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
errorText: `path="/var/lib/thing" suffix`,
|
||||||
|
expected: &CoordinateError{
|
||||||
|
Coordinates: file.Coordinates{
|
||||||
|
RealPath: "/var/lib/thing",
|
||||||
|
},
|
||||||
|
Reason: fmt.Errorf(`path="/var/lib/thing" suffix`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
errorText: "all your base are belong to us",
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.errorText, func(t *testing.T) {
|
||||||
|
got := ProcessPathErrors(fmt.Errorf("%s", test.errorText))
|
||||||
|
require.Equal(t, test.expected, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -105,6 +105,7 @@ func catalog(resolver file.Resolver, cls Classifier) (packages []pkg.Package, er
|
|||||||
var errs error
|
var errs error
|
||||||
locations, err := resolver.FilesByGlob(cls.FileGlob)
|
locations, err := resolver.FilesByGlob(cls.FileGlob)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
err = unknown.ProcessPathErrors(err) // convert any file.Resolver path errors to unknowns with locations
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, location := range locations {
|
for _, location := range locations {
|
||||||
|
|||||||
@ -1668,7 +1668,7 @@ func Test_Cataloger_ResilientToErrors(t *testing.T) {
|
|||||||
|
|
||||||
resolver := &panicyResolver{}
|
resolver := &panicyResolver{}
|
||||||
_, _, err := c.Catalog(context.Background(), resolver)
|
_, _, err := c.Catalog(context.Background(), resolver)
|
||||||
assert.Error(t, err)
|
assert.Nil(t, err) // non-coordinate-based FindBy* errors are now logged and not returned
|
||||||
assert.True(t, resolver.searchCalled)
|
assert.True(t, resolver.searchCalled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
FROM alpine@sha256:c5c5fda71656f28e49ac9c5416b3643eaa6a108a8093151d6d1afc9463be8e33
|
FROM alpine@sha256:c5c5fda71656f28e49ac9c5416b3643eaa6a108a8093151d6d1afc9463be8e33
|
||||||
RUN rm -rf /lib/apk/db/installed
|
RUN rm -rf /lib/apk/db/installed
|
||||||
COPY . /home/files
|
COPY . /home/files
|
||||||
|
# add a circular reference that will result in a failure while executing FindByGlob:
|
||||||
|
RUN mkdir -p /etc/alternatives && ln -s /etc/alternatives/java2 /etc/alternatives/java && ln -s /etc/alternatives/java /etc/alternatives/java2
|
||||||
|
|||||||
@ -22,6 +22,7 @@ func Test_Unknowns(t *testing.T) {
|
|||||||
assertInOutput(`no package identified in executable file`),
|
assertInOutput(`no package identified in executable file`),
|
||||||
assertInOutput(`unable to read files from java archive`),
|
assertInOutput(`unable to read files from java archive`),
|
||||||
assertInOutput(`no package identified in archive`),
|
assertInOutput(`no package identified in archive`),
|
||||||
|
assertInOutput(`cycle during symlink resolution`),
|
||||||
assertSuccessfulReturnCode,
|
assertSuccessfulReturnCode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user