From cfa7ad6ef93fd4884d4e5577364674330c3fe1d5 Mon Sep 17 00:00:00 2001 From: Ahmed ElMallah Date: Thu, 23 Jul 2026 03:38:51 -0700 Subject: [PATCH] fix(baseline): reject unsupported finding severities Validate baseline severities against the finding severity vocabulary instead of accepting arbitrary normalized strings. Preserve the legacy n/a spelling while rejecting policy-only and unknown custom values. --- internal/baseline/baseline.go | 20 ++++++++++++++++- internal/baseline/baseline_test.go | 35 ++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/internal/baseline/baseline.go b/internal/baseline/baseline.go index 79b855b2..ec235ca7 100644 --- a/internal/baseline/baseline.go +++ b/internal/baseline/baseline.go @@ -126,7 +126,7 @@ func (d Document) Validate() error { default: return fmt.Errorf("baseline entry %d has unsupported policy_status %q", idx, entry.PolicyStatus) } - if entry.Severity != "" && sdk.ParseSeverityLevel(string(entry.Severity)) != entry.Severity { + if !validBaselineSeverity(entry.Severity) { return fmt.Errorf("baseline entry %d has unsupported severity %q", idx, entry.Severity) } switch entry.Reachability { @@ -154,6 +154,24 @@ func (d Document) Validate() error { return nil } +func validBaselineSeverity(severity sdk.SeverityLevel) bool { + switch severity { + case "", + sdk.SeverityUnknown, + sdk.SeverityLevel("n/a"), + sdk.SeverityLow, + sdk.SeverityMedium, + sdk.SeverityHigh, + sdk.SeverityCritical, + sdk.SeverityNote, + sdk.SeverityWarning, + sdk.SeverityError: + return true + default: + return false + } +} + // Load reads and validates a baseline document. func Load(path string) (Document, error) { data, err := os.ReadFile(path) diff --git a/internal/baseline/baseline_test.go b/internal/baseline/baseline_test.go index 12a75e14..51c99cec 100644 --- a/internal/baseline/baseline_test.go +++ b/internal/baseline/baseline_test.go @@ -151,6 +151,41 @@ func TestDocumentUsesFriendlyPolicyStatusField(t *testing.T) { } } +func TestDocumentRejectsUnsupportedSeverity(t *testing.T) { + document := NewDocument([]sdk.Finding{{ + ID: "rule", Kind: sdk.FindingKindPackage, Auditor: "package", + RuleID: "rule", PackageRef: "pkg:npm/example@1.0.0", + }}, nil) + document.Entries[0].Severity = sdk.SeverityLevel("urgent") + if err := document.Validate(); err == nil || !strings.Contains(err.Error(), `unsupported severity "urgent"`) { + t.Fatalf("Validate() error = %v", err) + } +} + +func TestDocumentAcceptsFindingSeverityVocabulary(t *testing.T) { + for _, severity := range []sdk.SeverityLevel{ + "", + sdk.SeverityUnknown, + sdk.SeverityLevel("n/a"), + sdk.SeverityLow, + sdk.SeverityMedium, + sdk.SeverityHigh, + sdk.SeverityCritical, + sdk.SeverityNote, + sdk.SeverityWarning, + sdk.SeverityError, + } { + document := NewDocument([]sdk.Finding{{ + ID: "rule", Kind: sdk.FindingKindPackage, Auditor: "package", + RuleID: "rule", PackageRef: "pkg:npm/example@1.0.0", + }}, nil) + document.Entries[0].Severity = severity + if err := document.Validate(); err != nil { + t.Errorf("severity %q rejected: %v", severity, err) + } + } +} + func TestResolvePathSelections(t *testing.T) { root := t.TempDir() sbomPath := filepath.Join(root, "bom.json")