github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa1005/sa1005.go (about)

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