github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa9007/sa9007.go (about) 1 package sa9007 2 3 import ( 4 "fmt" 5 "os" 6 7 "github.com/amarpal/go-tools/analysis/lint" 8 "github.com/amarpal/go-tools/analysis/report" 9 "github.com/amarpal/go-tools/go/ir" 10 "github.com/amarpal/go-tools/go/ir/irutil" 11 "github.com/amarpal/go-tools/internal/passes/buildir" 12 13 "golang.org/x/tools/go/analysis" 14 ) 15 16 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 17 Analyzer: &analysis.Analyzer{ 18 Name: "SA9007", 19 Run: run, 20 Requires: []*analysis.Analyzer{buildir.Analyzer}, 21 }, 22 Doc: &lint.Documentation{ 23 Title: "Deleting a directory that shouldn't be deleted", 24 Text: ` 25 It is virtually never correct to delete system directories such as 26 /tmp or the user's home directory. However, it can be fairly easy to 27 do by mistake, for example by mistakingly using \'os.TempDir\' instead 28 of \'ioutil.TempDir\', or by forgetting to add a suffix to the result 29 of \'os.UserHomeDir\'. 30 31 Writing 32 33 d := os.TempDir() 34 defer os.RemoveAll(d) 35 36 in your unit tests will have a devastating effect on the stability of your system. 37 38 This check flags attempts at deleting the following directories: 39 40 - os.TempDir 41 - os.UserCacheDir 42 - os.UserConfigDir 43 - os.UserHomeDir 44 `, 45 Since: "2022.1", 46 Severity: lint.SeverityWarning, 47 MergeIf: lint.MergeIfAny, 48 }, 49 }) 50 51 var Analyzer = SCAnalyzer.Analyzer 52 53 func run(pass *analysis.Pass) (interface{}, error) { 54 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 55 for _, b := range fn.Blocks { 56 for _, instr := range b.Instrs { 57 call, ok := instr.(ir.CallInstruction) 58 if !ok { 59 continue 60 } 61 if !irutil.IsCallTo(call.Common(), "os.RemoveAll") { 62 continue 63 } 64 65 kind := "" 66 ex := "" 67 callName := "" 68 arg := irutil.Flatten(call.Common().Args[0]) 69 switch arg := arg.(type) { 70 case *ir.Call: 71 callName = irutil.CallName(&arg.Call) 72 if callName != "os.TempDir" { 73 continue 74 } 75 kind = "temporary" 76 ex = os.TempDir() 77 case *ir.Extract: 78 if arg.Index != 0 { 79 continue 80 } 81 first, ok := arg.Tuple.(*ir.Call) 82 if !ok { 83 continue 84 } 85 callName = irutil.CallName(&first.Call) 86 switch callName { 87 case "os.UserCacheDir": 88 kind = "cache" 89 ex, _ = os.UserCacheDir() 90 case "os.UserConfigDir": 91 kind = "config" 92 ex, _ = os.UserConfigDir() 93 case "os.UserHomeDir": 94 kind = "home" 95 ex, _ = os.UserHomeDir() 96 default: 97 continue 98 } 99 default: 100 continue 101 } 102 103 if ex == "" { 104 report.Report(pass, call, fmt.Sprintf("this call to os.RemoveAll deletes the user's entire %s directory, not a subdirectory therein", kind), 105 report.Related(arg, fmt.Sprintf("this call to %s returns the user's %s directory", callName, kind))) 106 } else { 107 report.Report(pass, call, fmt.Sprintf("this call to os.RemoveAll deletes the user's entire %s directory, not a subdirectory therein", kind), 108 report.Related(arg, fmt.Sprintf("this call to %s returns the user's %s directory, for example %s", callName, kind, ex))) 109 } 110 } 111 } 112 } 113 return nil, nil 114 }