Files
sif/internal/finding/severity_test.go
T
vmfunc 1237f3f09e feat(finding): normalized finding layer for notify and diff
scan results live in ~two dozen structs with no shared shape, so every
consumer that wants "what did this run turn up" reimplements the
type-switch. add internal/finding: an ordered Severity (info<low<medium<
high<critical, with parse/compare) and Flatten, the single type-switch
that collapses every scan result struct into flat, severity-ranked
Findings keyed module:identifier for stable dedup/diff.

wire collectFindings off Flatten in the run loop so notify and diff
(later bundles) build on one normalization path instead of re-deriving
it; the report path keeps emitting raw json blobs unchanged. expose
JavascriptScanResult.SupabaseFindings so the js internals stay private.

the guard test iterates a representative instance of every ResultType
and fails if Flatten lacks a case (falls through to :unhandled) - so a
new scanner can't ship without a Flatten case landing too.
2026-06-10 15:29:20 -07:00

85 lines
3.0 KiB
Go

/*
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
: :
: █▀ █ █▀▀ · Blazing-fast pentesting suite :
: ▄█ █ █▀ · BSD 3-Clause License :
: :
: (c) 2022-2026 vmfunc, xyzeva, :
: lunchcat alumni & contributors :
: :
·━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━·
*/
package finding
import "testing"
func TestParseSeverity(t *testing.T) {
tests := []struct {
in string
want Severity
}{
{"critical", SeverityCritical},
{"CRITICAL", SeverityCritical},
{" high ", SeverityHigh},
{"medium", SeverityMedium},
{"moderate", SeverityMedium},
{"warning", SeverityMedium},
{"low", SeverityLow},
{"info", SeverityInfo},
{"informational", SeverityInfo},
{"none", SeverityInfo},
{"", SeverityUnknown},
{"bogus", SeverityUnknown},
}
for _, tt := range tests {
if got := ParseSeverity(tt.in); got != tt.want {
t.Errorf("ParseSeverity(%q) = %v, want %v", tt.in, got, tt.want)
}
}
}
func TestSeverityOrdering(t *testing.T) {
// the ladder must be strictly increasing for AtLeast/sort to behave.
ordered := []Severity{
SeverityUnknown, SeverityInfo, SeverityLow,
SeverityMedium, SeverityHigh, SeverityCritical,
}
for i := 1; i < len(ordered); i++ {
if ordered[i-1] >= ordered[i] {
t.Errorf("severity ladder not increasing at %d: %v !< %v", i, ordered[i-1], ordered[i])
}
}
}
func TestSeverityAtLeast(t *testing.T) {
tests := []struct {
sev Severity
threshold Severity
want bool
}{
{SeverityHigh, SeverityMedium, true},
{SeverityMedium, SeverityMedium, true},
{SeverityLow, SeverityMedium, false},
{SeverityCritical, SeverityInfo, true},
{SeverityUnknown, SeverityInfo, false},
}
for _, tt := range tests {
if got := tt.sev.AtLeast(tt.threshold); got != tt.want {
t.Errorf("%v.AtLeast(%v) = %v, want %v", tt.sev, tt.threshold, got, tt.want)
}
}
}
func TestSeverityStringRoundTrip(t *testing.T) {
// every named rank renders to a string ParseSeverity maps back to the same
// rank, so the wire format is lossless for known severities.
for _, sev := range []Severity{
SeverityInfo, SeverityLow, SeverityMedium, SeverityHigh, SeverityCritical,
} {
if got := ParseSeverity(sev.String()); got != sev {
t.Errorf("round-trip %v -> %q -> %v", sev, sev.String(), got)
}
}
}