From 6d40c513f25b1c96d9f7bb37e315f2ad6f24a135 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Mon, 26 Sep 2022 10:59:19 -0400 Subject: [PATCH] fix: support exclude patterns on Windows (#1228) --- syft/source/source.go | 5 +++ syft/source/source_test.go | 69 ++++++++++++++++++++++++++++++++++ syft/source/source_win_test.go | 54 ++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 syft/source/source_win_test.go diff --git a/syft/source/source.go b/syft/source/source.go index f9ffe4d775c8..aa4af0107cef 100644 --- a/syft/source/source.go +++ b/syft/source/source.go @@ -398,6 +398,9 @@ func getDirectoryExclusionFunctions(root string, exclusions []string) ([]pathFil return nil, err } + // this handles Windows file paths by converting them to C:/something/else format + root = filepath.ToSlash(root) + if !strings.HasSuffix(root, "/") { root += "/" } @@ -420,6 +423,8 @@ func getDirectoryExclusionFunctions(root string, exclusions []string) ([]pathFil return []pathFilterFn{ func(path string, _ os.FileInfo) bool { for _, exclusion := range exclusions { + // this is required to handle Windows filepaths + path = filepath.ToSlash(path) matches, err := doublestar.Match(exclusion, path) if err != nil { return false diff --git a/syft/source/source_test.go b/syft/source/source_test.go index b7e960fcb457..e16e608da0ce 100644 --- a/syft/source/source_test.go +++ b/syft/source/source_test.go @@ -579,6 +579,75 @@ func TestImageExclusions(t *testing.T) { } } +func Test_crossPlatformExclusions(t *testing.T) { + testCases := []struct { + desc string + root string + path string + exclude string + match bool + }{ + { + desc: "linux doublestar", + root: "/usr", + path: "/usr/var/lib/etc.txt", + exclude: "**/*.txt", + match: true, + }, + { + desc: "linux relative", + root: "/usr/var/lib", + path: "/usr/var/lib/etc.txt", + exclude: "./*.txt", + match: true, + }, + { + desc: "linux one level", + root: "/usr", + path: "/usr/var/lib/etc.txt", + exclude: "*/*.txt", + match: false, + }, + // NOTE: since these tests will run in linux and macOS, the windows paths will be + // considered relative if they do not start with a forward slash and paths with backslashes + // won't be modified by the filepath.ToSlash call, so these are emulating the result of + // filepath.ToSlash usage + { + desc: "windows doublestar", + root: "/C:/User/stuff", + path: "/C:/User/stuff/thing.txt", + exclude: "**/*.txt", + match: true, + }, + { + desc: "windows relative", + root: "/C:/User/stuff", + path: "/C:/User/stuff/thing.txt", + exclude: "./*.txt", + match: true, + }, + { + desc: "windows one level", + root: "/C:/User/stuff", + path: "/C:/User/stuff/thing.txt", + exclude: "*/*.txt", + match: false, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + fns, err := getDirectoryExclusionFunctions(test.root, []string{test.exclude}) + require.NoError(t, err) + + for _, f := range fns { + result := f(test.path, nil) + require.Equal(t, test.match, result) + } + }) + } +} + // createArchive creates a new archive file at destinationArchivePath based on the directory found at sourceDirPath. func createArchive(t testing.TB, sourceDirPath, destinationArchivePath string) { t.Helper() diff --git a/syft/source/source_win_test.go b/syft/source/source_win_test.go new file mode 100644 index 000000000000..8fd5eb4b700d --- /dev/null +++ b/syft/source/source_win_test.go @@ -0,0 +1,54 @@ +//go:build windows +// +build windows + +package source + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_crossPlatformExclusions(t *testing.T) { + testCases := []struct { + desc string + root string + path string + exclude string + match bool + }{ + { + desc: "windows doublestar", + root: "C:\\User\\stuff", + path: "C:\\User\\stuff\\thing.txt", + exclude: "**/*.txt", + match: true, + }, + { + desc: "windows relative", + root: "C:\\User\\stuff", + path: "C:\\User\\stuff\\thing.txt", + exclude: "./*.txt", + match: true, + }, + { + desc: "windows one level", + root: "C:\\User\\stuff", + path: "C:\\User\\stuff\\thing.txt", + exclude: "*/*.txt", + match: false, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + fns, err := getDirectoryExclusionFunctions(test.root, []string{test.exclude}) + require.NoError(t, err) + + for _, f := range fns { + result := f(test.path, nil) + require.Equal(t, test.match, result) + } + }) + } +}