Implement format-short-text
This commit is contained in:
28
Gopkg.lock
generated
28
Gopkg.lock
generated
@@ -1,10 +1,10 @@
|
||||
memo = "bd95ed8c2b0aa32327ae55d88bff888b8198d238f7a71eee0f8663494664a0ac"
|
||||
memo = "e59ec63c1c329674a0e5e4236131c787e5b81bab37529104fdc02ed8fdf29283"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/Azure/azure-storage-go"
|
||||
packages = ["."]
|
||||
revision = "4fe73b0b4f68bf8a7cad2920ef563fe4c40ac5c0"
|
||||
revision = "32cfbe17a139c17f84be16bdf8f9c45c840a046b"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/Azure/go-autorest"
|
||||
@@ -22,7 +22,7 @@ memo = "bd95ed8c2b0aa32327ae55d88bff888b8198d238f7a71eee0f8663494664a0ac"
|
||||
branch = "master"
|
||||
name = "github.com/Sirupsen/logrus"
|
||||
packages = ["."]
|
||||
revision = "508f304878257fb578be3e863e3990ed9ec3aa2e"
|
||||
revision = "acfabf31db8f45a9174f54a0d48ea4d15627af4d"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/asaskevich/govalidator"
|
||||
@@ -50,8 +50,8 @@ memo = "bd95ed8c2b0aa32327ae55d88bff888b8198d238f7a71eee0f8663494664a0ac"
|
||||
[[projects]]
|
||||
name = "github.com/cheggaaa/pb"
|
||||
packages = ["."]
|
||||
revision = "b6229822fa186496fcbf34111237e7a9693c6971"
|
||||
version = "v1.0.13"
|
||||
revision = "f6ccf2184de4dd34495277e38dc19b6e7fbe0ea2"
|
||||
version = "v1.0.15"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/dgrijalva/jwt-go"
|
||||
@@ -123,7 +123,7 @@ memo = "bd95ed8c2b0aa32327ae55d88bff888b8198d238f7a71eee0f8663494664a0ac"
|
||||
branch = "master"
|
||||
name = "github.com/knqyf263/go-deb-version"
|
||||
packages = ["."]
|
||||
revision = "bec774d791d03b721a20bd3ca1fbdd566fd0f2b9"
|
||||
revision = "9865fe14d09b1c729188ac810466dde90f897ee3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -138,10 +138,10 @@ memo = "bd95ed8c2b0aa32327ae55d88bff888b8198d238f7a71eee0f8663494664a0ac"
|
||||
version = "v0.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
branch = "improve-db"
|
||||
name = "github.com/kotakanbe/goval-dictionary"
|
||||
packages = ["config","db","log","models"]
|
||||
revision = "545199055508ae62a6d3bd34ef83034fbfc04d7f"
|
||||
revision = "5f7aa97d45d565eaccc70c0c365e21624a9c6e3f"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -158,8 +158,8 @@ memo = "bd95ed8c2b0aa32327ae55d88bff888b8198d238f7a71eee0f8663494664a0ac"
|
||||
[[projects]]
|
||||
name = "github.com/labstack/gommon"
|
||||
packages = ["color","log"]
|
||||
revision = "9cedb429ffbe71a32a3ae7c65fd109cb7ae07804"
|
||||
version = "v0.2.0"
|
||||
revision = "1121fd3e243c202482226a7afe4dcd07ffc4139a"
|
||||
version = "v0.2.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-colorable"
|
||||
@@ -249,22 +249,22 @@ memo = "bd95ed8c2b0aa32327ae55d88bff888b8198d238f7a71eee0f8663494664a0ac"
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["curve25519","ed25519","ed25519/internal/edwards25519","ssh","ssh/agent","ssh/terminal"]
|
||||
revision = "04eae0b62feaaf659a0ce2c4e8dc70b6ae2fff67"
|
||||
revision = "ab89591268e0c8b748cbe4047b00197516011af5"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = ["context","idna","publicsuffix"]
|
||||
revision = "feeb485667d1fdabe727840fe00adc22431bc86e"
|
||||
revision = "84f0e6f92b10139f986b1756e149a7d9de270cdc"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "9ccfe848b9db8435a24c424abbc07a921adf1df5"
|
||||
revision = "1e99a4f9d247b28c670884b9a8d6801f39a47b77"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/text"
|
||||
packages = ["internal/gen","internal/triegen","internal/ucd","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
|
||||
revision = "470f45bf29f4147d6fbd7dfd0a02a848e49f5bf4"
|
||||
revision = "19e51611da83d6be54ddafce4a4af510cb3e9ea4"
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
name = "github.com/kotakanbe/go-cve-dictionary"
|
||||
|
||||
[[dependencies]]
|
||||
branch = "master"
|
||||
branch = "improve-db"
|
||||
name = "github.com/kotakanbe/goval-dictionary"
|
||||
|
||||
[[dependencies]]
|
||||
|
||||
@@ -463,7 +463,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
|
||||
|
||||
var res models.ScanResults
|
||||
for _, r := range results {
|
||||
res = append(res, r.FilterByCvssOver())
|
||||
res = append(res, r.FilterByCvssOver(c.Conf.CvssScoreOver))
|
||||
|
||||
// TODO Add sort function to ScanResults
|
||||
|
||||
@@ -545,10 +545,14 @@ func fillCveInfoFromCveDB(r *models.ScanResult) error {
|
||||
func fillCveInfoFromOvalDB(r *models.ScanResult) error {
|
||||
var ovalClient oval.Client
|
||||
switch r.Family {
|
||||
case "ubuntu", "debian":
|
||||
case "debian":
|
||||
ovalClient = oval.NewDebian()
|
||||
case "rhel", "centos":
|
||||
case "ubuntu":
|
||||
ovalClient = oval.NewUbuntu()
|
||||
case "rhel":
|
||||
ovalClient = oval.NewRedhat()
|
||||
case "centos":
|
||||
ovalClient = oval.NewCentOS()
|
||||
case "amazon", "oraclelinux", "Raspbian", "FreeBSD":
|
||||
//TODO implement OracleLinux
|
||||
return nil
|
||||
|
||||
517
models/models.go
517
models/models.go
@@ -22,7 +22,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
cvedict "github.com/kotakanbe/go-cve-dictionary/models"
|
||||
)
|
||||
|
||||
@@ -117,6 +116,8 @@ func (r ScanResult) ConvertNvdToModel(cveID string, nvd cvedict.Nvd) *CveContent
|
||||
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,
|
||||
@@ -132,10 +133,7 @@ func (r ScanResult) ConvertJvnToModel(cveID string, jvn cvedict.Jvn) *CveContent
|
||||
cpes = append(cpes, Cpe{CpeName: c.CpeName})
|
||||
}
|
||||
|
||||
refs := []Reference{{
|
||||
Link: jvn.JvnLink,
|
||||
Source: string(JVN),
|
||||
}}
|
||||
refs := []Reference{}
|
||||
for _, r := range jvn.References {
|
||||
refs = append(refs, Reference{
|
||||
Link: r.Link,
|
||||
@@ -152,6 +150,7 @@ func (r ScanResult) ConvertJvnToModel(cveID string, jvn cvedict.Jvn) *CveContent
|
||||
Severity: jvn.Severity,
|
||||
Cvss2Score: jvn.Score,
|
||||
Cvss2Vector: vector,
|
||||
SourceLink: jvn.JvnLink,
|
||||
Cpes: cpes,
|
||||
References: refs,
|
||||
Published: jvn.PublishedDate,
|
||||
@@ -160,15 +159,22 @@ func (r ScanResult) ConvertJvnToModel(cveID string, jvn cvedict.Jvn) *CveContent
|
||||
}
|
||||
|
||||
// FilterByCvssOver is filter function.
|
||||
func (r ScanResult) FilterByCvssOver() ScanResult {
|
||||
func (r ScanResult) FilterByCvssOver(over float64) ScanResult {
|
||||
// TODO: Set correct default value
|
||||
if config.Conf.CvssScoreOver == 0 {
|
||||
config.Conf.CvssScoreOver = -1.1
|
||||
if over == 0 {
|
||||
over = -1.1
|
||||
}
|
||||
|
||||
// TODO: Filter by ignore cves???
|
||||
filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
|
||||
return config.Conf.CvssScoreOver <= v.CveContents.CvssV2Score()
|
||||
values := v.CveContents.Cvss2Scores()
|
||||
for _, v := range values {
|
||||
score := v.Value.Score
|
||||
if over <= score {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
copiedScanResult := r
|
||||
@@ -234,12 +240,12 @@ func (r ScanResult) FormatServerName() string {
|
||||
}
|
||||
|
||||
// CveSummary summarize the number of CVEs group by CVSSv2 Severity
|
||||
func (r ScanResult) CveSummary() string {
|
||||
func (r ScanResult) CveSummary(ignoreUnscoreCves bool) string {
|
||||
var high, medium, low, unknown int
|
||||
for _, vInfo := range r.ScannedCves {
|
||||
score := vInfo.CveContents.CvssV2Score()
|
||||
score := vInfo.CveContents.MaxCvss2Score().Value.Score
|
||||
if score < 0.1 {
|
||||
score = vInfo.CveContents.CvssV3Score()
|
||||
score = vInfo.CveContents.MaxCvss3Score().Value.Score
|
||||
}
|
||||
switch {
|
||||
case 7.0 <= score:
|
||||
@@ -253,7 +259,7 @@ func (r ScanResult) CveSummary() string {
|
||||
}
|
||||
}
|
||||
|
||||
if config.Conf.IgnoreUnscoredCves {
|
||||
if ignoreUnscoreCves {
|
||||
return fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d)",
|
||||
high+medium+low, high, medium, low)
|
||||
}
|
||||
@@ -298,23 +304,25 @@ const (
|
||||
FailedToFindVersionInChangelog = "FailedToFindVersionInChangelog"
|
||||
)
|
||||
|
||||
// CpeNameMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
var CpeNameMatch = Confidence{100, CpeNameMatchStr}
|
||||
var (
|
||||
// CpeNameMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
CpeNameMatch = Confidence{100, CpeNameMatchStr}
|
||||
|
||||
// YumUpdateSecurityMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
var YumUpdateSecurityMatch = Confidence{100, YumUpdateSecurityMatchStr}
|
||||
// YumUpdateSecurityMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
YumUpdateSecurityMatch = Confidence{100, YumUpdateSecurityMatchStr}
|
||||
|
||||
// PkgAuditMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
var PkgAuditMatch = Confidence{100, PkgAuditMatchStr}
|
||||
// PkgAuditMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
PkgAuditMatch = Confidence{100, PkgAuditMatchStr}
|
||||
|
||||
// OvalMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
var OvalMatch = Confidence{100, OvalMatchStr}
|
||||
// OvalMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
OvalMatch = Confidence{100, OvalMatchStr}
|
||||
|
||||
// ChangelogExactMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
var ChangelogExactMatch = Confidence{95, ChangelogExactMatchStr}
|
||||
// ChangelogExactMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
ChangelogExactMatch = Confidence{95, ChangelogExactMatchStr}
|
||||
|
||||
// ChangelogLenientMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
var ChangelogLenientMatch = Confidence{50, ChangelogLenientMatchStr}
|
||||
// ChangelogLenientMatch is a ranking how confident the CVE-ID was deteted correctly
|
||||
ChangelogLenientMatch = Confidence{50, ChangelogLenientMatchStr}
|
||||
)
|
||||
|
||||
// VulnInfos is VulnInfo list, getter/setter, sortable methods.
|
||||
type VulnInfos map[string]VulnInfo
|
||||
@@ -340,22 +348,33 @@ type VulnInfo struct {
|
||||
CveContents CveContents
|
||||
}
|
||||
|
||||
// NilToEmpty set nil slice or map fields to empty to avoid null in JSON
|
||||
func (v *VulnInfo) NilToEmpty() {
|
||||
if v.CpeNames == nil {
|
||||
v.CpeNames = []string{}
|
||||
}
|
||||
if v.DistroAdvisories == nil {
|
||||
v.DistroAdvisories = []DistroAdvisory{}
|
||||
}
|
||||
if v.PackageNames == nil {
|
||||
v.PackageNames = []string{}
|
||||
}
|
||||
if v.CveContents == nil {
|
||||
v.CveContents = NewCveContents()
|
||||
}
|
||||
// Cvss2CalcURL returns CVSS v2 caluclator's URL
|
||||
func (v VulnInfo) Cvss2CalcURL() string {
|
||||
return "https://nvd.nist.gov/vuln-metrics/cvss/v2-calculator?name=" + v.CveID
|
||||
}
|
||||
|
||||
// Cvss3CalcURL returns CVSS v3 caluclator's URL
|
||||
func (v VulnInfo) Cvss3CalcURL() string {
|
||||
return "https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?name=" + v.CveID
|
||||
}
|
||||
|
||||
// TODO
|
||||
// NilToEmpty set nil slice or map fields to empty to avoid null in JSON
|
||||
// func (v *VulnInfo) NilToEmpty() {
|
||||
// if v.CpeNames == nil {
|
||||
// v.CpeNames = []string{}
|
||||
// }
|
||||
// if v.DistroAdvisories == nil {
|
||||
// v.DistroAdvisories = []DistroAdvisory{}
|
||||
// }
|
||||
// if v.PackageNames == nil {
|
||||
// v.PackageNames = []string{}
|
||||
// }
|
||||
// if v.CveContents == nil {
|
||||
// v.CveContents = NewCveContents()
|
||||
// }
|
||||
// }
|
||||
|
||||
// CveContentType is a source of CVE information
|
||||
type CveContentType string
|
||||
|
||||
@@ -387,9 +406,6 @@ const (
|
||||
// RedHat is RedHat
|
||||
RedHat CveContentType = "redhat"
|
||||
|
||||
// CentOS is CentOS
|
||||
CentOS CveContentType = "centos"
|
||||
|
||||
// Debian is Debian
|
||||
Debian CveContentType = "debian"
|
||||
|
||||
@@ -400,6 +416,29 @@ const (
|
||||
Unknown CveContentType = "unknown"
|
||||
)
|
||||
|
||||
// CveContentTypes has slide of CveContentType
|
||||
type CveContentTypes []CveContentType
|
||||
|
||||
// AllCveContetTypes has all of CveContentTypes
|
||||
var AllCveContetTypes = CveContentTypes{NVD, JVN, RedHat, Debian, Ubuntu}
|
||||
|
||||
// Except returns CveContentTypes except for given args
|
||||
func (c CveContentTypes) Except(excepts ...CveContentType) (excepted CveContentTypes) {
|
||||
for _, ctype := range c {
|
||||
found := false
|
||||
for _, except := range excepts {
|
||||
if ctype == except {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
excepted = append(excepted, ctype)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CveContents has CveContent
|
||||
type CveContents map[CveContentType]CveContent
|
||||
|
||||
@@ -412,25 +451,339 @@ func NewCveContents(conts ...CveContent) CveContents {
|
||||
return m
|
||||
}
|
||||
|
||||
// CvssV2Score returns CVSS V2 Score
|
||||
func (v CveContents) CvssV2Score() float64 {
|
||||
//TODO
|
||||
if cont, found := v[NVD]; found {
|
||||
return cont.Cvss2Score
|
||||
} else if cont, found := v[JVN]; found {
|
||||
return cont.Cvss2Score
|
||||
} else if cont, found := v[RedHat]; found {
|
||||
return cont.Cvss2Score
|
||||
}
|
||||
return -1.1
|
||||
// CveContentStr has CveContentType and Value
|
||||
type CveContentStr struct {
|
||||
Type CveContentType
|
||||
Value string
|
||||
}
|
||||
|
||||
// CvssV3Score returns CVSS V2 Score
|
||||
func (v CveContents) CvssV3Score() float64 {
|
||||
if cont, found := v[RedHat]; found {
|
||||
return cont.Cvss3Score
|
||||
// Except returns CveContents except given keys for enumeration
|
||||
func (v CveContents) Except(exceptCtypes ...CveContentType) (values CveContents) {
|
||||
for ctype, content := range v {
|
||||
found := false
|
||||
for _, exceptCtype := range exceptCtypes {
|
||||
if ctype == exceptCtype {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
values[ctype] = content
|
||||
}
|
||||
}
|
||||
return -1.1
|
||||
return
|
||||
}
|
||||
|
||||
// CveContentCvss2 has CveContentType and Cvss2
|
||||
type CveContentCvss2 struct {
|
||||
Type CveContentType
|
||||
Value Cvss2
|
||||
}
|
||||
|
||||
// Cvss2 has CVSS v2
|
||||
type Cvss2 struct {
|
||||
Score float64
|
||||
Vector string
|
||||
Severity string
|
||||
}
|
||||
|
||||
func cvss2ScoreToSeverity(score float64) string {
|
||||
if 7.0 <= score {
|
||||
return "HIGH"
|
||||
} else if 4.0 <= score {
|
||||
return "MEDIUM"
|
||||
}
|
||||
return "LOW"
|
||||
}
|
||||
|
||||
// Cvss2Scores returns CVSS V2 Scores
|
||||
func (v CveContents) Cvss2Scores() (values []CveContentCvss2) {
|
||||
order := []CveContentType{NVD, RedHat, JVN}
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found && 0 < cont.Cvss2Score {
|
||||
// https://nvd.nist.gov/vuln-metrics/cvss
|
||||
sev := cont.Severity
|
||||
if ctype == NVD {
|
||||
sev = cvss2ScoreToSeverity(cont.Cvss2Score)
|
||||
}
|
||||
values = append(values, CveContentCvss2{
|
||||
Type: ctype,
|
||||
Value: Cvss2{
|
||||
Score: cont.Cvss2Score,
|
||||
Vector: cont.Cvss2Vector,
|
||||
Severity: sev,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MaxCvss2Score returns Max CVSS V2 Score
|
||||
func (v CveContents) MaxCvss2Score() CveContentCvss2 {
|
||||
//TODO Severity Ubuntu, Debian...
|
||||
order := []CveContentType{NVD, RedHat, JVN}
|
||||
max := 0.0
|
||||
value := CveContentCvss2{
|
||||
Type: Unknown,
|
||||
Value: Cvss2{},
|
||||
}
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found && max < cont.Cvss2Score {
|
||||
// https://nvd.nist.gov/vuln-metrics/cvss
|
||||
sev := cont.Severity
|
||||
if ctype == NVD {
|
||||
sev = cvss2ScoreToSeverity(cont.Cvss2Score)
|
||||
}
|
||||
value = CveContentCvss2{
|
||||
Type: ctype,
|
||||
Value: Cvss2{
|
||||
Score: cont.Cvss2Score,
|
||||
Vector: cont.Cvss2Vector,
|
||||
Severity: sev,
|
||||
},
|
||||
}
|
||||
max = cont.Cvss2Score
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// CveContentCvss3 has CveContentType and Cvss3
|
||||
type CveContentCvss3 struct {
|
||||
Type CveContentType
|
||||
Value Cvss3
|
||||
}
|
||||
|
||||
// Cvss3 has CVSS v3
|
||||
type Cvss3 struct {
|
||||
Score float64
|
||||
Vector string
|
||||
Severity string
|
||||
}
|
||||
|
||||
func cvss3ScoreToSeverity(score float64) string {
|
||||
if 9.0 <= score {
|
||||
return "CRITICAL"
|
||||
} else if 7.0 <= score {
|
||||
return "HIGH"
|
||||
} else if 4.0 <= score {
|
||||
return "MEDIUM"
|
||||
}
|
||||
return "LOW"
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// https://nvd.nist.gov/vuln-metrics/cvss
|
||||
sev := cont.Severity
|
||||
if ctype == NVD {
|
||||
sev = cvss3ScoreToSeverity(cont.Cvss2Score)
|
||||
}
|
||||
values = append(values, CveContentCvss3{
|
||||
Type: ctype,
|
||||
Value: Cvss3{
|
||||
Score: cont.Cvss3Score,
|
||||
Vector: cont.Cvss3Vector,
|
||||
Severity: sev,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MaxCvss3Score returns Max CVSS V3 Score
|
||||
func (v CveContents) MaxCvss3Score() CveContentCvss3 {
|
||||
//TODO Severity Ubuntu, Debian...
|
||||
order := []CveContentType{RedHat}
|
||||
max := 0.0
|
||||
value := CveContentCvss3{
|
||||
Type: Unknown,
|
||||
Value: Cvss3{},
|
||||
}
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found && max < cont.Cvss3Score {
|
||||
// https://nvd.nist.gov/vuln-metrics/cvss
|
||||
sev := cont.Severity
|
||||
if ctype == NVD {
|
||||
sev = cvss3ScoreToSeverity(cont.Cvss2Score)
|
||||
}
|
||||
value = CveContentCvss3{
|
||||
Type: ctype,
|
||||
Value: Cvss3{
|
||||
Score: cont.Cvss3Score,
|
||||
Vector: cont.Cvss3Vector,
|
||||
Severity: sev,
|
||||
},
|
||||
}
|
||||
max = cont.Cvss3Score
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// Titles returns tilte (TUI)
|
||||
func (v CveContents) Titles(lang, myFamily string) (values []CveContentStr) {
|
||||
if lang == "ja" {
|
||||
if cont, found := v[JVN]; found && 0 < len(cont.Title) {
|
||||
values = append(values, CveContentStr{JVN, cont.Title})
|
||||
}
|
||||
}
|
||||
|
||||
order := CveContentTypes{NVD, NewCveContentType(myFamily)}
|
||||
order = append(order, AllCveContetTypes.Except(append(order, JVN)...)...)
|
||||
for _, ctype := range order {
|
||||
// Only JVN has meaningful title. so return first 100 char of summary
|
||||
if cont, found := v[ctype]; found && 0 < len(cont.Summary) {
|
||||
summary := strings.Replace(cont.Summary, "\n", " ", -1)
|
||||
index := 75
|
||||
if len(summary) < index {
|
||||
index = len(summary)
|
||||
}
|
||||
values = append(values, CveContentStr{
|
||||
Type: ctype,
|
||||
Value: summary[0:index] + "...",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if len(values) == 0 {
|
||||
values = []CveContentStr{{
|
||||
Type: Unknown,
|
||||
Value: "-",
|
||||
}}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Summaries returns summaries
|
||||
func (v CveContents) Summaries(lang, myFamily string) (values []CveContentStr) {
|
||||
if lang == "ja" {
|
||||
if cont, found := v[JVN]; found && 0 < len(cont.Summary) {
|
||||
summary := cont.Title
|
||||
summary += "\n" + strings.Replace(
|
||||
strings.Replace(cont.Summary, "\n", " ", -1), "\r", " ", -1)
|
||||
values = append(values, CveContentStr{JVN, summary})
|
||||
}
|
||||
}
|
||||
|
||||
order := CveContentTypes{NVD, NewCveContentType(myFamily)}
|
||||
order = append(order, AllCveContetTypes.Except(append(order, JVN)...)...)
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found && 0 < len(cont.Summary) {
|
||||
summary := strings.Replace(cont.Summary, "\n", " ", -1)
|
||||
values = append(values, CveContentStr{
|
||||
Type: ctype,
|
||||
Value: summary,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if len(values) == 0 {
|
||||
values = []CveContentStr{{
|
||||
Type: Unknown,
|
||||
Value: "-",
|
||||
}}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SourceLinks returns link of source
|
||||
func (v CveContents) SourceLinks(lang, myFamily string) (values []CveContentStr) {
|
||||
if lang == "ja" {
|
||||
if cont, found := v[JVN]; found && !cont.Empty() {
|
||||
values = append(values, CveContentStr{JVN, cont.SourceLink})
|
||||
}
|
||||
}
|
||||
|
||||
order := CveContentTypes{NVD, NewCveContentType(myFamily)}
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found {
|
||||
values = append(values, CveContentStr{ctype, cont.SourceLink})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Severities returns Severities
|
||||
// func (v CveContents) Severities(myFamily string) (values []CveContentValue) {
|
||||
// order := CveContentTypes{NVD, NewCveContentType(myFamily)}
|
||||
// order = append(order, AllCveContetTypes.Except(append(order)...)...)
|
||||
|
||||
// for _, ctype := range order {
|
||||
// if cont, found := v[ctype]; found && 0 < len(cont.Severity) {
|
||||
// values = append(values, CveContentValue{
|
||||
// Type: ctype,
|
||||
// Value: cont.Severity,
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
// CveContentCpes has CveContentType and Value
|
||||
type CveContentCpes struct {
|
||||
Type CveContentType
|
||||
Value []Cpe
|
||||
}
|
||||
|
||||
// Cpes returns affected CPEs of this Vulnerability
|
||||
func (v CveContents) Cpes(myFamily string) (values []CveContentCpes) {
|
||||
order := CveContentTypes{NewCveContentType(myFamily)}
|
||||
order = append(order, AllCveContetTypes.Except(append(order)...)...)
|
||||
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found && 0 < len(cont.Cpes) {
|
||||
values = append(values, CveContentCpes{
|
||||
Type: ctype,
|
||||
Value: cont.Cpes,
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CveContentRefs has CveContentType and Cpes
|
||||
type CveContentRefs struct {
|
||||
Type CveContentType
|
||||
Value []Reference
|
||||
}
|
||||
|
||||
// References returns References
|
||||
func (v CveContents) References(myFamily string) (values []CveContentRefs) {
|
||||
order := CveContentTypes{NewCveContentType(myFamily)}
|
||||
order = append(order, AllCveContetTypes.Except(append(order)...)...)
|
||||
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found && 0 < len(cont.References) {
|
||||
values = append(values, CveContentRefs{
|
||||
Type: ctype,
|
||||
Value: cont.References,
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CweIDs returns CweIDs
|
||||
func (v CveContents) CweIDs(myFamily string) (values []CveContentStr) {
|
||||
order := CveContentTypes{NewCveContentType(myFamily)}
|
||||
order = append(order, AllCveContetTypes.Except(append(order)...)...)
|
||||
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found && 0 < len(cont.CweID) {
|
||||
values = append(values, CveContentStr{
|
||||
Type: ctype,
|
||||
Value: cont.CweID,
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CveContent has abstraction of various vulnerability information
|
||||
@@ -444,8 +797,9 @@ type CveContent struct {
|
||||
Cvss2Vector string
|
||||
Cvss3Score float64
|
||||
Cvss3Vector string
|
||||
SourceLink string
|
||||
Cpes []Cpe
|
||||
References []Reference
|
||||
References References
|
||||
CweID string
|
||||
Published time.Time
|
||||
LastModified time.Time
|
||||
@@ -461,11 +815,22 @@ type Cpe struct {
|
||||
CpeName string
|
||||
}
|
||||
|
||||
// References is a slice of Reference
|
||||
type References []Reference
|
||||
|
||||
// Find elements that matches the function passed in argument
|
||||
func (r References) Find(f func(r Reference) bool) (refs []Reference) {
|
||||
for _, rr := range r {
|
||||
refs = append(refs, rr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Reference has a related link of the CVE
|
||||
type Reference struct {
|
||||
RefID string
|
||||
Source string
|
||||
Link string
|
||||
RefID string
|
||||
}
|
||||
|
||||
// Packages is Map of Package
|
||||
@@ -504,6 +869,15 @@ func (ps Packages) Merge(other Packages) Packages {
|
||||
return merged
|
||||
}
|
||||
|
||||
// FormatVersionsFromTo returns updatable packages
|
||||
func (ps Packages) FormatVersionsFromTo() string {
|
||||
ss := []string{}
|
||||
for _, pack := range ps {
|
||||
ss = append(ss, pack.FormatVersionFromTo())
|
||||
}
|
||||
return strings.Join(ss, "\n")
|
||||
}
|
||||
|
||||
// FormatUpdatablePacksSummary returns a summary of updatable packages
|
||||
func (ps Packages) FormatUpdatablePacksSummary() string {
|
||||
nUpdatable := 0
|
||||
@@ -527,15 +901,8 @@ type Package struct {
|
||||
NotFixedYet bool // Ubuntu OVAL Only
|
||||
}
|
||||
|
||||
// Changelog has contents of changelog and how to get it.
|
||||
// Method: modesl.detectionMethodStr
|
||||
type Changelog struct {
|
||||
Contents string
|
||||
Method string
|
||||
}
|
||||
|
||||
// FormatCurrentVer returns package name-version-release
|
||||
func (p Package) FormatCurrentVer() string {
|
||||
// FormatVer returns package name-version-release
|
||||
func (p Package) FormatVer() string {
|
||||
str := p.Name
|
||||
if 0 < len(p.Version) {
|
||||
str = fmt.Sprintf("%s-%s", str, p.Version)
|
||||
@@ -558,6 +925,18 @@ func (p Package) FormatNewVer() string {
|
||||
return str
|
||||
}
|
||||
|
||||
// FormatVersionFromTo formats installed and new package version
|
||||
func (p Package) FormatVersionFromTo() string {
|
||||
return fmt.Sprintf("%s -> %s", p.FormatVer(), p.FormatNewVer())
|
||||
}
|
||||
|
||||
// Changelog has contents of changelog and how to get it.
|
||||
// Method: modesl.detectionMethodStr
|
||||
type Changelog struct {
|
||||
Contents string
|
||||
Method string
|
||||
}
|
||||
|
||||
// DistroAdvisory has Amazon Linux, RHEL, FreeBSD Security Advisory information.
|
||||
type DistroAdvisory struct {
|
||||
AdvisoryID string
|
||||
|
||||
@@ -12,34 +12,23 @@ import (
|
||||
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
|
||||
)
|
||||
|
||||
// Debian is the interface for Debian OVAL
|
||||
type Debian struct{}
|
||||
// DebianBase is the base struct of Debian and Ubuntu
|
||||
type DebianBase struct{}
|
||||
|
||||
// NewDebian creates OVAL client for Debian
|
||||
func NewDebian() Debian {
|
||||
return Debian{}
|
||||
}
|
||||
|
||||
// FillCveInfoFromOvalDB returns scan result after updating CVE info by OVAL
|
||||
func (o Debian) FillCveInfoFromOvalDB(r *models.ScanResult) error {
|
||||
// fillCveInfoFromOvalDB returns scan result after updating CVE info by OVAL
|
||||
func (o DebianBase) fillCveInfoFromOvalDB(r *models.ScanResult) error {
|
||||
ovalconf.Conf.DBType = config.Conf.OvalDBType
|
||||
ovalconf.Conf.DBPath = config.Conf.OvalDBPath
|
||||
util.Log.Infof("open oval-dictionary db (%s): %s",
|
||||
config.Conf.OvalDBType, config.Conf.OvalDBPath)
|
||||
|
||||
if err := db.OpenDB(); err != nil {
|
||||
return fmt.Errorf("Failed to open OVAL DB. err: %s", err)
|
||||
ovaldb, err := db.NewDB(r.Family)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var d db.OvalDB
|
||||
switch r.Family {
|
||||
case "debian":
|
||||
d = db.NewDebian()
|
||||
case "ubuntu":
|
||||
d = db.NewUbuntu()
|
||||
}
|
||||
for _, pack := range r.Packages {
|
||||
definitions, err := d.GetByPackName(r.Release, pack.Name)
|
||||
definitions, err := ovaldb.GetByPackName(r.Release, pack.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to get Debian OVAL info by package name: %v", err)
|
||||
}
|
||||
@@ -59,7 +48,7 @@ func (o Debian) FillCveInfoFromOvalDB(r *models.ScanResult) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o Debian) fillOvalInfo(r *models.ScanResult, definition *ovalmodels.Definition) {
|
||||
func (o DebianBase) fillOvalInfo(r *models.ScanResult, definition *ovalmodels.Definition) {
|
||||
ovalContent := *o.convertToModel(definition)
|
||||
ovalContent.Type = models.NewCveContentType(r.Family)
|
||||
vinfo, ok := r.ScannedCves[definition.Debian.CveID]
|
||||
@@ -89,7 +78,7 @@ func (o Debian) fillOvalInfo(r *models.ScanResult, definition *ovalmodels.Defini
|
||||
r.ScannedCves[definition.Debian.CveID] = vinfo
|
||||
}
|
||||
|
||||
func (o Debian) convertToModel(def *ovalmodels.Definition) *models.CveContent {
|
||||
func (o DebianBase) convertToModel(def *ovalmodels.Definition) *models.CveContent {
|
||||
var refs []models.Reference
|
||||
for _, r := range def.References {
|
||||
refs = append(refs, models.Reference{
|
||||
@@ -98,6 +87,7 @@ func (o Debian) convertToModel(def *ovalmodels.Definition) *models.CveContent {
|
||||
RefID: r.RefID,
|
||||
})
|
||||
}
|
||||
|
||||
return &models.CveContent{
|
||||
CveID: def.Debian.CveID,
|
||||
Title: def.Title,
|
||||
@@ -106,3 +96,51 @@ func (o Debian) convertToModel(def *ovalmodels.Definition) *models.CveContent {
|
||||
References: refs,
|
||||
}
|
||||
}
|
||||
|
||||
// Debian is the interface for Debian OVAL
|
||||
type Debian struct {
|
||||
DebianBase
|
||||
}
|
||||
|
||||
// NewDebian creates OVAL client for Debian
|
||||
func NewDebian() *Debian {
|
||||
return &Debian{}
|
||||
}
|
||||
|
||||
// FillCveInfoFromOvalDB returns scan result after updating CVE info by OVAL
|
||||
func (o Debian) FillCveInfoFromOvalDB(r *models.ScanResult) error {
|
||||
if err := o.fillCveInfoFromOvalDB(r); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, vuln := range r.ScannedCves {
|
||||
if cont, ok := vuln.CveContents[models.Debian]; ok {
|
||||
cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
|
||||
vuln.CveContents[models.Debian] = cont
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ubuntu is the interface for Debian OVAL
|
||||
type Ubuntu struct {
|
||||
DebianBase
|
||||
}
|
||||
|
||||
// NewUbuntu creates OVAL client for Debian
|
||||
func NewUbuntu() *Ubuntu {
|
||||
return &Ubuntu{}
|
||||
}
|
||||
|
||||
// FillCveInfoFromOvalDB returns scan result after updating CVE info by OVAL
|
||||
func (o Ubuntu) FillCveInfoFromOvalDB(r *models.ScanResult) error {
|
||||
if err := o.fillCveInfoFromOvalDB(r); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, vuln := range r.ScannedCves {
|
||||
if cont, ok := vuln.CveContents[models.Ubuntu]; ok {
|
||||
cont.SourceLink = "http://people.ubuntu.com/~ubuntu-security/cve/" + cont.CveID
|
||||
vuln.CveContents[models.Ubuntu] = cont
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -14,27 +14,31 @@ import (
|
||||
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
|
||||
)
|
||||
|
||||
// Redhat is the interface for Redhat OVAL
|
||||
type Redhat struct{}
|
||||
// RedHatBase is the base struct for RedHat and CentOS
|
||||
type RedHatBase struct{}
|
||||
|
||||
// NewRedhat creates OVAL client for Redhat
|
||||
func NewRedhat() Redhat {
|
||||
return Redhat{}
|
||||
// FillCveInfoFromOvalDB returns scan result after updating CVE info by OVAL
|
||||
func (o RedHatBase) FillCveInfoFromOvalDB(r *models.ScanResult) error {
|
||||
if err := o.fillCveInfoFromOvalDB(r); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, vuln := range r.ScannedCves {
|
||||
if cont, ok := vuln.CveContents[models.RedHat]; ok {
|
||||
cont.SourceLink = "https://access.redhat.com/security/cve/" + cont.CveID
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FillCveInfoFromOvalDB returns scan result after updating CVE info by OVAL
|
||||
func (o Redhat) FillCveInfoFromOvalDB(r *models.ScanResult) error {
|
||||
func (o RedHatBase) fillCveInfoFromOvalDB(r *models.ScanResult) error {
|
||||
ovalconf.Conf.DBType = config.Conf.OvalDBType
|
||||
ovalconf.Conf.DBPath = config.Conf.OvalDBPath
|
||||
util.Log.Infof("open oval-dictionary db (%s): %s",
|
||||
config.Conf.OvalDBType, config.Conf.OvalDBPath)
|
||||
|
||||
if err := db.OpenDB(); err != nil {
|
||||
return fmt.Errorf("Failed to open OVAL DB. err: %s", err)
|
||||
}
|
||||
|
||||
d := db.NewRedHat()
|
||||
|
||||
defer d.Close()
|
||||
for _, pack := range r.Packages {
|
||||
definitions, err := d.GetByPackName(r.Release, pack.Name)
|
||||
if err != nil {
|
||||
@@ -56,7 +60,7 @@ func (o Redhat) FillCveInfoFromOvalDB(r *models.ScanResult) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o Redhat) fillOvalInfo(r *models.ScanResult, definition *ovalmodels.Definition) {
|
||||
func (o RedHatBase) fillOvalInfo(r *models.ScanResult, definition *ovalmodels.Definition) {
|
||||
for _, cve := range definition.Advisory.Cves {
|
||||
ovalContent := *o.convertToModel(cve.CveID, definition)
|
||||
vinfo, ok := r.ScannedCves[cve.CveID]
|
||||
@@ -87,7 +91,7 @@ func (o Redhat) fillOvalInfo(r *models.ScanResult, definition *ovalmodels.Defini
|
||||
}
|
||||
}
|
||||
|
||||
func (o Redhat) convertToModel(cveID string, def *ovalmodels.Definition) *models.CveContent {
|
||||
func (o RedHatBase) convertToModel(cveID string, def *ovalmodels.Definition) *models.CveContent {
|
||||
for _, cve := range def.Advisory.Cves {
|
||||
if cve.CveID != cveID {
|
||||
continue
|
||||
@@ -114,6 +118,7 @@ func (o Redhat) convertToModel(cveID string, def *ovalmodels.Definition) *models
|
||||
Cvss2Vector: vec2,
|
||||
Cvss3Score: score3,
|
||||
Cvss3Vector: vec3,
|
||||
SourceLink: "https://access.redhat.com/security/cve/" + cve.CveID,
|
||||
References: refs,
|
||||
CweID: cve.Cwe,
|
||||
Published: def.Advisory.Issued,
|
||||
@@ -125,7 +130,7 @@ func (o Redhat) convertToModel(cveID string, def *ovalmodels.Definition) *models
|
||||
|
||||
// ParseCvss2 divide CVSSv2 string into score and vector
|
||||
// 5/AV:N/AC:L/Au:N/C:N/I:N/A:P
|
||||
func (o Redhat) parseCvss2(scoreVector string) (score float64, vector string) {
|
||||
func (o RedHatBase) parseCvss2(scoreVector string) (score float64, vector string) {
|
||||
var err error
|
||||
ss := strings.Split(scoreVector, "/")
|
||||
if 1 < len(ss) {
|
||||
@@ -139,7 +144,7 @@ func (o Redhat) parseCvss2(scoreVector string) (score float64, vector string) {
|
||||
|
||||
// ParseCvss3 divide CVSSv3 string into score and vector
|
||||
// 5.6/CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L
|
||||
func (o Redhat) parseCvss3(scoreVector string) (score float64, vector string) {
|
||||
func (o RedHatBase) parseCvss3(scoreVector string) (score float64, vector string) {
|
||||
var err error
|
||||
ss := strings.Split(scoreVector, "/CVSS:3.0/")
|
||||
if 1 < len(ss) {
|
||||
@@ -150,3 +155,23 @@ func (o Redhat) parseCvss3(scoreVector string) (score float64, vector string) {
|
||||
}
|
||||
return 0, ""
|
||||
}
|
||||
|
||||
// RedHat is the interface for RedhatBase OVAL
|
||||
type RedHat struct {
|
||||
RedHatBase
|
||||
}
|
||||
|
||||
// NewRedhat creates OVAL client for Redhat
|
||||
func NewRedhat() RedHat {
|
||||
return RedHat{}
|
||||
}
|
||||
|
||||
// CentOS is the interface for CentOS OVAL
|
||||
type CentOS struct {
|
||||
RedHatBase
|
||||
}
|
||||
|
||||
// NewCentOS creates OVAL client for CentOS
|
||||
func NewCentOS() CentOS {
|
||||
return CentOS{}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestParseCvss2(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
s, v := Redhat{}.parseCvss2(tt.in)
|
||||
s, v := RedHatBase{}.parseCvss2(tt.in)
|
||||
if s != tt.out.score || v != tt.out.vector {
|
||||
t.Errorf("\nexpected: %f, %s\n actual: %f, %s",
|
||||
tt.out.score, tt.out.vector, s, v)
|
||||
@@ -60,7 +60,7 @@ func TestParseCvss3(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
s, v := Redhat{}.parseCvss3(tt.in)
|
||||
s, v := RedHatBase{}.parseCvss3(tt.in)
|
||||
if s != tt.out.score || v != tt.out.vector {
|
||||
t.Errorf("\nexpected: %f, %s\n actual: %f, %s",
|
||||
tt.out.score, tt.out.vector, s, v)
|
||||
|
||||
@@ -50,7 +50,9 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
conf.EMail.SubjectPrefix, r.ServerInfo())
|
||||
} else {
|
||||
subject = fmt.Sprintf("%s%s %s",
|
||||
conf.EMail.SubjectPrefix, r.ServerInfo(), r.CveSummary())
|
||||
conf.EMail.SubjectPrefix,
|
||||
r.ServerInfo(),
|
||||
r.CveSummary(config.Conf.IgnoreUnscoredCves))
|
||||
}
|
||||
message = formatFullPlainText(r)
|
||||
if err := sender.Send(subject, message); err != nil {
|
||||
@@ -72,7 +74,7 @@ One Line Summary
|
||||
|
||||
subject := fmt.Sprintf("%s %s",
|
||||
conf.EMail.SubjectPrefix,
|
||||
totalResult.CveSummary(),
|
||||
totalResult.CveSummary(config.Conf.IgnoreUnscoredCves),
|
||||
)
|
||||
return sender.Send(subject, message)
|
||||
}
|
||||
|
||||
@@ -156,7 +156,10 @@ func msgText(r models.ScanResult) string {
|
||||
// notifyUsers = getNotifyUsers(config.Conf.Slack.NotifyUsers)
|
||||
// }
|
||||
serverInfo := fmt.Sprintf("*%s*", r.ServerInfo())
|
||||
return fmt.Sprintf("%s\n%s\n>%s", notifyUsers, serverInfo, r.CveSummary())
|
||||
return fmt.Sprintf("%s\n%s\n>%s",
|
||||
notifyUsers,
|
||||
serverInfo,
|
||||
r.CveSummary(config.Conf.IgnoreUnscoredCves))
|
||||
}
|
||||
|
||||
func toSlackAttachments(scanResult models.ScanResult) (attaches []*attachment) {
|
||||
|
||||
173
report/util.go
173
report/util.go
@@ -25,10 +25,9 @@ import (
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/gosuri/uitable"
|
||||
"github.com/k0kubun/pp"
|
||||
)
|
||||
|
||||
const maxColWidth = 80
|
||||
const maxColWidth = 100
|
||||
|
||||
func formatScanSummary(rs ...models.ScanResult) string {
|
||||
table := uitable.New()
|
||||
@@ -65,7 +64,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {
|
||||
if len(r.Errors) == 0 {
|
||||
cols = []interface{}{
|
||||
r.FormatServerName(),
|
||||
r.CveSummary(),
|
||||
r.CveSummary(config.Conf.IgnoreUnscoredCves),
|
||||
r.Packages.FormatUpdatablePacksSummary(),
|
||||
}
|
||||
} else {
|
||||
@@ -87,15 +86,14 @@ func formatShortPlainText(r models.ScanResult) string {
|
||||
|
||||
vulns := r.ScannedCves
|
||||
if !config.Conf.IgnoreUnscoredCves {
|
||||
//TODO Refactoring
|
||||
vulns = r.ScannedCves.Find(func(v models.VulnInfo) bool {
|
||||
if 0 < v.CveContents.CvssV2Score() || 0 < v.CveContents.CvssV3Score() {
|
||||
if 0 < v.CveContents.MaxCvss2Score().Value.Score ||
|
||||
0 < v.CveContents.MaxCvss3Score().Value.Score {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
pp.Println(vulns)
|
||||
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(r.ServerInfo()); i++ {
|
||||
@@ -104,7 +102,7 @@ func formatShortPlainText(r models.ScanResult) string {
|
||||
header := fmt.Sprintf("%s\n%s\n%s\t%s\n\n",
|
||||
r.ServerInfo(),
|
||||
buf.String(),
|
||||
r.CveSummary(),
|
||||
r.CveSummary(config.Conf.IgnoreUnscoredCves),
|
||||
r.Packages.FormatUpdatablePacksSummary(),
|
||||
)
|
||||
|
||||
@@ -114,84 +112,91 @@ func formatShortPlainText(r models.ScanResult) string {
|
||||
header, r.Errors)
|
||||
}
|
||||
|
||||
//TODO
|
||||
// if len(cves) == 0 {
|
||||
// return fmt.Sprintf(`
|
||||
// %s
|
||||
// No CVE-IDs are found in updatable packages.
|
||||
// %s
|
||||
// `, header, r.Packages.FormatUpdatablePacksSummary())
|
||||
// }
|
||||
if len(vulns) == 0 {
|
||||
return fmt.Sprintf(`
|
||||
%s
|
||||
No CVE-IDs are found in updatable packages.
|
||||
%s
|
||||
`, header, r.Packages.FormatUpdatablePacksSummary())
|
||||
}
|
||||
|
||||
// for _, d := range cves {
|
||||
// var packsVer string
|
||||
// for _, p := range d.Packages {
|
||||
// packsVer += fmt.Sprintf(
|
||||
// "%s -> %s\n", p.FormatCurrentVer(), p.FormatNewVer())
|
||||
// }
|
||||
// for _, n := range d.CpeNames {
|
||||
// packsVer += n
|
||||
// }
|
||||
for _, vuln := range vulns {
|
||||
//TODO
|
||||
// var packsVer string
|
||||
// for _, name := range vuln.PackageNames {
|
||||
// // packages detected by OVAL may not be actually installed
|
||||
// if pack, ok := r.Packages[name]; ok {
|
||||
// packsVer += fmt.Sprintf("%s\n",
|
||||
// pack.FormatVersionFromTo())
|
||||
// }
|
||||
// }
|
||||
// for _, name := range vuln.CpeNames {
|
||||
// packsVer += name + "\n"
|
||||
// }
|
||||
|
||||
// var scols []string
|
||||
// switch {
|
||||
// // case config.Conf.Lang == "ja" &&
|
||||
// //TODO
|
||||
// // 0 < d.CveDetail.Jvn.CvssScore():
|
||||
// // summary := fmt.Sprintf("%s\n%s\n%s\n%sConfidence: %v",
|
||||
// // d.CveDetail.Jvn.CveTitle(),
|
||||
// // d.CveDetail.Jvn.Link(),
|
||||
// // distroLinks(d, r.Family)[0].url,
|
||||
// // packsVer,
|
||||
// // d.VulnInfo.Confidence,
|
||||
// // )
|
||||
// // scols = []string{
|
||||
// // d.CveDetail.CveID,
|
||||
// // fmt.Sprintf("%-4.1f (%s)",
|
||||
// // d.CveDetail.CvssScore(config.Conf.Lang),
|
||||
// // d.CveDetail.Jvn.CvssSeverity(),
|
||||
// // ),
|
||||
// // summary,
|
||||
// // }
|
||||
summaries := vuln.CveContents.Summaries(config.Conf.Lang, r.Family)
|
||||
links := vuln.CveContents.SourceLinks(config.Conf.Lang, r.Family)
|
||||
if len(links) == 0 {
|
||||
links = []models.CveContentStr{{
|
||||
Type: models.NVD,
|
||||
Value: "https://nvd.nist.gov/vuln/detail/" + vuln.CveID,
|
||||
}}
|
||||
}
|
||||
|
||||
// case 0 < d.CvssV2Score():
|
||||
// var nvd *models.CveContent
|
||||
// if cont, found := d.Get(models.NVD); found {
|
||||
// nvd = cont
|
||||
// }
|
||||
// summary := fmt.Sprintf("%s\n%s/%s\n%s\n%sConfidence: %v",
|
||||
// nvd.Summary,
|
||||
// cveDetailsBaseURL,
|
||||
// d.VulnInfo.CveID,
|
||||
// distroLinks(d, r.Family)[0].url,
|
||||
// packsVer,
|
||||
// d.VulnInfo.Confidence,
|
||||
// )
|
||||
// scols = []string{
|
||||
// d.VulnInfo.CveID,
|
||||
// fmt.Sprintf("%-4.1f (%s)",
|
||||
// d.CvssV2Score(),
|
||||
// "TODO",
|
||||
// ),
|
||||
// summary,
|
||||
// }
|
||||
// default:
|
||||
// summary := fmt.Sprintf("%s\n%sConfidence: %v",
|
||||
// distroLinks(d, r.Family)[0].url, packsVer, d.VulnInfo.Confidence)
|
||||
// scols = []string{
|
||||
// d.VulnInfo.CveID,
|
||||
// "?",
|
||||
// summary,
|
||||
// }
|
||||
// }
|
||||
cvsses := ""
|
||||
for _, cvss := range vuln.CveContents.Cvss2Scores() {
|
||||
c2 := cvss.Value
|
||||
cvsses += fmt.Sprintf("%3.1f/%s (%s)\n",
|
||||
c2.Score, c2.Vector, cvss.Type)
|
||||
}
|
||||
cvsses += fmt.Sprintf("%s\n", vuln.Cvss2CalcURL())
|
||||
|
||||
// cols := make([]interface{}, len(scols))
|
||||
// for i := range cols {
|
||||
// cols[i] = scols[i]
|
||||
// }
|
||||
// stable.AddRow(cols...)
|
||||
// stable.AddRow("")
|
||||
// }
|
||||
for _, cvss := range vuln.CveContents.Cvss3Scores() {
|
||||
c3 := cvss.Value
|
||||
cvsses += fmt.Sprintf("%3.1f/CVSS:3.0/%s (%s)\n",
|
||||
c3.Score, c3.Vector, cvss.Type)
|
||||
}
|
||||
if 0 < len(vuln.CveContents.Cvss3Scores()) {
|
||||
cvsses += fmt.Sprintf("%s\n", vuln.Cvss3CalcURL())
|
||||
}
|
||||
|
||||
var maxCvss string
|
||||
v2Max := vuln.CveContents.MaxCvss2Score()
|
||||
v3Max := vuln.CveContents.MaxCvss3Score()
|
||||
if v2Max.Value.Score <= v3Max.Value.Score {
|
||||
maxCvss = fmt.Sprintf("%3.1f %s (%s)",
|
||||
v3Max.Value.Score,
|
||||
strings.ToUpper(v3Max.Value.Severity),
|
||||
v3Max.Type)
|
||||
} else {
|
||||
maxCvss = fmt.Sprintf("%3.1f %s (%s)",
|
||||
v2Max.Value.Score,
|
||||
strings.ToUpper(v2Max.Value.Severity),
|
||||
v2Max.Type)
|
||||
}
|
||||
|
||||
rightCol := fmt.Sprintf(`%s
|
||||
%s
|
||||
---
|
||||
%s
|
||||
%sConfidence: %v`,
|
||||
maxCvss,
|
||||
summaries[0].Value,
|
||||
links[0].Value,
|
||||
cvsses,
|
||||
// packsVer,
|
||||
vuln.Confidence,
|
||||
)
|
||||
|
||||
leftCol := fmt.Sprintf("%s", vuln.CveID)
|
||||
scols := []string{leftCol, rightCol}
|
||||
cols := make([]interface{}, len(scols))
|
||||
for i := range cols {
|
||||
cols[i] = scols[i]
|
||||
}
|
||||
stable.AddRow(cols...)
|
||||
stable.AddRow("")
|
||||
}
|
||||
return fmt.Sprintf("%s\n%s\n", header, stable)
|
||||
}
|
||||
|
||||
@@ -205,7 +210,7 @@ func formatFullPlainText(r models.ScanResult) string {
|
||||
header := fmt.Sprintf("%s\n%s\n%s\t%s\n",
|
||||
r.ServerInfo(),
|
||||
buf.String(),
|
||||
r.CveSummary(),
|
||||
r.CveSummary(config.Conf.IgnoreUnscoredCves),
|
||||
r.Packages.FormatUpdatablePacksSummary(),
|
||||
)
|
||||
|
||||
@@ -481,7 +486,7 @@ func addPackages(table *uitable.Table, packs []models.Package) *uitable.Table {
|
||||
title = "Package"
|
||||
}
|
||||
ver := fmt.Sprintf(
|
||||
"%s -> %s", p.FormatCurrentVer(), p.FormatNewVer())
|
||||
"%s -> %s", p.FormatVer(), p.FormatNewVer())
|
||||
table.AddRow(title, ver)
|
||||
}
|
||||
return table
|
||||
@@ -522,7 +527,7 @@ func formatOneChangelog(p models.Package) string {
|
||||
}
|
||||
|
||||
packVer := fmt.Sprintf("%s -> %s",
|
||||
p.FormatCurrentVer(), p.FormatNewVer())
|
||||
p.FormatVer(), p.FormatNewVer())
|
||||
var delim bytes.Buffer
|
||||
for i := 0; i < len(packVer); i++ {
|
||||
delim.WriteString("-")
|
||||
|
||||
@@ -673,6 +673,7 @@ func (o *debian) parseChangelog(changelog, name, ver string, confidence models.C
|
||||
|
||||
pack := o.Packages[name]
|
||||
pack.Changelog = clog
|
||||
// TODO Mutex
|
||||
o.Packages[name] = pack
|
||||
|
||||
cves := []DetectedCveID{}
|
||||
|
||||
@@ -326,6 +326,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (models.VulnInfos, er
|
||||
o.log.Debugf("%s", pp.Sprintf("%v", packages))
|
||||
|
||||
// set candidate version info
|
||||
//TODO Mutex??
|
||||
o.Packages.MergeNewVersion(packages)
|
||||
|
||||
// Collect CVE-IDs in changelog
|
||||
@@ -355,6 +356,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (models.VulnInfos, er
|
||||
Contents: *clog,
|
||||
Method: models.ChangelogExactMatchStr,
|
||||
}
|
||||
//TODO Mutex
|
||||
o.Packages[p.Name] = p
|
||||
break
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user