From 80f9b2488c6de1c10f9598f565d2763fa59cbca1 Mon Sep 17 00:00:00 2001 From: serosset Date: Thu, 30 Jun 2022 23:19:11 +0000 Subject: [PATCH] Add support to expand glob patterns --- cmd/root.go | 33 ++++++++- cmd/root_test.go | 93 ++++++++++++++++++++++++-- docs/usage.md | 22 +++++- go.mod | 1 + go.sum | 2 + testdata/subdir1/good.yml | 2 + testdata/subdir1/subdir2/good.yml | 2 + testdata/subdir1/subdir2/whitelist.yml | 2 + testdata/subdir1/whitelist.yml | 2 + 9 files changed, 150 insertions(+), 9 deletions(-) create mode 100644 testdata/subdir1/good.yml create mode 100644 testdata/subdir1/subdir2/good.yml create mode 100644 testdata/subdir1/subdir2/whitelist.yml create mode 100644 testdata/subdir1/whitelist.yml diff --git a/cmd/root.go b/cmd/root.go index e4822d03..f14afae3 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -26,6 +26,7 @@ import ( "errors" "fmt" "os" + "path/filepath" "runtime" "strings" "time" @@ -36,6 +37,7 @@ import ( "github.com/get-woke/woke/pkg/parser" "github.com/get-woke/woke/pkg/printer" + "github.com/bmatcuk/doublestar/v4" "github.com/mitchellh/go-homedir" "github.com/rs/zerolog" "github.com/rs/zerolog/log" @@ -121,7 +123,11 @@ func rootRunE(cmd *cobra.Command, args []string) error { return err } - findings := p.ParsePaths(print, parseArgs(args)...) + files, err := parseArgs(args) + if err != nil { + return err + } + findings := p.ParsePaths(print, files...) if exitOneOnFailure && findings > 0 { // We intentionally return an error if exitOneOnFailure is true, but don't want to show usage @@ -162,7 +168,7 @@ func GetRootCmd() cobra.Command { return *rootCmd } -func parseArgs(args []string) []string { +func parseArgs(args []string) ([]string, error) { if len(args) == 0 { args = parser.DefaultPath } @@ -170,8 +176,29 @@ func parseArgs(args []string) []string { if stdin { args = []string{os.Stdin.Name()} } + // Perform glob expansion. + var files []string + for _, arg := range args { + var f []string + var err error + if strings.Contains(arg, "**") { + // Double star glob expansion. + base, pattern := doublestar.SplitPattern(arg) + fsys := os.DirFS(base) + f, err = doublestar.Glob(fsys, pattern) + for i := range f { + f[i] = filepath.Join(base, f[i]) + } + } else { + f, err = filepath.Glob(arg) + } + if err != nil { + return nil, err + } + files = append(files, f...) + } - return args + return files, nil } func setDebugLogLevel() { diff --git a/cmd/root_test.go b/cmd/root_test.go index 87071e7e..a2b9d46e 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -4,7 +4,9 @@ import ( "bytes" "io" "os" + "path/filepath" "regexp" + "strings" "testing" "github.com/get-woke/woke/pkg/output" @@ -70,12 +72,93 @@ func TestParseArgs(t *testing.T) { t.Cleanup(func() { stdin = false }) - assert.Equal(t, parser.DefaultPath, parseArgs([]string{})) - assert.Equal(t, []string{"../.."}, parseArgs([]string{"../.."})) + tests := []struct { + stdin bool + args []string + expectedArgs []string + expectedError error + }{ + { + stdin: false, + args: []string{}, + expectedArgs: parser.DefaultPath, + expectedError: nil, + }, + { + stdin: false, + args: []string{"../.."}, + expectedArgs: []string{"../.."}, + expectedError: nil, + }, + + // Test glob expansion + { + stdin: false, + args: []string{"../testdata/*.yml"}, + expectedArgs: []string{"../testdata/good.yml", "../testdata/whitelist.yml"}, + expectedError: nil, + }, + { + stdin: false, + args: []string{"../testdata/g??d.yml"}, + expectedArgs: []string{"../testdata/good.yml"}, + expectedError: nil, + }, + { + stdin: false, + args: []string{"../testdata/[a-z]ood.yml"}, + expectedArgs: []string{"../testdata/good.yml"}, + expectedError: nil, + }, + { + stdin: false, + args: []string{"../testdata/*/*.yml"}, + expectedArgs: []string{"../testdata/subdir1/good.yml", "../testdata/subdir1/whitelist.yml"}, + expectedError: nil, + }, + { + stdin: false, + args: []string{"../testdata/**/*.yml"}, + expectedArgs: []string{ + "../testdata/good.yml", + "../testdata/whitelist.yml", + "../testdata/subdir1/good.yml", + "../testdata/subdir1/whitelist.yml", + "../testdata/subdir1/subdir2/good.yml", + "../testdata/subdir1/subdir2/whitelist.yml", + }, + expectedError: nil, + }, - stdin = true - assert.Equal(t, []string{os.Stdin.Name()}, parseArgs([]string{})) - assert.Equal(t, []string{os.Stdin.Name()}, parseArgs([]string{"../.."})) + // Bad glob pattern + { + stdin: false, + args: []string{"r[.go"}, + expectedArgs: nil, + expectedError: filepath.ErrBadPattern, + }, + + { + stdin: true, + args: []string{}, + expectedArgs: []string{os.Stdin.Name()}, + expectedError: nil, + }, + { + stdin: true, + args: []string{"../.."}, + expectedArgs: []string{os.Stdin.Name()}, + expectedError: nil, + }, + } + for _, tt := range tests { + t.Run(strings.Join(tt.args, " "), func(t *testing.T) { + stdin = tt.stdin + files, err := parseArgs(tt.args) + assert.ErrorIs(t, err, tt.expectedError) + assert.Equal(t, tt.expectedArgs, files) + }) + } } func TestRunE(t *testing.T) { diff --git a/docs/usage.md b/docs/usage.md index 642ba963..05b75a67 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -28,10 +28,30 @@ No findings found. ### File globs By default, `woke` will run against all text files in your current directory. -To change this, supply a space-separated list of globs as the first argument. +To change this, supply a space-separated list of file glob patterns. +`woke` supports the following glob pattern: + +``` +pattern: + { term } +term: + '*' matches any sequence of non-Separator characters + '?' matches any single non-Separator character + '[' [ '^' ] { character-range } ']' + character class (must be non-empty) + c matches character c (c != '*', '?', '\\', '[') + '\\' c matches character c + +character-range: + c matches character c (c != '\\', '-', ']') + '\\' c matches character c + lo '-' hi matches character c for lo <= c <= hi +``` This can be something like `**/*.go`, or a space-separated list of filenames. +If `woke` is invoked from a shell, the invoking shell performs file glob pattern expansion according to the shell glob rules. + ```bash $ woke test.txt test.txt:2:2-11: `Blacklist` may be insensitive, use `denylist`, `blocklist` instead (warning) diff --git a/go.mod b/go.mod index a6bb25d9..7c5d61a4 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( require ( github.com/acomagu/bufpipe v1.0.3 // indirect + github.com/bmatcuk/doublestar/v4 v4.0.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect diff --git a/go.sum b/go.sum index 8ef6fe46..4b35be2d 100644 --- a/go.sum +++ b/go.sum @@ -60,6 +60,8 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/bmatcuk/doublestar/v4 v4.0.2 h1:X0krlUVAVmtr2cRoTqR8aDMrDqnB36ht8wpWTiQ3jsA= +github.com/bmatcuk/doublestar/v4 v4.0.2/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/caitlinelfring/go-env-default v1.0.0 h1:DY8qY3OKb9PrGe+sjop7dw7tOKh6kV8cdZONlESSbZc= github.com/caitlinelfring/go-env-default v1.0.0/go.mod h1:vY8iS64s+wIBKayqiNGJsWMwc19NrxaNNTyWzuPvE44= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/testdata/subdir1/good.yml b/testdata/subdir1/good.yml new file mode 100644 index 00000000..806ceece --- /dev/null +++ b/testdata/subdir1/good.yml @@ -0,0 +1,2 @@ +--- +this file has no findings. diff --git a/testdata/subdir1/subdir2/good.yml b/testdata/subdir1/subdir2/good.yml new file mode 100644 index 00000000..806ceece --- /dev/null +++ b/testdata/subdir1/subdir2/good.yml @@ -0,0 +1,2 @@ +--- +this file has no findings. diff --git a/testdata/subdir1/subdir2/whitelist.yml b/testdata/subdir1/subdir2/whitelist.yml new file mode 100644 index 00000000..9c5d68cf --- /dev/null +++ b/testdata/subdir1/subdir2/whitelist.yml @@ -0,0 +1,2 @@ +--- +this file also has a whitelist finding diff --git a/testdata/subdir1/whitelist.yml b/testdata/subdir1/whitelist.yml new file mode 100644 index 00000000..9c5d68cf --- /dev/null +++ b/testdata/subdir1/whitelist.yml @@ -0,0 +1,2 @@ +--- +this file also has a whitelist finding