108 lines
2.2 KiB
Go
108 lines
2.2 KiB
Go
/*
|
|
©AngelaMos | 2026
|
|
terminal.go
|
|
|
|
Color-coded terminal output formatter for scan results
|
|
|
|
Renders findings grouped by severity with color-coded labels,
|
|
file paths, evidence snippets, and MITRE technique IDs. Prints
|
|
a summary banner with counts per severity level.
|
|
*/
|
|
|
|
package report
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
|
|
"github.com/CarterPerez-dev/sentinel/internal/ui"
|
|
"github.com/CarterPerez-dev/sentinel/pkg/types"
|
|
)
|
|
|
|
var severityColor = map[types.Severity]func(
|
|
a ...any,
|
|
) string{
|
|
types.SeverityCritical: ui.HiRedBold,
|
|
types.SeverityHigh: ui.RedBold,
|
|
types.SeverityMedium: ui.YellowBold,
|
|
types.SeverityLow: ui.CyanBold,
|
|
types.SeverityInfo: ui.Dim,
|
|
}
|
|
|
|
func PrintTerminal(result types.ScanResult) {
|
|
findings := result.Findings
|
|
sort.Slice(findings, func(i, j int) bool {
|
|
return findings[i].Severity > findings[j].Severity
|
|
})
|
|
|
|
if len(findings) == 0 {
|
|
fmt.Printf(
|
|
" %s No persistence indicators found.\n\n",
|
|
ui.GreenBold(ui.Check),
|
|
)
|
|
printSummary(result)
|
|
return
|
|
}
|
|
|
|
fmt.Printf(" %s\n\n", ui.Dim(ui.HRule(56)))
|
|
|
|
for _, f := range findings {
|
|
colorFn := severityColor[f.Severity]
|
|
label := colorFn(
|
|
fmt.Sprintf("[%s]", f.Severity.Label()),
|
|
)
|
|
fmt.Printf(" %s %s\n", label, f.Title)
|
|
fmt.Printf(
|
|
" %s %s\n",
|
|
ui.Dim("Path:"),
|
|
ui.Cyan(f.Path),
|
|
)
|
|
if f.Evidence != "" {
|
|
fmt.Printf(
|
|
" %s %s\n",
|
|
ui.Dim("Evidence:"),
|
|
truncateEvidence(f.Evidence),
|
|
)
|
|
}
|
|
fmt.Printf(
|
|
" %s %s\n\n",
|
|
ui.Dim("MITRE:"),
|
|
ui.Blue(f.MITRE),
|
|
)
|
|
}
|
|
|
|
printSummary(result)
|
|
}
|
|
|
|
func printSummary(result types.ScanResult) {
|
|
s := result.Summary
|
|
fmt.Printf(" %s\n", ui.Dim(ui.HRule(56)))
|
|
fmt.Printf(
|
|
" %s %s %s %s %s %s %s %s %s %s\n",
|
|
ui.Dim("Summary:"),
|
|
ui.HiRedBold(fmt.Sprintf("%d", s.Critical)),
|
|
ui.Dim("critical"),
|
|
ui.RedBold(fmt.Sprintf("%d", s.High)),
|
|
ui.Dim("high"),
|
|
ui.YellowBold(fmt.Sprintf("%d", s.Medium)),
|
|
ui.Dim("medium"),
|
|
ui.CyanBold(fmt.Sprintf("%d", s.Low)),
|
|
ui.Dim("low"),
|
|
ui.Dim(fmt.Sprintf("%d info", s.Info)),
|
|
)
|
|
fmt.Printf(
|
|
" %s Completed in %dms\n\n",
|
|
ui.Dim(ui.Timer),
|
|
result.DurationMs,
|
|
)
|
|
}
|
|
|
|
const maxEvidenceLen = 100
|
|
|
|
func truncateEvidence(s string) string {
|
|
if len(s) <= maxEvidenceLen {
|
|
return s
|
|
}
|
|
return s[:maxEvidenceLen] + "..."
|
|
}
|