Sort results order by CVSS score, CVE-ID

This commit is contained in:
Kota Kanbe
2017-05-23 15:48:59 +09:00
committed by kota kanbe
parent a31974a3c0
commit 73b011eba7
6 changed files with 270 additions and 39 deletions

View File

@@ -299,8 +299,6 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
c.Conf.HTTPProxy = p.httpProxy
c.Conf.Pipe = p.pipe
c.Conf.FormatXML = p.formatXML
c.Conf.FormatJSON = p.formatJSON
c.Conf.FormatOneEMail = p.formatOneEMail
@@ -310,6 +308,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
c.Conf.GZIP = p.gzip
c.Conf.Diff = p.diff
c.Conf.Pipe = p.pipe
var dir string
var err error
@@ -405,30 +404,11 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
}
util.Log.Infof("Loaded: %s", dir)
//TODO dir
if res, err = report.FillCveInfos(res, dir); err != nil {
util.Log.Error(err)
return subcommands.ExitFailure
}
// TODO Filter, Sort
// TODO Add sort function to ScanResults
//remove
// for _, vuln := range r.ScannedCves {
// // if _, ok := vuln.CveContents.Get(models.NewCveContentType(r.Family)); !ok {
// // pp.Printf("not in oval: %s %f\n%v\n",
// // vuln.CveID, vuln.CveContents.CvssV2Score(), vuln.Packages)
// // } else {
// // fmt.Printf(" in oval: %s %f\n",
// // vuln.CveID, vuln.CveContents.CvssV2Score())
// // }
// // if vuln.CveContents.CvssV2Score() < 0.1 &&
// // vuln.CveContents.CvssV3Score() < 0.1 {
// // pp.Println(vuln)
// // }
// }
// }
for _, w := range reports {
if err := w.Write(res...); err != nil {
util.Log.Errorf("Failed to report: %s", err)

View File

@@ -185,7 +185,7 @@ func severityToScoreForUbuntu(severity string) float64 {
// 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
// Use the definition of CVSSv3 because the exact definition of severity and score is not described.
func severityToScoreForRedHat(severity string) float64 {
switch strings.ToUpper(severity) {
case "CRITICAL":
@@ -231,7 +231,6 @@ func cvss3ScoreToSeverity(score float64) string {
// Cvss3Scores returns CVSS V3 Score
func (v CveContents) Cvss3Scores() (values []CveContentCvss3) {
//TODO Severity Ubuntu, Debian...
order := []CveContentType{RedHat}
for _, ctype := range order {
if cont, found := v[ctype]; found && 0 < cont.Cvss3Score {
@@ -255,7 +254,6 @@ func (v CveContents) Cvss3Scores() (values []CveContentCvss3) {
// MaxCvss3Score returns Max CVSS V3 Score
func (v CveContents) MaxCvss3Score() CveContentCvss3 {
//TODO Severity Ubuntu, Debian...
order := []CveContentType{RedHat}
max := 0.0
value := CveContentCvss3{
@@ -283,6 +281,18 @@ func (v CveContents) MaxCvss3Score() CveContentCvss3 {
return value
}
// MaxCvssScore returns max CVSS Score
// If there is no CVSS Score, return Severity as a numerical value.
func (v CveContents) MaxCvssScore() float64 {
v3Max := v.MaxCvss3Score()
v2Max := v.MaxCvss2Score()
max := v3Max.Value.Score
if max < v2Max.Value.Score {
max = v2Max.Value.Score
}
return max
}
// FormatMaxCvssScore returns Max CVSS Score
func (v CveContents) FormatMaxCvssScore() string {
v2Max := v.MaxCvss2Score()

View File

@@ -21,18 +21,6 @@ import (
"testing"
)
var m = CveContent{
Type: RedHat,
CveID: "CVE-2017-0001",
Title: "title",
Summary: "summary",
Severity: "High",
Cvss2Score: 8.0,
Cvss2Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
Cvss3Score: 9.0,
Cvss3Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
}
func TestExcept(t *testing.T) {
var tests = []struct {
in CveContents
@@ -284,6 +272,69 @@ func TestMaxCvss3Scores(t *testing.T) {
}
}
func TestMaxCvssScores(t *testing.T) {
var tests = []struct {
in CveContents
out float64
}{
{
in: CveContents{
NVD: {
Type: NVD,
Cvss3Score: 7.0,
},
RedHat: {
Type: RedHat,
Cvss2Score: 8.0,
},
},
out: 8.0,
},
{
in: CveContents{
RedHat: {
Type: RedHat,
Cvss3Score: 8.0,
},
},
out: 8.0,
},
{
in: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "HIGH",
},
},
out: 10.0,
},
{
in: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "MEDIUM",
},
NVD: {
Type: NVD,
Cvss2Score: 7.0,
},
},
out: 7.0,
},
// Empty
{
in: CveContents{},
out: 0,
},
}
for i, tt := range tests {
actual := tt.in.MaxCvssScore()
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\n[%d] expected: %v\n actual: %v\n", i, tt.out, actual)
}
}
}
func TestFormatMaxCvssScore(t *testing.T) {
var tests = []struct {
in CveContents

View File

@@ -19,6 +19,7 @@ package models
import (
"fmt"
"sort"
"time"
)
@@ -36,7 +37,7 @@ func (v VulnInfos) Find(f func(VulnInfo) bool) VulnInfos {
return filtered
}
// FindScoredVulns return socred vulnerabilities
// FindScoredVulns return scored vulnerabilities
func (v VulnInfos) FindScoredVulns() VulnInfos {
return v.Find(func(vv VulnInfo) bool {
if 0 < vv.CveContents.MaxCvss2Score().Value.Score ||
@@ -47,6 +48,22 @@ func (v VulnInfos) FindScoredVulns() VulnInfos {
})
}
// ToSortedSlice returns slice of VulnInfos that is sorted by CVE-ID
func (v VulnInfos) ToSortedSlice() (sorted []VulnInfo) {
for k := range v {
sorted = append(sorted, v[k])
}
sort.Slice(sorted, func(i, j int) bool {
maxI := sorted[i].CveContents.MaxCvssScore()
maxJ := sorted[j].CveContents.MaxCvssScore()
if maxI != maxJ {
return maxJ < maxI
}
return sorted[i].CveID < sorted[j].CveID
})
return
}
// VulnInfo holds a vulnerability information and unsecure packages
type VulnInfo struct {
CveID string

View File

@@ -15,3 +15,174 @@ 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"
)
func TestToSortedSlice(t *testing.T) {
var tests = []struct {
in VulnInfos
out []VulnInfo
}{
{
in: VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
NVD: {
Type: NVD,
Cvss3Score: 6.0,
},
RedHat: {
Type: RedHat,
Cvss2Score: 7.0,
},
},
},
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: CveContents{
NVD: {
Type: NVD,
Cvss3Score: 7.0,
},
RedHat: {
Type: RedHat,
Cvss2Score: 8.0,
},
},
},
},
out: []VulnInfo{
{
CveID: "CVE-2017-0001",
CveContents: CveContents{
NVD: {
Type: NVD,
Cvss3Score: 7.0,
},
RedHat: {
Type: RedHat,
Cvss2Score: 8.0,
},
},
},
{
CveID: "CVE-2017-0002",
CveContents: CveContents{
NVD: {
Type: NVD,
Cvss3Score: 6.0,
},
RedHat: {
Type: RedHat,
Cvss2Score: 7.0,
},
},
},
},
},
// When max scores are the same, sort by CVE-ID
{
in: VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
NVD: {
Type: NVD,
Cvss3Score: 6.0,
},
RedHat: {
Type: RedHat,
Cvss2Score: 7.0,
},
},
},
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: CveContents{
RedHat: {
Type: RedHat,
Cvss2Score: 7.0,
},
},
},
},
out: []VulnInfo{
{
CveID: "CVE-2017-0001",
CveContents: CveContents{
RedHat: {
Type: RedHat,
Cvss2Score: 7.0,
},
},
},
{
CveID: "CVE-2017-0002",
CveContents: CveContents{
NVD: {
Type: NVD,
Cvss3Score: 6.0,
},
RedHat: {
Type: RedHat,
Cvss2Score: 7.0,
},
},
},
},
},
// When there are no cvss scores, sort by severity
{
in: VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "High",
},
},
},
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "Low",
},
},
},
},
out: []VulnInfo{
{
CveID: "CVE-2017-0002",
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "High",
},
},
},
{
CveID: "CVE-2017-0001",
CveContents: CveContents{
Ubuntu: {
Type: Ubuntu,
Severity: "Low",
},
},
},
},
},
}
for _, tt := range tests {
actual := tt.in.ToSortedSlice()
if !reflect.DeepEqual(tt.out, actual) {
t.Errorf("\nexpected: %v\n actual: %v\n", tt.out, actual)
}
}
}

