Skip to content
12 changes: 11 additions & 1 deletion pkg/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,19 @@ type Severity int

type VendorSeverity map[string]Severity

type CVSS struct {
V2Vector string `json:"v2_vector,omitempty"`
V3Vector string `json:"v3_vector,omitempty"`
V2Score float64 `json:"v2_score,omitempty"`
V3Score float64 `json:"v3_score,omitempty"`
}

type CVSSVector struct {
V2 string `json:"v2,omitempty"`
V3 string `json:"v3,omitempty"`
}

type VendorCVSS map[string]CVSS
type VendorVectors map[string]CVSSVector

const (
Expand Down Expand Up @@ -96,7 +105,8 @@ type Vulnerability struct {
Description string `json:",omitempty"`
Severity string `json:",omitempty"`
VendorSeverity VendorSeverity `json:",omitempty"`
VendorVectors VendorVectors `json:",omitempty"`
VendorVectors VendorVectors `json:",omitempty"` // Deprecated: VendorVectors is only for backwards compatibility. Use CVSS instead.
CVSS VendorCVSS `json:",omitempty"`
References []string `json:",omitempty"`
}

Expand Down
6 changes: 3 additions & 3 deletions pkg/vulnsrc/nvd/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ type BaseMetricV2 struct {
}

type CvssV2 struct {
BaseScore float64
VectorString string `json:"vectorString"`
BaseScore float64 `json:"baseScore"`
VectorString string `json:"vectorString"`
}

type BaseMetricV3 struct {
CvssV3 CvssV3
}

type CvssV3 struct {
BaseScore float64
BaseScore float64 `json:"baseScore"`
BaseSeverity string
VectorString string `json:"vectorString"`
}
Expand Down
27 changes: 22 additions & 5 deletions pkg/vulnsrc/vulnerability/vulnerability.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

var (
sources = []string{Nvd, RedHat, Debian, DebianOVAL, Alpine, Amazon, OracleOVAL, SuseCVRF, Photon,
sources = []string{Nvd, RedHat, Debian, DebianOVAL, Ubuntu, Alpine, Amazon, OracleOVAL, SuseCVRF, Photon,
RubySec, RustSec, PhpSecurityAdvisories, NodejsSecurityWg, PythonSafetyDB,
GHSAComposer, GHSAMaven, GHSANpm, GHSANuget, GHSAPip, GHSARubygems}
)
Expand All @@ -21,18 +21,35 @@ var (
getVulnerabilityDetailFunc = db.Config{}.GetVulnerabilityDetail
)

func GetDetail(vulnID string) (types.Severity, types.VendorSeverity, types.VendorVectors, string, string, []string) {
func GetDetail(vulnID string) (types.Severity, types.VendorSeverity, types.VendorCVSS, types.VendorVectors, string, string, []string) {
details, err := getVulnerabilityDetailFunc(vulnID)
if err != nil {
log.Println(err)
return types.SeverityUnknown, nil, nil, "", "", nil
return types.SeverityUnknown, nil, nil, nil, "", "", nil
} else if len(details) == 0 {
return types.SeverityUnknown, nil, nil, "", "", nil
return types.SeverityUnknown, nil, nil, nil, "", "", nil
}

return getSeverity(details), getVendorSeverity(details), getVendorVectors(details), getTitle(details), getDescription(details), getReferences(details)
return getSeverity(details), getVendorSeverity(details), getCVSS(details), getVendorVectors(details), getTitle(details), getDescription(details), getReferences(details)
}

func getCVSS(details map[string]types.VulnerabilityDetail) types.VendorCVSS {
vc := make(types.VendorCVSS)
for vendor, detail := range details {
if (detail.CvssVector == "" || detail.CvssScore == 0) && (detail.CvssVectorV3 == "" || detail.CvssScoreV3 == 0) {
continue
}
vc[vendor] = types.CVSS{
V2Vector: detail.CvssVector,
V3Vector: detail.CvssVectorV3,
V2Score: detail.CvssScore,
V3Score: detail.CvssScoreV3,
}
}
return vc
}

// Deprecated: Use getCVSS instead.
func getVendorVectors(details map[string]types.VulnerabilityDetail) types.VendorVectors {
vv := make(types.VendorVectors)
for vendor, detail := range details {
Expand Down
55 changes: 53 additions & 2 deletions pkg/vulnsrc/vulnerability/vulnerability_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func TestGetDetail(t *testing.T) {
expectedSeverity types.Severity
expectedVendorSeverity types.VendorSeverity
expectedVendorVectors types.VendorVectors
expectedVendorCVSS types.VendorCVSS
expectedTitle string
expectedDescription string
expectedRefs []string
Expand All @@ -37,7 +38,6 @@ func TestGetDetail(t *testing.T) {
},
Ubuntu: {
ID: "CVE-2020-1234",
CvssScore: 1.2,
CvssScoreV3: 3.4,
CvssVectorV3: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
Severity: types.SeverityLow,
Expand Down Expand Up @@ -70,6 +70,18 @@ func TestGetDetail(t *testing.T) {
V3: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
},
},
expectedVendorCVSS: types.VendorCVSS{
RedHat: types.CVSS{
V2Vector: "AV:N/AC:M/Au:N/C:N/I:P/A:N",
V3Vector: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
V2Score: 4.2,
V3Score: 5.6,
},
Ubuntu: types.CVSS{
V3Vector: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
V3Score: 3.4,
},
},
expectedTitle: "test vulnerability",
expectedDescription: "a test vulnerability where vendor rates it lower than NVD",
expectedRefs: []string{"http://foo-bar.com/baz"},
Expand Down Expand Up @@ -104,9 +116,47 @@ func TestGetDetail(t *testing.T) {
expectedSeverity: types.SeverityMedium,
expectedVendorSeverity: types.VendorSeverity{"redhat": 2, "ubuntu": 1, "nodejs-security-wg": 4},
expectedVendorVectors: types.VendorVectors{},
expectedVendorCVSS: types.VendorCVSS{},
expectedTitle: "test vulnerability",
expectedDescription: "a test vulnerability where vendor rates it lower than NVD",
},
{
name: "happy path, classifications for redhat (only CVSSv3), ubuntu and nodejs with variety vectors but no scores",
getVulnerabilityDetailFunc: func(cveID string) (m map[string]types.VulnerabilityDetail, err error) {
return map[string]types.VulnerabilityDetail{
RedHat: {
ID: "CVE-2020-1234",
CvssVector: "AV:N/AC:M/Au:N/C:N/I:P/A:N",
CvssVectorV3: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
Title: "test vulnerability",
Description: "a test vulnerability where vendor rates it lower than NVD",
},
Ubuntu: {
ID: "CVE-2020-1234",
Severity: types.SeverityLow,
SeverityV3: types.SeverityMedium,
Title: "test vulnerability",
Description: "a test vulnerability where vendor rates it lower than NVD",
},
NodejsSecurityWg: {
ID: "CVE-2020-1234",
Title: "test vulnerability",
Description: "a test vulnerability where vendor rates it lower than NVD",
},
}, nil
},
expectedSeverity: types.SeverityLow,
expectedVendorSeverity: types.VendorSeverity{"ubuntu": 1},
expectedVendorVectors: types.VendorVectors{
"redhat": types.CVSSVector{
V2: "AV:N/AC:M/Au:N/C:N/I:P/A:N",
V3: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
},
},
expectedVendorCVSS: types.VendorCVSS{},
expectedTitle: "test vulnerability",
expectedDescription: "a test vulnerability where vendor rates it lower than NVD",
},
{
name: "sad path, getVulnerabilityDetailFunc returns an error",
getVulnerabilityDetailFunc: func(cveID string) (m map[string]types.VulnerabilityDetail, err error) {
Expand All @@ -123,10 +173,11 @@ func TestGetDetail(t *testing.T) {
}()
getVulnerabilityDetailFunc = tc.getVulnerabilityDetailFunc

gotSeverity, gotVendorSeverity, gotVendorVectors, gotTitle, gotDescription, gotRefs := GetDetail("CVE-2020-123")
gotSeverity, gotVendorSeverity, gotVendorCVSS, gotVendorVectors, gotTitle, gotDescription, gotRefs := GetDetail("CVE-2020-123")
assert.Equal(t, tc.expectedSeverity, gotSeverity, tc.name)
assert.Equal(t, tc.expectedVendorSeverity, gotVendorSeverity, tc.name)
assert.Equal(t, tc.expectedVendorVectors, gotVendorVectors, tc.name)
assert.Equal(t, tc.expectedVendorCVSS, gotVendorCVSS, tc.name)
assert.Equal(t, tc.expectedTitle, gotTitle, tc.name)
assert.Equal(t, tc.expectedDescription, gotDescription, tc.name)
assert.Equal(t, tc.expectedRefs, gotRefs, tc.name)
Expand Down
7 changes: 4 additions & 3 deletions pkg/vulnsrc/vulnsrc.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,15 @@ var (
)

func (o fullOptimizer) fullOptimize(tx *bolt.Tx, cveID string) error {
severity, vs, vv, title, description, references := getDetailFunc(cveID)
severity, vs, vc, vv, title, description, references := getDetailFunc(cveID)
vuln := types.Vulnerability{
Title: title,
Description: description,
Severity: severity.String(), // TODO: We have to keep this key until we deprecate
References: references,
VendorSeverity: vs,
VendorVectors: vv,
VendorVectors: vv, // TODO: We have to keep this for backwards compatibility.
CVSS: vc,
}

if err := o.dbc.PutVulnerability(tx, cveID, vuln); err != nil {
Expand Down Expand Up @@ -208,7 +209,7 @@ func (o lightOptimizer) Optimize() error {

func (o lightOptimizer) lightOptimize(cveID string, tx *bolt.Tx) error {
// get correct severity
severity, vendorSeverity, _, _, _, _ := getDetailFunc(cveID)
severity, vendorSeverity, _, _, _, _, _ := getDetailFunc(cveID)
vuln := types.Vulnerability{
VendorSeverity: vendorSeverity,
}
Expand Down
26 changes: 21 additions & 5 deletions pkg/vulnsrc/vulnsrc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,11 +392,19 @@ func Test_fullOptimize(t *testing.T) {
getDetailFunc = oldgetDetailFunc
}()

getDetailFunc = func(vulnID string) (severity types.Severity, vendorSeverity types.VendorSeverity, vendorVectors types.VendorVectors, s string, s2 string, strings []string) {
getDetailFunc = func(vulnID string) (severity types.Severity, vendorSeverity types.VendorSeverity, vendorCVSS types.VendorCVSS, vendorVectors types.VendorVectors, s string, s2 string, strings []string) {
return types.SeverityCritical, types.VendorSeverity{
"redhat": types.SeverityHigh,
"ubuntu": types.SeverityLow,
}, types.VendorVectors{
}, types.VendorCVSS{
"redhat": types.CVSS{
V2Vector: "AV:N/AC:M/Au:N/C:N/I:P/A:N",
V2Score: 4.5,
V3Vector: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
V3Score: 5.6,
},
},
types.VendorVectors{
"redhat": types.CVSSVector{
V2: "AV:N/AC:M/Au:N/C:N/I:P/A:N",
V3: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
Expand All @@ -420,8 +428,16 @@ func Test_fullOptimize(t *testing.T) {
"redhat": types.SeverityHigh,
"ubuntu": types.SeverityLow,
},
VendorVectors: map[string]types.CVSSVector{
CVSS: map[string]types.CVSS{
"redhat": {
V2Vector: "AV:N/AC:M/Au:N/C:N/I:P/A:N",
V2Score: 4.5,
V3Vector: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
V3Score: 5.6,
},
},
VendorVectors: types.VendorVectors{
"redhat": types.CVSSVector{
V2: "AV:N/AC:M/Au:N/C:N/I:P/A:N",
V3: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
},
Expand All @@ -444,11 +460,11 @@ func Test_lightOptimize(t *testing.T) {
getDetailFunc = oldgetDetailFunc
}()

getDetailFunc = func(vulnID string) (severity types.Severity, vendorSeverity types.VendorSeverity, vendorVectors types.VendorVectors, s string, s2 string, strings []string) {
getDetailFunc = func(vulnID string) (severity types.Severity, vendorSeverity types.VendorSeverity, vendorCVSS types.VendorCVSS, vendorVectors types.VendorVectors, s string, s2 string, strings []string) {
return types.SeverityCritical, types.VendorSeverity{
"redhat": types.SeverityHigh,
"ubuntu": types.SeverityLow,
}, types.VendorVectors{}, "test title", "test description", []string{"test reference"}
}, types.VendorCVSS{}, types.VendorVectors{}, "test title", "test description", []string{"test reference"}
}

mockDBOperation := new(db.MockOperation)
Expand Down