sa1005.go (1846B)
1 package sa1005 2 3 import ( 4 "go/ast" 5 "strings" 6 7 "honnef.co/go/tools/analysis/code" 8 "honnef.co/go/tools/analysis/lint" 9 "honnef.co/go/tools/analysis/report" 10 "honnef.co/go/tools/pattern" 11 12 "golang.org/x/tools/go/analysis" 13 ) 14 15 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 16 Analyzer: &analysis.Analyzer{ 17 Name: "SA1005", 18 Run: run, 19 Requires: code.RequiredAnalyzers, 20 }, 21 Doc: &lint.RawDocumentation{ 22 Title: `Invalid first argument to \'exec.Command\'`, 23 Text: `\'os/exec\' runs programs directly (using variants of the fork and exec 24 system calls on Unix systems). This shouldn't be confused with running 25 a command in a shell. The shell will allow for features such as input 26 redirection, pipes, and general scripting. The shell is also 27 responsible for splitting the user's input into a program name and its 28 arguments. For example, the equivalent to 29 30 ls / /tmp 31 32 would be 33 34 exec.Command("ls", "/", "/tmp") 35 36 If you want to run a command in a shell, consider using something like 37 the following – but be aware that not all systems, particularly 38 Windows, will have a \'/bin/sh\' program: 39 40 exec.Command("/bin/sh", "-c", "ls | grep Awesome")`, 41 Since: "2017.1", 42 Severity: lint.SeverityWarning, 43 MergeIf: lint.MergeIfAny, 44 }, 45 }) 46 47 var Analyzer = SCAnalyzer.Analyzer 48 49 var query = pattern.MustParse(`(CallExpr (Symbol "os/exec.Command") arg1:_)`) 50 51 func run(pass *analysis.Pass) (any, error) { 52 for _, m := range code.Matches(pass, query) { 53 arg1 := m.State["arg1"].(ast.Expr) 54 val, ok := code.ExprToString(pass, arg1) 55 if !ok { 56 continue 57 } 58 if !strings.Contains(val, " ") || strings.Contains(val, `\`) || strings.Contains(val, "/") { 59 continue 60 } 61 report.Report(pass, arg1, 62 "first argument to exec.Command looks like a shell command, but a program name or path are expected") 63 } 64 return nil, nil 65 }