Sort results order by CVSS score, CVE-ID
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user