View File

@@ -111,7 +111,7 @@ func formatShortPlainText(r models.ScanResult) string {
stable := uitable.New()
stable.MaxColWidth = maxColWidth
stable.Wrap = true
for _, vuln := range vulns {
for _, vuln := range vulns.ToSortedSlice() {
summaries := vuln.CveContents.Summaries(config.Conf.Lang, r.Family)
links := vuln.CveContents.SourceLinks(
config.Conf.Lang, r.Family, vuln.CveID)
@@ -178,7 +178,7 @@ func formatFullPlainText(r models.ScanResult) string {
table := uitable.New()
table.MaxColWidth = maxColWidth
table.Wrap = true
for _, vuln := range vulns {
for _, vuln := range vulns.ToSortedSlice() {
table.AddRow(vuln.CveID)
table.AddRow("----------------")
table.AddRow("Max Score", vuln.CveContents.FormatMaxCvssScore())
@@ -209,12 +209,14 @@ func formatFullPlainText(r models.ScanResult) string {
}
packsVer := []string{}
sort.Strings(vuln.PackageNames)
for _, name := range vuln.PackageNames {
// packages detected by OVAL may not be actually installed
if pack, ok := r.Packages[name]; ok {
packsVer = append(packsVer, pack.FormatVersionFromTo())
}
}
sort.Strings(vuln.CpeNames)
for _, name := range vuln.CpeNames {
packsVer = append(packsVer, name)
}