| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- package report
- import (
- "fmt"
- "math"
- "strings"
- "github.com/charmbracelet/lipgloss"
- "github.com/zricethezav/gitleaks/v8/sources"
- )
- // Finding contains a whole bunch of information about a secret finding.
- // Plenty of real estate in this bad boy so fillerup as needed.
- type Finding struct {
- // Rule is the name of the rule that was matched
- RuleID string
- Description string
- StartLine int
- EndLine int
- StartColumn int
- EndColumn int
- Line string `json:"-"`
- Match string
- // Captured secret
- Secret string
- // File is the name of the file containing the finding
- File string
- SymlinkFile string
- Commit string
- Link string `json:",omitempty"`
- // Entropy is the shannon entropy of Value
- Entropy float32
- Author string
- Email string
- Date string
- Message string
- Tags []string
- // unique identifier
- Fingerprint string
- // Fragment used for multi-part rule checking, CEL filtering,
- // and eventually ML validation
- Fragment *sources.Fragment `json:",omitempty"`
- // TODO keeping private for now to during experimental phase
- requiredFindings []*RequiredFinding
- }
- type RequiredFinding struct {
- // contains a subset of the Finding fields
- // only used for reporting
- RuleID string
- StartLine int
- EndLine int
- StartColumn int
- EndColumn int
- Line string `json:"-"`
- Match string
- Secret string
- }
- func (f *Finding) AddRequiredFindings(afs []*RequiredFinding) {
- if f.requiredFindings == nil {
- f.requiredFindings = make([]*RequiredFinding, 0)
- }
- f.requiredFindings = append(f.requiredFindings, afs...)
- }
- // Redact removes sensitive information from a finding.
- func (f *Finding) Redact(percent uint) {
- secret := maskSecret(f.Secret, percent)
- if percent >= 100 {
- secret = "REDACTED"
- }
- f.Line = strings.ReplaceAll(f.Line, f.Secret, secret)
- f.Match = strings.ReplaceAll(f.Match, f.Secret, secret)
- f.Secret = secret
- }
- func maskSecret(secret string, percent uint) string {
- if percent > 100 {
- percent = 100
- }
- len := float64(len(secret))
- if len <= 0 {
- return secret
- }
- prc := float64(100 - percent)
- lth := int64(math.RoundToEven(len * prc / float64(100)))
- return secret[:lth] + "..."
- }
- func (f *Finding) PrintRequiredFindings() {
- if len(f.requiredFindings) == 0 {
- return
- }
- fmt.Printf("%-12s ", "Required:")
- // Create orange style for secrets
- orangeStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#bf9478"))
- for i, aux := range f.requiredFindings {
- auxSecret := strings.TrimSpace(aux.Secret)
- // Truncate long secrets for readability
- if len(auxSecret) > 40 {
- auxSecret = auxSecret[:37] + "..."
- }
- // Format: rule-id:line:secret
- if i == 0 {
- fmt.Printf("%s:%d:%s\n", aux.RuleID, aux.StartLine, orangeStyle.Render(auxSecret))
- } else {
- fmt.Printf("%-12s %s:%d:%s\n", "", aux.RuleID, aux.StartLine, orangeStyle.Render(auxSecret))
- }
- }
- }
|