diff --git a/syft/scope/resolvers/all_layers_resolver.go b/syft/scope/resolvers/all_layers_resolver.go index 049d4a19d..ab732cf17 100644 --- a/syft/scope/resolvers/all_layers_resolver.go +++ b/syft/scope/resolvers/all_layers_resolver.go @@ -67,12 +67,26 @@ func (r *AllLayersResolver) FilesByPath(paths ...file.Path) ([]file.Reference, e for _, path := range paths { for idx, layerIdx := range r.layers { - ref := r.img.Layers[layerIdx].Tree.File(path) + tree := r.img.Layers[layerIdx].Tree + ref := tree.File(path) if ref == nil { // no file found, keep looking through layers continue } + // don't consider directories (special case: there is no path information for /) + if ref.Path == "/" { + continue + } else if r.img.FileCatalog.Exists(*ref) { + metadata, err := r.img.FileCatalog.Get(*ref) + if err != nil { + return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", ref.Path, err) + } + if metadata.Metadata.IsDir { + continue + } + } + results, err := r.fileByRef(*ref, uniqueFileIDs, idx) if err != nil { return nil, err @@ -97,6 +111,20 @@ func (r *AllLayersResolver) FilesByGlob(patterns ...string) ([]file.Reference, e } for _, ref := range refs { + + // don't consider directories (special case: there is no path information for /) + if ref.Path == "/" { + continue + } else if r.img.FileCatalog.Exists(ref) { + metadata, err := r.img.FileCatalog.Get(ref) + if err != nil { + return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", ref.Path, err) + } + if metadata.Metadata.IsDir { + continue + } + } + results, err := r.fileByRef(ref, uniqueFileIDs, idx) if err != nil { return nil, err diff --git a/syft/scope/resolvers/all_layers_resolver_test.go b/syft/scope/resolvers/all_layers_resolver_test.go index 97d30c0e3..e99ebd8a6 100644 --- a/syft/scope/resolvers/all_layers_resolver_test.go +++ b/syft/scope/resolvers/all_layers_resolver_test.go @@ -1,9 +1,10 @@ package resolvers import ( - "github.com/anchore/stereoscope/pkg/imagetest" "testing" + "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/stereoscope/pkg/file" ) @@ -80,6 +81,11 @@ func TestAllLayersResolver_FilesByPath(t *testing.T) { }, }, }, + { + name: "ignore directories", + linkPath: "/bin", + resolutions: []resolution{}, + }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { @@ -188,6 +194,11 @@ func TestAllLayersResolver_FilesByGlob(t *testing.T) { }, }, }, + { + name: "ignore directories", + glob: "**/bin", + resolutions: []resolution{}, + }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { diff --git a/syft/scope/resolvers/directory_resolver.go b/syft/scope/resolvers/directory_resolver.go index 3d8d4ed07..5677c6dde 100644 --- a/syft/scope/resolvers/directory_resolver.go +++ b/syft/scope/resolvers/directory_resolver.go @@ -33,12 +33,18 @@ func (s DirectoryResolver) FilesByPath(userPaths ...file.Path) ([]file.Reference // a path relative to root should be prefixed with the resolvers directory path, otherwise it should be left as is userStrPath = path.Join(s.Path, userStrPath) } - _, err := os.Stat(userStrPath) + fileMeta, err := os.Stat(userStrPath) if os.IsNotExist(err) { continue } else if err != nil { log.Errorf("path (%s) is not valid: %v", userStrPath, err) } + + // don't consider directories + if fileMeta.IsDir() { + continue + } + references = append(references, file.NewFileReference(file.Path(userStrPath))) } @@ -69,9 +75,12 @@ func (s DirectoryResolver) FilesByGlob(patterns ...string) ([]file.Reference, er if err != nil { continue } + + // don't consider directories if fileMeta.IsDir() { continue } + matchedPath := file.Path(match) result = append(result, file.NewFileReference(matchedPath)) } diff --git a/syft/scope/resolvers/directory_resolver_test.go b/syft/scope/resolvers/directory_resolver_test.go index 5f2b93e19..809148da8 100644 --- a/syft/scope/resolvers/directory_resolver_test.go +++ b/syft/scope/resolvers/directory_resolver_test.go @@ -48,6 +48,12 @@ func TestDirectoryResolver_FilesByPath(t *testing.T) { expected: "test-fixtures/image-symlinks/file-1.txt", refCount: 1, }, + { + name: "directories ignored", + root: "./test-fixtures/", + input: "/image-symlinks", + refCount: 0, + }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { diff --git a/syft/scope/resolvers/image_squash_resolver.go b/syft/scope/resolvers/image_squash_resolver.go index 1e398f0ac..47ae8507f 100644 --- a/syft/scope/resolvers/image_squash_resolver.go +++ b/syft/scope/resolvers/image_squash_resolver.go @@ -26,16 +26,32 @@ func (r *ImageSquashResolver) FilesByPath(paths ...file.Path) ([]file.Reference, uniqueFiles := make([]file.Reference, 0) for _, path := range paths { - ref := r.img.SquashedTree().File(path) + tree := r.img.SquashedTree() + ref := tree.File(path) if ref == nil { // no file found, keep looking through layers continue } + // don't consider directories (special case: there is no path information for /) + if ref.Path == "/" { + continue + } else if r.img.FileCatalog.Exists(*ref) { + metadata, err := r.img.FileCatalog.Get(*ref) + if err != nil { + return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", ref.Path, err) + } + if metadata.Metadata.IsDir { + continue + } + } + + // a file may be a symlink, process it as such and resolve it resolvedRef, err := r.img.ResolveLinkByImageSquash(*ref) if err != nil { return nil, fmt.Errorf("failed to resolve link from img (ref=%+v): %w", ref, err) } + if resolvedRef != nil && !uniqueFileIDs.Contains(*resolvedRef) { uniqueFileIDs.Add(*resolvedRef) uniqueFiles = append(uniqueFiles, *resolvedRef) @@ -57,6 +73,20 @@ func (r *ImageSquashResolver) FilesByGlob(patterns ...string) ([]file.Reference, } for _, ref := range refs { + + // don't consider directories (special case: there is no path information for /) + if ref.Path == "/" { + continue + } else if r.img.FileCatalog.Exists(ref) { + metadata, err := r.img.FileCatalog.Get(ref) + if err != nil { + return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", ref.Path, err) + } + if metadata.Metadata.IsDir { + continue + } + } + resolvedRefs, err := r.FilesByPath(ref.Path) if err != nil { return nil, fmt.Errorf("failed to find files by path (ref=%+v): %w", ref, err) diff --git a/syft/scope/resolvers/image_squash_resolver_test.go b/syft/scope/resolvers/image_squash_resolver_test.go index b3530b566..dee432125 100644 --- a/syft/scope/resolvers/image_squash_resolver_test.go +++ b/syft/scope/resolvers/image_squash_resolver_test.go @@ -1,9 +1,10 @@ package resolvers import ( - "github.com/anchore/stereoscope/pkg/imagetest" "testing" + "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/stereoscope/pkg/file" ) @@ -44,6 +45,11 @@ func TestImageSquashResolver_FilesByPath(t *testing.T) { resolveLayer: 8, resolvePath: "/link-dead", }, + { + name: "ignore directories", + linkPath: "/bin", + resolvePath: "", + }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { @@ -60,10 +66,20 @@ func TestImageSquashResolver_FilesByPath(t *testing.T) { t.Fatalf("could not use resolver: %+v", err) } - if len(refs) != 1 { + expectedRefs := 1 + if c.resolvePath == "" { + expectedRefs = 0 + } + + if len(refs) != expectedRefs { t.Fatalf("unexpected number of resolutions: %d", len(refs)) } + if expectedRefs == 0 { + // nothing else to assert + return + } + actual := refs[0] if actual.Path != file.Path(c.resolvePath) { @@ -119,6 +135,11 @@ func TestImageSquashResolver_FilesByGlob(t *testing.T) { resolveLayer: 8, resolvePath: "/link-dead", }, + { + name: "ignore directories", + glob: "**/bin", + resolvePath: "", + }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { @@ -135,10 +156,20 @@ func TestImageSquashResolver_FilesByGlob(t *testing.T) { t.Fatalf("could not use resolver: %+v", err) } - if len(refs) != 1 { + expectedRefs := 1 + if c.resolvePath == "" { + expectedRefs = 0 + } + + if len(refs) != expectedRefs { t.Fatalf("unexpected number of resolutions: %d", len(refs)) } + if expectedRefs == 0 { + // nothing else to assert + return + } + actual := refs[0] if actual.Path != file.Path(c.resolvePath) { diff --git a/syft/scope/scope_test.go b/syft/scope/scope_test.go index 1597ed7f2..b6656fc07 100644 --- a/syft/scope/scope_test.go +++ b/syft/scope/scope_test.go @@ -61,9 +61,15 @@ func TestDirectoryScope(t *testing.T) { { desc: "path detected", input: "test-fixtures", - inputPaths: []file.Path{file.Path("test-fixtures/path-detected")}, + inputPaths: []file.Path{file.Path("test-fixtures/path-detected/.vimrc")}, expRefs: 1, }, + { + desc: "directory ignored", + input: "test-fixtures", + inputPaths: []file.Path{file.Path("test-fixtures/path-detected")}, + expRefs: 0, + }, { desc: "no files-by-path detected", input: "test-fixtures",