Files
wehub-resource-sync 48b3ccf279
Test / test (windows-latest) (push) Waiting to run
gitleaks / gitleaks (push) Has been skipped
Test / test (ubuntu-latest) (push) Failing after 0s
chore: import upstream snapshot with attribution
2026-07-13 12:12:29 +08:00

127 lines
2.8 KiB
Go

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))
}
}
}