Refactoring
This commit is contained in:
297
models/scanresults.go
Normal file
297
models/scanresults.go
Normal file
@@ -0,0 +1,297 @@
|
||||
/* Vuls - Vulnerability Scanner
|
||||
Copyright (C) 2016 Future Architect, Inc. Japan.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
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 (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
cvedict "github.com/kotakanbe/go-cve-dictionary/models"
|
||||
)
|
||||
|
||||
// ScanResults is slice of ScanResult.
|
||||
type ScanResults []ScanResult
|
||||
|
||||
//TODO
|
||||
// // Len implement Sort Interface
|
||||
// func (s ScanResults) Len() int {
|
||||
// return len(s)
|
||||
// }
|
||||
|
||||
// // Swap implement Sort Interface
|
||||
// func (s ScanResults) Swap(i, j int) {
|
||||
// s[i], s[j] = s[j], s[i]
|
||||
// }
|
||||
|
||||
// // Less implement Sort Interface
|
||||
// func (s ScanResults) Less(i, j int) bool {
|
||||
// if s[i].ServerName == s[j].ServerName {
|
||||
// return s[i].Container.ContainerID < s[i].Container.ContainerID
|
||||
// }
|
||||
// return s[i].ServerName < s[j].ServerName
|
||||
// }
|
||||
|
||||
// ScanResult has the result of scanned CVE information.
|
||||
type ScanResult struct {
|
||||
ScannedAt time.Time
|
||||
JSONVersion string
|
||||
Lang string
|
||||
ServerName string // TOML Section key
|
||||
Family string
|
||||
Release string
|
||||
Container Container
|
||||
Platform Platform
|
||||
|
||||
// Scanned Vulns by SSH scan + CPE + OVAL
|
||||
ScannedCves VulnInfos
|
||||
|
||||
Packages Packages
|
||||
Errors []string
|
||||
Optional [][]interface{}
|
||||
}
|
||||
|
||||
// ConvertNvdToModel convert NVD to CveContent
|
||||
func (r ScanResult) ConvertNvdToModel(cveID string, nvd cvedict.Nvd) *CveContent {
|
||||
var cpes []Cpe
|
||||
for _, c := range nvd.Cpes {
|
||||
cpes = append(cpes, Cpe{CpeName: c.CpeName})
|
||||
}
|
||||
|
||||
var refs []Reference
|
||||
for _, r := range nvd.References {
|
||||
refs = append(refs, Reference{
|
||||
Link: r.Link,
|
||||
Source: r.Source,
|
||||
})
|
||||
}
|
||||
|
||||
validVec := true
|
||||
for _, v := range []string{
|
||||
nvd.AccessVector,
|
||||
nvd.AccessComplexity,
|
||||
nvd.Authentication,
|
||||
nvd.ConfidentialityImpact,
|
||||
nvd.IntegrityImpact,
|
||||
nvd.AvailabilityImpact,
|
||||
} {
|
||||
if len(v) == 0 {
|
||||
validVec = false
|
||||
}
|
||||
}
|
||||
|
||||
vector := ""
|
||||
if validVec {
|
||||
vector = fmt.Sprintf("AV:%s/AC:%s/Au:%s/C:%s/I:%s/A:%s",
|
||||
string(nvd.AccessVector[0]),
|
||||
string(nvd.AccessComplexity[0]),
|
||||
string(nvd.Authentication[0]),
|
||||
string(nvd.ConfidentialityImpact[0]),
|
||||
string(nvd.IntegrityImpact[0]),
|
||||
string(nvd.AvailabilityImpact[0]))
|
||||
}
|
||||
|
||||
//TODO CVSSv3
|
||||
return &CveContent{
|
||||
Type: NVD,
|
||||
CveID: cveID,
|
||||
Summary: nvd.Summary,
|
||||
Cvss2Score: nvd.Score,
|
||||
Cvss2Vector: vector,
|
||||
Severity: "", // severity is not contained in NVD
|
||||
SourceLink: "https://nvd.nist.gov/vuln/detail/" + cveID,
|
||||
Cpes: cpes,
|
||||
CweID: nvd.CweID,
|
||||
References: refs,
|
||||
Published: nvd.PublishedDate,
|
||||
LastModified: nvd.LastModifiedDate,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertJvnToModel convert JVN to CveContent
|
||||
func (r ScanResult) ConvertJvnToModel(cveID string, jvn cvedict.Jvn) *CveContent {
|
||||
var cpes []Cpe
|
||||
for _, c := range jvn.Cpes {
|
||||
cpes = append(cpes, Cpe{CpeName: c.CpeName})
|
||||
}
|
||||
|
||||
refs := []Reference{}
|
||||
for _, r := range jvn.References {
|
||||
refs = append(refs, Reference{
|
||||
Link: r.Link,
|
||||
Source: r.Source,
|
||||
})
|
||||
}
|
||||
|
||||
vector := strings.TrimSuffix(strings.TrimPrefix(jvn.Vector, "("), ")")
|
||||
return &CveContent{
|
||||
Type: JVN,
|
||||
CveID: cveID,
|
||||
Title: jvn.Title,
|
||||
Summary: jvn.Summary,
|
||||
Severity: jvn.Severity,
|
||||
Cvss2Score: jvn.Score,
|
||||
Cvss2Vector: vector,
|
||||
SourceLink: jvn.JvnLink,
|
||||
Cpes: cpes,
|
||||
References: refs,
|
||||
Published: jvn.PublishedDate,
|
||||
LastModified: jvn.LastModifiedDate,
|
||||
}
|
||||
}
|
||||
|
||||
// FilterByCvssOver is filter function.
|
||||
func (r ScanResult) FilterByCvssOver(over float64) ScanResult {
|
||||
// TODO: Set correct default value
|
||||
if over == 0 {
|
||||
over = -1.1
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
copiedScanResult := r
|
||||
copiedScanResult.ScannedCves = filtered
|
||||
return copiedScanResult
|
||||
}
|
||||
|
||||
// ReportFileName returns the filename on localhost without extention
|
||||
func (r ScanResult) ReportFileName() (name string) {
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
return fmt.Sprintf("%s", r.ServerName)
|
||||
}
|
||||
return fmt.Sprintf("%s@%s", r.Container.Name, r.ServerName)
|
||||
}
|
||||
|
||||
// ReportKeyName returns the name of key on S3, Azure-Blob without extention
|
||||
func (r ScanResult) ReportKeyName() (name string) {
|
||||
timestr := r.ScannedAt.Format(time.RFC3339)
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
return fmt.Sprintf("%s/%s", timestr, r.ServerName)
|
||||
}
|
||||
return fmt.Sprintf("%s/%s@%s", timestr, r.Container.Name, r.ServerName)
|
||||
}
|
||||
|
||||
// ServerInfo returns server name one line
|
||||
func (r ScanResult) ServerInfo() string {
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
return fmt.Sprintf("%s (%s%s)",
|
||||
r.ServerName, r.Family, r.Release)
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"%s / %s (%s%s) on %s",
|
||||
r.Container.Name,
|
||||
r.Container.ContainerID,
|
||||
r.Family,
|
||||
r.Release,
|
||||
r.ServerName,
|
||||
)
|
||||
}
|
||||
|
||||
// ServerInfoTui returns server infromation for TUI sidebar
|
||||
func (r ScanResult) ServerInfoTui() string {
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
return fmt.Sprintf("%s (%s%s)",
|
||||
r.ServerName, r.Family, r.Release)
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"|-- %s (%s%s)",
|
||||
r.Container.Name,
|
||||
r.Family,
|
||||
r.Release,
|
||||
// r.Container.ContainerID,
|
||||
)
|
||||
}
|
||||
|
||||
// FormatServerName returns server and container name
|
||||
func (r ScanResult) FormatServerName() string {
|
||||
if len(r.Container.ContainerID) == 0 {
|
||||
return r.ServerName
|
||||
}
|
||||
return fmt.Sprintf("%s@%s",
|
||||
r.Container.Name, r.ServerName)
|
||||
}
|
||||
|
||||
// CveSummary summarize the number of CVEs group by CVSSv2 Severity
|
||||
func (r ScanResult) CveSummary(ignoreUnscoreCves bool) string {
|
||||
var high, medium, low, unknown int
|
||||
for _, vInfo := range r.ScannedCves {
|
||||
score := vInfo.CveContents.MaxCvss2Score().Value.Score
|
||||
if score < 0.1 {
|
||||
score = vInfo.CveContents.MaxCvss3Score().Value.Score
|
||||
}
|
||||
switch {
|
||||
case 7.0 <= score:
|
||||
high++
|
||||
case 4.0 <= score:
|
||||
medium++
|
||||
case 0 < score:
|
||||
low++
|
||||
default:
|
||||
unknown++
|
||||
}
|
||||
}
|
||||
|
||||
if ignoreUnscoreCves {
|
||||
return fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d)",
|
||||
high+medium+low, high, medium, low)
|
||||
}
|
||||
return fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d ?:%d)",
|
||||
high+medium+low+unknown, high, medium, low, unknown)
|
||||
}
|
||||
|
||||
// FormatTextReportHeadedr returns header of text report
|
||||
func (r ScanResult) FormatTextReportHeadedr() string {
|
||||
serverInfo := r.ServerInfo()
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(serverInfo); i++ {
|
||||
buf.WriteString("=")
|
||||
}
|
||||
return fmt.Sprintf("%s\n%s\n%s\t%s\n",
|
||||
r.ServerInfo(),
|
||||
buf.String(),
|
||||
r.CveSummary(config.Conf.IgnoreUnscoredCves),
|
||||
r.Packages.FormatUpdatablePacksSummary(),
|
||||
)
|
||||
}
|
||||
|
||||
// Container has Container information
|
||||
type Container struct {
|
||||
ContainerID string
|
||||
Name string
|
||||
Image string
|
||||
Type string
|
||||
}
|
||||
|
||||
// Platform has platform information
|
||||
type Platform struct {
|
||||
Name string // aws or azure or gcp or other...
|
||||
InstanceID string
|
||||
}
|
||||
Reference in New Issue
Block a user