mirror of
https://github.com/securego/gosec.git
synced 2024-12-25 03:55:54 +00:00
Merge pull request #98 from endophage/recursive
adding support for arbitrary paths with ellipses
This commit is contained in:
commit
b5308ff621
8 changed files with 490 additions and 83 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -26,3 +26,5 @@ _testmain.go
|
||||||
*.prof
|
*.prof
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
.vscode
|
||||||
|
|
68
filelist.go
68
filelist.go
|
@ -15,60 +15,58 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"log"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ryanuber/go-glob"
|
||||||
)
|
)
|
||||||
|
|
||||||
type filelist struct {
|
// fileList uses a map for patterns to ensure each pattern only
|
||||||
paths map[string]bool
|
// appears once
|
||||||
globs []string
|
type fileList struct {
|
||||||
|
patterns map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFileList(paths ...string) *filelist {
|
func newFileList(paths ...string) *fileList {
|
||||||
|
f := &fileList{
|
||||||
f := &filelist{
|
patterns: make(map[string]struct{}),
|
||||||
make(map[string]bool),
|
|
||||||
make([]string, 0),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, path := range paths {
|
|
||||||
if e := f.Set(path); e != nil {
|
|
||||||
// #nosec
|
|
||||||
fmt.Fprintf(os.Stderr, "Unable to add %s to filelist: %s\n", path, e)
|
|
||||||
}
|
}
|
||||||
|
for _, p := range paths {
|
||||||
|
f.patterns[p] = struct{}{}
|
||||||
}
|
}
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *filelist) String() string {
|
func (f *fileList) String() string {
|
||||||
return strings.Join(f.globs, ", ")
|
ps := make([]string, 0, len(f.patterns))
|
||||||
|
for p := range f.patterns {
|
||||||
|
ps = append(ps, p)
|
||||||
|
}
|
||||||
|
return strings.Join(ps, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *filelist) Set(path string) error {
|
func (f *fileList) Set(path string) error {
|
||||||
f.globs = append(f.globs, path)
|
if path == "" {
|
||||||
matches, e := filepath.Glob(path)
|
// don't bother adding the empty path
|
||||||
if e != nil {
|
return nil
|
||||||
return e
|
|
||||||
}
|
|
||||||
for _, each := range matches {
|
|
||||||
abs, e := filepath.Abs(each)
|
|
||||||
if e != nil {
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
f.paths[abs] = true
|
|
||||||
}
|
}
|
||||||
|
f.patterns[path] = struct{}{}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f filelist) Contains(path string) bool {
|
func (f fileList) Contains(path string) bool {
|
||||||
_, present := f.paths[path]
|
for p := range f.patterns {
|
||||||
return present
|
if glob.Glob(p, path) {
|
||||||
|
log.Printf("excluding: %s\n", path)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Printf("including: %s\n", path)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
func (f filelist) Dump() {
|
func (f fileList) Dump() {
|
||||||
for k, _ := range f.paths {
|
for k, _ := range f.paths {
|
||||||
println(k)
|
println(k)
|
||||||
}
|
}
|
||||||
|
|
245
filelist_test.go
Normal file
245
filelist_test.go
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_newFileList(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
paths []string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want *fileList
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil paths",
|
||||||
|
args: args{paths: nil},
|
||||||
|
want: &fileList{patterns: map[string]struct{}{}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty paths",
|
||||||
|
args: args{paths: []string{}},
|
||||||
|
want: &fileList{patterns: map[string]struct{}{}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "have paths",
|
||||||
|
args: args{paths: []string{"*_test.go"}},
|
||||||
|
want: &fileList{patterns: map[string]struct{}{
|
||||||
|
"*_test.go": struct{}{},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
if got := newFileList(tt.args.paths...); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("%q. newFileList() = %v, want %v", tt.name, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_fileList_String(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
patterns []string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil patterns",
|
||||||
|
fields: fields{patterns: nil},
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty patterns",
|
||||||
|
fields: fields{patterns: []string{}},
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "one pattern",
|
||||||
|
fields: fields{patterns: []string{"foo"}},
|
||||||
|
want: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "two patterns",
|
||||||
|
fields: fields{patterns: []string{"foo", "bar"}},
|
||||||
|
want: "foo, bar",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
f := newFileList(tt.fields.patterns...)
|
||||||
|
if got := f.String(); got != tt.want {
|
||||||
|
t.Errorf("%q. fileList.String() = %v, want %v", tt.name, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_fileList_Set(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
patterns []string
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
want map[string]struct{}
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "add empty path",
|
||||||
|
fields: fields{patterns: nil},
|
||||||
|
args: args{path: ""},
|
||||||
|
want: map[string]struct{}{},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add path to nil patterns",
|
||||||
|
fields: fields{patterns: nil},
|
||||||
|
args: args{path: "foo"},
|
||||||
|
want: map[string]struct{}{
|
||||||
|
"foo": struct{}{},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add path to empty patterns",
|
||||||
|
fields: fields{patterns: []string{}},
|
||||||
|
args: args{path: "foo"},
|
||||||
|
want: map[string]struct{}{
|
||||||
|
"foo": struct{}{},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "add path to populated patterns",
|
||||||
|
fields: fields{patterns: []string{"foo"}},
|
||||||
|
args: args{path: "bar"},
|
||||||
|
want: map[string]struct{}{
|
||||||
|
"foo": struct{}{},
|
||||||
|
"bar": struct{}{},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
f := newFileList(tt.fields.patterns...)
|
||||||
|
if err := f.Set(tt.args.path); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("%q. fileList.Set() error = %v, wantErr %v", tt.name, err, tt.wantErr)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(f.patterns, tt.want) {
|
||||||
|
t.Errorf("%q. got state fileList.patterns = %v, want state %v", tt.name, f.patterns, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_fileList_Contains(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
patterns []string
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil patterns",
|
||||||
|
fields: fields{patterns: nil},
|
||||||
|
args: args{path: "foo"},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty patterns",
|
||||||
|
fields: fields{patterns: nil},
|
||||||
|
args: args{path: "foo"},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "one pattern, no wildcard, no match",
|
||||||
|
fields: fields{patterns: []string{"foo"}},
|
||||||
|
args: args{path: "bar"},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "one pattern, no wildcard, match",
|
||||||
|
fields: fields{patterns: []string{"foo"}},
|
||||||
|
args: args{path: "foo"},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "one pattern, wildcard prefix, match",
|
||||||
|
fields: fields{patterns: []string{"*foo"}},
|
||||||
|
args: args{path: "foo"},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "one pattern, wildcard suffix, match",
|
||||||
|
fields: fields{patterns: []string{"foo*"}},
|
||||||
|
args: args{path: "foo"},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "one pattern, wildcard both ends, match",
|
||||||
|
fields: fields{patterns: []string{"*foo*"}},
|
||||||
|
args: args{path: "foo"},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "default test match 1",
|
||||||
|
fields: fields{patterns: []string{"*_test.go"}},
|
||||||
|
args: args{path: "foo_test.go"},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "default test match 2",
|
||||||
|
fields: fields{patterns: []string{"*_test.go"}},
|
||||||
|
args: args{path: "bar/foo_test.go"},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "default test match 3",
|
||||||
|
fields: fields{patterns: []string{"*_test.go"}},
|
||||||
|
args: args{path: "/bar/foo_test.go"},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "default test match 4",
|
||||||
|
fields: fields{patterns: []string{"*_test.go"}},
|
||||||
|
args: args{path: "baz/bar/foo_test.go"},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "default test match 5",
|
||||||
|
fields: fields{patterns: []string{"*_test.go"}},
|
||||||
|
args: args{path: "/baz/bar/foo_test.go"},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "many patterns, no match",
|
||||||
|
fields: fields{patterns: []string{"*_one.go", "*_two.go"}},
|
||||||
|
args: args{path: "/baz/bar/foo_test.go"},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "many patterns, match",
|
||||||
|
fields: fields{patterns: []string{"*_one.go", "*_two.go", "*_test.go"}},
|
||||||
|
args: args{path: "/baz/bar/foo_test.go"},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
f := newFileList(tt.fields.patterns...)
|
||||||
|
if got := f.Contains(tt.args.path); got != tt.want {
|
||||||
|
t.Errorf("%q. fileList.Contains() = %v, want %v", tt.name, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
127
main.go
127
main.go
|
@ -29,22 +29,30 @@ import (
|
||||||
"github.com/GoASTScanner/gas/output"
|
"github.com/GoASTScanner/gas/output"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type recursion bool
|
||||||
|
|
||||||
|
const (
|
||||||
|
recurse recursion = true
|
||||||
|
noRecurse recursion = false
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
// #nosec flag
|
// #nosec flag
|
||||||
var flagIgnoreNoSec = flag.Bool("nosec", false, "Ignores #nosec comments when set")
|
flagIgnoreNoSec = flag.Bool("nosec", false, "Ignores #nosec comments when set")
|
||||||
|
|
||||||
// format output
|
// format output
|
||||||
var flagFormat = flag.String("fmt", "text", "Set output format. Valid options are: json, csv, html, or text")
|
flagFormat = flag.String("fmt", "text", "Set output format. Valid options are: json, csv, html, or text")
|
||||||
|
|
||||||
// output file
|
// output file
|
||||||
var flagOutput = flag.String("out", "", "Set output file for results")
|
flagOutput = flag.String("out", "", "Set output file for results")
|
||||||
|
|
||||||
// config file
|
// config file
|
||||||
var flagConfig = flag.String("conf", "", "Path to optional config file")
|
flagConfig = flag.String("conf", "", "Path to optional config file")
|
||||||
|
|
||||||
// quiet
|
// quiet
|
||||||
var flagQuiet = flag.Bool("quiet", false, "Only show output when errors are found")
|
flagQuiet = flag.Bool("quiet", false, "Only show output when errors are found")
|
||||||
|
|
||||||
var usageText = `
|
usageText = `
|
||||||
GAS - Go AST Scanner
|
GAS - Go AST Scanner
|
||||||
|
|
||||||
Gas analyzes Go source code to look for common programming mistakes that
|
Gas analyzes Go source code to look for common programming mistakes that
|
||||||
|
@ -67,7 +75,8 @@ USAGE:
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
var logger *log.Logger
|
logger *log.Logger
|
||||||
|
)
|
||||||
|
|
||||||
func extendConfList(conf map[string]interface{}, name string, inputStr string) {
|
func extendConfList(conf map[string]interface{}, name string, inputStr string) {
|
||||||
if inputStr == "" {
|
if inputStr == "" {
|
||||||
|
@ -145,7 +154,7 @@ func main() {
|
||||||
flag.Usage = usage
|
flag.Usage = usage
|
||||||
|
|
||||||
// Exclude files
|
// Exclude files
|
||||||
excluded := newFileList("**/*_test.go")
|
excluded := newFileList("*_test.go")
|
||||||
flag.Var(excluded, "skip", "File pattern to exclude from scan")
|
flag.Var(excluded, "skip", "File pattern to exclude from scan")
|
||||||
|
|
||||||
incRules := ""
|
incRules := ""
|
||||||
|
@ -183,44 +192,13 @@ func main() {
|
||||||
analyzer := gas.NewAnalyzer(config, logger)
|
analyzer := gas.NewAnalyzer(config, logger)
|
||||||
AddRules(&analyzer, config)
|
AddRules(&analyzer, config)
|
||||||
|
|
||||||
// Traverse directory structure if './...'
|
toAnalyze := getFilesToAnalyze(flag.Args(), excluded)
|
||||||
if flag.NArg() == 1 && flag.Arg(0) == "./..." {
|
|
||||||
|
|
||||||
cwd, err := os.Getwd()
|
for _, file := range toAnalyze {
|
||||||
if err != nil {
|
if err := analyzer.Process(file); err != nil {
|
||||||
logger.Fatalf("Unable to traverse path %s, reason - %s", flag.Arg(0), err)
|
|
||||||
}
|
|
||||||
filepath.Walk(cwd, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if excluded.Contains(path) && info.IsDir() {
|
|
||||||
logger.Printf("Skipping %s\n", path)
|
|
||||||
return filepath.SkipDir
|
|
||||||
}
|
|
||||||
if !info.IsDir() && !excluded.Contains(path) &&
|
|
||||||
strings.HasSuffix(path, ".go") {
|
|
||||||
err = analyzer.Process(path)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal(err)
|
logger.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Process each file individually
|
|
||||||
for _, filename := range flag.Args() {
|
|
||||||
if finfo, err := os.Stat(filename); err == nil {
|
|
||||||
if !finfo.IsDir() && !excluded.Contains(filename) &&
|
|
||||||
strings.HasSuffix(filename, ".go") {
|
|
||||||
if err = analyzer.Process(filename); err != nil {
|
|
||||||
logger.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
issuesFound := len(analyzer.Issues) > 0
|
issuesFound := len(analyzer.Issues) > 0
|
||||||
// Exit quietly if nothing was found
|
// Exit quietly if nothing was found
|
||||||
|
@ -245,3 +223,68 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getFilesToAnalyze lists all files
|
||||||
|
func getFilesToAnalyze(paths []string, excluded *fileList) []string {
|
||||||
|
log.Println("getFilesToAnalyze: start")
|
||||||
|
var toAnalyze []string
|
||||||
|
for _, path := range paths {
|
||||||
|
log.Printf("getFilesToAnalyze: processing \"%s\"\n", path)
|
||||||
|
// get the absolute path before doing anything else
|
||||||
|
path, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if filepath.Base(path) == "..." {
|
||||||
|
toAnalyze = append(
|
||||||
|
toAnalyze,
|
||||||
|
listFiles(filepath.Dir(path), recurse, excluded)...,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
var (
|
||||||
|
finfo os.FileInfo
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if finfo, err = os.Stat(path); err != nil {
|
||||||
|
logger.Fatal(err)
|
||||||
|
}
|
||||||
|
if !finfo.IsDir() {
|
||||||
|
if shouldInclude(path, excluded) {
|
||||||
|
toAnalyze = append(toAnalyze, path)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toAnalyze = listFiles(path, noRecurse, excluded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Println("getFilesToAnalyze: end")
|
||||||
|
return toAnalyze
|
||||||
|
}
|
||||||
|
|
||||||
|
// listFiles returns a list of all files found that pass the shouldInclude check.
|
||||||
|
// If doRecursiveWalk it true, it will walk the tree rooted at absPath, otherwise it
|
||||||
|
// will only include files directly within the dir referenced by absPath.
|
||||||
|
func listFiles(absPath string, doRecursiveWalk recursion, excluded *fileList) []string {
|
||||||
|
var files []string
|
||||||
|
|
||||||
|
walk := func(path string, info os.FileInfo, err error) error {
|
||||||
|
if info.IsDir() && doRecursiveWalk == noRecurse {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
if shouldInclude(path, excluded) {
|
||||||
|
files = append(files, path)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := filepath.Walk(absPath, walk); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
|
||||||
|
// shouldInclude checks if a specific path which is expected to reference
|
||||||
|
// a regular file should be included
|
||||||
|
func shouldInclude(path string, excluded *fileList) bool {
|
||||||
|
return filepath.Ext(path) == ".go" && !excluded.Contains(path)
|
||||||
|
}
|
||||||
|
|
45
main_test.go
Normal file
45
main_test.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func Test_shouldInclude(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
path string
|
||||||
|
excluded *fileList
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "non .go file",
|
||||||
|
args: args{
|
||||||
|
path: "thing.txt",
|
||||||
|
excluded: newFileList(),
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: ".go file, not excluded",
|
||||||
|
args: args{
|
||||||
|
path: "thing.go",
|
||||||
|
excluded: newFileList(),
|
||||||
|
},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: ".go file, excluded",
|
||||||
|
args: args{
|
||||||
|
path: "thing.go",
|
||||||
|
excluded: newFileList("thing.go"),
|
||||||
|
},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
if got := shouldInclude(tt.args.path, tt.args.excluded); got != tt.want {
|
||||||
|
t.Errorf("%q. shouldInclude() = %v, want %v", tt.name, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
vendor.conf
Normal file
2
vendor.conf
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Import path | revision | Repository(optional)
|
||||||
|
github.com/ryanuber/go-glob 572520ed46dbddaed19ea3d9541bdd0494163693
|
21
vendor/github.com/ryanuber/go-glob/LICENSE
generated
vendored
Normal file
21
vendor/github.com/ryanuber/go-glob/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Ryan Uber
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
51
vendor/github.com/ryanuber/go-glob/glob.go
generated
vendored
Normal file
51
vendor/github.com/ryanuber/go-glob/glob.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package glob
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// The character which is treated like a glob
|
||||||
|
const GLOB = "*"
|
||||||
|
|
||||||
|
// Glob will test a string pattern, potentially containing globs, against a
|
||||||
|
// subject string. The result is a simple true/false, determining whether or
|
||||||
|
// not the glob pattern matched the subject text.
|
||||||
|
func Glob(pattern, subj string) bool {
|
||||||
|
// Empty pattern can only match empty subject
|
||||||
|
if pattern == "" {
|
||||||
|
return subj == pattern
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the pattern _is_ a glob, it matches everything
|
||||||
|
if pattern == GLOB {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(pattern, GLOB)
|
||||||
|
|
||||||
|
if len(parts) == 1 {
|
||||||
|
// No globs in pattern, so test for equality
|
||||||
|
return subj == pattern
|
||||||
|
}
|
||||||
|
|
||||||
|
leadingGlob := strings.HasPrefix(pattern, GLOB)
|
||||||
|
trailingGlob := strings.HasSuffix(pattern, GLOB)
|
||||||
|
end := len(parts) - 1
|
||||||
|
|
||||||
|
// Check the first section. Requires special handling.
|
||||||
|
if !leadingGlob && !strings.HasPrefix(subj, parts[0]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go over the middle parts and ensure they match.
|
||||||
|
for i := 1; i < end; i++ {
|
||||||
|
if !strings.Contains(subj, parts[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim evaluated text from subj as we loop over the pattern.
|
||||||
|
idx := strings.Index(subj, parts[i]) + len(parts[i])
|
||||||
|
subj = subj[idx:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reached the last section. Requires special handling.
|
||||||
|
return trailingGlob || strings.HasSuffix(subj, parts[end])
|
||||||
|
}
|
Loading…
Reference in a new issue