| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- package detect
- import (
- "encoding/json"
- "fmt"
- "regexp"
- "strings"
- "github.com/rs/zerolog/log"
- "github.com/zricethezav/gitleaks/v8/config"
- "github.com/zricethezav/gitleaks/v8/report"
- )
- type Options struct {
- Verbose bool
- Redact bool
- }
- func DetectFindings(cfg config.Config, b []byte, filePath string, commit string) []report.Finding {
- var findings []report.Finding
- linePairs := regexp.MustCompile("\n").FindAllIndex(b, -1)
- // check if we should skip file based on the global allowlist or if the file is the same as the gitleaks config
- if cfg.Allowlist.PathAllowed(filePath) || filePath == cfg.Path {
- return findings
- }
- for _, r := range cfg.Rules {
- pathSkip := false
- if r.Allowlist.CommitAllowed(commit) {
- continue
- }
- if r.Allowlist.PathAllowed(filePath) {
- continue
- }
- // Check if path should be considered
- if r.Path != nil {
- if r.Path.Match([]byte(filePath)) {
- if r.Regex == nil {
- // This is a path only rule
- f := report.Finding{
- Description: r.Description,
- File: filePath,
- RuleID: r.RuleID,
- Match: fmt.Sprintf("file detected: %s", filePath),
- Tags: r.Tags,
- }
- findings = append(findings, f)
- pathSkip = true
- }
- } else {
- pathSkip = true
- }
- }
- if pathSkip {
- continue
- }
- matchIndices := r.Regex.FindAllIndex(b, -1)
- for _, m := range matchIndices {
- location := getLocation(linePairs, m[0], m[1])
- secret := strings.Trim(string(b[m[0]:m[1]]), "\n")
- f := report.Finding{
- Description: r.Description,
- File: filePath,
- RuleID: r.RuleID,
- StartLine: location.startLine,
- EndLine: location.endLine,
- StartColumn: location.startColumn,
- EndColumn: location.endColumn,
- Secret: secret,
- Match: secret,
- Tags: r.Tags,
- }
- if r.Allowlist.RegexAllowed(f.Secret) || cfg.Allowlist.RegexAllowed(f.Secret) {
- continue
- }
- // extract secret from secret group if set
- if r.SecretGroup != 0 {
- groups := r.Regex.FindStringSubmatch(secret)
- if len(groups)-1 > r.SecretGroup || len(groups) == 0 {
- // Config validation should prevent this
- break
- }
- secret = groups[r.SecretGroup]
- f.Secret = secret
- }
- // extract secret from secret group if set
- if r.EntropySet() {
- include, entropy := r.IncludeEntropy(secret)
- if include {
- f.Entropy = float32(entropy)
- findings = append(findings, f)
- }
- } else {
- findings = append(findings, f)
- }
- }
- }
- return dedupe(findings)
- }
- func limit(s string) string {
- if len(s) > 500 {
- return s[:500] + "..."
- }
- return s
- }
- func printFinding(f report.Finding) {
- var b []byte
- b, _ = json.MarshalIndent(f, "", " ")
- fmt.Println(string(b))
- }
- func dedupe(findings []report.Finding) []report.Finding {
- var retFindings []report.Finding
- for _, f := range findings {
- include := true
- if strings.Contains(strings.ToLower(f.RuleID), "generic") {
- for _, fPrime := range findings {
- if f.StartLine == fPrime.StartLine &&
- f.EndLine == fPrime.EndLine &&
- f.Commit == fPrime.Commit &&
- f.RuleID != fPrime.RuleID &&
- strings.Contains(fPrime.Secret, f.Secret) &&
- !strings.Contains(strings.ToLower(fPrime.RuleID), "generic") {
- genericMatch := strings.Replace(f.Match, f.Secret, "REDACTED", -1)
- betterMatch := strings.Replace(fPrime.Match, fPrime.Secret, "REDACTED", -1)
- log.Debug().Msgf("skipping %s finding (%s), %s rule takes precendence (%s)", f.RuleID, genericMatch, fPrime.RuleID, betterMatch)
- include = false
- break
- }
- }
- }
- if include {
- retFindings = append(retFindings, f)
- }
- }
- return retFindings
- }
|