diff --git a/models/models.go b/models/models.go index 61f60f52fc..d5e75bf0be 100644 --- a/models/models.go +++ b/models/models.go @@ -277,6 +277,14 @@ func (ps PackageInfoList) FindByName(name string) (result PackageInfo, found boo // return PackageInfo{}, false // } +// PackageInfosByName implements sort.Interface for []PackageInfo based on +// the Name field. +type PackageInfosByName []PackageInfo + +func (a PackageInfosByName) Len() int { return len(a) } +func (a PackageInfosByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a PackageInfosByName) Less(i, j int) bool { return a[i].Name < a[j].Name } + // PackageInfo has installed packages. type PackageInfo struct { gorm.Model `json:"-" xml:"-"` diff --git a/scan/base.go b/scan/base.go index 173c03a5dc..04485ea44e 100644 --- a/scan/base.go +++ b/scan/base.go @@ -226,6 +226,8 @@ func (l base) isAwsInstanceID(str string) bool { func (l *base) convertToModel() (models.ScanResult, error) { var scoredCves, unscoredCves, ignoredCves models.CveInfos for _, p := range l.UnsecurePackages { + sort.Sort(models.PackageInfosByName(p.Packs)) + // ignoreCves found := false for _, icve := range l.getServerInfo().IgnoreCves { diff --git a/scan/serverapi_test.go b/scan/serverapi_test.go index 59825b127c..a9b5b64e41 100644 --- a/scan/serverapi_test.go +++ b/scan/serverapi_test.go @@ -1,6 +1,12 @@ package scan -import "testing" +import ( + "github.com/future-architect/vuls/config" + "github.com/future-architect/vuls/models" + cve "github.com/kotakanbe/go-cve-dictionary/models" + "reflect" + "testing" +) func TestPackageCveInfosSetGet(t *testing.T) { var test = struct { @@ -45,3 +51,108 @@ func TestPackageCveInfosSetGet(t *testing.T) { } } } + +func TestGetScanResults(t *testing.T) { + // setup servers + c := config.ServerInfo{ + ServerName: "ubuntu", + } + deb1 := newDebian(c) + deb2 := newDebian(c) + + cpis1 := []CvePacksInfo{ + { + CveID: "CVE1", + CveDetail: cve.CveDetail{CveID: "CVE1"}, + Packs: []models.PackageInfo{ + {Name: "mysql-client-5.5"}, + {Name: "mysql-server-5.5"}, + {Name: "mysql-common-5.5"}, + }, + }, + { + CveID: "CVE2", + CveDetail: cve.CveDetail{CveID: "CVE2"}, + Packs: []models.PackageInfo{ + {Name: "mysql-common-5.5"}, + {Name: "mysql-server-5.5"}, + {Name: "mysql-client-5.5"}, + }, + }, + } + cpis2 := []CvePacksInfo{ + { + CveID: "CVE3", + CveDetail: cve.CveDetail{CveID: "CVE3"}, + Packs: []models.PackageInfo{ + {Name: "libcurl3"}, + {Name: "curl"}, + }, + }, + { + CveID: "CVE4", + CveDetail: cve.CveDetail{CveID: "CVE4"}, + Packs: []models.PackageInfo{ + {Name: "bind9"}, + {Name: "libdns100"}, + }, + }, + } + deb1.setUnsecurePackages(cpis1) + servers = append(servers, deb1) + + deb2.setUnsecurePackages(cpis2) + servers = append(servers, deb2) + + // prepare expected data + expectedUnKnownPackages := []map[string][]models.PackageInfo{ + { + "CVE1": { + {Name: "mysql-client-5.5"}, + {Name: "mysql-common-5.5"}, + {Name: "mysql-server-5.5"}, + }, + }, + { + "CVE2": { + {Name: "mysql-client-5.5"}, + {Name: "mysql-common-5.5"}, + {Name: "mysql-server-5.5"}, + }, + }, + { + "CVE3": { + {Name: "curl"}, + {Name: "libcurl3"}, + }, + }, + { + "CVE4": { + {Name: "bind9"}, + {Name: "libdns100"}, + }, + }, + } + + // check scanResults + scanResults, _ := GetScanResults() + if len(scanResults) != 2 { + t.Errorf("length of scanResults should be 2") + } + for i, result := range scanResults { + if result.ServerName != "ubuntu" { + t.Errorf("expected ubuntu, actual %s", result.ServerName) + } + + unKnownCves := result.UnknownCves + if len(unKnownCves) != 2 { + t.Errorf("length of unKnownCves should be 2") + } + for j, unKnownCve := range unKnownCves { + expected := expectedUnKnownPackages[i*2+j][unKnownCve.CveDetail.CveID] + if !reflect.DeepEqual(expected, unKnownCve.Packages) { + t.Errorf("expected %v, actual %v", expected, unKnownCve.Packages) + } + } + } +}