From 97114e709b45da2a25fb8d18800177f25def9fd9 Mon Sep 17 00:00:00 2001 From: MaineK00n Date: Wed, 26 May 2021 08:42:36 +0900 Subject: [PATCH] feat(gost): support ubuntu --- gost/gost.go | 2 + gost/ubuntu.go | 169 ++++++++++++++++++++++++++++++++++++++++++ gost/ubuntu_test.go | 72 ++++++++++++++++++ models/cvecontents.go | 3 + models/vulninfos.go | 6 ++ 5 files changed, 252 insertions(+) create mode 100644 gost/ubuntu.go create mode 100644 gost/ubuntu_test.go diff --git a/gost/gost.go b/gost/gost.go index 7caf4c45..53c5ce3f 100644 --- a/gost/gost.go +++ b/gost/gost.go @@ -69,6 +69,8 @@ func NewClient(cnf config.GostConf, family string) (Client, error) { return RedHat{Base{DBDriver: driver}}, nil case constant.Debian, constant.Raspbian: return Debian{Base{DBDriver: driver}}, nil + case constant.Ubuntu: + return Ubuntu{Base{DBDriver: driver}}, nil case constant.Windows: return Microsoft{Base{DBDriver: driver}}, nil default: diff --git a/gost/ubuntu.go b/gost/ubuntu.go new file mode 100644 index 00000000..00a85c52 --- /dev/null +++ b/gost/ubuntu.go @@ -0,0 +1,169 @@ +// +build !scanner + +package gost + +import ( + "encoding/json" + "strings" + + "github.com/future-architect/vuls/logging" + "github.com/future-architect/vuls/models" + "github.com/future-architect/vuls/util" + gostmodels "github.com/knqyf263/gost/models" +) + +// Ubuntu is Gost client for Ubuntu +type Ubuntu struct { + Base +} + +func (ubu Ubuntu) supported(version string) bool { + _, ok := map[string]string{ + "1404": "trusty", + "1604": "xenial", + "1804": "bionic", + "2004": "focal", + "2010": "groovy", + "2104": "hirsute", + }[version] + return ok +} + +// DetectUnfixed fills cve information that has in Gost +func (ubu Ubuntu) DetectUnfixed(r *models.ScanResult, _ bool) (nCVEs int, err error) { + ubuReleaseVer := strings.Replace(r.Release, ".", "", 1) + if !ubu.supported(ubuReleaseVer) { + logging.Log.Warnf("Ubuntu %s is not supported yet", r.Release) + return 0, nil + } + + linuxImage := "linux-image-" + r.RunningKernel.Release + // Add linux and set the version of running kernel to search OVAL. + if r.Container.ContainerID == "" { + newVer := "" + if p, ok := r.Packages[linuxImage]; ok { + newVer = p.NewVersion + } + r.Packages["linux"] = models.Package{ + Name: "linux", + Version: r.RunningKernel.Version, + NewVersion: newVer, + } + } + + packCvesList := []packCves{} + if ubu.DBDriver.Cnf.IsFetchViaHTTP() { + url, _ := util.URLPathJoin(ubu.DBDriver.Cnf.GetURL(), "ubuntu", ubuReleaseVer, "pkg") + responses, err := getAllUnfixedCvesViaHTTP(r, url) + if err != nil { + return 0, err + } + + for _, res := range responses { + ubuCves := map[string]gostmodels.UbuntuCVE{} + if err := json.Unmarshal([]byte(res.json), &ubuCves); err != nil { + return 0, err + } + cves := []models.CveContent{} + for _, ubucve := range ubuCves { + cves = append(cves, *ubu.ConvertToModel(&ubucve)) + } + packCvesList = append(packCvesList, packCves{ + packName: res.request.packName, + isSrcPack: res.request.isSrcPack, + cves: cves, + }) + } + } else { + if ubu.DBDriver.DB == nil { + return 0, nil + } + for _, pack := range r.Packages { + ubuCves := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name) + cves := []models.CveContent{} + for _, ubucve := range ubuCves { + cves = append(cves, *ubu.ConvertToModel(&ubucve)) + } + packCvesList = append(packCvesList, packCves{ + packName: pack.Name, + isSrcPack: false, + cves: cves, + }) + } + + // SrcPack + for _, pack := range r.SrcPackages { + ubuCves := ubu.DBDriver.DB.GetUnfixedCvesUbuntu(ubuReleaseVer, pack.Name) + cves := []models.CveContent{} + for _, ubucve := range ubuCves { + cves = append(cves, *ubu.ConvertToModel(&ubucve)) + } + packCvesList = append(packCvesList, packCves{ + packName: pack.Name, + isSrcPack: true, + cves: cves, + }) + } + } + + delete(r.Packages, "linux") + + for _, p := range packCvesList { + for _, cve := range p.cves { + v, ok := r.ScannedCves[cve.CveID] + if ok { + if v.CveContents == nil { + v.CveContents = models.NewCveContents(cve) + } else { + v.CveContents[models.UbuntuAPI] = cve + } + } else { + v = models.VulnInfo{ + CveID: cve.CveID, + CveContents: models.NewCveContents(cve), + Confidences: models.Confidences{models.UbuntuAPIMatch}, + } + nCVEs++ + } + + names := []string{} + if p.isSrcPack { + if srcPack, ok := r.SrcPackages[p.packName]; ok { + for _, binName := range srcPack.BinaryNames { + if _, ok := r.Packages[binName]; ok { + names = append(names, binName) + } + } + } + } else { + if p.packName == "linux" { + names = append(names, linuxImage) + } else { + names = append(names, p.packName) + } + } + + for _, name := range names { + v.AffectedPackages = v.AffectedPackages.Store(models.PackageFixStatus{ + Name: name, + FixState: "open", + NotFixedYet: true, + }) + } + r.ScannedCves[cve.CveID] = v + } + } + return nCVEs, nil +} + +// ConvertToModel converts gost model to vuls model +func (ubu Ubuntu) ConvertToModel(cve *gostmodels.UbuntuCVE) *models.CveContent { + return &models.CveContent{ + Type: models.UbuntuAPI, + CveID: cve.Candidate, + Summary: cve.Description, + Cvss2Severity: cve.Priority, + Cvss3Severity: cve.Priority, + SourceLink: "https://ubuntu.com/security/" + cve.Candidate, + } +} diff --git a/gost/ubuntu_test.go b/gost/ubuntu_test.go new file mode 100644 index 00000000..b8ef2d22 --- /dev/null +++ b/gost/ubuntu_test.go @@ -0,0 +1,72 @@ +package gost + +import "testing" + +func TestUbuntu_Supported(t *testing.T) { + type args struct { + ubuReleaseVer string + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "14.04 is supported", + args: args{ + ubuReleaseVer: "1404", + }, + want: true, + }, + { + name: "16.04 is supported", + args: args{ + ubuReleaseVer: "1604", + }, + want: true, + }, + { + name: "18.04 is supported", + args: args{ + ubuReleaseVer: "1804", + }, + want: true, + }, + { + name: "20.04 is supported", + args: args{ + ubuReleaseVer: "2004", + }, + want: true, + }, + { + name: "20.10 is supported", + args: args{ + ubuReleaseVer: "2010", + }, + want: true, + }, + { + name: "21.04 is supported", + args: args{ + ubuReleaseVer: "2104", + }, + want: true, + }, + { + name: "empty string is not supported yet", + args: args{ + ubuReleaseVer: "", + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ubu := Ubuntu{} + if got := ubu.supported(tt.args.ubuReleaseVer); got != tt.want { + t.Errorf("Ubuntu.Supported() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/models/cvecontents.go b/models/cvecontents.go index 983aa8b3..b8081fd5 100644 --- a/models/cvecontents.go +++ b/models/cvecontents.go @@ -282,6 +282,9 @@ const ( // Ubuntu is Ubuntu Ubuntu CveContentType = "ubuntu" + // UbuntuAPI is Ubuntu + UbuntuAPI CveContentType = "ubuntu_api" + // Oracle is Oracle Linux Oracle CveContentType = "oracle" diff --git a/models/vulninfos.go b/models/vulninfos.go index 4361ded4..4449803a 100644 --- a/models/vulninfos.go +++ b/models/vulninfos.go @@ -826,6 +826,9 @@ const ( // DebianSecurityTrackerMatchStr is a String representation of DebianSecurityTrackerMatch DebianSecurityTrackerMatchStr = "DebianSecurityTrackerMatch" + // UbuntuAPIMatchStr is a String representation of UbuntuAPIMatch + UbuntuAPIMatchStr = "UbuntuAPIMatch" + // TrivyMatchStr is a String representation of Trivy TrivyMatchStr = "TrivyMatch" @@ -867,6 +870,9 @@ var ( // DebianSecurityTrackerMatch ranking how confident the CVE-ID was detected correctly DebianSecurityTrackerMatch = Confidence{100, DebianSecurityTrackerMatchStr, 0} + // UbuntuAPIMatch ranking how confident the CVE-ID was detected correctly + UbuntuAPIMatch = Confidence{100, UbuntuAPIMatchStr, 0} + // TrivyMatch ranking how confident the CVE-ID was detected correctly TrivyMatch = Confidence{100, TrivyMatchStr, 0}