feat(06-02): implement CSVFormatter with Unmask support
- Fixed 9-column header: id,provider,confidence,key,source,line,detected_at,verified,verify_status - Uses encoding/csv for automatic quoting of commas/quotes in source paths - Honors Options.Unmask for key column - Registers under "csv" in output registry
This commit is contained in:
60
pkg/output/csv.go
Normal file
60
pkg/output/csv.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package output
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/salvacybersec/keyhunter/pkg/engine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Register("csv", CSVFormatter{})
|
||||
}
|
||||
|
||||
// CSVFormatter renders findings as comma-separated values with a fixed
|
||||
// header row. The id column is the zero-based index of the finding within
|
||||
// the slice -- the storage-layer rowid is intentionally not used here so
|
||||
// the formatter can run against in-memory results from a scan that has
|
||||
// not been persisted yet. Standard encoding/csv quoting rules apply, so
|
||||
// fields containing commas, quotes, or newlines are escaped automatically.
|
||||
type CSVFormatter struct{}
|
||||
|
||||
// csvHeader is the exact, ordered header row written before any findings.
|
||||
// Column order must remain stable: downstream consumers may parse by index.
|
||||
// Columns: id, provider, confidence, key, source, line, detected_at, verified, verify_status
|
||||
var csvHeader = []string{
|
||||
"id", "provider", "confidence", "key", "source",
|
||||
"line", "detected_at", "verified", "verify_status",
|
||||
}
|
||||
|
||||
// Format implements the Formatter interface.
|
||||
func (CSVFormatter) Format(findings []engine.Finding, w io.Writer, opts Options) error {
|
||||
cw := csv.NewWriter(w)
|
||||
if err := cw.Write(csvHeader); err != nil {
|
||||
return err
|
||||
}
|
||||
for i, f := range findings {
|
||||
key := f.KeyMasked
|
||||
if opts.Unmask {
|
||||
key = f.KeyValue
|
||||
}
|
||||
row := []string{
|
||||
strconv.Itoa(i),
|
||||
f.ProviderName,
|
||||
f.Confidence,
|
||||
key,
|
||||
f.Source,
|
||||
strconv.Itoa(f.LineNumber),
|
||||
f.DetectedAt.Format(time.RFC3339),
|
||||
strconv.FormatBool(f.Verified),
|
||||
f.VerifyStatus,
|
||||
}
|
||||
if err := cw.Write(row); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cw.Flush()
|
||||
return cw.Error()
|
||||
}
|
||||
Reference in New Issue
Block a user