package ci_test import ( "testing" "time" "github.com/google/go-cmp/cmp" "github.com/google/osv-scanner/v2/internal/ci" "github.com/google/osv-scanner/v2/pkg/models" "github.com/ossf/osv-schema/bindings/go/osvschema" "google.golang.org/protobuf/testing/protocmp" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" ) func parseTime(t *testing.T, str string) time.Time { t.Helper() ti, err := time.Parse(time.RFC3339, str) if err != nil { panic(err) } return ti } func TestLoadVulnResults(t *testing.T) { t.Parallel() tests := []struct { name string path string want models.VulnerabilityResults wantErr bool }{ { name: "does_not_exist", path: "./testdata/does_not_exist", want: models.VulnerabilityResults{}, wantErr: true, }, { name: "invalid_json", path: "./testdata/not-json.txt", want: models.VulnerabilityResults{}, wantErr: true, }, { name: "results_empty", path: "./testdata/results-empty.json", want: models.VulnerabilityResults{ Results: []models.PackageSource{}, ExperimentalAnalysisConfig: models.ExperimentalAnalysisConfig{ Licenses: models.ExperimentalLicenseConfig{ Summary: true, Allowlist: []models.License{"MIT"}, }, }, ImageMetadata: nil, LicenseSummary: nil, }, wantErr: false, }, { name: "results_some", path: "./testdata/results-some.json", want: models.VulnerabilityResults{ Results: []models.PackageSource{ { Source: models.SourceInfo{ Path: "/path/to/different-dir/go.mod", Type: "lockfile", }, ExperimentalPES: nil, Packages: []models.PackageVulns{ { Package: models.PackageInfo{ Name: "github.com/gogo/protobuf", Version: "1.3.1", Ecosystem: "Go", }, Vulnerabilities: []*osvschema.Vulnerability{ { SchemaVersion: "1.4.0", Id: "GO-2021-0053", Modified: timestamppb.New(parseTime(t, "2023-06-12T18:45:41Z")), Published: timestamppb.New(parseTime(t, "2021-04-14T20:04:52Z")), Aliases: []string{"CVE-2021-3121", "GHSA-c3h9-896r-86jm"}, Summary: "Panic due to improper input validation in github.com/gogo/protobuf", Details: "Due to improper bounds checking, maliciously crafted input to generated Unmarshal methods can cause an out-of-bounds panic. If parsing messages from untrusted parties, this may be used as a denial of service vector.", Affected: []*osvschema.Affected{ { Package: &osvschema.Package{ Ecosystem: "Go", Name: "github.com/gogo/protobuf", Purl: "pkg:golang/github.com/gogo/protobuf", }, Ranges: []*osvschema.Range{{ Type: osvschema.Range_SEMVER, Events: []*osvschema.Event{ {Introduced: "0"}, {Fixed: "1.3.2"}, }, }}, DatabaseSpecific: &structpb.Struct{ Fields: map[string]*structpb.Value{ "source": { Kind: &structpb.Value_StringValue{ StringValue: "https://vuln.go.dev/ID/GO-2021-0053.json", }, }, }, }, EcosystemSpecific: &structpb.Struct{ Fields: map[string]*structpb.Value{ "imports": { Kind: &structpb.Value_ListValue{ ListValue: &structpb.ListValue{ Values: []*structpb.Value{ { Kind: &structpb.Value_StructValue{ StructValue: &structpb.Struct{ Fields: map[string]*structpb.Value{ "path": { Kind: &structpb.Value_StringValue{ StringValue: "github.com/gogo/protobuf/plugin/unmarshal", }, }, "symbols": { Kind: &structpb.Value_ListValue{ ListValue: &structpb.ListValue{ Values: []*structpb.Value{ { Kind: &structpb.Value_StringValue{ StringValue: "unmarshal.Generate", }, }, { Kind: &structpb.Value_StringValue{ StringValue: "unmarshal.field", }, }, }, }, }, }, }, }, }, }, }, }, }, }, }, }, }, }, References: []*osvschema.Reference{ {Type: osvschema.Reference_FIX, Url: "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc"}, }, DatabaseSpecific: &structpb.Struct{ Fields: map[string]*structpb.Value{ "url": { Kind: &structpb.Value_StringValue{ StringValue: "https://pkg.go.dev/vuln/GO-2021-0053", }, }, }, }, }, }, Groups: []models.GroupInfo{{IDs: []string{"GO-2021-0053"}}}, Licenses: nil, LicenseViolations: nil, }, }, }, }, ExperimentalAnalysisConfig: models.ExperimentalAnalysisConfig{ Licenses: models.ExperimentalLicenseConfig{ Summary: true, }, }, ImageMetadata: nil, LicenseSummary: nil, }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() got, err := ci.LoadVulnResults(tt.path) if (err != nil) != tt.wantErr { t.Errorf("LoadVulnResults() error = %v, wantErr %v", err, tt.wantErr) return } if diff := cmp.Diff(tt.want, got, protocmp.Transform()); diff != "" { t.Errorf("LoadVulnResults() returned unexpected result (-want +got):\n%s", diff) } }) } }