gopkg.in/alecthomas/gometalinter.v3@v3.0.0/_linters/src/github.com/securego/gosec/helpers.go (about)

     1  // (c) Copyright 2016 Hewlett Packard Enterprise Development LP
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package gosec
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"go/ast"
    21  	"go/token"
    22  	"go/types"
    23  	"os"
    24  	"os/user"
    25  	"path/filepath"
    26  	"runtime"
    27  	"strconv"
    28  	"strings"
    29  )
    30  
    31  // MatchCallByPackage ensures that the specified package is imported,
    32  // adjusts the name for any aliases and ignores cases that are
    33  // initialization only imports.
    34  //
    35  // Usage:
    36  // 	node, matched := MatchCallByPackage(n, ctx, "math/rand", "Read")
    37  //
    38  func MatchCallByPackage(n ast.Node, c *Context, pkg string, names ...string) (*ast.CallExpr, bool) {
    39  
    40  	importedName, found := GetImportedName(pkg, c)
    41  	if !found {
    42  		return nil, false
    43  	}
    44  
    45  	if callExpr, ok := n.(*ast.CallExpr); ok {
    46  		packageName, callName, err := GetCallInfo(callExpr, c)
    47  		if err != nil {
    48  			return nil, false
    49  		}
    50  		if packageName == importedName {
    51  			for _, name := range names {
    52  				if callName == name {
    53  					return callExpr, true
    54  				}
    55  			}
    56  		}
    57  	}
    58  	return nil, false
    59  }
    60  
    61  // MatchCallByType ensures that the node is a call expression to a
    62  // specific object type.
    63  //
    64  // Usage:
    65  // 	node, matched := MatchCallByType(n, ctx, "bytes.Buffer", "WriteTo", "Write")
    66  //
    67  func MatchCallByType(n ast.Node, ctx *Context, requiredType string, calls ...string) (*ast.CallExpr, bool) {
    68  	if callExpr, ok := n.(*ast.CallExpr); ok {
    69  		typeName, callName, err := GetCallInfo(callExpr, ctx)
    70  		if err != nil {
    71  			return nil, false
    72  		}
    73  		if typeName == requiredType {
    74  			for _, call := range calls {
    75  				if call == callName {
    76  					return callExpr, true
    77  				}
    78  			}
    79  		}
    80  	}
    81  	return nil, false
    82  }
    83  
    84  // MatchCompLit will match an ast.CompositeLit based on the supplied type
    85  func MatchCompLit(n ast.Node, ctx *Context, required string) *ast.CompositeLit {
    86  	if complit, ok := n.(*ast.CompositeLit); ok {
    87  		typeOf := ctx.Info.TypeOf(complit)
    88  		if typeOf.String() == required {
    89  			return complit
    90  		}
    91  	}
    92  	return nil
    93  }
    94  
    95  // GetInt will read and return an integer value from an ast.BasicLit
    96  func GetInt(n ast.Node) (int64, error) {
    97  	if node, ok := n.(*ast.BasicLit); ok && node.Kind == token.INT {
    98  		return strconv.ParseInt(node.Value, 0, 64)
    99  	}
   100  	return 0, fmt.Errorf("Unexpected AST node type: %T", n)
   101  }
   102  
   103  // GetFloat will read and return a float value from an ast.BasicLit
   104  func GetFloat(n ast.Node) (float64, error) {
   105  	if node, ok := n.(*ast.BasicLit); ok && node.Kind == token.FLOAT {
   106  		return strconv.ParseFloat(node.Value, 64)
   107  	}
   108  	return 0.0, fmt.Errorf("Unexpected AST node type: %T", n)
   109  }
   110  
   111  // GetChar will read and return a char value from an ast.BasicLit
   112  func GetChar(n ast.Node) (byte, error) {
   113  	if node, ok := n.(*ast.BasicLit); ok && node.Kind == token.CHAR {
   114  		return node.Value[0], nil
   115  	}
   116  	return 0, fmt.Errorf("Unexpected AST node type: %T", n)
   117  }
   118  
   119  // GetString will read and return a string value from an ast.BasicLit
   120  func GetString(n ast.Node) (string, error) {
   121  	if node, ok := n.(*ast.BasicLit); ok && node.Kind == token.STRING {
   122  		return strconv.Unquote(node.Value)
   123  	}
   124  	return "", fmt.Errorf("Unexpected AST node type: %T", n)
   125  }
   126  
   127  // GetCallObject returns the object and call expression and associated
   128  // object for a given AST node. nil, nil will be returned if the
   129  // object cannot be resolved.
   130  func GetCallObject(n ast.Node, ctx *Context) (*ast.CallExpr, types.Object) {
   131  	switch node := n.(type) {
   132  	case *ast.CallExpr:
   133  		switch fn := node.Fun.(type) {
   134  		case *ast.Ident:
   135  			return node, ctx.Info.Uses[fn]
   136  		case *ast.SelectorExpr:
   137  			return node, ctx.Info.Uses[fn.Sel]
   138  		}
   139  	}
   140  	return nil, nil
   141  }
   142  
   143  // GetCallInfo returns the package or type and name  associated with a
   144  // call expression.
   145  func GetCallInfo(n ast.Node, ctx *Context) (string, string, error) {
   146  	switch node := n.(type) {
   147  	case *ast.CallExpr:
   148  		switch fn := node.Fun.(type) {
   149  		case *ast.SelectorExpr:
   150  			switch expr := fn.X.(type) {
   151  			case *ast.Ident:
   152  				if expr.Obj != nil && expr.Obj.Kind == ast.Var {
   153  					t := ctx.Info.TypeOf(expr)
   154  					if t != nil {
   155  						return t.String(), fn.Sel.Name, nil
   156  					}
   157  					return "undefined", fn.Sel.Name, fmt.Errorf("missing type info")
   158  				}
   159  				return expr.Name, fn.Sel.Name, nil
   160  			}
   161  		case *ast.Ident:
   162  			return ctx.Pkg.Name(), fn.Name, nil
   163  		}
   164  	}
   165  	return "", "", fmt.Errorf("unable to determine call info")
   166  }
   167  
   168  // GetImportedName returns the name used for the package within the
   169  // code. It will resolve aliases and ignores initalization only imports.
   170  func GetImportedName(path string, ctx *Context) (string, bool) {
   171  	importName, imported := ctx.Imports.Imported[path]
   172  	if !imported {
   173  		return "", false
   174  	}
   175  
   176  	if _, initonly := ctx.Imports.InitOnly[path]; initonly {
   177  		return "", false
   178  	}
   179  
   180  	if alias, ok := ctx.Imports.Aliased[path]; ok {
   181  		importName = alias
   182  	}
   183  	return importName, true
   184  }
   185  
   186  // GetImportPath resolves the full import path of an identifer based on
   187  // the imports in the current context.
   188  func GetImportPath(name string, ctx *Context) (string, bool) {
   189  	for path := range ctx.Imports.Imported {
   190  		if imported, ok := GetImportedName(path, ctx); ok && imported == name {
   191  			return path, true
   192  		}
   193  	}
   194  	return "", false
   195  }
   196  
   197  // GetLocation returns the filename and line number of an ast.Node
   198  func GetLocation(n ast.Node, ctx *Context) (string, int) {
   199  	fobj := ctx.FileSet.File(n.Pos())
   200  	return fobj.Name(), fobj.Line(n.Pos())
   201  }
   202  
   203  // Gopath returns all GOPATHs
   204  func Gopath() []string {
   205  	defaultGoPath := runtime.GOROOT()
   206  	if u, err := user.Current(); err == nil {
   207  		defaultGoPath = filepath.Join(u.HomeDir, "go")
   208  	}
   209  	path := Getenv("GOPATH", defaultGoPath)
   210  	paths := strings.Split(path, string(os.PathListSeparator))
   211  	for idx, path := range paths {
   212  		if abs, err := filepath.Abs(path); err == nil {
   213  			paths[idx] = abs
   214  		}
   215  	}
   216  	return paths
   217  }
   218  
   219  // Getenv returns the values of the environment variable, otherwise
   220  //returns the default if variable is not set
   221  func Getenv(key, userDefault string) string {
   222  	if val := os.Getenv(key); val != "" {
   223  		return val
   224  	}
   225  	return userDefault
   226  }
   227  
   228  // GetPkgRelativePath returns the Go relative relative path derived
   229  // form the given path
   230  func GetPkgRelativePath(path string) (string, error) {
   231  	abspath, err := filepath.Abs(path)
   232  	if err != nil {
   233  		abspath = path
   234  	}
   235  	if strings.HasSuffix(abspath, ".go") {
   236  		abspath = filepath.Dir(abspath)
   237  	}
   238  	for _, base := range Gopath() {
   239  		projectRoot := filepath.FromSlash(fmt.Sprintf("%s/src/", base))
   240  		if strings.HasPrefix(abspath, projectRoot) {
   241  			return strings.TrimPrefix(abspath, projectRoot), nil
   242  		}
   243  	}
   244  	return "", errors.New("no project relative path found")
   245  }
   246  
   247  // GetPkgAbsPath returns the Go package absolute path derived from
   248  // the given path
   249  func GetPkgAbsPath(pkgPath string) (string, error) {
   250  	absPath, err := filepath.Abs(pkgPath)
   251  	if err != nil {
   252  		return "", err
   253  	}
   254  	if _, err := os.Stat(absPath); os.IsNotExist(err) {
   255  		return "", errors.New("no project absolute path found")
   256  	}
   257  	return absPath, nil
   258  }
   259  
   260  // ConcatString recusively concatenates strings from a binary expression
   261  func ConcatString(n *ast.BinaryExpr) (string, bool) {
   262  	var s string
   263  	// sub expressions are found in X object, Y object is always last BasicLit
   264  	if rightOperand, ok := n.Y.(*ast.BasicLit); ok {
   265  		if str, err := GetString(rightOperand); err == nil {
   266  			s = str + s
   267  		}
   268  	} else {
   269  		return "", false
   270  	}
   271  	if leftOperand, ok := n.X.(*ast.BinaryExpr); ok {
   272  		if recursion, ok := ConcatString(leftOperand); ok {
   273  			s = recursion + s
   274  		}
   275  	} else if leftOperand, ok := n.X.(*ast.BasicLit); ok {
   276  		if str, err := GetString(leftOperand); err == nil {
   277  			s = str + s
   278  		}
   279  	} else {
   280  		return "", false
   281  	}
   282  	return s, true
   283  }
   284  
   285  // FindVarIdentities returns array of all variable identities in a given binary expression
   286  func FindVarIdentities(n *ast.BinaryExpr, c *Context) ([]*ast.Ident, bool) {
   287  	identities := []*ast.Ident{}
   288  	// sub expressions are found in X object, Y object is always the last term
   289  	if rightOperand, ok := n.Y.(*ast.Ident); ok {
   290  		obj := c.Info.ObjectOf(rightOperand)
   291  		if _, ok := obj.(*types.Var); ok && !TryResolve(rightOperand, c) {
   292  			identities = append(identities, rightOperand)
   293  		}
   294  	}
   295  	if leftOperand, ok := n.X.(*ast.BinaryExpr); ok {
   296  		if leftIdentities, ok := FindVarIdentities(leftOperand, c); ok {
   297  			identities = append(identities, leftIdentities...)
   298  		}
   299  	} else {
   300  		if leftOperand, ok := n.X.(*ast.Ident); ok {
   301  			obj := c.Info.ObjectOf(leftOperand)
   302  			if _, ok := obj.(*types.Var); ok && !TryResolve(leftOperand, c) {
   303  				identities = append(identities, leftOperand)
   304  			}
   305  		}
   306  	}
   307  
   308  	if len(identities) > 0 {
   309  		return identities, true
   310  	}
   311  	// if nil or error, return false
   312  	return nil, false
   313  }