Repository navigation
Expand file tree
/
Copy pathoutput.go
More file actions
98 lines (88 loc) · 3.01 KB
/
Copy pathoutput.go
File metadata and controls
98 lines (88 loc) · 3.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package main
import (
"encoding/json"
"fmt"
"io"
"strconv"
"strings"
"github.com/betterleaks/betterleaks/config"
"github.com/betterleaks/betterleaks/report"
)
const redactPercent = 90
type emitFunc func(Finding) error
func newEmitter(w io.Writer, format string, cfg *config.Config, redact bool) (emitFunc, func() error, error) {
switch format {
case "tsv":
return tsvEmitter(w, redact)
case "json":
return jsonEmitter(w, redact)
case "sarif":
return sarifEmitter(w, cfg, redact)
default:
return nil, nil, fmt.Errorf("unknown format %q", format)
}
}
func maybeRedact(r report.Finding, redact bool) report.Finding {
if redact {
r.Redact(redactPercent)
}
return r
}
func validationDetails(r report.Finding, redact bool) (string, map[string]any) {
if !redact {
return r.ValidationReason, r.ValidationMeta
}
validation := report.NewCredentialReport(r, []string{r.Secret}, false).Validation
return validation.Reason, validation.Metadata
}
func tsvEmitter(w io.Writer, redact bool) (emitFunc, func() error, error) {
if _, err := fmt.Fprintln(w, "commit\tpath\tblob\trule\tline\tsecret\tentropy\tstatus\treason\tmetadata"); err != nil {
return nil, nil, err
}
emit := func(f Finding) error {
reason, metadata := validationDetails(f.Report, redact)
var metadataJSON []byte
var err error
if len(metadata) > 0 {
metadataJSON, err = json.Marshal(metadata)
if err != nil {
return err
}
}
r := maybeRedact(f.Report, redact)
_, err = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\n",
f.Commit, tsvEscape(f.Path), f.Blob, r.RuleID, r.StartLine,
tsvEscape(r.Secret), strconv.FormatFloat(float64(r.Entropy), 'f', 2, 64),
string(r.ValidationStatus), tsvEscape(reason), tsvEscape(string(metadataJSON)))
return err
}
return emit, func() error { return nil }, nil
}
func jsonEmitter(w io.Writer, redact bool) (emitFunc, func() error, error) {
enc := json.NewEncoder(w)
emit := func(f Finding) error {
reason, metadata := validationDetails(f.Report, redact)
r := maybeRedact(f.Report, redact)
return enc.Encode(struct {
Blob string `json:"blob"`
Commit string `json:"commit,omitempty"`
Date string `json:"date,omitempty"`
Path string `json:"path,omitempty"`
RuleID string `json:"rule"`
Desc string `json:"description"`
StartLine int `json:"line"`
Secret string `json:"secret"`
Match string `json:"match"`
Entropy float32 `json:"entropy"`
Status string `json:"status,omitempty"`
Reason string `json:"validation_reason,omitempty"`
Metadata map[string]any `json:"validation_metadata,omitempty"`
}{f.Blob, f.Commit, f.Date, f.Path, r.RuleID, r.Description, r.StartLine, r.Secret, r.Match, r.Entropy, string(r.ValidationStatus), reason, metadata})
}
return emit, func() error { return nil }, nil
}
func tsvEscape(s string) string {
s = strings.ReplaceAll(s, "\t", `\t`)
s = strings.ReplaceAll(s, "\n", `\n`)
return s
}