blob: 1d160be1249fae73a930a884ba3b709a51697692 [file] [log] [blame]
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sarif
import (
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"golang.org/x/vuln/internal/govulncheck"
)
func scanLevel(f *govulncheck.Finding) string {
fr := f.Trace[0]
if fr.Function != "" {
return "symbol"
}
if fr.Package != "" {
return "package"
}
return "module"
}
func newTestHandler() *handler {
h := NewHandler(nil)
h.cfg = &govulncheck.Config{}
return h
}
func TestHandlerSymbol(t *testing.T) {
fs := `
{
"finding": {
"osv": "GO-2021-0054",
"trace": [
{
"module": "github.com/tidwall/gjson"
}
]
}
}
{
"finding": {
"osv": "GO-2021-0265",
"trace": [
{
"module": "github.com/tidwall/gjson"
}
]
}
}
{
"finding": {
"osv": "GO-2020-0015",
"trace": [
{
"module": "golang.org/x/text"
}
]
}
}
{
"finding": {
"osv": "GO-2021-0054",
"trace": [
{
"module": "github.com/tidwall/gjson",
"package": "github.com/tidwall/gjson"
}
]
}
}
{
"finding": {
"osv": "GO-2021-0265",
"trace": [
{
"module": "github.com/tidwall/gjson",
"package": "github.com/tidwall/gjson"
}
]
}
}
{
"finding": {
"osv": "GO-2021-0265",
"trace": [
{
"module": "github.com/tidwall/gjson",
"package": "github.com/tidwall/gjson",
"function": "Get",
"receiver": "Result"
},
{
"module": "golang.org/vuln",
"package": "golang.org/vuln",
"function": "main"
}
]
}
}`
h := newTestHandler()
if err := govulncheck.HandleJSON(strings.NewReader(fs), h); err != nil {
t.Fatal(err)
}
want := map[string]string{
"GO-2021-0265": "symbol",
"GO-2021-0054": "package",
"GO-2020-0015": "module",
}
got := make(map[string]string)
for osv, fs := range h.findings {
got[osv] = scanLevel(fs[0])
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("(-want;got+): %s", diff)
}
}
func TestHandlerPackage(t *testing.T) {
fs := `
{
"finding": {
"osv": "GO-2021-0054",
"trace": [
{
"module": "github.com/tidwall/gjson"
}
]
}
}
{
"finding": {
"osv": "GO-2021-0265",
"trace": [
{
"module": "github.com/tidwall/gjson"
}
]
}
}
{
"finding": {
"osv": "GO-2020-0015",
"trace": [
{
"module": "golang.org/x/text"
}
]
}
}
{
"finding": {
"osv": "GO-2021-0054",
"trace": [
{
"module": "github.com/tidwall/gjson",
"package": "github.com/tidwall/gjson"
}
]
}
}
{
"finding": {
"osv": "GO-2021-0265",
"trace": [
{
"module": "github.com/tidwall/gjson",
"package": "github.com/tidwall/gjson"
}
]
}
}`
h := newTestHandler()
if err := govulncheck.HandleJSON(strings.NewReader(fs), h); err != nil {
t.Fatal(err)
}
want := map[string]string{
"GO-2021-0265": "package",
"GO-2021-0054": "package",
"GO-2020-0015": "module",
}
got := make(map[string]string)
for osv, fs := range h.findings {
got[osv] = scanLevel(fs[0])
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("(-want;got+): %s", diff)
}
}
func TestHandlerModule(t *testing.T) {
fs := `
{
"finding": {
"osv": "GO-2021-0054",
"trace": [
{
"module": "github.com/tidwall/gjson"
}
]
}
}
{
"finding": {
"osv": "GO-2021-0265",
"trace": [
{
"module": "github.com/tidwall/gjson"
}
]
}
}
{
"finding": {
"osv": "GO-2020-0015",
"trace": [
{
"module": "golang.org/x/text"
}
]
}
}`
h := newTestHandler()
if err := govulncheck.HandleJSON(strings.NewReader(fs), h); err != nil {
t.Fatal(err)
}
want := map[string]string{
"GO-2021-0265": "module",
"GO-2021-0054": "module",
"GO-2020-0015": "module",
}
got := make(map[string]string)
for osv, fs := range h.findings {
got[osv] = scanLevel(fs[0])
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("(-want;got+): %s", diff)
}
}
func TestMoreSpecific(t *testing.T) {
frame := func(m, p, f string) *govulncheck.Frame {
return &govulncheck.Frame{
Module: m,
Package: p,
Function: f,
}
}
for _, tc := range []struct {
name string
want int
trace1 []*govulncheck.Frame
trace2 []*govulncheck.Frame
}{
{"sym-vs-sym", 0,
[]*govulncheck.Frame{
frame("m1", "p1", "v1"), frame("m1", "p1", "f2")},
[]*govulncheck.Frame{
frame("m1", "p1", "v2"), frame("m1", "p1", "f1"), frame("m2", "p2", "f2")},
},
{"sym-vs-pkg", -1,
[]*govulncheck.Frame{
frame("m1", "p1", "v1"), frame("m1", "p1", "f2")},
[]*govulncheck.Frame{
frame("m1", "p1", "")},
},
{"pkg-vs-sym", 1,
[]*govulncheck.Frame{
frame("m1", "p1", "")},
[]*govulncheck.Frame{
frame("m1", "p1", "v1"), frame("m2", "p2", "v2")},
},
{"pkg-vs-mod", -1,
[]*govulncheck.Frame{
frame("m1", "p1", "")},
[]*govulncheck.Frame{
frame("m1", "", "")},
},
{"mod-vs-sym", 1,
[]*govulncheck.Frame{
frame("m1", "", "")},
[]*govulncheck.Frame{
frame("m1", "p1", "v2"), frame("m1", "p1", "f1")},
},
{"mod-vs-mod", 0,
[]*govulncheck.Frame{
frame("m1", "", "")},
[]*govulncheck.Frame{
frame("m2", "", "")},
},
} {
tc := tc
t.Run(tc.name, func(t *testing.T) {
f1 := &govulncheck.Finding{Trace: tc.trace1}
f2 := &govulncheck.Finding{Trace: tc.trace2}
if got := moreSpecific(f1, f2); got != tc.want {
t.Errorf("want %d; got %d", tc.want, got)
}
})
}
}
func TestResultMessage(t *testing.T) {
config := func(l govulncheck.ScanLevel) *govulncheck.Config {
return &govulncheck.Config{ScanLevel: l}
}
finding := func(m, p, f string) *govulncheck.Finding {
return &govulncheck.Finding{
Trace: []*govulncheck.Frame{
{Module: m, Package: p, Function: f},
},
}
}
for _, tc := range []struct {
findings []*govulncheck.Finding
level govulncheck.ScanLevel
want string
}{
{[]*govulncheck.Finding{finding("m", "p", "f1"), finding("m", "p", "f2")}, govulncheck.ScanLevelSymbol,
"Your code calls vulnerable functions in 1 package (p)."},
{[]*govulncheck.Finding{finding("m", "p", "")}, govulncheck.ScanLevelPackage,
"Your code imports 1 vulnerable package (p). Run the call-level analysis to understand whether your code actually calls the vulnerabilities."},
{[]*govulncheck.Finding{finding("m", "p1", ""), finding("m", "p2", ""), finding("m", "p3", "")}, govulncheck.ScanLevelSymbol,
"Your code imports 3 vulnerable packages (p1, p2, and p3), but doesn’t appear to call any of the vulnerable symbols."},
{[]*govulncheck.Finding{finding("m1", "", ""), finding("m2", "", "")}, govulncheck.ScanLevelModule,
"Your code depends on 2 vulnerable modules (m1 and m2). Run the call-level analysis to understand whether your code actually calls the vulnerabilities."},
{[]*govulncheck.Finding{finding("m1", "", ""), finding("m2", "", "")}, govulncheck.ScanLevelPackage,
"Your code depends on 2 vulnerable modules (m1 and m2), but doesn't appear to import any of the vulnerable symbols."},
{[]*govulncheck.Finding{finding("m1", "", ""), finding("m2", "", "")}, govulncheck.ScanLevelSymbol,
"Your code depends on 2 vulnerable modules (m1 and m2), but doesn't appear to call any of the vulnerable symbols."},
} {
got := resultMessage(tc.findings, config(tc.level))
if tc.want != got {
t.Errorf("want %s; got %s", tc.want, got)
}
}
}