github.com/prysmaticlabs/prysm@v1.4.4/tools/analyzers/cryptorand/analyzer.go (about) 1 // Package cryptorand implements a static analyzer to ensure that the crypto/rand package is used 2 // for randomness throughout the codebase. 3 package cryptorand 4 5 import ( 6 "errors" 7 "fmt" 8 "go/ast" 9 "strings" 10 11 "golang.org/x/tools/go/analysis" 12 "golang.org/x/tools/go/analysis/passes/inspect" 13 "golang.org/x/tools/go/ast/inspector" 14 ) 15 16 // Doc explaining the tool. 17 const Doc = "Tool to enforce the use of stronger crypto: crypto/rand instead of math/rand" 18 19 var errWeakCrypto = errors.New("crypto-secure RNGs are required, use CSPRNG or PRNG defined in github.com/prysmaticlabs/prysm/shared/rand") 20 21 // Analyzer runs static analysis. 22 var Analyzer = &analysis.Analyzer{ 23 Name: "cryptorand", 24 Doc: Doc, 25 Requires: []*analysis.Analyzer{inspect.Analyzer}, 26 Run: run, 27 } 28 29 func run(pass *analysis.Pass) (interface{}, error) { 30 inspection, ok := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 31 if !ok { 32 return nil, errors.New("analyzer is not type *inspector.Inspector") 33 } 34 35 nodeFilter := []ast.Node{ 36 (*ast.File)(nil), 37 (*ast.ImportSpec)(nil), 38 (*ast.CallExpr)(nil), 39 } 40 41 aliases := make(map[string]string) 42 disallowedFns := []string{"NewSource", "New", "Seed", "Int63", "Uint32", "Uint64", "Int31", "Int", 43 "Int63n", "Int31n", "Intn", "Float64", "Float32", "Perm", "Shuffle", "Read"} 44 45 inspection.Preorder(nodeFilter, func(node ast.Node) { 46 switch stmt := node.(type) { 47 case *ast.File: 48 // Reset aliases (per file). 49 aliases = make(map[string]string) 50 case *ast.ImportSpec: 51 // Collect aliases to rand packages. 52 pkg := stmt.Path.Value 53 if strings.HasSuffix(pkg, "/rand\"") && !strings.Contains(pkg, "/prysm/shared/rand") { 54 if stmt.Name != nil { 55 aliases[stmt.Name.Name] = stmt.Path.Value 56 } else { 57 aliases["rand"] = stmt.Path.Value 58 } 59 } 60 case *ast.CallExpr: 61 // Check if any of disallowed functions have been used. 62 for pkg, path := range aliases { 63 for _, fn := range disallowedFns { 64 if isPkgDot(stmt.Fun, pkg, fn) { 65 pass.Reportf(node.Pos(), fmt.Sprintf( 66 "%s: %s.%s() (from %s)", errWeakCrypto.Error(), pkg, fn, path)) 67 } 68 } 69 } 70 } 71 }) 72 73 return nil, nil 74 } 75 76 func isPkgDot(expr ast.Expr, pkg, name string) bool { 77 sel, ok := expr.(*ast.SelectorExpr) 78 return ok && isIdent(sel.X, pkg) && isIdent(sel.Sel, name) 79 } 80 81 func isIdent(expr ast.Expr, ident string) bool { 82 id, ok := expr.(*ast.Ident) 83 return ok && id.Name == ident 84 }