Use Severity ranking in OVAL when the CVSS scores are empty.

This commit is contained in:
Kota Kanbe
2017-05-21 23:04:21 +09:00
committed by kota kanbe
parent eb02bdd95a
commit a31974a3c0
4 changed files with 278 additions and 7 deletions

View File

@@ -106,6 +106,7 @@ func (v CveContents) Cvss2Scores() (values []CveContentCvss2) {
})
}
}
return
}
@@ -136,9 +137,69 @@ func (v CveContents) MaxCvss2Score() CveContentCvss2 {
max = cont.Cvss2Score
}
}
if 0 < max {
return value
}
// If CVSS score isn't on NVD, RedHat and JVN use OVAL's Severity information.
// Convert severity to cvss srore, then returns max severity.
// Only Ubuntu, RedHat and Oracle OVAL has severity data.
order = []CveContentType{Ubuntu, RedHat, Oracle}
for _, ctype := range order {
if cont, found := v[ctype]; found && 0 < len(cont.Severity) {
score := 0.0
switch cont.Type {
case Ubuntu:
score = severityToScoreForUbuntu(cont.Severity)
case Oracle, RedHat:
score = severityToScoreForRedHat(cont.Severity)
}
if max < score {
value = CveContentCvss2{
Type: ctype,
Value: Cvss2{
Score: score,
Vector: cont.Cvss2Vector,
Severity: cont.Severity,
},
}
}
max = score
}
}
return value
}
// Convert Severity to Score for Ubuntu OVAL
func severityToScoreForUbuntu(severity string) float64 {
switch strings.ToUpper(severity) {
case "HIGH":
return 10.0
case "MEDIUM":
return 6.9
case "LOW":
return 3.9
}
return 0
}
// Convert Severity to Score for RedHat, Oracle OVAL
// https://access.redhat.com/security/updates/classification
// Since I don't know the definition, Use the definition of CVSSv3
func severityToScoreForRedHat(severity string) float64 {
switch strings.ToUpper(severity) {
case "CRITICAL":
return 10.0
case "IMPORTANT":
return 8.9
case "MODERATE":
return 6.9
case "LOW":
return 3.9
}
return 0
}
// CveContentCvss3 has CveContentType and Cvss3
type CveContentCvss3 struct {
Type CveContentType
@@ -477,6 +538,9 @@ const (
// Ubuntu is Ubuntu
Ubuntu CveContentType = "ubuntu"
// Oracle is Oracle Linux
Oracle CveContentType = "oracle"
// Unknown is Unknown
Unknown CveContentType = "unknown"
)

View File

@@ -158,6 +158,22 @@ func TestMaxCvss2Scores(t *testing.T) {
},
},
},
// Severity in OVAL
{
in: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "HIGH",
},
},
out: CveContentCvss2{
Type: Ubuntu,
Value: Cvss2{
Score: 10,
Severity: "HIGH",
},
},
},
// Empty
{
in: CveContents{},

View File

@@ -166,13 +166,14 @@ func (r ScanResult) FilterByCvssOver(over float64) ScanResult {
// TODO: Filter by ignore cves???
filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
//TODO in the case of only oval, no cvecontents
values := v.CveContents.Cvss2Scores()
for _, vals := range values {
score := vals.Value.Score
if over <= score {
return true
}
v2Max := v.CveContents.MaxCvss2Score()
v3Max := v.CveContents.MaxCvss3Score()
max := v2Max.Value.Score
if max < v3Max.Value.Score {
max = v3Max.Value.Score
}
if over <= max {
return true
}
return false
})

View File

@@ -15,3 +15,193 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package models
import (
"reflect"
"testing"
"time"
"github.com/k0kubun/pp"
)
func TestFilterByCvssOver(t *testing.T) {
type in struct {
over float64
rs ScanResult
}
var tests = []struct {
in in
out ScanResult
}{
{
in: in{
over: 7.0,
rs: ScanResult{
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: NewCveContents(
CveContent{
Type: NVD,
CveID: "CVE-2017-0001",
Cvss2Score: 7.1,
LastModified: time.Time{},
},
),
},
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: NewCveContents(
CveContent{
Type: NVD,
CveID: "CVE-2017-0002",
Cvss2Score: 6.9,
LastModified: time.Time{},
},
),
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: NVD,
CveID: "CVE-2017-0003",
Cvss2Score: 6.9,
LastModified: time.Time{},
},
CveContent{
Type: JVN,
CveID: "CVE-2017-0003",
Cvss2Score: 7.2,
LastModified: time.Time{},
},
),
},
},
},
},
out: ScanResult{
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: NewCveContents(
CveContent{
Type: NVD,
CveID: "CVE-2017-0001",
Cvss2Score: 7.1,
LastModified: time.Time{},
},
),
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: NVD,
CveID: "CVE-2017-0003",
Cvss2Score: 6.9,
LastModified: time.Time{},
},
CveContent{
Type: JVN,
CveID: "CVE-2017-0003",
Cvss2Score: 7.2,
LastModified: time.Time{},
},
),
},
},
},
},
// OVAL Severity
{
in: in{
over: 7.0,
rs: ScanResult{
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: NewCveContents(
CveContent{
Type: Ubuntu,
CveID: "CVE-2017-0001",
Severity: "HIGH",
LastModified: time.Time{},
},
),
},
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: NewCveContents(
CveContent{
Type: RedHat,
CveID: "CVE-2017-0002",
Severity: "CRITICAL",
LastModified: time.Time{},
},
),
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: Oracle,
CveID: "CVE-2017-0003",
Severity: "IMPORTANT",
LastModified: time.Time{},
},
),
},
},
},
},
out: ScanResult{
ScannedCves: VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: NewCveContents(
CveContent{
Type: Ubuntu,
CveID: "CVE-2017-0001",
Severity: "HIGH",
LastModified: time.Time{},
},
),
},
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: NewCveContents(
CveContent{
Type: RedHat,
CveID: "CVE-2017-0002",
Severity: "CRITICAL",
LastModified: time.Time{},
},
),
},
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: NewCveContents(
CveContent{
Type: Oracle,
CveID: "CVE-2017-0003",
Severity: "IMPORTANT",
LastModified: time.Time{},
},
),
},
},
},
},
}
for _, tt := range tests {
actual := tt.in.rs.FilterByCvssOver(tt.in.over)
for k := range tt.out.ScannedCves {
if !reflect.DeepEqual(tt.out.ScannedCves[k], actual.ScannedCves[k]) {
o := pp.Sprintf("%v", tt.out.ScannedCves[k])
a := pp.Sprintf("%v", actual.ScannedCves[k])
t.Errorf("[%s] expected: %v\n actual: %v\n", k, o, a)
}
}
}
}