From fe616acd98fc4b77c64df9f08c835d7a93c6981c Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 30 Nov 2021 15:34:37 -0500 Subject: [PATCH] directory resolver should account for the proc cwd relative to the root path (#644) Signed-off-by: Alex Goodman --- syft/source/directory_resolver.go | 40 ++++++++++----- syft/source/directory_resolver_test.go | 70 ++++++++++++++++++++++---- 2 files changed, 87 insertions(+), 23 deletions(-) diff --git a/syft/source/directory_resolver.go b/syft/source/directory_resolver.go index 594f66ba7..ba94a3167 100644 --- a/syft/source/directory_resolver.go +++ b/syft/source/directory_resolver.go @@ -33,10 +33,11 @@ type pathFilterFn func(string) bool // directoryResolver implements path and content access for the directory data source. type directoryResolver struct { - path string - cwd string - fileTree *filetree.FileTree - metadata map[file.ID]FileMetadata + path string + cwdRelativeToRoot string + cwd string + fileTree *filetree.FileTree + metadata map[file.ID]FileMetadata // TODO: wire up to report these paths in the json report pathFilterFns []pathFilterFn refsByMIMEType map[string][]file.Reference @@ -49,18 +50,27 @@ func newDirectoryResolver(root string, pathFilters ...pathFilterFn) (*directoryR return nil, fmt.Errorf("could not create directory resolver: %w", err) } + var cwdRelRoot string + if path.IsAbs(root) { + cwdRelRoot, err = filepath.Rel(cwd, root) + if err != nil { + return nil, fmt.Errorf("could not create directory resolver: %w", err) + } + } + if pathFilters == nil { pathFilters = []pathFilterFn{isUnixSystemRuntimePath} } resolver := directoryResolver{ - path: root, - cwd: cwd, - fileTree: filetree.NewFileTree(), - metadata: make(map[file.ID]FileMetadata), - pathFilterFns: pathFilters, - refsByMIMEType: make(map[string][]file.Reference), - errPaths: make(map[string]error), + path: root, + cwd: cwd, + cwdRelativeToRoot: cwdRelRoot, + fileTree: filetree.NewFileTree(), + metadata: make(map[file.ID]FileMetadata), + pathFilterFns: pathFilters, + refsByMIMEType: make(map[string][]file.Reference), + errPaths: make(map[string]error), } return &resolver, indexAllRoots(root, resolver.indexTree) @@ -200,7 +210,11 @@ func (r directoryResolver) requestPath(userPath string) (string, error) { if filepath.IsAbs(userPath) { // don't allow input to potentially hop above root path userPath = path.Join(r.path, userPath) + } else { + // ensure we take into account any relative difference between the root path and the CWD for relative requests + userPath = path.Join(r.cwdRelativeToRoot, userPath) } + var err error userPath, err = filepath.Abs(userPath) if err != nil { @@ -212,7 +226,9 @@ func (r directoryResolver) requestPath(userPath string) (string, error) { func (r directoryResolver) responsePath(path string) string { // always return references relative to the request path (not absolute path) if filepath.IsAbs(path) { - return strings.TrimPrefix(path, r.cwd+string(filepath.Separator)) + // we need to account for the cwd relative to the running process and the given root for the directory resolver + prefix := filepath.Clean(filepath.Join(r.cwd, r.cwdRelativeToRoot)) + return strings.TrimPrefix(path, prefix+string(filepath.Separator)) } return path } diff --git a/syft/source/directory_resolver_test.go b/syft/source/directory_resolver_test.go index a2cc3fab7..8cfcd5393 100644 --- a/syft/source/directory_resolver_test.go +++ b/syft/source/directory_resolver_test.go @@ -10,6 +10,8 @@ import ( "syscall" "testing" + "github.com/stretchr/testify/require" + "github.com/scylladb/go-set/strset" "github.com/anchore/stereoscope/pkg/file" @@ -17,6 +19,60 @@ import ( "github.com/wagoodman/go-progress" ) +func TestDirectoryResolver_FilesByPath_absoluteRoot(t *testing.T) { + cases := []struct { + name string + relativeRoot string + input string + expected []string + }{ + { + name: "should find a file from an absolute input", + relativeRoot: "./test-fixtures/", + input: "/image-symlinks/file-1.txt", + expected: []string{ + "image-symlinks/file-1.txt", + }, + }, + { + name: "should find a file from a relative path", + relativeRoot: "./test-fixtures/", + input: "image-symlinks/file-1.txt", + expected: []string{ + "image-symlinks/file-1.txt", + }, + }, + { + name: "should find a file from a relative path (root above cwd)", + relativeRoot: "../", + input: "sbom/sbom.go", + expected: []string{ + "sbom/sbom.go", + }, + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + // note: this test is all about asserting correct functionality when the given analysis path + // is an absolute path + absRoot, err := filepath.Abs(c.relativeRoot) + require.NoError(t, err) + + resolver, err := newDirectoryResolver(absRoot) + assert.NoError(t, err) + + refs, err := resolver.FilesByPath(c.input) + require.NoError(t, err) + assert.Len(t, refs, len(c.expected)) + s := strset.New() + for _, actual := range refs { + s.Add(actual.RealPath) + } + assert.ElementsMatch(t, c.expected, s.List()) + }) + } +} + func TestDirectoryResolver_FilesByPath(t *testing.T) { cases := []struct { name string @@ -85,18 +141,10 @@ func TestDirectoryResolver_FilesByPath(t *testing.T) { } refs, err := resolver.FilesByPath(c.input) - if err != nil { - t.Fatalf("could not use resolver: %+v, %+v", err, refs) - } - - if len(refs) != c.refCount { - t.Errorf("unexpected number of refs: %d != %d", len(refs), c.refCount) - } - + require.NoError(t, err) + assert.Len(t, refs, c.refCount) for _, actual := range refs { - if actual.RealPath != c.expected { - t.Errorf("bad resolve path: '%s'!='%s'", actual.RealPath, c.expected) - } + assert.Equal(t, c.expected, actual.RealPath) } }) }