From 9ac5e0814e509d8a04c4a599e7c8d033b93a3fe7 Mon Sep 17 00:00:00 2001 From: Özgür Kesim Date: Fri, 15 Aug 2025 11:33:47 +0200 Subject: update README; rename main.go --- README.md | 9 +-- goosebumps.go | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 174 ---------------------------------------------------------- 3 files changed, 179 insertions(+), 178 deletions(-) create mode 100644 goosebumps.go delete mode 100644 main.go diff --git a/README.md b/README.md index f6cd77a..796616a 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ The checks are performed transitively, following dependencies. # Parameters ``` -Usage of ./goosebumps: +Usage of goosebumps: -cc check for imports of cgo -ci @@ -21,10 +21,11 @@ Usage of ./goosebumps: check for imports of net/http/pprof -cu check for imports of unsafe + -d string + directory with go.mod file (default ".") -exempt string domains exempt from the search, seperated by space (default "golang.org") - -mod string - go.mod file (default "go.mod") -modcache string - location of go mod cache (default "$GOPATH/pkg/mod") + location of go mod cache (default "/home/oec/pkg/mod") + -r recursively search for go.mod files ``` diff --git a/goosebumps.go b/goosebumps.go new file mode 100644 index 0000000..a0f522f --- /dev/null +++ b/goosebumps.go @@ -0,0 +1,174 @@ +package main + +import ( + "flag" + "fmt" + "go/ast" + "go/parser" + "go/token" + "io/fs" + "log" + "os" + "path/filepath" + "strings" + "sync" + + "golang.org/x/mod/modfile" +) + +var ( + dir = flag.String("d", ".", "directory with go.mod file") + recurse = flag.Bool("r", false, "recursively search for go.mod files") + cache = flag.String("modcache", getmodcache(), "location of go mod cache") + excheckm = flag.String("exempt", "golang.org", "domains exempt from the search, seperated by space") + + checkInit = flag.Bool("ci", false, "check for implementations of init()") + checkUnsafe = flag.Bool("cu", false, "check for imports of unsafe") + checkCgo = flag.Bool("cc", false, "check for imports of cgo") + checkPprof = flag.Bool("cp", false, "check for imports of net/http/pprof") + + exceptions []string + + relcache string +) + +func getmodcache() string { + if c := os.Getenv("GOMODCACHE"); c != "" { + return c + } else if c = os.Getenv("GOPATH"); c != "" { + return c + "/pkg/mod" + } else { + return os.Getenv("HOME") + "/pkg/mod" + } + +} + +func main() { + flag.Parse() + + exceptions = strings.Fields(*excheckm) + relcache = strings.Replace(*cache, os.Getenv("HOME"), "~", 1) + + if !(*checkInit || *checkUnsafe || *checkCgo || *checkPprof) { + fmt.Println("Nothing to check. Use -ci|-cu|-cc|-cp") + return + } + + if !*recurse { + file := filepath.Join(*dir, "go.mod") + singleFile(file) + return + } + + recursive(*dir) +} + +func singleFile(file string) { + data, err := os.ReadFile(file) + if err != nil { + log.Fatal(err) + } + + mf, err := modfile.Parse(file, data, nil) + if err != nil { + log.Fatal(err) + } + + var wg sync.WaitGroup + wg.Add(len(mf.Require)) + pathcache := map[string]bool{} + for _, r := range mf.Require { + path := strings.Join(r.Syntax.Token, "@") + if _, done := pathcache[path]; done { + continue + } + pathcache[path] = true + go func() { + defer wg.Done() + checkPath(*cache + "/" + path) + }() + } + + wg.Wait() +} + +func recursive(dir string) { + filepath.WalkDir(dir, func(p string, info fs.DirEntry, err error) error { + if err != nil { + return err + } else if info.IsDir() { + return nil + } else if "go.mod" == filepath.Base(p) { + fmt.Printf("analyzing %s\n", p) + singleFile(p) + } + return nil + }) +} + +func isExempt(path string) bool { + for _, pattern := range exceptions { + if strings.Contains(path, pattern) { + return true + } + } + return false +} + +func modpath(filename string) string { + return filepath.Join(relcache, strings.TrimPrefix(filename, *cache+"/")) +} + +func checkPath(path string) { + var fset = token.NewFileSet() + + filter := func(inf fs.FileInfo) bool { + return !strings.HasSuffix(inf.Name(), "_test.go") + } + + filepath.WalkDir(path, func(p string, info fs.DirEntry, err error) error { + if err != nil { + return err + } else if !info.IsDir() { + return nil + } else if isExempt(p) { + return nil + } + + pkgs, err := parser.ParseDir(fset, p, filter, parser.Mode(0)) + if err != nil { + return err + } + + for _, pkg := range pkgs { + for filename, file := range pkg.Files { + if *checkUnsafe || *checkCgo || *checkPprof { + for _, imp := range file.Imports { + if *checkUnsafe && imp.Path.Value == `"unsafe"` { + fmt.Println("unsafe in", modpath(filename)) + } else if *checkCgo && imp.Path.Value == `"C"` { + fmt.Println("cgo in", modpath(filename)) + } else if *checkPprof && imp.Path.Value == `"net/http/pprof"` { + fmt.Println("pprof in", modpath(filename)) + } + } + } + + if !*checkInit { + break + } + INIT: + for _, decl := range file.Decls { + if f, ok := decl.(*ast.FuncDecl); ok { + if f.Name.Name == "init" { + fmt.Println("init in", modpath(filename)) + break INIT + } + } + } + } + } + + return nil + }) +} diff --git a/main.go b/main.go deleted file mode 100644 index a0f522f..0000000 --- a/main.go +++ /dev/null @@ -1,174 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "go/ast" - "go/parser" - "go/token" - "io/fs" - "log" - "os" - "path/filepath" - "strings" - "sync" - - "golang.org/x/mod/modfile" -) - -var ( - dir = flag.String("d", ".", "directory with go.mod file") - recurse = flag.Bool("r", false, "recursively search for go.mod files") - cache = flag.String("modcache", getmodcache(), "location of go mod cache") - excheckm = flag.String("exempt", "golang.org", "domains exempt from the search, seperated by space") - - checkInit = flag.Bool("ci", false, "check for implementations of init()") - checkUnsafe = flag.Bool("cu", false, "check for imports of unsafe") - checkCgo = flag.Bool("cc", false, "check for imports of cgo") - checkPprof = flag.Bool("cp", false, "check for imports of net/http/pprof") - - exceptions []string - - relcache string -) - -func getmodcache() string { - if c := os.Getenv("GOMODCACHE"); c != "" { - return c - } else if c = os.Getenv("GOPATH"); c != "" { - return c + "/pkg/mod" - } else { - return os.Getenv("HOME") + "/pkg/mod" - } - -} - -func main() { - flag.Parse() - - exceptions = strings.Fields(*excheckm) - relcache = strings.Replace(*cache, os.Getenv("HOME"), "~", 1) - - if !(*checkInit || *checkUnsafe || *checkCgo || *checkPprof) { - fmt.Println("Nothing to check. Use -ci|-cu|-cc|-cp") - return - } - - if !*recurse { - file := filepath.Join(*dir, "go.mod") - singleFile(file) - return - } - - recursive(*dir) -} - -func singleFile(file string) { - data, err := os.ReadFile(file) - if err != nil { - log.Fatal(err) - } - - mf, err := modfile.Parse(file, data, nil) - if err != nil { - log.Fatal(err) - } - - var wg sync.WaitGroup - wg.Add(len(mf.Require)) - pathcache := map[string]bool{} - for _, r := range mf.Require { - path := strings.Join(r.Syntax.Token, "@") - if _, done := pathcache[path]; done { - continue - } - pathcache[path] = true - go func() { - defer wg.Done() - checkPath(*cache + "/" + path) - }() - } - - wg.Wait() -} - -func recursive(dir string) { - filepath.WalkDir(dir, func(p string, info fs.DirEntry, err error) error { - if err != nil { - return err - } else if info.IsDir() { - return nil - } else if "go.mod" == filepath.Base(p) { - fmt.Printf("analyzing %s\n", p) - singleFile(p) - } - return nil - }) -} - -func isExempt(path string) bool { - for _, pattern := range exceptions { - if strings.Contains(path, pattern) { - return true - } - } - return false -} - -func modpath(filename string) string { - return filepath.Join(relcache, strings.TrimPrefix(filename, *cache+"/")) -} - -func checkPath(path string) { - var fset = token.NewFileSet() - - filter := func(inf fs.FileInfo) bool { - return !strings.HasSuffix(inf.Name(), "_test.go") - } - - filepath.WalkDir(path, func(p string, info fs.DirEntry, err error) error { - if err != nil { - return err - } else if !info.IsDir() { - return nil - } else if isExempt(p) { - return nil - } - - pkgs, err := parser.ParseDir(fset, p, filter, parser.Mode(0)) - if err != nil { - return err - } - - for _, pkg := range pkgs { - for filename, file := range pkg.Files { - if *checkUnsafe || *checkCgo || *checkPprof { - for _, imp := range file.Imports { - if *checkUnsafe && imp.Path.Value == `"unsafe"` { - fmt.Println("unsafe in", modpath(filename)) - } else if *checkCgo && imp.Path.Value == `"C"` { - fmt.Println("cgo in", modpath(filename)) - } else if *checkPprof && imp.Path.Value == `"net/http/pprof"` { - fmt.Println("pprof in", modpath(filename)) - } - } - } - - if !*checkInit { - break - } - INIT: - for _, decl := range file.Decls { - if f, ok := decl.(*ast.FuncDecl); ok { - if f.Name.Name == "init" { - fmt.Println("init in", modpath(filename)) - break INIT - } - } - } - } - } - - return nil - }) -} -- cgit v1.2.3