github.com/google/go-safeweb@v0.0.0-20231219055052-64d8cfc90fbb/cmd/bancheck/bannedapi/banned_api.go (about) 1 // Copyright 2020 Google LLC 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 // https://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 bannedapi provides the tools for doing static analysis 16 // and checking for usage of banned APIs. 17 package bannedapi 18 19 import ( 20 "errors" 21 "flag" 22 "fmt" 23 "go/token" 24 "go/types" 25 "path/filepath" 26 "strings" 27 28 "github.com/google/go-safeweb/cmd/bancheck/config" 29 "golang.org/x/tools/go/analysis" 30 ) 31 32 // NewAnalyzer returns an analyzer that checks for usage of banned APIs. 33 func NewAnalyzer() *analysis.Analyzer { 34 fs := flag.NewFlagSet("", flag.ExitOnError) 35 fs.String("configs", "", "Config files with banned APIs separated by a comma") 36 37 a := &analysis.Analyzer{ 38 Name: "bannedAPI", 39 Doc: "Checks for usage of banned APIs", 40 Run: checkBannedAPIs, 41 Flags: *fs, 42 } 43 44 return a 45 } 46 47 func checkBannedAPIs(pass *analysis.Pass) (interface{}, error) { 48 cfgFiles := pass.Analyzer.Flags.Lookup("configs").Value.String() 49 if cfgFiles == "" { 50 return nil, errors.New("missing config files") 51 } 52 53 cfg, err := config.ReadConfigs(strings.Split(cfgFiles, ",")) 54 if err != nil { 55 return nil, err 56 } 57 58 checkBannedImports(pass, bannedAPIMap(cfg.Imports)) 59 checkBannedFunctions(pass, bannedAPIMap(cfg.Functions)) 60 61 return nil, nil 62 } 63 64 func checkBannedImports(pass *analysis.Pass, bannedImports map[string][]config.BannedAPI) (interface{}, error) { 65 for _, f := range pass.Files { 66 for _, i := range f.Imports { 67 importName := strings.Trim(i.Path.Value, "\"") 68 err := reportIfBanned(importName, bannedImports, i.Pos(), pass) 69 if err != nil { 70 return false, err 71 } 72 } 73 } 74 return nil, nil 75 } 76 77 func checkBannedFunctions(pass *analysis.Pass, bannedFns map[string][]config.BannedAPI) (interface{}, error) { 78 for id, obj := range pass.TypesInfo.Uses { 79 fn, ok := obj.(*types.Func) 80 if !ok { 81 continue 82 } 83 84 fnName := fmt.Sprintf("%s.%s", fn.Pkg().Path(), fn.Name()) 85 err := reportIfBanned(fnName, bannedFns, id.Pos(), pass) 86 if err != nil { 87 return false, err 88 } 89 } 90 return nil, nil 91 } 92 93 func reportIfBanned(apiName string, bannedAPIs map[string][]config.BannedAPI, position token.Pos, pass *analysis.Pass) error { 94 for _, banCfg := range bannedAPIs[apiName] { 95 if apiName != banCfg.Name { 96 return nil 97 } 98 pkgAllowed, err := isPkgAllowed(pass.Pkg, banCfg) 99 if err != nil { 100 return err 101 } 102 if pkgAllowed { 103 continue 104 } 105 pass.Report(analysis.Diagnostic{ 106 Pos: position, 107 Message: fmt.Sprintf("Banned API found %q. Additional info: %s", apiName, banCfg.Msg), 108 }) 109 } 110 return nil 111 } 112 113 // isPkgAllowed checks if the Go package should be exempted from reporting banned API usages. 114 func isPkgAllowed(pkg *types.Package, bannedAPI config.BannedAPI) (bool, error) { 115 for _, e := range bannedAPI.Exemptions { 116 match, err := filepath.Match(e.AllowedPkg, pkg.Path()) 117 if err != nil { 118 return false, err 119 } 120 if match { 121 return true, nil 122 } 123 } 124 return false, nil 125 } 126 127 // bannedAPIMap builds a mapping of fully qualified API name to a list of 128 // all its config.BannedAPI entries. 129 func bannedAPIMap(bannedAPIs []config.BannedAPI) map[string][]config.BannedAPI { 130 m := make(map[string][]config.BannedAPI) 131 for _, API := range bannedAPIs { 132 m[API.Name] = append(m[API.Name], API) 133 } 134 return m 135 }