Fix -to-slack
This commit is contained in:
@@ -59,22 +59,40 @@ func (v CveContents) Except(exceptCtypes ...CveContentType) (values CveContents)
|
||||
return
|
||||
}
|
||||
|
||||
// CveContentCvss2 has CveContentType and Cvss2
|
||||
type CveContentCvss2 struct {
|
||||
// CveContentCvss has CveContentType and Cvss2
|
||||
type CveContentCvss struct {
|
||||
Type CveContentType
|
||||
Value Cvss2
|
||||
Value Cvss
|
||||
}
|
||||
|
||||
// Cvss2 has CVSS v2
|
||||
type Cvss2 struct {
|
||||
// CvssType Represent the type of CVSS
|
||||
type CvssType string
|
||||
|
||||
const (
|
||||
// CVSS2 means CVSS vesion2
|
||||
CVSS2 CvssType = "2"
|
||||
|
||||
// CVSS3 means CVSS vesion3
|
||||
CVSS3 CvssType = "3"
|
||||
)
|
||||
|
||||
// Cvss has CVSS Score
|
||||
type Cvss struct {
|
||||
Type CvssType
|
||||
Score float64
|
||||
Vector string
|
||||
Severity string
|
||||
}
|
||||
|
||||
// Format CVSS Score and Vector
|
||||
func (c Cvss2) Format() string {
|
||||
return fmt.Sprintf("%3.1f/%s", c.Score, c.Vector)
|
||||
func (c Cvss) Format() string {
|
||||
switch c.Type {
|
||||
case CVSS2:
|
||||
return fmt.Sprintf("%3.1f/%s", c.Score, c.Vector)
|
||||
case CVSS3:
|
||||
return fmt.Sprintf("%3.1f/CVSS:3.0/%s", c.Score, c.Vector)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func cvss2ScoreToSeverity(score float64) string {
|
||||
@@ -87,7 +105,7 @@ func cvss2ScoreToSeverity(score float64) string {
|
||||
}
|
||||
|
||||
// Cvss2Scores returns CVSS V2 Scores
|
||||
func (v CveContents) Cvss2Scores() (values []CveContentCvss2) {
|
||||
func (v CveContents) Cvss2Scores() (values []CveContentCvss) {
|
||||
order := []CveContentType{NVD, RedHat, JVN}
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found && 0 < cont.Cvss2Score {
|
||||
@@ -96,9 +114,10 @@ func (v CveContents) Cvss2Scores() (values []CveContentCvss2) {
|
||||
if ctype == NVD {
|
||||
sev = cvss2ScoreToSeverity(cont.Cvss2Score)
|
||||
}
|
||||
values = append(values, CveContentCvss2{
|
||||
values = append(values, CveContentCvss{
|
||||
Type: ctype,
|
||||
Value: Cvss2{
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: cont.Cvss2Score,
|
||||
Vector: cont.Cvss2Vector,
|
||||
Severity: sev,
|
||||
@@ -111,13 +130,13 @@ func (v CveContents) Cvss2Scores() (values []CveContentCvss2) {
|
||||
}
|
||||
|
||||
// MaxCvss2Score returns Max CVSS V2 Score
|
||||
func (v CveContents) MaxCvss2Score() CveContentCvss2 {
|
||||
func (v CveContents) MaxCvss2Score() CveContentCvss {
|
||||
//TODO Severity Ubuntu, Debian...
|
||||
order := []CveContentType{NVD, RedHat, JVN}
|
||||
max := 0.0
|
||||
value := CveContentCvss2{
|
||||
value := CveContentCvss{
|
||||
Type: Unknown,
|
||||
Value: Cvss2{},
|
||||
Value: Cvss{Type: CVSS2},
|
||||
}
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found && max < cont.Cvss2Score {
|
||||
@@ -126,9 +145,10 @@ func (v CveContents) MaxCvss2Score() CveContentCvss2 {
|
||||
if ctype == NVD {
|
||||
sev = cvss2ScoreToSeverity(cont.Cvss2Score)
|
||||
}
|
||||
value = CveContentCvss2{
|
||||
value = CveContentCvss{
|
||||
Type: ctype,
|
||||
Value: Cvss2{
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: cont.Cvss2Score,
|
||||
Vector: cont.Cvss2Vector,
|
||||
Severity: sev,
|
||||
@@ -155,9 +175,10 @@ func (v CveContents) MaxCvss2Score() CveContentCvss2 {
|
||||
score = severityToScoreForRedHat(cont.Severity)
|
||||
}
|
||||
if max < score {
|
||||
value = CveContentCvss2{
|
||||
value = CveContentCvss{
|
||||
Type: ctype,
|
||||
Value: Cvss2{
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: score,
|
||||
Vector: cont.Cvss2Vector,
|
||||
Severity: cont.Severity,
|
||||
@@ -201,45 +222,46 @@ func severityToScoreForRedHat(severity string) float64 {
|
||||
}
|
||||
|
||||
// CveContentCvss3 has CveContentType and Cvss3
|
||||
type CveContentCvss3 struct {
|
||||
Type CveContentType
|
||||
Value Cvss3
|
||||
}
|
||||
// type CveContentCvss3 struct {
|
||||
// Type CveContentType
|
||||
// Value Cvss3
|
||||
// }
|
||||
|
||||
// Cvss3 has CVSS v3 Score, Vector and Severity
|
||||
type Cvss3 struct {
|
||||
Score float64
|
||||
Vector string
|
||||
Severity string
|
||||
}
|
||||
// type Cvss3 struct {
|
||||
// Score float64
|
||||
// Vector string
|
||||
// Severity string
|
||||
// }
|
||||
|
||||
// Format CVSS Score and Vector
|
||||
func (c Cvss3) Format() string {
|
||||
return fmt.Sprintf("%3.1f/CVSS:3.0/%s", c.Score, c.Vector)
|
||||
}
|
||||
// func (c Cvss3) Format() string {
|
||||
// return fmt.Sprintf("%3.1f/CVSS:3.0/%s", c.Score, c.Vector)
|
||||
// }
|
||||
|
||||
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"
|
||||
}
|
||||
// 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) {
|
||||
func (v CveContents) Cvss3Scores() (values []CveContentCvss) {
|
||||
// TODO implement NVD
|
||||
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
|
||||
values = append(values, CveContentCvss3{
|
||||
values = append(values, CveContentCvss{
|
||||
Type: ctype,
|
||||
Value: Cvss3{
|
||||
Value: Cvss{
|
||||
Type: CVSS3,
|
||||
Score: cont.Cvss3Score,
|
||||
Vector: cont.Cvss3Vector,
|
||||
Severity: sev,
|
||||
@@ -251,21 +273,22 @@ func (v CveContents) Cvss3Scores() (values []CveContentCvss3) {
|
||||
}
|
||||
|
||||
// MaxCvss3Score returns Max CVSS V3 Score
|
||||
func (v CveContents) MaxCvss3Score() CveContentCvss3 {
|
||||
func (v CveContents) MaxCvss3Score() CveContentCvss {
|
||||
// TODO implement NVD
|
||||
order := []CveContentType{RedHat}
|
||||
max := 0.0
|
||||
value := CveContentCvss3{
|
||||
value := CveContentCvss{
|
||||
Type: Unknown,
|
||||
Value: Cvss3{},
|
||||
Value: Cvss{Type: CVSS3},
|
||||
}
|
||||
for _, ctype := range order {
|
||||
if cont, found := v[ctype]; found && max < cont.Cvss3Score {
|
||||
// https://nvd.nist.gov/vuln-metrics/cvss
|
||||
sev := cont.Severity
|
||||
value = CveContentCvss3{
|
||||
value = CveContentCvss{
|
||||
Type: ctype,
|
||||
Value: Cvss3{
|
||||
Value: Cvss{
|
||||
Type: CVSS3,
|
||||
Score: cont.Cvss3Score,
|
||||
Vector: cont.Cvss3Vector,
|
||||
Severity: sev,
|
||||
@@ -279,12 +302,12 @@ func (v CveContents) MaxCvss3Score() CveContentCvss3 {
|
||||
|
||||
// MaxCvssScore returns max CVSS Score
|
||||
// If there is no CVSS Score, return Severity as a numerical value.
|
||||
func (v CveContents) MaxCvssScore() float64 {
|
||||
func (v CveContents) MaxCvssScore() CveContentCvss {
|
||||
v3Max := v.MaxCvss3Score()
|
||||
v2Max := v.MaxCvss2Score()
|
||||
max := v3Max.Value.Score
|
||||
if max < v2Max.Value.Score {
|
||||
max = v2Max.Value.Score
|
||||
max := v3Max
|
||||
if max.Value.Score < v2Max.Value.Score {
|
||||
max = v2Max
|
||||
}
|
||||
return max
|
||||
}
|
||||
@@ -396,13 +419,13 @@ func (v CveContents) SourceLinks(lang, myFamily, cveID string) (values []CveCont
|
||||
}
|
||||
|
||||
// Severities returns Severities
|
||||
// func (v CveContents) Severities(myFamily string) (values []CveContentValue) {
|
||||
// func (v CveContents) Severities(myFamily string) (values []CveContentStr) {
|
||||
// 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{
|
||||
// values = append(values, CveContentStr{
|
||||
// Type: ctype,
|
||||
// Value: cont.Severity,
|
||||
// })
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestExcept(t *testing.T) {
|
||||
func TestCvss2Scores(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in CveContents
|
||||
out []CveContentCvss2
|
||||
out []CveContentCvss
|
||||
}{
|
||||
{
|
||||
in: CveContents{
|
||||
@@ -70,10 +70,11 @@ func TestCvss2Scores(t *testing.T) {
|
||||
// Severity is NIOT included in NVD
|
||||
},
|
||||
},
|
||||
out: []CveContentCvss2{
|
||||
out: []CveContentCvss{
|
||||
{
|
||||
Type: NVD,
|
||||
Value: Cvss2{
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 8.1,
|
||||
Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
Severity: "HIGH",
|
||||
@@ -81,7 +82,8 @@ func TestCvss2Scores(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Type: RedHat,
|
||||
Value: Cvss2{
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 8.0,
|
||||
Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
Severity: "HIGH",
|
||||
@@ -89,7 +91,8 @@ func TestCvss2Scores(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Type: JVN,
|
||||
Value: Cvss2{
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 8.2,
|
||||
Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
Severity: "HIGH",
|
||||
@@ -114,7 +117,7 @@ func TestCvss2Scores(t *testing.T) {
|
||||
func TestMaxCvss2Scores(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in CveContents
|
||||
out CveContentCvss2
|
||||
out CveContentCvss
|
||||
}{
|
||||
{
|
||||
in: CveContents{
|
||||
@@ -137,9 +140,10 @@ func TestMaxCvss2Scores(t *testing.T) {
|
||||
// Severity is NIOT included in NVD
|
||||
},
|
||||
},
|
||||
out: CveContentCvss2{
|
||||
out: CveContentCvss{
|
||||
Type: JVN,
|
||||
Value: Cvss2{
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 8.2,
|
||||
Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P",
|
||||
Severity: "HIGH",
|
||||
@@ -154,9 +158,10 @@ func TestMaxCvss2Scores(t *testing.T) {
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
out: CveContentCvss2{
|
||||
out: CveContentCvss{
|
||||
Type: Ubuntu,
|
||||
Value: Cvss2{
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 10,
|
||||
Severity: "HIGH",
|
||||
},
|
||||
@@ -165,9 +170,10 @@ func TestMaxCvss2Scores(t *testing.T) {
|
||||
// Empty
|
||||
{
|
||||
in: CveContents{},
|
||||
out: CveContentCvss2{
|
||||
out: CveContentCvss{
|
||||
Type: Unknown,
|
||||
Value: Cvss2{
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 0.0,
|
||||
Vector: "",
|
||||
Severity: "",
|
||||
@@ -186,7 +192,7 @@ func TestMaxCvss2Scores(t *testing.T) {
|
||||
func TestCvss3Scores(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in CveContents
|
||||
out []CveContentCvss3
|
||||
out []CveContentCvss
|
||||
}{
|
||||
{
|
||||
in: CveContents{
|
||||
@@ -203,10 +209,11 @@ func TestCvss3Scores(t *testing.T) {
|
||||
// Severity is NIOT included in NVD
|
||||
},
|
||||
},
|
||||
out: []CveContentCvss3{
|
||||
out: []CveContentCvss{
|
||||
{
|
||||
Type: RedHat,
|
||||
Value: Cvss3{
|
||||
Value: Cvss{
|
||||
Type: CVSS3,
|
||||
Score: 8.0,
|
||||
Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
Severity: "HIGH",
|
||||
@@ -231,7 +238,7 @@ func TestCvss3Scores(t *testing.T) {
|
||||
func TestMaxCvss3Scores(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in CveContents
|
||||
out CveContentCvss3
|
||||
out CveContentCvss
|
||||
}{
|
||||
{
|
||||
in: CveContents{
|
||||
@@ -242,9 +249,10 @@ func TestMaxCvss3Scores(t *testing.T) {
|
||||
Cvss3Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
},
|
||||
},
|
||||
out: CveContentCvss3{
|
||||
out: CveContentCvss{
|
||||
Type: RedHat,
|
||||
Value: Cvss3{
|
||||
Value: Cvss{
|
||||
Type: CVSS3,
|
||||
Score: 8.0,
|
||||
Vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
|
||||
Severity: "HIGH",
|
||||
@@ -254,9 +262,10 @@ func TestMaxCvss3Scores(t *testing.T) {
|
||||
// Empty
|
||||
{
|
||||
in: CveContents{},
|
||||
out: CveContentCvss3{
|
||||
out: CveContentCvss{
|
||||
Type: Unknown,
|
||||
Value: Cvss3{
|
||||
Value: Cvss{
|
||||
Type: CVSS3,
|
||||
Score: 0.0,
|
||||
Vector: "",
|
||||
Severity: "",
|
||||
@@ -275,7 +284,7 @@ func TestMaxCvss3Scores(t *testing.T) {
|
||||
func TestMaxCvssScores(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in CveContents
|
||||
out float64
|
||||
out CveContentCvss
|
||||
}{
|
||||
{
|
||||
in: CveContents{
|
||||
@@ -288,7 +297,13 @@ func TestMaxCvssScores(t *testing.T) {
|
||||
Cvss2Score: 8.0,
|
||||
},
|
||||
},
|
||||
out: 8.0,
|
||||
out: CveContentCvss{
|
||||
Type: RedHat,
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 8.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
in: CveContents{
|
||||
@@ -297,7 +312,13 @@ func TestMaxCvssScores(t *testing.T) {
|
||||
Cvss3Score: 8.0,
|
||||
},
|
||||
},
|
||||
out: 8.0,
|
||||
out: CveContentCvss{
|
||||
Type: RedHat,
|
||||
Value: Cvss{
|
||||
Type: CVSS3,
|
||||
Score: 8.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
in: CveContents{
|
||||
@@ -306,7 +327,14 @@ func TestMaxCvssScores(t *testing.T) {
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
out: 10.0,
|
||||
out: CveContentCvss{
|
||||
Type: Ubuntu,
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 10.0,
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
in: CveContents{
|
||||
@@ -319,12 +347,25 @@ func TestMaxCvssScores(t *testing.T) {
|
||||
Cvss2Score: 7.0,
|
||||
},
|
||||
},
|
||||
out: 7.0,
|
||||
out: CveContentCvss{
|
||||
Type: NVD,
|
||||
Value: Cvss{
|
||||
Type: CVSS2,
|
||||
Score: 7.0,
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
// Empty
|
||||
{
|
||||
in: CveContents{},
|
||||
out: 0,
|
||||
in: CveContents{},
|
||||
out: CveContentCvss{
|
||||
Type: Unknown,
|
||||
Value: Cvss{
|
||||
Type: CVSS3,
|
||||
Score: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
|
||||
@@ -90,33 +90,28 @@ type Package struct {
|
||||
NotFixedYet bool // Ubuntu OVAL Only
|
||||
}
|
||||
|
||||
// FormatVer returns package name-version-release
|
||||
// FormatVer returns package version-release
|
||||
func (p Package) FormatVer() string {
|
||||
str := p.Name
|
||||
if 0 < len(p.Version) {
|
||||
str = fmt.Sprintf("%s-%s", str, p.Version)
|
||||
}
|
||||
ver := p.Version
|
||||
if 0 < len(p.Release) {
|
||||
str = fmt.Sprintf("%s-%s", str, p.Release)
|
||||
ver = fmt.Sprintf("%s-%s", ver, p.Release)
|
||||
}
|
||||
return str
|
||||
return ver
|
||||
}
|
||||
|
||||
// FormatNewVer returns package name-version-release
|
||||
// FormatNewVer returns package version-release
|
||||
func (p Package) FormatNewVer() string {
|
||||
str := p.Name
|
||||
if 0 < len(p.NewVersion) {
|
||||
str = fmt.Sprintf("%s-%s", str, p.NewVersion)
|
||||
}
|
||||
ver := p.NewVersion
|
||||
if 0 < len(p.NewRelease) {
|
||||
str = fmt.Sprintf("%s-%s", str, p.NewRelease)
|
||||
ver = fmt.Sprintf("%s-%s", ver, p.NewRelease)
|
||||
}
|
||||
return str
|
||||
return ver
|
||||
}
|
||||
|
||||
// FormatVersionFromTo formats installed and new package version
|
||||
func (p Package) FormatVersionFromTo() string {
|
||||
return fmt.Sprintf("%s -> %s", p.FormatVer(), p.FormatNewVer())
|
||||
return fmt.Sprintf("%s-%s -> %s",
|
||||
p.Name, p.FormatVer(), p.FormatNewVer())
|
||||
}
|
||||
|
||||
// Changelog has contents of changelog and how to get it.
|
||||
|
||||
@@ -59,8 +59,8 @@ func (v VulnInfos) ToSortedSlice() (sorted []VulnInfo) {
|
||||
sort.Slice(sorted, func(i, j int) bool {
|
||||
maxI := sorted[i].CveContents.MaxCvssScore()
|
||||
maxJ := sorted[j].CveContents.MaxCvssScore()
|
||||
if maxI != maxJ {
|
||||
return maxJ < maxI
|
||||
if maxI.Value.Score != maxJ.Value.Score {
|
||||
return maxJ.Value.Score < maxI.Value.Score
|
||||
}
|
||||
return sorted[i].CveID < sorted[j].CveID
|
||||
})
|
||||
|
||||
240
report/slack.go
240
report/slack.go
@@ -21,6 +21,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
@@ -44,6 +45,7 @@ type attachment struct {
|
||||
Color string `json:"color"`
|
||||
Fields []*field `json:"fields"`
|
||||
MrkdwnIn []string `json:"mrkdwn_in"`
|
||||
Footer string `json:"footer"`
|
||||
}
|
||||
type message struct {
|
||||
Text string `json:"text"`
|
||||
@@ -66,12 +68,12 @@ func (w SlackWriter) Write(rs ...models.ScanResult) error {
|
||||
}
|
||||
|
||||
if 0 < len(r.Errors) {
|
||||
//TODO
|
||||
// serverInfo := fmt.Sprintf("*%s*", r.ServerInfo())
|
||||
// notifyUsers := getNotifyUsers(config.Conf.Slack.NotifyUsers)
|
||||
// txt := fmt.Sprintf("%s\n%s\nError: %s", notifyUsers, serverInfo, r.Errors)
|
||||
serverInfo := fmt.Sprintf("*%s*", r.ServerInfo())
|
||||
notifyUsers := getNotifyUsers(config.Conf.Slack.NotifyUsers)
|
||||
txt := fmt.Sprintf("%s\n%s\nError: %s",
|
||||
notifyUsers, serverInfo, r.Errors)
|
||||
msg := message{
|
||||
// Text: txt,
|
||||
Text: txt,
|
||||
Username: conf.AuthUser,
|
||||
IconEmoji: conf.IconEmoji,
|
||||
Channel: channel,
|
||||
@@ -152,9 +154,9 @@ func send(msg message) error {
|
||||
|
||||
func msgText(r models.ScanResult) string {
|
||||
notifyUsers := ""
|
||||
// if 0 < len(r.KnownCves) || 0 < len(r.UnknownCves) {
|
||||
// notifyUsers = getNotifyUsers(config.Conf.Slack.NotifyUsers)
|
||||
// }
|
||||
if 0 < len(r.ScannedCves) {
|
||||
notifyUsers = getNotifyUsers(config.Conf.Slack.NotifyUsers)
|
||||
}
|
||||
serverInfo := fmt.Sprintf("*%s*", r.ServerInfo())
|
||||
return fmt.Sprintf("%s\n%s\n>%s",
|
||||
notifyUsers,
|
||||
@@ -162,50 +164,62 @@ func msgText(r models.ScanResult) string {
|
||||
r.ScannedCves.FormatCveSummary())
|
||||
}
|
||||
|
||||
func toSlackAttachments(scanResult models.ScanResult) (attaches []*attachment) {
|
||||
// cves := scanResult.KnownCves
|
||||
// if !config.Conf.IgnoreUnscoredCves {
|
||||
// cves = append(cves, scanResult.UnknownCves...)
|
||||
// }
|
||||
func toSlackAttachments(r models.ScanResult) (attaches []*attachment) {
|
||||
var vinfos []models.VulnInfo
|
||||
if config.Conf.IgnoreUnscoredCves {
|
||||
vinfos = r.ScannedCves.FindScoredVulns().ToSortedSlice()
|
||||
} else {
|
||||
vinfos = r.ScannedCves.ToSortedSlice()
|
||||
}
|
||||
|
||||
// for _, cveInfo := range cves {
|
||||
// cveID := cveInfo.VulnInfo.CveID
|
||||
for _, vinfo := range vinfos {
|
||||
curent := []string{}
|
||||
for _, name := range vinfo.PackageNames {
|
||||
if p, ok := r.Packages[name]; ok {
|
||||
curent = append(curent,
|
||||
fmt.Sprintf("%s-%s", p.Name, p.FormatVer()))
|
||||
} else {
|
||||
curent = append(curent, name)
|
||||
}
|
||||
}
|
||||
for _, n := range vinfo.CpeNames {
|
||||
curent = append(curent, n)
|
||||
}
|
||||
|
||||
// curentPackages := []string{}
|
||||
// for _, p := range cveInfo.Packages {
|
||||
// curentPackages = append(curentPackages, p.FormatCurrentVer())
|
||||
// }
|
||||
// for _, n := range cveInfo.CpeNames {
|
||||
// curentPackages = append(curentPackages, n)
|
||||
// }
|
||||
new := []string{}
|
||||
for _, name := range vinfo.PackageNames {
|
||||
if p, ok := r.Packages[name]; ok {
|
||||
new = append(new, p.FormatNewVer())
|
||||
} else {
|
||||
new = append(new, "?")
|
||||
}
|
||||
}
|
||||
for range vinfo.CpeNames {
|
||||
new = append(new, "?")
|
||||
}
|
||||
|
||||
// newPackages := []string{}
|
||||
// for _, p := range cveInfo.Packages {
|
||||
// newPackages = append(newPackages, p.FormatNewVer())
|
||||
// }
|
||||
|
||||
// a := attachment{
|
||||
// Title: cveID,
|
||||
// TitleLink: fmt.Sprintf("%s/%s", nvdBaseURL, cveID),
|
||||
// Text: attachmentText(cveInfo, scanResult.Family),
|
||||
// MrkdwnIn: []string{"text", "pretext"},
|
||||
// Fields: []*field{
|
||||
// {
|
||||
// // Title: "Current Package/CPE",
|
||||
// Title: "Installed",
|
||||
// Value: strings.Join(curentPackages, "\n"),
|
||||
// Short: true,
|
||||
// },
|
||||
// {
|
||||
// Title: "Candidate",
|
||||
// Value: strings.Join(newPackages, "\n"),
|
||||
// Short: true,
|
||||
// },
|
||||
// },
|
||||
// Color: color(cveInfo.CvssV2Score()),
|
||||
// }
|
||||
// attaches = append(attaches, &a)
|
||||
// }
|
||||
a := attachment{
|
||||
Title: vinfo.CveID,
|
||||
TitleLink: "https://nvd.nist.gov/vuln/detail/" + vinfo.CveID,
|
||||
Text: attachmentText(vinfo, r.Family),
|
||||
MrkdwnIn: []string{"text", "pretext"},
|
||||
Fields: []*field{
|
||||
{
|
||||
// Title: "Current Package/CPE",
|
||||
Title: "Installed",
|
||||
Value: strings.Join(curent, "\n"),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Candidate",
|
||||
Value: strings.Join(new, "\n"),
|
||||
Short: true,
|
||||
},
|
||||
},
|
||||
Color: color(vinfo.CveContents.MaxCvssScore().Value.Score),
|
||||
}
|
||||
attaches = append(attaches, &a)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -223,80 +237,62 @@ func color(cvssScore float64) string {
|
||||
}
|
||||
}
|
||||
|
||||
// func attachmentText(cveInfo models.CveInfo, osFamily string) string {
|
||||
// linkText := links(cveInfo, osFamily)
|
||||
//TODO
|
||||
// return ""
|
||||
// switch {
|
||||
// case config.Conf.Lang == "ja" &&
|
||||
// 0 < cveInfo.CveDetail.Jvn.CvssScore():
|
||||
func attachmentText(vinfo models.VulnInfo, osFamily string) string {
|
||||
maxCvss := vinfo.CveContents.MaxCvssScore()
|
||||
vectors := []string{}
|
||||
for _, cvss := range vinfo.CveContents.Cvss2Scores() {
|
||||
calcURL := ""
|
||||
switch cvss.Value.Type {
|
||||
case models.CVSS2:
|
||||
calcURL = fmt.Sprintf(
|
||||
"https://nvd.nist.gov/vuln-metrics/cvss/v2-calculator?vector=%s",
|
||||
cvss.Value.Vector)
|
||||
case models.CVSS3:
|
||||
calcURL = fmt.Sprintf(
|
||||
"https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?vector=%s",
|
||||
cvss.Value.Vector)
|
||||
}
|
||||
v := fmt.Sprintf("<%s|%s> (<%s|%s>)",
|
||||
calcURL,
|
||||
cvss.Value.Format(),
|
||||
vinfo.CveContents[cvss.Type].SourceLink,
|
||||
cvss.Type)
|
||||
vectors = append(vectors, v)
|
||||
}
|
||||
|
||||
// jvn := cveInfo.CveDetail.Jvn
|
||||
// return fmt.Sprintf("*%4.1f (%s)* <%s|%s>\n%s\n%s\n*Confidence:* %v",
|
||||
// cveInfo.CveDetail.CvssScore(config.Conf.Lang),
|
||||
// jvn.CvssSeverity(),
|
||||
// fmt.Sprintf(cvssV2CalcBaseURL, cveInfo.CveDetail.CveID),
|
||||
// jvn.CvssVector(),
|
||||
// jvn.CveTitle(),
|
||||
// linkText,
|
||||
// cveInfo.VulnInfo.Confidence,
|
||||
// )
|
||||
// case 0 < cveInfo.CveDetail.CvssScore("en"):
|
||||
// nvd := cveInfo.CveDetail.Nvd
|
||||
// return fmt.Sprintf("*%4.1f (%s)* <%s|%s>\n%s\n%s\n*Confidence:* %v",
|
||||
// cveInfo.CveDetail.CvssScore(config.Conf.Lang),
|
||||
// nvd.CvssSeverity(),
|
||||
// fmt.Sprintf(cvssV2CalcBaseURL, cveInfo.CveDetail.CveID),
|
||||
// nvd.CvssVector(),
|
||||
// nvd.CveSummary(),
|
||||
// linkText,
|
||||
// cveInfo.VulnInfo.Confidence,
|
||||
// )
|
||||
// default:
|
||||
// nvd := cveInfo.CveDetail.Nvd
|
||||
// return fmt.Sprintf("?\n%s\n%s\n*Confidence:* %v",
|
||||
// nvd.CveSummary(), linkText, cveInfo.VulnInfo.Confidence)
|
||||
// }
|
||||
// }
|
||||
severity := strings.ToUpper(maxCvss.Value.Severity)
|
||||
if severity == "" {
|
||||
severity = "?"
|
||||
}
|
||||
|
||||
// func links(cveInfo models.CveInfo, osFamily string) string {
|
||||
// links := []string{}
|
||||
return fmt.Sprintf("*%4.1f (%s)* %s\n%s\n```%s```",
|
||||
maxCvss.Value.Score,
|
||||
severity,
|
||||
cweIDs(vinfo, osFamily),
|
||||
strings.Join(vectors, "\n"),
|
||||
vinfo.CveContents.Summaries(config.Conf.Lang, osFamily)[0].Value,
|
||||
)
|
||||
}
|
||||
|
||||
// //TODO
|
||||
// // cweID := cveInfo.CveDetail.CweID()
|
||||
// // if 0 < len(cweID) {
|
||||
// // links = append(links, fmt.Sprintf("<%s|%s>",
|
||||
// // cweURL(cweID), cweID))
|
||||
// // if config.Conf.Lang == "ja" {
|
||||
// // links = append(links, fmt.Sprintf("<%s|%s(JVN)>",
|
||||
// // cweJvnURL(cweID), cweID))
|
||||
// // }
|
||||
// // }
|
||||
func cweIDs(vinfo models.VulnInfo, osFamily string) string {
|
||||
links := []string{}
|
||||
for _, cwe := range vinfo.CveContents.CweIDs(osFamily) {
|
||||
if config.Conf.Lang == "ja" {
|
||||
links = append(links, fmt.Sprintf("<%s|%s>",
|
||||
cweJvnURL(cwe.Value), cwe.Value))
|
||||
} else {
|
||||
links = append(links, fmt.Sprintf("<%s|%s>",
|
||||
cweURL(cwe.Value), cwe.Value))
|
||||
}
|
||||
}
|
||||
return strings.Join(links, " / ")
|
||||
}
|
||||
|
||||
// cveID := cveInfo.VulnInfo.CveID
|
||||
// //TODO
|
||||
// // if config.Conf.Lang == "ja" && 0 < len(cveInfo.CveDetail.Jvn.Link()) {
|
||||
// // jvn := fmt.Sprintf("<%s|JVN>", cveInfo.CveDetail.Jvn.Link())
|
||||
// // links = append(links, jvn)
|
||||
// // }
|
||||
// dlinks := distroLinks(cveInfo, osFamily)
|
||||
// for _, link := range dlinks {
|
||||
// links = append(links,
|
||||
// fmt.Sprintf("<%s|%s>", link.url, link.title))
|
||||
// }
|
||||
// links = append(links, fmt.Sprintf("<%s|MITRE>",
|
||||
// fmt.Sprintf("%s%s", mitreBaseURL, cveID)))
|
||||
// links = append(links, fmt.Sprintf("<%s|CVEDetails>",
|
||||
// fmt.Sprintf("%s/%s", cveDetailsBaseURL, cveID)))
|
||||
|
||||
// return strings.Join(links, " / ")
|
||||
// }
|
||||
|
||||
// // See testcase
|
||||
// func getNotifyUsers(notifyUsers []string) string {
|
||||
// slackStyleTexts := []string{}
|
||||
// for _, username := range notifyUsers {
|
||||
// slackStyleTexts = append(slackStyleTexts, fmt.Sprintf("<%s>", username))
|
||||
// }
|
||||
// return strings.Join(slackStyleTexts, " ")
|
||||
// }
|
||||
// See testcase
|
||||
func getNotifyUsers(notifyUsers []string) string {
|
||||
slackStyleTexts := []string{}
|
||||
for _, username := range notifyUsers {
|
||||
slackStyleTexts = append(slackStyleTexts, fmt.Sprintf("<%s>", username))
|
||||
}
|
||||
return strings.Join(slackStyleTexts, " ")
|
||||
}
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
package report
|
||||
|
||||
// func TestGetNotifyUsers(t *testing.T) {
|
||||
// var tests = []struct {
|
||||
// in []string
|
||||
// expected string
|
||||
// }{
|
||||
// {
|
||||
// []string{"@user1", "@user2"},
|
||||
// "<@user1> <@user2>",
|
||||
// },
|
||||
// }
|
||||
import "testing"
|
||||
|
||||
// for _, tt := range tests {
|
||||
// actual := getNotifyUsers(tt.in)
|
||||
// if tt.expected != actual {
|
||||
// t.Errorf("expected %s, actual %s", tt.expected, actual)
|
||||
// }
|
||||
// }
|
||||
func TestGetNotifyUsers(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in []string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
[]string{"@user1", "@user2"},
|
||||
"<@user1> <@user2>",
|
||||
},
|
||||
}
|
||||
|
||||
// }
|
||||
for _, tt := range tests {
|
||||
actual := getNotifyUsers(tt.in)
|
||||
if tt.expected != actual {
|
||||
t.Errorf("expected %s, actual %s", tt.expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -493,8 +493,8 @@ func formatOneChangelog(p models.Package) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
packVer := fmt.Sprintf("%s -> %s",
|
||||
p.FormatVer(), p.FormatNewVer())
|
||||
packVer := fmt.Sprintf("%s-%s -> %s",
|
||||
p.Name, p.FormatVer(), p.FormatNewVer())
|
||||
var delim bytes.Buffer
|
||||
for i := 0; i < len(packVer); i++ {
|
||||
delim.WriteString("-")
|
||||
|
||||
Reference in New Issue
Block a user