blob: bd415aa7340547bec61f4a49bc75d72c0704c812 [file] [log] [blame]
// Copyright 2022 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 report
import (
"path/filepath"
"reflect"
"testing"
"github.com/google/go-cmp/cmp"
"golang.org/x/vulndb/internal/cveschema5"
)
var (
testStdLibRecord = &cveschema5.CVERecord{
DataType: "CVE_RECORD",
DataVersion: "5.0",
Metadata: cveschema5.Metadata{
ID: "CVE-9999-0001",
},
Containers: cveschema5.Containers{
CNAContainer: cveschema5.CNAPublishedContainer{
ProviderMetadata: cveschema5.ProviderMetadata{
OrgID: GoOrgUUID,
},
Descriptions: []cveschema5.Description{
{
Lang: "en",
Value: `A description`,
},
},
Affected: []cveschema5.Affected{
{
Vendor: "Go standard library",
Product: "crypto/rand",
CollectionURL: "https://pkg.go.dev",
PackageName: "crypto/rand",
Versions: []cveschema5.VersionRange{
{
Introduced: "0",
Fixed: "1.17.11",
Status: cveschema5.StatusAffected,
VersionType: "semver",
},
{
Introduced: "1.18.0",
Fixed: "1.18.3",
Status: cveschema5.StatusAffected,
VersionType: "semver",
},
},
Platforms: []string{
"windows",
},
ProgramRoutines: []cveschema5.ProgramRoutine{
{
Name: "TestSymbol",
},
},
DefaultStatus: cveschema5.StatusUnaffected,
},
},
ProblemTypes: []cveschema5.ProblemType{
{
Descriptions: []cveschema5.ProblemTypeDescription{
{
Lang: "en",
Description: "CWE-835: Loop with Unreachable Exit Condition ('Infinite Loop')",
},
},
},
},
References: []cveschema5.Reference{
{
URL: "https://go.dev/cl/12345",
},
{
URL: "https://go.googlesource.com/go/+/abcde",
},
{
URL: "https://go.dev/issue/12345",
},
{
URL: "https://groups.google.com/g/golang-announce/c/abcdef",
},
{
// This normally reports in the format .../vuln/GO-YYYY-XXXX, but our logic
// relies on file path so this "abnormal" formatting is so that tests pass.
URL: "https://pkg.go.dev/vuln/std-report",
},
},
Credits: []cveschema5.Credit{
{
Lang: "en",
Value: "A Credit",
},
},
},
},
}
testThirdPartyRecord = &cveschema5.CVERecord{
DataType: "CVE_RECORD",
DataVersion: "5.0",
Metadata: cveschema5.Metadata{
ID: "CVE-9999-0001",
},
Containers: cveschema5.Containers{
CNAContainer: cveschema5.CNAPublishedContainer{
ProviderMetadata: cveschema5.ProviderMetadata{
OrgID: GoOrgUUID,
},
Descriptions: []cveschema5.Description{
{
Lang: "en",
Value: `Unsanitized input in the default logger in github.com/gin-gonic/gin before v1.6.0 allows remote attackers to inject arbitrary log lines.`,
},
},
Affected: []cveschema5.Affected{
{
Vendor: "github.com/gin-gonic/gin",
Product: "github.com/gin-gonic/gin",
CollectionURL: "https://pkg.go.dev",
PackageName: "github.com/gin-gonic/gin",
Versions: []cveschema5.VersionRange{
{
Introduced: "0",
Fixed: "1.6.0",
Status: cveschema5.StatusAffected,
VersionType: "semver",
},
},
ProgramRoutines: []cveschema5.ProgramRoutine{
{
Name: "defaultLogFormatter",
},
},
DefaultStatus: cveschema5.StatusUnaffected,
},
},
ProblemTypes: []cveschema5.ProblemType{
{
Descriptions: []cveschema5.ProblemTypeDescription{
{
Lang: "en",
Description: "CWE-20: Improper Input Validation",
},
},
},
},
References: []cveschema5.Reference{
{
URL: "https://github.com/gin-gonic/gin/pull/2237",
},
{
URL: "https://github.com/gin-gonic/gin/commit/a71af9c144f9579f6dbe945341c1df37aaf09c0d",
},
{
// This normally reports in the format .../vuln/GO-YYYY-XXXX, but our logic
// relies on file path so this "abnormal" formatting is so that tests pass.
URL: "https://pkg.go.dev/vuln/report",
},
},
Credits: []cveschema5.Credit{
{
Lang: "en",
Value: "@thinkerou <thinkerou@gmail.com>",
},
},
},
},
}
testNoVersionsRecord = &cveschema5.CVERecord{
DataType: "CVE_RECORD",
DataVersion: "5.0",
Metadata: cveschema5.Metadata{
ID: "CVE-9999-0001",
},
Containers: cveschema5.Containers{
CNAContainer: cveschema5.CNAPublishedContainer{
ProviderMetadata: cveschema5.ProviderMetadata{
OrgID: GoOrgUUID,
},
Descriptions: []cveschema5.Description{
{
Lang: "en",
Value: `Unsanitized input in the default logger in github.com/gin-gonic/gin before v1.6.0 allows remote attackers to inject arbitrary log lines.`,
},
},
Affected: []cveschema5.Affected{
{
Vendor: "github.com/gin-gonic/gin",
Product: "github.com/gin-gonic/gin",
CollectionURL: "https://pkg.go.dev",
PackageName: "github.com/gin-gonic/gin",
Versions: nil,
ProgramRoutines: []cveschema5.ProgramRoutine{
{
Name: "defaultLogFormatter",
},
},
DefaultStatus: cveschema5.StatusAffected,
},
},
ProblemTypes: []cveschema5.ProblemType{
{
Descriptions: []cveschema5.ProblemTypeDescription{
{
Lang: "en",
Description: "CWE-20: Improper Input Validation",
},
},
},
},
References: []cveschema5.Reference{
{
URL: "https://github.com/gin-gonic/gin/pull/2237",
},
{
URL: "https://github.com/gin-gonic/gin/commit/a71af9c144f9579f6dbe945341c1df37aaf09c0d",
},
{
// This normally reports in the format .../vuln/GO-YYYY-XXXX, but our logic
// relies on file path so this "abnormal" formatting is so that tests pass.
URL: "https://pkg.go.dev/vuln/no-versions",
},
},
Credits: []cveschema5.Credit{
{
Lang: "en",
Value: "@thinkerou <thinkerou@gmail.com>",
},
},
},
},
}
)
func TestToCVE5(t *testing.T) {
tests := []struct {
name string
filename string
want *cveschema5.CVERecord
}{
{
name: "Standard Library Report",
filename: "testdata/std-report.yaml",
want: testStdLibRecord,
},
{
name: "Third Party Report",
filename: "testdata/report.yaml",
want: testThirdPartyRecord,
},
{
name: "No Versions Report",
filename: "testdata/no-versions.yaml",
want: testNoVersionsRecord,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
r, err := Read(test.filename)
if err != nil {
t.Fatal(err)
}
got, err := r.ToCVE5()
if err != nil {
t.Fatalf("ToCVE5() failed unexpectedly; err=%v", err)
}
if diff := cmp.Diff(test.want, got); diff != "" {
t.Fatalf("ToCVE5(): unexpected diffs (-want,+got):\n%v", diff)
}
})
}
}
func TestCVEFilename(t *testing.T) {
want := filepath.FromSlash("data/cve/v5/GO-1999-0001.json")
r := &Report{ID: "GO-1999-0001"}
if got := r.CVEFilename(); got != want {
t.Errorf("got %s, want %s", got, want)
}
}
func TestVersionRangeToVersionRange(t *testing.T) {
tests := []struct {
name string
versions []VersionRange
wantRange []cveschema5.VersionRange
wantDefault cveschema5.VersionStatus
}{
{
name: "nil",
versions: nil,
wantRange: nil,
wantDefault: cveschema5.StatusAffected,
},
{
name: "empty",
versions: []VersionRange{},
wantRange: nil,
wantDefault: cveschema5.StatusAffected,
},
{
name: "basic",
versions: []VersionRange{
{
Introduced: "1.0.0",
Fixed: "1.0.1",
},
{
Introduced: "1.2.0",
Fixed: "1.2.3",
},
},
wantRange: []cveschema5.VersionRange{
{
Introduced: "1.0.0",
Fixed: "1.0.1",
Status: cveschema5.StatusAffected,
VersionType: typeSemver,
},
{
Introduced: "1.2.0",
Fixed: "1.2.3",
Status: cveschema5.StatusAffected,
VersionType: typeSemver,
},
},
wantDefault: cveschema5.StatusUnaffected,
},
{
name: "no initial introduced",
versions: []VersionRange{
{
Fixed: "1.0.1",
},
{
Introduced: "1.2.0",
Fixed: "1.2.3",
},
},
wantRange: []cveschema5.VersionRange{
{
Introduced: "0",
Fixed: "1.0.1",
Status: cveschema5.StatusAffected,
VersionType: typeSemver,
},
{
Introduced: "1.2.0",
Fixed: "1.2.3",
Status: cveschema5.StatusAffected,
VersionType: typeSemver,
},
},
wantDefault: cveschema5.StatusUnaffected,
},
{
name: "no fix",
versions: []VersionRange{
{
Introduced: "1.0.0",
},
},
wantRange: []cveschema5.VersionRange{
{
Introduced: "0",
Fixed: "1.0.0",
Status: cveschema5.StatusUnaffected,
VersionType: typeSemver,
},
},
wantDefault: cveschema5.StatusAffected,
},
{
name: "no final fix",
versions: []VersionRange{
{
Introduced: "1.0.0",
Fixed: "1.0.3",
},
{
Introduced: "1.1.0",
},
},
wantRange: []cveschema5.VersionRange{
{
Introduced: "0",
Fixed: "1.0.0",
Status: cveschema5.StatusUnaffected,
VersionType: typeSemver,
},
{
Introduced: "1.0.3",
Fixed: "1.1.0",
Status: cveschema5.StatusUnaffected,
VersionType: typeSemver,
},
},
wantDefault: cveschema5.StatusAffected,
},
{
name: "no initial introduced and no final fix",
versions: []VersionRange{
{
Fixed: "1.0.3",
},
{
Introduced: "1.0.5",
Fixed: "1.0.7",
},
{
Introduced: "1.1.0",
},
},
wantRange: []cveschema5.VersionRange{
{
Introduced: "1.0.3",
Fixed: "1.0.5",
Status: cveschema5.StatusUnaffected,
VersionType: typeSemver,
},
{
Introduced: "1.0.7",
Fixed: "1.1.0",
Status: cveschema5.StatusUnaffected,
VersionType: typeSemver,
},
},
wantDefault: cveschema5.StatusAffected,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotRange, gotStatus := versionRangeToVersionRange(tt.versions)
if !reflect.DeepEqual(gotRange, tt.wantRange) {
t.Errorf("versionRangeToVersionRange() got version range = %v, want %v", gotRange, tt.wantRange)
}
if !reflect.DeepEqual(gotStatus, tt.wantDefault) {
t.Errorf("versionRangeToVersionRange() got default status = %v, want %v", gotStatus, tt.wantDefault)
}
})
}
}