Files
claude-code c30be8b441
werkator build succeeded after 00:06
werkator/vulnerability-data build succeeded after 00:03
The shared agentic workflow, and the required checks for Go (0.12.1) (#15)
Co-authored-by: claude-code <claude-code@javagil.de>
2026-10-20 15:01:21 +02:00

91 lines
3.4 KiB
Go

package main
import (
"strings"
"testing"
)
// A cut of govulncheck v1.8.0's JSON stream: GO-2026-0001 is called,
// GO-2026-0002 only imported.
const stream = `{"config": {"scanner_version": "v1.8.0", "go_version": "go1.26.8"}}
{"osv": {"id": "GO-2026-0001", "summary": "Called in net/http"}}
{"osv": {"id": "GO-2026-0002", "summary": "Imported only"}}
{"finding": {"osv": "GO-2026-0001", "fixed_version": "v1.26.9", "trace": [{"module": "stdlib", "version": "v1.26.8"}]}}
{"finding": {"osv": "GO-2026-0001", "fixed_version": "v1.26.9", "trace": [{"module": "stdlib", "version": "v1.26.8", "package": "net/http", "function": "Serve"}, {"module": "werkdock", "function": "main"}]}}
{"finding": {"osv": "GO-2026-0002", "fixed_version": "v1.26.9", "trace": [{"module": "stdlib", "package": "mime"}]}}
`
func TestParseMarksOnlyCalledVulnerabilities(t *testing.T) {
vulns, err := parse(strings.NewReader(stream))
if err != nil {
t.Fatal(err)
}
if v := vulns["GO-2026-0001"]; !v.Calls["stdlib@v1.26.8 net/http.Serve"] || len(v.Calls) != 1 ||
v.Fixed != "v1.26.9" || v.Summary != "Called in net/http" {
t.Errorf("GO-2026-0001 = %+v, want the call of net/http.Serve in v1.26.8, fixed in v1.26.9, with its summary", v)
}
if v := vulns["GO-2026-0002"]; len(v.Calls) != 0 {
t.Errorf("GO-2026-0002 = %+v, want not called", v)
}
}
func called(id string, calls ...string) vuln {
v := vuln{ID: id, Calls: map[string]bool{}}
for _, c := range calls {
v.Calls[c] = true
}
return v
}
func TestVerdictInFullFailsOnEveryCalledVulnerability(t *testing.T) {
head := map[string]vuln{"A": called("A", "m@v1 p.F"), "B": {ID: "B"}}
if got := ids(verdict(head, nil, nil)); got != "A" {
t.Errorf("verdict = %q, want A", got)
}
}
func TestVerdictWithBaselineFailsOnlyOnWhatTheBranchBrings(t *testing.T) {
head := map[string]vuln{"OLD": called("OLD", "m@v1 p.F"), "NEW": called("NEW", "m@v1 p.G"), "NOWCALLED": called("NOWCALLED", "m@v1 p.H")}
base := map[string]vuln{"OLD": called("OLD", "m@v1 p.F"), "NOWCALLED": {ID: "NOWCALLED"}}
if got := ids(verdict(head, base, nil)); got != "NEW NOWCALLED" {
t.Errorf("verdict = %q, want NEW NOWCALLED", got)
}
}
func TestVerdictWithBaselineFailsOnAnotherCallOfTheSameAdvisory(t *testing.T) {
base := map[string]vuln{"A": called("A", "m@v1 p.F")}
for name, head := range map[string]vuln{
"another symbol": called("A", "m@v1 p.F", "m@v1 p.G"),
"another affected version": called("A", "m@v2 p.F"),
} {
if got := ids(verdict(map[string]vuln{"A": head}, base, nil)); got != "A" {
t.Errorf("%s: verdict = %q, want A", name, got)
}
}
}
func TestVerdictSkipsExceptions(t *testing.T) {
head := map[string]vuln{"A": called("A", "m@v1 p.F")}
if got := ids(verdict(head, nil, map[string]string{"A": "not reachable from a sandbox"})); got != "" {
t.Errorf("verdict = %q, want none", got)
}
}
func TestExceptionNeedsAReason(t *testing.T) {
if _, err := parseExceptions(strings.NewReader("# comment\n\nGO-2026-0001\n")); err == nil {
t.Error("an exception without a reason was accepted")
}
got, err := parseExceptions(strings.NewReader("GO-2026-0001 werkdock never parses multipart forms\n"))
if err != nil || got["GO-2026-0001"] != "werkdock never parses multipart forms" {
t.Errorf("parseExceptions = %v, %v", got, err)
}
}
func ids(vs []vuln) string {
var s []string
for _, v := range vs {
s = append(s, v.ID)
}
return strings.Join(s, " ")
}