gopkg.in/alecthomas/gometalinter.v3@v3.0.0/_linters/src/github.com/securego/gosec/cmd/gosecutil/tools.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 main
    16  
    17  import (
    18  	"flag"
    19  	"fmt"
    20  	"go/ast"
    21  	"go/importer"
    22  	"go/parser"
    23  	"go/token"
    24  	"go/types"
    25  	"os"
    26  	"strings"
    27  )
    28  
    29  type command func(args ...string)
    30  type utilities struct {
    31  	commands map[string]command
    32  	call     []string
    33  }
    34  
    35  // Custom commands / utilities to run instead of default analyzer
    36  func newUtils() *utilities {
    37  	utils := make(map[string]command)
    38  	utils["ast"] = dumpAst
    39  	utils["callobj"] = dumpCallObj
    40  	utils["uses"] = dumpUses
    41  	utils["types"] = dumpTypes
    42  	utils["defs"] = dumpDefs
    43  	utils["comments"] = dumpComments
    44  	utils["imports"] = dumpImports
    45  	return &utilities{utils, make([]string, 0)}
    46  }
    47  
    48  func (u *utilities) String() string {
    49  	i := 0
    50  	keys := make([]string, len(u.commands))
    51  	for k := range u.commands {
    52  		keys[i] = k
    53  		i++
    54  	}
    55  	return strings.Join(keys, ", ")
    56  }
    57  
    58  func (u *utilities) Set(opt string) error {
    59  	if _, ok := u.commands[opt]; !ok {
    60  		return fmt.Errorf("valid tools are: %s", u.String())
    61  
    62  	}
    63  	u.call = append(u.call, opt)
    64  	return nil
    65  }
    66  
    67  func (u *utilities) run(args ...string) {
    68  	for _, util := range u.call {
    69  		if cmd, ok := u.commands[util]; ok {
    70  			cmd(args...)
    71  		}
    72  	}
    73  }
    74  
    75  func shouldSkip(path string) bool {
    76  	st, e := os.Stat(path)
    77  	if e != nil {
    78  		// #nosec
    79  		fmt.Fprintf(os.Stderr, "Skipping: %s - %s\n", path, e)
    80  		return true
    81  	}
    82  	if st.IsDir() {
    83  		// #nosec
    84  		fmt.Fprintf(os.Stderr, "Skipping: %s - directory\n", path)
    85  		return true
    86  	}
    87  	return false
    88  }
    89  
    90  func dumpAst(files ...string) {
    91  	for _, arg := range files {
    92  		// Ensure file exists and not a directory
    93  		if shouldSkip(arg) {
    94  			continue
    95  		}
    96  
    97  		// Create the AST by parsing src.
    98  		fset := token.NewFileSet() // positions are relative to fset
    99  		f, err := parser.ParseFile(fset, arg, nil, 0)
   100  		if err != nil {
   101  			// #nosec
   102  			fmt.Fprintf(os.Stderr, "Unable to parse file %s\n", err)
   103  			continue
   104  		}
   105  
   106  		// Print the AST. #nosec
   107  		ast.Print(fset, f)
   108  	}
   109  }
   110  
   111  type context struct {
   112  	fileset  *token.FileSet
   113  	comments ast.CommentMap
   114  	info     *types.Info
   115  	pkg      *types.Package
   116  	config   *types.Config
   117  	root     *ast.File
   118  }
   119  
   120  func createContext(filename string) *context {
   121  	fileset := token.NewFileSet()
   122  	root, e := parser.ParseFile(fileset, filename, nil, parser.ParseComments)
   123  	if e != nil {
   124  		// #nosec
   125  		fmt.Fprintf(os.Stderr, "Unable to parse file: %s. Reason: %s\n", filename, e)
   126  		return nil
   127  	}
   128  	comments := ast.NewCommentMap(fileset, root, root.Comments)
   129  	info := &types.Info{
   130  		Types:      make(map[ast.Expr]types.TypeAndValue),
   131  		Defs:       make(map[*ast.Ident]types.Object),
   132  		Uses:       make(map[*ast.Ident]types.Object),
   133  		Selections: make(map[*ast.SelectorExpr]*types.Selection),
   134  		Scopes:     make(map[ast.Node]*types.Scope),
   135  		Implicits:  make(map[ast.Node]types.Object),
   136  	}
   137  	config := types.Config{Importer: importer.Default()}
   138  	pkg, e := config.Check("main.go", fileset, []*ast.File{root}, info)
   139  	if e != nil {
   140  		// #nosec
   141  		fmt.Fprintf(os.Stderr, "Type check failed for file: %s. Reason: %s\n", filename, e)
   142  		return nil
   143  	}
   144  	return &context{fileset, comments, info, pkg, &config, root}
   145  }
   146  
   147  func printObject(obj types.Object) {
   148  	fmt.Println("OBJECT")
   149  	if obj == nil {
   150  		fmt.Println("object is nil")
   151  		return
   152  	}
   153  	fmt.Printf("   Package = %v\n", obj.Pkg())
   154  	if obj.Pkg() != nil {
   155  		fmt.Println("   Path = ", obj.Pkg().Path())
   156  		fmt.Println("   Name = ", obj.Pkg().Name())
   157  		fmt.Println("   String = ", obj.Pkg().String())
   158  	}
   159  	fmt.Printf("   Name = %v\n", obj.Name())
   160  	fmt.Printf("   Type = %v\n", obj.Type())
   161  	fmt.Printf("   Id = %v\n", obj.Id())
   162  }
   163  
   164  func checkContext(ctx *context, file string) bool {
   165  	// #nosec
   166  	if ctx == nil {
   167  		fmt.Fprintln(os.Stderr, "Failed to create context for file: ", file)
   168  		return false
   169  	}
   170  	return true
   171  }
   172  
   173  func dumpCallObj(files ...string) {
   174  
   175  	for _, file := range files {
   176  		if shouldSkip(file) {
   177  			continue
   178  		}
   179  		context := createContext(file)
   180  		if !checkContext(context, file) {
   181  			return
   182  		}
   183  		ast.Inspect(context.root, func(n ast.Node) bool {
   184  			var obj types.Object
   185  			switch node := n.(type) {
   186  			case *ast.Ident:
   187  				obj = context.info.ObjectOf(node) //context.info.Uses[node]
   188  			case *ast.SelectorExpr:
   189  				obj = context.info.ObjectOf(node.Sel) //context.info.Uses[node.Sel]
   190  			default:
   191  				obj = nil
   192  			}
   193  			if obj != nil {
   194  				printObject(obj)
   195  			}
   196  			return true
   197  		})
   198  	}
   199  }
   200  
   201  func dumpUses(files ...string) {
   202  	for _, file := range files {
   203  		if shouldSkip(file) {
   204  			continue
   205  		}
   206  		context := createContext(file)
   207  		if !checkContext(context, file) {
   208  			return
   209  		}
   210  		for ident, obj := range context.info.Uses {
   211  			fmt.Printf("IDENT: %v, OBJECT: %v\n", ident, obj)
   212  		}
   213  	}
   214  }
   215  
   216  func dumpTypes(files ...string) {
   217  	for _, file := range files {
   218  		if shouldSkip(file) {
   219  			continue
   220  		}
   221  		context := createContext(file)
   222  		if !checkContext(context, file) {
   223  			return
   224  		}
   225  		for expr, tv := range context.info.Types {
   226  			fmt.Printf("EXPR: %v, TYPE: %v\n", expr, tv)
   227  		}
   228  	}
   229  }
   230  
   231  func dumpDefs(files ...string) {
   232  	for _, file := range files {
   233  		if shouldSkip(file) {
   234  			continue
   235  		}
   236  		context := createContext(file)
   237  		if !checkContext(context, file) {
   238  			return
   239  		}
   240  		for ident, obj := range context.info.Defs {
   241  			fmt.Printf("IDENT: %v, OBJ: %v\n", ident, obj)
   242  		}
   243  	}
   244  }
   245  
   246  func dumpComments(files ...string) {
   247  	for _, file := range files {
   248  		if shouldSkip(file) {
   249  			continue
   250  		}
   251  		context := createContext(file)
   252  		if !checkContext(context, file) {
   253  			return
   254  		}
   255  		for _, group := range context.comments.Comments() {
   256  			fmt.Println(group.Text())
   257  		}
   258  	}
   259  }
   260  
   261  func dumpImports(files ...string) {
   262  	for _, file := range files {
   263  		if shouldSkip(file) {
   264  			continue
   265  		}
   266  		context := createContext(file)
   267  		if !checkContext(context, file) {
   268  			return
   269  		}
   270  		for _, pkg := range context.pkg.Imports() {
   271  			fmt.Println(pkg.Path(), pkg.Name())
   272  			for _, name := range pkg.Scope().Names() {
   273  				fmt.Println("  => ", name)
   274  			}
   275  		}
   276  	}
   277  }
   278  
   279  func main() {
   280  	tools := newUtils()
   281  	flag.Var(tools, "tool", "Utils to assist with rule development")
   282  	flag.Parse()
   283  
   284  	if len(tools.call) > 0 {
   285  		tools.run(flag.Args()...)
   286  		os.Exit(0)
   287  	}
   288  }