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 }