src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

sa9007.go (3008B)


      1 package sa9007
      2 
      3 import (
      4 	"fmt"
      5 	"os"
      6 
      7 	"honnef.co/go/tools/analysis/lint"
      8 	"honnef.co/go/tools/analysis/report"
      9 	"honnef.co/go/tools/go/ir"
     10 	"honnef.co/go/tools/go/ir/irutil"
     11 	"honnef.co/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.RawDocumentation{
     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 mistakenly 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) (any, 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 }