blob: 1d235ab64d329d1417a89076bddf0cffc2d884d8 [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 genericosv
import (
"flag"
"io/fs"
"path/filepath"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
osvschema "github.com/google/osv-scanner/pkg/models"
"golang.org/x/vulndb/internal/proxy"
"golang.org/x/vulndb/internal/report"
)
var (
realProxy = flag.Bool("proxy", false, "if true, contact the real module proxy and update expected responses")
update = flag.Bool("update", false, "if true, update test YAML reports to reflect new expected behavior")
)
var (
testdataDir = "testdata"
testOSVDir = filepath.Join(testdataDir, "osv")
testYAMLDir = filepath.Join(testdataDir, "yaml")
)
// To update test cases to reflect new expected behavior
// (only use -proxy if the calls to the proxy will change):
// go test ./internal/genericosv/... -update -proxy -run TestToReport
func TestToReport(t *testing.T) {
if err := filepath.WalkDir(testOSVDir, func(path string, f fs.DirEntry, err error) error {
if err != nil {
return err
}
if f.IsDir() || filepath.Ext(path) != ".json" {
return nil
}
ghsaID := strings.TrimSuffix(f.Name(), ".json")
t.Run(ghsaID, func(t *testing.T) {
t.Parallel()
pc, err := proxy.NewTestClient(t, *realProxy)
if err != nil {
t.Fatal(err)
}
osv := Entry{}
if err := report.UnmarshalFromFile(path, &osv); err != nil {
t.Fatal(err)
}
got := osv.ToReport("GO-TEST-ID", pc)
yamlFile := filepath.Join(testYAMLDir, ghsaID+".yaml")
if *update {
if err := got.Write(yamlFile); err != nil {
t.Fatal(err)
}
}
want, err := report.Read(yamlFile)
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("ToReport() mismatch (-want +got)\n%s", diff)
}
})
return nil
}); err != nil {
t.Fatal(err)
}
}
// TODO(https://go.dev/issues/61769): unskip test cases as we add features.
// To update proxy responses:
// go test ./internal/genericosv/... -proxy -run TestAffectedToModules
func TestAffectedToModules(t *testing.T) {
for _, tc := range []struct {
name string
desc string
in []osvschema.Affected
want []*report.Module
skip bool
}{
{
name: "ok",
desc: "module is already OK",
in: []osvschema.Affected{{
Package: osvschema.Package{
Ecosystem: osvschema.EcosystemGo,
Name: "github.com/influxdata/influxdb",
},
Ranges: []osvschema.Range{{
Type: osvschema.RangeEcosystem,
Events: []osvschema.Event{
{
Introduced: "0.3.2",
},
{
Fixed: "1.7.6",
},
},
}},
}},
want: []*report.Module{{
Module: "github.com/influxdata/influxdb",
Versions: []report.VersionRange{
{
Introduced: "0.3.2",
Fixed: "1.7.6",
}},
VulnerableAt: "1.7.5",
}},
},
{
name: "import_path",
desc: "find module from import path",
in: []osvschema.Affected{{
Package: osvschema.Package{
Ecosystem: osvschema.EcosystemGo,
Name: "github.com/influxdata/influxdb/services/httpd",
},
Ranges: []osvschema.Range{{
Type: osvschema.RangeEcosystem,
Events: []osvschema.Event{
{
Introduced: "0.3.2",
},
{
Fixed: "1.7.6",
},
},
}},
}},
want: []*report.Module{{
Module: "github.com/influxdata/influxdb",
Versions: []report.VersionRange{
{
Introduced: "0.3.2",
Fixed: "1.7.6",
},
},
Packages: []*report.Package{
{
Package: "github.com/influxdata/influxdb/services/httpd",
},
},
VulnerableAt: "1.7.5",
}},
},
{
name: "major_version",
desc: "correct major version of module path",
in: []osvschema.Affected{{
Package: osvschema.Package{
Ecosystem: osvschema.EcosystemGo,
Name: "github.com/nats-io/nats-server",
},
Ranges: []osvschema.Range{{
Type: osvschema.RangeEcosystem,
Events: []osvschema.Event{
{
Introduced: "2.2.0",
},
{
Fixed: "2.8.3",
},
},
}},
}},
want: []*report.Module{{
Module: "github.com/nats-io/nats-server/v2",
Versions: []report.VersionRange{
{
Introduced: "2.2.0",
Fixed: "2.8.3",
},
},
VulnerableAt: "2.8.2",
}},
},
{
name: "canonicalize",
desc: "canonicalize module path",
in: []osvschema.Affected{{
Package: osvschema.Package{
Ecosystem: osvschema.EcosystemGo,
Name: "github.com/golang/vulndb",
},
Ranges: []osvschema.Range{{
Type: osvschema.RangeEcosystem,
Events: []osvschema.Event{
{
Fixed: "0.0.0-20230712151357-4fee11d0f8f9",
},
},
}},
}},
want: []*report.Module{{
Module: "golang.org/x/vulndb",
Versions: []report.VersionRange{
{
Fixed: "0.0.0-20230712151357-4fee11d0f8f9",
},
},
}},
},
{
name: "add_incompatible",
desc: "add +incompatible",
in: []osvschema.Affected{{
Package: osvschema.Package{
Ecosystem: osvschema.EcosystemGo,
Name: "github.com/docker/docker",
},
Ranges: []osvschema.Range{{
Type: osvschema.RangeEcosystem,
Events: []osvschema.Event{
{
Fixed: "23.0.0",
},
},
}},
}},
want: []*report.Module{{
Module: "github.com/docker/docker",
Versions: []report.VersionRange{
{
Fixed: "23.0.0+incompatible",
},
},
}},
skip: true,
},
{
name: "remove_duplicates",
desc: "remove major version duplicates",
in: []osvschema.Affected{{
Package: osvschema.Package{
Ecosystem: osvschema.EcosystemGo,
Name: "github.com/hashicorp/go-getter/v2",
},
Ranges: []osvschema.Range{{
Type: osvschema.RangeEcosystem,
Events: []osvschema.Event{
{
Introduced: "0",
},
{
Fixed: "2.1.0",
},
},
}},
},
{
Package: osvschema.Package{
Ecosystem: osvschema.EcosystemGo,
Name: "github.com/hashicorp/go-getter",
},
Ranges: []osvschema.Range{{
Type: osvschema.RangeEcosystem,
Events: []osvschema.Event{
{
Introduced: "2.0.0",
},
{
Fixed: "2.1.0",
},
},
}},
}},
want: []*report.Module{{
Module: "github.com/hashicorp/go-getter/v2",
Versions: []report.VersionRange{
{
Introduced: "2.0.0",
Fixed: "2.1.0",
},
},
}},
skip: true,
},
} {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if tc.skip {
t.Skip("skipping (not implemented yet)")
}
pc, err := proxy.NewTestClient(t, *realProxy)
if err != nil {
t.Fatal(err)
}
var gotNotes []string
addNote := func(note string) {
gotNotes = append(gotNotes, note)
}
got := affectedToModules(tc.in, addNote, pc)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("%s: affectedToModules() mismatch (-want +got)\n%s", tc.desc, diff)
}
if len(gotNotes) > 0 {
t.Errorf("%s: affectedToModules() output unexpected notes = %s", tc.desc, gotNotes)
}
})
}
}