blob: 715a8c4967cdeb1059230c043e1a810aa3757d47 [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 scan
import (
"context"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"golang.org/x/vuln/internal/client"
"golang.org/x/vuln/internal/osv"
"golang.org/x/vuln/internal/test"
)
func TestRunQuery(t *testing.T) {
e := &osv.Entry{
ID: "GO-1999-0001",
Affected: []osv.Affected{{
Module: osv.Module{Path: "bad.com"},
Ranges: []osv.Range{{
Type: osv.RangeTypeSemver,
Events: []osv.RangeEvent{{Introduced: "0"}, {Fixed: "1.2.3"}},
}},
EcosystemSpecific: osv.EcosystemSpecific{
Packages: []osv.Package{{
Path: "bad.com",
}, {
Path: "bad.com/bad",
}},
},
}, {
Module: osv.Module{Path: "unfixable.com"},
Ranges: []osv.Range{{
Type: osv.RangeTypeSemver,
Events: []osv.RangeEvent{{Introduced: "0"}}, // no fix
}},
EcosystemSpecific: osv.EcosystemSpecific{
Packages: []osv.Package{{
Path: "unfixable.com",
}},
},
}},
}
e2 := &osv.Entry{
ID: "GO-1999-0002",
Affected: []osv.Affected{{
Module: osv.Module{Path: "bad.com"},
Ranges: []osv.Range{{
Type: osv.RangeTypeSemver,
Events: []osv.RangeEvent{{Introduced: "0"}, {Fixed: "1.2.0"}},
}},
EcosystemSpecific: osv.EcosystemSpecific{
Packages: []osv.Package{{
Path: "bad.com/pkg",
},
},
},
}},
}
stdlib := &osv.Entry{
ID: "GO-2000-0003",
Affected: []osv.Affected{{
Module: osv.Module{Path: "stdlib"},
Ranges: []osv.Range{{
Type: osv.RangeTypeSemver,
Events: []osv.RangeEvent{{Introduced: "0"}, {Fixed: "1.19.4"}},
}},
EcosystemSpecific: osv.EcosystemSpecific{
Packages: []osv.Package{{
Path: "net/http",
}},
},
}},
}
c, err := client.NewInMemoryClient([]*osv.Entry{e, e2, stdlib})
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
for _, tc := range []struct {
query []string
want []*osv.Entry
}{
{
query: []string{"stdlib@go1.18"},
want: []*osv.Entry{stdlib},
},
{
query: []string{"stdlib@1.18"},
want: []*osv.Entry{stdlib},
},
{
query: []string{"stdlib@v1.18.0"},
want: []*osv.Entry{stdlib},
},
{
query: []string{"bad.com@1.2.3"},
want: nil,
},
{
query: []string{"bad.com@v1.1.0"},
want: []*osv.Entry{e, e2},
},
{
query: []string{"unfixable.com@2.0.0"},
want: []*osv.Entry{e},
},
{
// each entry should only show up once
query: []string{"bad.com@1.1.0", "unfixable.com@2.0.0"},
want: []*osv.Entry{e, e2},
},
{
query: []string{"stdlib@1.18", "unfixable.com@2.0.0"},
want: []*osv.Entry{stdlib, e},
},
} {
t.Run(strings.Join(tc.query, ","), func(t *testing.T) {
h := test.NewMockHandler()
err := runQuery(ctx, h, &config{patterns: tc.query}, c)
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(h.OSVMessages, tc.want); diff != "" {
t.Errorf("runQuery: unexpected diff:\n%s", diff)
}
})
}
}
func TestParseModuleQuery(t *testing.T) {
for _, tc := range []struct {
pattern, wantMod, wantVer string
wantErr string
}{
{
pattern: "stdlib@go1.18",
wantMod: "stdlib",
wantVer: "go1.18",
},
{
pattern: "golang.org/x/tools@v0.0.0-20140414041502-123456789012",
wantMod: "golang.org/x/tools",
wantVer: "v0.0.0-20140414041502-123456789012",
},
{
pattern: "golang.org/x/tools",
wantErr: "invalid query",
},
{
pattern: "golang.org/x/tools@1.0.0.2",
wantErr: "not valid semver",
},
} {
t.Run(tc.pattern, func(t *testing.T) {
gotMod, gotVer, err := parseModuleQuery(tc.pattern)
if tc.wantErr == "" {
if err != nil {
t.Fatal(err)
}
if gotMod != tc.wantMod || gotVer != tc.wantVer {
t.Errorf("parseModuleQuery = (%s, %s), want (%s, %s)", gotMod, gotVer, tc.wantMod, tc.wantVer)
}
} else {
if err == nil || !strings.Contains(err.Error(), tc.wantErr) {
t.Errorf("parseModuleQuery = %v, want err containing %s", err, tc.wantErr)
}
}
})
}
}