github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/fanal/analyzer/secret/secret.go (about) 1 package secret 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io" 8 "os" 9 "path/filepath" 10 "strings" 11 12 "github.com/samber/lo" 13 "golang.org/x/exp/slices" 14 "golang.org/x/xerrors" 15 16 "github.com/devseccon/trivy/pkg/fanal/analyzer" 17 "github.com/devseccon/trivy/pkg/fanal/secret" 18 "github.com/devseccon/trivy/pkg/fanal/types" 19 "github.com/devseccon/trivy/pkg/fanal/utils" 20 ) 21 22 // To make sure SecretAnalyzer implements analyzer.Initializer 23 var _ analyzer.Initializer = &SecretAnalyzer{} 24 25 const version = 1 26 27 var ( 28 skipFiles = []string{ 29 "go.mod", 30 "go.sum", 31 "package-lock.json", 32 "yarn.lock", 33 "pnpm-lock.yaml", 34 "Pipfile.lock", 35 "Gemfile.lock", 36 } 37 skipDirs = []string{".git", "node_modules"} 38 skipExts = []string{ 39 ".jpg", ".png", ".gif", ".doc", ".pdf", ".bin", ".svg", ".socket", ".deb", ".rpm", 40 ".zip", ".gz", ".gzip", ".tar", ".pyc", 41 } 42 ) 43 44 func init() { 45 // The scanner will be initialized later via InitScanner() 46 analyzer.RegisterAnalyzer(NewSecretAnalyzer(secret.Scanner{}, "")) 47 } 48 49 // SecretAnalyzer is an analyzer for secrets 50 type SecretAnalyzer struct { 51 scanner secret.Scanner 52 configPath string 53 } 54 55 func NewSecretAnalyzer(s secret.Scanner, configPath string) *SecretAnalyzer { 56 return &SecretAnalyzer{ 57 scanner: s, 58 configPath: configPath, 59 } 60 } 61 62 // Init initializes and sets a secret scanner 63 func (a *SecretAnalyzer) Init(opt analyzer.AnalyzerOptions) error { 64 if opt.SecretScannerOption.ConfigPath == a.configPath && !lo.IsEmpty(a.scanner) { 65 // This check is for tools importing Trivy and customize analyzers 66 // Never reach here in Trivy OSS 67 return nil 68 } 69 configPath := opt.SecretScannerOption.ConfigPath 70 c, err := secret.ParseConfig(configPath) 71 if err != nil { 72 return xerrors.Errorf("secret config error: %w", err) 73 } 74 a.scanner = secret.NewScanner(c) 75 a.configPath = configPath 76 return nil 77 } 78 79 func (a *SecretAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) { 80 // Do not scan binaries 81 binary, err := utils.IsBinary(input.Content, input.Info.Size()) 82 if binary || err != nil { 83 return nil, nil 84 } 85 86 content, err := io.ReadAll(input.Content) 87 if err != nil { 88 return nil, xerrors.Errorf("read error %s: %w", input.FilePath, err) 89 } 90 91 content = bytes.ReplaceAll(content, []byte("\r"), []byte("")) 92 93 filePath := input.FilePath 94 // Files extracted from the image have an empty input.Dir. 95 // Also, paths to these files do not have "/" prefix. 96 // We need to add a "/" prefix to properly filter paths from the config file. 97 if input.Dir == "" { // add leading `/` for files extracted from image 98 filePath = fmt.Sprintf("/%s", filePath) 99 } 100 101 result := a.scanner.Scan(secret.ScanArgs{ 102 FilePath: filePath, 103 Content: content, 104 }) 105 106 if len(result.Findings) == 0 { 107 return nil, nil 108 } 109 110 return &analyzer.AnalysisResult{ 111 Secrets: []types.Secret{result}, 112 }, nil 113 } 114 115 func (a *SecretAnalyzer) Required(filePath string, fi os.FileInfo) bool { 116 // Skip small files 117 if fi.Size() < 10 { 118 return false 119 } 120 121 dir, fileName := filepath.Split(filePath) 122 dir = filepath.ToSlash(dir) 123 dirs := strings.Split(dir, "/") 124 125 // Check if the directory should be skipped 126 for _, skipDir := range skipDirs { 127 if slices.Contains(dirs, skipDir) { 128 return false 129 } 130 } 131 132 // Check if the file should be skipped 133 if slices.Contains(skipFiles, fileName) { 134 return false 135 } 136 137 // Skip the config file for secret scanning 138 if filepath.Base(a.configPath) == filePath { 139 return false 140 } 141 142 // Check if the file extension should be skipped 143 ext := filepath.Ext(fileName) 144 if slices.Contains(skipExts, ext) { 145 return false 146 } 147 148 if a.scanner.AllowPath(filePath) { 149 return false 150 } 151 152 return true 153 } 154 155 func (a *SecretAnalyzer) Type() analyzer.Type { 156 return analyzer.TypeSecret 157 } 158 159 func (a *SecretAnalyzer) Version() int { 160 return version 161 }