test(06-03): add failing tests for SARIF 2.1.0 formatter
This commit is contained in:
166
pkg/output/sarif_test.go
Normal file
166
pkg/output/sarif_test.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package output
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/salvacybersec/keyhunter/pkg/engine"
|
||||
)
|
||||
|
||||
// decodeSARIF runs the formatter and unmarshals the output into a sarifDoc
|
||||
// for structural assertions.
|
||||
func decodeSARIF(t *testing.T, findings []engine.Finding, opts Options) sarifDoc {
|
||||
t.Helper()
|
||||
var buf bytes.Buffer
|
||||
if err := (SARIFFormatter{}).Format(findings, &buf, opts); err != nil {
|
||||
t.Fatalf("Format returned error: %v", err)
|
||||
}
|
||||
var doc sarifDoc
|
||||
if err := json.Unmarshal(buf.Bytes(), &doc); err != nil {
|
||||
t.Fatalf("failed to unmarshal SARIF output: %v\npayload: %s", err, buf.String())
|
||||
}
|
||||
return doc
|
||||
}
|
||||
|
||||
func TestSARIF_Empty(t *testing.T) {
|
||||
doc := decodeSARIF(t, nil, Options{})
|
||||
if doc.Version != "2.1.0" {
|
||||
t.Errorf("version: got %q want 2.1.0", doc.Version)
|
||||
}
|
||||
if doc.Schema != "https://json.schemastore.org/sarif-2.1.0.json" {
|
||||
t.Errorf("schema: got %q", doc.Schema)
|
||||
}
|
||||
if len(doc.Runs) != 1 {
|
||||
t.Fatalf("runs: got %d want 1", len(doc.Runs))
|
||||
}
|
||||
if len(doc.Runs[0].Results) != 0 {
|
||||
t.Errorf("results: got %d want 0", len(doc.Runs[0].Results))
|
||||
}
|
||||
if len(doc.Runs[0].Tool.Driver.Rules) != 0 {
|
||||
t.Errorf("rules: got %d want 0", len(doc.Runs[0].Tool.Driver.Rules))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSARIF_DedupRules(t *testing.T) {
|
||||
findings := []engine.Finding{
|
||||
{ProviderName: "openai", KeyMasked: "sk-aaaa...bbbb", Confidence: "high", Source: "a.go", LineNumber: 10},
|
||||
{ProviderName: "openai", KeyMasked: "sk-cccc...dddd", Confidence: "high", Source: "b.go", LineNumber: 20},
|
||||
{ProviderName: "anthropic", KeyMasked: "sk-ant-x...y", Confidence: "medium", Source: "c.go", LineNumber: 5},
|
||||
}
|
||||
doc := decodeSARIF(t, findings, Options{})
|
||||
rules := doc.Runs[0].Tool.Driver.Rules
|
||||
if len(rules) != 2 {
|
||||
t.Fatalf("rules: got %d want 2", len(rules))
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for _, r := range rules {
|
||||
seen[r.ID] = true
|
||||
if r.Name != r.ID {
|
||||
t.Errorf("rule name %q != id %q", r.Name, r.ID)
|
||||
}
|
||||
if !strings.Contains(r.ShortDescription.Text, "Leaked") {
|
||||
t.Errorf("shortDescription missing 'Leaked': %q", r.ShortDescription.Text)
|
||||
}
|
||||
}
|
||||
if !seen["openai"] || !seen["anthropic"] {
|
||||
t.Errorf("expected openai and anthropic rules, got %+v", seen)
|
||||
}
|
||||
if got := len(doc.Runs[0].Results); got != 3 {
|
||||
t.Errorf("results: got %d want 3", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSARIF_LevelMapping(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
"high": "error",
|
||||
"medium": "warning",
|
||||
"low": "note",
|
||||
"unknown": "warning",
|
||||
}
|
||||
for conf, wantLevel := range cases {
|
||||
findings := []engine.Finding{{ProviderName: "p", KeyMasked: "k", Confidence: conf, Source: "f", LineNumber: 1}}
|
||||
doc := decodeSARIF(t, findings, Options{})
|
||||
if got := doc.Runs[0].Results[0].Level; got != wantLevel {
|
||||
t.Errorf("confidence %q: got level %q want %q", conf, got, wantLevel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSARIF_LineFloor(t *testing.T) {
|
||||
findings := []engine.Finding{
|
||||
{ProviderName: "p", KeyMasked: "k", Confidence: "high", Source: "f", LineNumber: 0},
|
||||
{ProviderName: "p", KeyMasked: "k", Confidence: "high", Source: "f", LineNumber: -5},
|
||||
{ProviderName: "p", KeyMasked: "k", Confidence: "high", Source: "f", LineNumber: 42},
|
||||
}
|
||||
doc := decodeSARIF(t, findings, Options{})
|
||||
results := doc.Runs[0].Results
|
||||
if results[0].Locations[0].PhysicalLocation.Region.StartLine != 1 {
|
||||
t.Errorf("zero line should floor to 1, got %d", results[0].Locations[0].PhysicalLocation.Region.StartLine)
|
||||
}
|
||||
if results[1].Locations[0].PhysicalLocation.Region.StartLine != 1 {
|
||||
t.Errorf("negative line should floor to 1, got %d", results[1].Locations[0].PhysicalLocation.Region.StartLine)
|
||||
}
|
||||
if results[2].Locations[0].PhysicalLocation.Region.StartLine != 42 {
|
||||
t.Errorf("line 42 should pass through, got %d", results[2].Locations[0].PhysicalLocation.Region.StartLine)
|
||||
}
|
||||
if results[0].Locations[0].PhysicalLocation.ArtifactLocation.URI != "f" {
|
||||
t.Errorf("artifactLocation.uri not preserved")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSARIF_Masking(t *testing.T) {
|
||||
findings := []engine.Finding{{
|
||||
ProviderName: "openai",
|
||||
KeyValue: "sk-SECRETVALUE1234",
|
||||
KeyMasked: "sk-SECRE...1234",
|
||||
Confidence: "high",
|
||||
Source: "a.go",
|
||||
LineNumber: 1,
|
||||
}}
|
||||
|
||||
// Default: masked.
|
||||
doc := decodeSARIF(t, findings, Options{})
|
||||
msg := doc.Runs[0].Results[0].Message.Text
|
||||
if !strings.Contains(msg, "sk-SECRE...1234") {
|
||||
t.Errorf("masked message should contain KeyMasked, got %q", msg)
|
||||
}
|
||||
if strings.Contains(msg, "SECRETVALUE1234") {
|
||||
t.Errorf("masked message leaked full key: %q", msg)
|
||||
}
|
||||
|
||||
// Unmasked.
|
||||
doc = decodeSARIF(t, findings, Options{Unmask: true})
|
||||
msg = doc.Runs[0].Results[0].Message.Text
|
||||
if !strings.Contains(msg, "sk-SECRETVALUE1234") {
|
||||
t.Errorf("unmasked message should contain KeyValue, got %q", msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSARIF_ToolVersionFallback(t *testing.T) {
|
||||
doc := decodeSARIF(t, nil, Options{})
|
||||
d := doc.Runs[0].Tool.Driver
|
||||
if d.Name != "keyhunter" {
|
||||
t.Errorf("default tool name: got %q want keyhunter", d.Name)
|
||||
}
|
||||
if d.Version != "dev" {
|
||||
t.Errorf("default tool version: got %q want dev", d.Version)
|
||||
}
|
||||
|
||||
doc = decodeSARIF(t, nil, Options{ToolName: "custom", ToolVersion: "1.2.3"})
|
||||
d = doc.Runs[0].Tool.Driver
|
||||
if d.Name != "custom" || d.Version != "1.2.3" {
|
||||
t.Errorf("explicit name/version not honored: %+v", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSARIF_RegisteredInRegistry(t *testing.T) {
|
||||
f, err := Get("sarif")
|
||||
if err != nil {
|
||||
t.Fatalf("sarif formatter not registered: %v", err)
|
||||
}
|
||||
if _, ok := f.(SARIFFormatter); !ok {
|
||||
t.Errorf("registered formatter is not SARIFFormatter: %T", f)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user