From 4b78d9a1c0b967981adcb48c83ea4ecaf97cede0 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 28 Sep 2020 15:02:21 -0400 Subject: [PATCH] support home dir expansion (#188) Signed-off-by: Alex Goodman --- syft/scope/scope.go | 35 ++++++++++++------- syft/scope/scope_test.go | 75 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 93 insertions(+), 17 deletions(-) diff --git a/syft/scope/scope.go b/syft/scope/scope.go index b0cb796af..88406d4eb 100644 --- a/syft/scope/scope.go +++ b/syft/scope/scope.go @@ -9,7 +9,8 @@ import ( "fmt" "strings" - "github.com/anchore/syft/internal/log" + "github.com/mitchellh/go-homedir" + "github.com/spf13/afero" "github.com/anchore/stereoscope" @@ -48,7 +49,10 @@ type Scope struct { // NewScope produces a Scope based on userInput like dir: or image:tag func NewScope(userInput string, o Option) (Scope, func(), error) { fs := afero.NewOsFs() - parsedScheme, location := detectScheme(fs, image.DetectSource, userInput) + parsedScheme, location, err := detectScheme(fs, image.DetectSource, userInput) + if err != nil { + return Scope{}, func() {}, fmt.Errorf("unable to parse input=%q: %w", userInput, err) + } switch parsedScheme { case directoryScheme: @@ -134,32 +138,39 @@ func (s Scope) Source() interface{} { type sourceDetector func(string) (image.Source, string, error) -func detectScheme(fs afero.Fs, imageDetector sourceDetector, userInput string) (scheme, string) { +func detectScheme(fs afero.Fs, imageDetector sourceDetector, userInput string) (scheme, string, error) { if strings.HasPrefix(userInput, "dir:") { // blindly trust the user's scheme - return directoryScheme, strings.TrimPrefix(userInput, "dir:") + dirLocation, err := homedir.Expand(strings.TrimPrefix(userInput, "dir:")) + if err != nil { + return unknownScheme, "", fmt.Errorf("unable to expand directory path: %w", err) + } + return directoryScheme, dirLocation, nil } // we should attempt to let stereoscope determine what the source is first --just because the source is a valid directory // doesn't mean we yet know if it is an OCI layout directory (to be treated as an image) or if it is a generic filesystem directory. source, imageSpec, err := imageDetector(userInput) if err != nil { - // this is not necessarily an error we care a - log.Debugf("unable to detect the scheme from %q: %w", userInput, err) - return unknownScheme, "" + return unknownScheme, "", fmt.Errorf("unable to detect the scheme from %q: %w", userInput, err) } if source == image.UnknownSource { - fileMeta, err := fs.Stat(userInput) + dirLocation, err := homedir.Expand(userInput) if err != nil { - return unknownScheme, "" + return unknownScheme, "", fmt.Errorf("unable to expand potential directory path: %w", err) + } + + fileMeta, err := fs.Stat(dirLocation) + if err != nil { + return unknownScheme, "", nil } if fileMeta.IsDir() { - return directoryScheme, userInput + return directoryScheme, dirLocation, nil } - return unknownScheme, "" + return unknownScheme, "", nil } - return imageScheme, imageSpec + return imageScheme, imageSpec, nil } diff --git a/syft/scope/scope_test.go b/syft/scope/scope_test.go index 74722cba4..017d45e0a 100644 --- a/syft/scope/scope_test.go +++ b/syft/scope/scope_test.go @@ -1,6 +1,7 @@ package scope import ( + "github.com/mitchellh/go-homedir" "github.com/spf13/afero" "os" "testing" @@ -368,30 +369,94 @@ func TestDetectScheme(t *testing.T) { expectedScheme: directoryScheme, expectedLocation: ".", }, + // we should support tilde expansion + { + name: "tilde-expansion-image-implicit", + userInput: "~/some-path", + detection: detectorResult{ + src: image.OciDirectorySource, + ref: "~/some-path", + }, + expectedScheme: imageScheme, + expectedLocation: "~/some-path", + }, + { + name: "tilde-expansion-dir-implicit", + userInput: "~/some-path", + detection: detectorResult{ + src: image.UnknownSource, + ref: "", + }, + dirs: []string{"~/some-path"}, + expectedScheme: directoryScheme, + expectedLocation: "~/some-path", + }, + { + name: "tilde-expansion-dir-explicit-exists", + userInput: "dir:~/some-path", + dirs: []string{"~/some-path"}, + expectedScheme: directoryScheme, + expectedLocation: "~/some-path", + }, + { + name: "tilde-expansion-dir-explicit-dne", + userInput: "dir:~/some-path", + expectedScheme: directoryScheme, + expectedLocation: "~/some-path", + }, + { + name: "tilde-expansion-dir-implicit-dne", + userInput: "~/some-path", + expectedScheme: unknownScheme, + expectedLocation: "", + }, } for _, test := range testCases { t.Run(test.name, func(t *testing.T) { fs := afero.NewMemMapFs() for _, p := range test.dirs { - err := fs.Mkdir(p, os.ModePerm) + expandedExpectedLocation, err := homedir.Expand(p) + if err != nil { + t.Fatalf("unable to expand path=%q: %+v", p, err) + } + err = fs.Mkdir(expandedExpectedLocation, os.ModePerm) if err != nil { t.Fatalf("failed to create dummy tar: %+v", err) } } imageDetector := func(string) (image.Source, string, error) { - return test.detection.src, test.detection.ref, test.detection.err + // lean on the users real home directory value + switch test.detection.src { + case image.OciDirectorySource, image.DockerTarballSource, image.OciTarballSource: + expandedExpectedLocation, err := homedir.Expand(test.expectedLocation) + if err != nil { + t.Fatalf("unable to expand path=%q: %+v", test.expectedLocation, err) + } + return test.detection.src, expandedExpectedLocation, test.detection.err + default: + return test.detection.src, test.detection.ref, test.detection.err + } } - actualScheme, actualLocation := detectScheme(fs, imageDetector, test.userInput) + actualScheme, actualLocation, err := detectScheme(fs, imageDetector, test.userInput) + if err != nil { + t.Fatalf("unexpected err : %+v", err) + } if actualScheme != test.expectedScheme { t.Errorf("expected scheme %q , got %q", test.expectedScheme, actualScheme) } - if actualLocation != test.expectedLocation { - t.Errorf("expected location %q , got %q", test.expectedLocation, actualLocation) + // lean on the users real home directory value + expandedExpectedLocation, err := homedir.Expand(test.expectedLocation) + if err != nil { + t.Fatalf("unable to expand path=%q: %+v", test.expectedLocation, err) + } + + if actualLocation != expandedExpectedLocation { + t.Errorf("expected location %q , got %q", expandedExpectedLocation, actualLocation) } }) }