Skip to content

Commit

Permalink
feat(cve/mitre): support go-cve-dictionary:mitre
Browse files Browse the repository at this point in the history
  • Loading branch information
MaineK00n committed Jun 28, 2024
1 parent 9beb5fc commit a7e725a
Show file tree
Hide file tree
Showing 14 changed files with 576 additions and 68 deletions.
15 changes: 7 additions & 8 deletions detector/detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ func FillCvesWithNvdJvnFortinet(r *models.ScanResult, cnf config.GoCveDictConf,
nvds, exploits, mitigations := models.ConvertNvdToModel(d.CveID, d.Nvds)
jvns := models.ConvertJvnToModel(d.CveID, d.Jvns)
fortinets := models.ConvertFortinetToModel(d.CveID, d.Fortinets)
mitres := models.ConvertMitreToModel(d.CveID, d.Mitres)

alerts := fillCertAlerts(&d)
for cveID, vinfo := range r.ScannedCves {
Expand All @@ -475,18 +476,16 @@ func FillCvesWithNvdJvnFortinet(r *models.ScanResult, cnf config.GoCveDictConf,
}
for _, con := range append(jvns, fortinets...) {
if !con.Empty() {
found := false
for _, cveCont := range vinfo.CveContents[con.Type] {
if con.SourceLink == cveCont.SourceLink {
found = true
break
}
}
if !found {
if !slices.ContainsFunc(vinfo.CveContents[con.Type], func(e models.CveContent) bool {
return con.SourceLink == e.SourceLink
}) {
vinfo.CveContents[con.Type] = append(vinfo.CveContents[con.Type], con)
}
}
}
for _, con := range mitres {
vinfo.CveContents[con.Type] = append(vinfo.CveContents[con.Type], con)
}
vinfo.AlertDict = alerts
vinfo.Exploits = append(vinfo.Exploits, exploits...)
vinfo.Mitigations = append(vinfo.Mitigations, mitigations...)
Expand Down
2 changes: 1 addition & 1 deletion detector/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func getMinusDiffCves(previous, current models.ScanResult) models.VulnInfos {
}

func isCveInfoUpdated(cveID string, previous, current models.ScanResult) bool {
cTypes := append([]models.CveContentType{models.Nvd, models.Jvn}, models.GetCveContentTypes(current.Family)...)
cTypes := append([]models.CveContentType{models.Mitre, models.Nvd, models.Jvn}, models.GetCveContentTypes(current.Family)...)

prevLastModified := map[models.CveContentType][]time.Time{}
preVinfo, ok := previous.ScannedCves[cveID]
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.1
github.com/vulsio/go-cti v0.0.5-0.20240318121747-822b3ef289cb
github.com/vulsio/go-cve-dictionary v0.10.2-0.20240319004433-af03be313b77
github.com/vulsio/go-cve-dictionary v0.10.2-0.20240628072614-73f15707be8e
github.com/vulsio/go-exploitdb v0.4.7-0.20240318122115-ccb3abc151a1
github.com/vulsio/go-kev v0.1.4-0.20240318121733-b3386e67d3fb
github.com/vulsio/go-msfdb v0.2.4-0.20240318121704-8bfc812656dc
Expand Down Expand Up @@ -97,7 +97,7 @@ require (
github.com/Microsoft/hcsshim v0.12.0 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect
github.com/PuerkitoBio/goquery v1.9.1 // indirect
github.com/PuerkitoBio/goquery v1.9.2 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,8 @@ github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg=
github.com/ProtonMail/go-crypto v1.1.0-alpha.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VPW7UI=
github.com/PuerkitoBio/goquery v1.9.1/go.mod h1:cW1n6TmIMDoORQU5IU/P1T3tGFunOeXEpGP2WHRwkbY=
github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE=
github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/Ullaakut/nmap/v2 v2.2.2 h1:178Ety3d8T21sF6WZxyj7QVZUhnC1tL1J+tHLLW507Q=
Expand Down Expand Up @@ -1168,8 +1168,8 @@ github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinC
github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk=
github.com/vulsio/go-cti v0.0.5-0.20240318121747-822b3ef289cb h1:aC6CqML20oYEI5Wjx04uwpARsXjdGCrOk4ken+l4dG8=
github.com/vulsio/go-cti v0.0.5-0.20240318121747-822b3ef289cb/go.mod h1:MHlQMcrMMUGXVc9G1JBZg1J/frsugODntu7CfLInEFs=
github.com/vulsio/go-cve-dictionary v0.10.2-0.20240319004433-af03be313b77 h1:utQlIgdHOqx+TOHecQm3vk4Bu9QHZcwkKj2DMQ4F3mo=
github.com/vulsio/go-cve-dictionary v0.10.2-0.20240319004433-af03be313b77/go.mod h1:NYtVYgM43dITGd0wVGTGhBqGHYisdK7k6pLo+71rMzU=
github.com/vulsio/go-cve-dictionary v0.10.2-0.20240628072614-73f15707be8e h1:z/rVzYJy6LCeSzoLFZuiAFfe45giUYdsyPL+iprlC78=
github.com/vulsio/go-cve-dictionary v0.10.2-0.20240628072614-73f15707be8e/go.mod h1:Kxpy1CE1D/Wsu7HH+5K1RAQQ6PErMOPHZ2W0+bsxqNc=
github.com/vulsio/go-exploitdb v0.4.7-0.20240318122115-ccb3abc151a1 h1:rQRTmiO2gYEhyjthvGseV34Qj+nwrVgZEnFvk6Z2AqM=
github.com/vulsio/go-exploitdb v0.4.7-0.20240318122115-ccb3abc151a1/go.mod h1:ml2oTRyR37hUyyP4kWD9NSlBYIQuJUVNaAfbflSu4i4=
github.com/vulsio/go-kev v0.1.4-0.20240318121733-b3386e67d3fb h1:j03zKKkR+WWaPoPzMBwNxpDsc1mYDtt9s1VrHaIxmfw=
Expand Down
115 changes: 81 additions & 34 deletions models/cvecontents.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package models

import (
"fmt"
"slices"
"sort"
"strings"
"time"
Expand Down Expand Up @@ -75,7 +77,7 @@ func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string, confidences Co
}
}

order := append(append(CveContentTypes{Nvd}, GetCveContentTypes(myFamily)...), GitHub)
order := append(append(CveContentTypes{Mitre, Nvd}, GetCveContentTypes(myFamily)...), GitHub)
for _, ctype := range order {
if conts, found := v[ctype]; found {
for _, cont := range conts {
Expand All @@ -87,15 +89,9 @@ func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string, confidences Co
}
}

jvnMatch := false
for _, confidence := range confidences {
if confidence.DetectionMethod == JvnVendorProductMatchStr {
jvnMatch = true
break
}
}

if lang == "ja" || jvnMatch {
if lang == "ja" || slices.ContainsFunc(confidences, func(e Confidence) bool {
return e.DetectionMethod == JvnVendorProductMatchStr
}) {
if conts, found := v[Jvn]; found {
for _, cont := range conts {
if 0 < len(cont.SourceLink) {
Expand Down Expand Up @@ -159,7 +155,7 @@ func (v CveContents) Cpes(myFamily string) (values []CveContentCpes) {
return
}

// CveContentRefs has CveContentType and Cpes
// CveContentRefs has CveContentType and References
type CveContentRefs struct {
Type CveContentType
Value []Reference
Expand Down Expand Up @@ -224,20 +220,54 @@ func (v CveContents) UniqCweIDs(myFamily string) (values []CveContentStr) {
return values
}

// CveContentSSVCs has CveContentType and SSVC
type CveContentSSVC struct {
Type CveContentType
Value SSVC
}

func (v CveContents) SSVC() (value []CveContentSSVC) {
conts, ok := v[Mitre]
if !ok {
return
}
for _, cont := range conts {
if cont.SSVC == nil {
continue
}
t := Mitre
if s, ok := cont.Optional["source"]; ok {
t = CveContentType(fmt.Sprintf("%s(%s)", Mitre, s))
}
value = append(value, CveContentSSVC{
Type: t,
Value: *cont.SSVC,
})
}
return
}

// Sort elements for integration-testing
func (v CveContents) Sort() {
for contType, contents := range v {
// CVSS3 desc, CVSS2 desc, SourceLink asc
// CVSS40 desc, CVSS3 desc, CVSS2 desc, SourceLink asc
sort.Slice(contents, func(i, j int) bool {
if contents[i].Cvss3Score > contents[j].Cvss3Score {
if contents[i].Cvss40Score > contents[j].Cvss40Score {
return true
} else if contents[i].Cvss3Score == contents[i].Cvss3Score {
if contents[i].Cvss2Score > contents[j].Cvss2Score {
}
if contents[i].Cvss40Score == contents[j].Cvss40Score {
if contents[i].Cvss3Score > contents[j].Cvss3Score {
return true
} else if contents[i].Cvss2Score == contents[i].Cvss2Score {
if contents[i].SourceLink < contents[j].SourceLink {
}
if contents[i].Cvss3Score == contents[i].Cvss3Score {
if contents[i].Cvss2Score > contents[j].Cvss2Score {
return true
}
if contents[i].Cvss2Score == contents[i].Cvss2Score {
if contents[i].SourceLink < contents[j].SourceLink {
return true
}
}
}
}
return false
Expand Down Expand Up @@ -267,23 +297,27 @@ func (v CveContents) Sort() {

// CveContent has abstraction of various vulnerability information
type CveContent struct {
Type CveContentType `json:"type"`
CveID string `json:"cveID"`
Title string `json:"title"`
Summary string `json:"summary"`
Cvss2Score float64 `json:"cvss2Score"`
Cvss2Vector string `json:"cvss2Vector"`
Cvss2Severity string `json:"cvss2Severity"`
Cvss3Score float64 `json:"cvss3Score"`
Cvss3Vector string `json:"cvss3Vector"`
Cvss3Severity string `json:"cvss3Severity"`
SourceLink string `json:"sourceLink"`
Cpes []Cpe `json:"cpes,omitempty"`
References References `json:"references,omitempty"`
CweIDs []string `json:"cweIDs,omitempty"`
Published time.Time `json:"published"`
LastModified time.Time `json:"lastModified"`
Optional map[string]string `json:"optional,omitempty"`
Type CveContentType `json:"type"`
CveID string `json:"cveID"`
Title string `json:"title"`
Summary string `json:"summary"`
Cvss2Score float64 `json:"cvss2Score"`
Cvss2Vector string `json:"cvss2Vector"`
Cvss2Severity string `json:"cvss2Severity"`
Cvss3Score float64 `json:"cvss3Score"`
Cvss3Vector string `json:"cvss3Vector"`
Cvss3Severity string `json:"cvss3Severity"`
Cvss40Score float64 `json:"cvss40Score"`
Cvss40Vector string `json:"cvss40Vector"`
Cvss40Severity string `json:"cvss40Severity"`
SSVC *SSVC `json:"ssvc,omitempty"`
SourceLink string `json:"sourceLink"`
Cpes []Cpe `json:"cpes,omitempty"`
References References `json:"references,omitempty"`
CweIDs []string `json:"cweIDs,omitempty"`
Published time.Time `json:"published"`
LastModified time.Time `json:"lastModified"`
Optional map[string]string `json:"optional,omitempty"`
}

// Empty checks the content is empty
Expand All @@ -297,6 +331,8 @@ type CveContentType string
// NewCveContentType create CveContentType
func NewCveContentType(name string) CveContentType {
switch name {
case "mitre":
return Mitre
case "nvd":
return Nvd
case "jvn":
Expand Down Expand Up @@ -415,6 +451,9 @@ func GetCveContentTypes(family string) []CveContentType {
}

const (
// Mitre is Mitre
Mitre CveContentType = "mitre"

// Nvd is Nvd JSON
Nvd CveContentType = "nvd"

Expand Down Expand Up @@ -556,6 +595,7 @@ type CveContentTypes []CveContentType

// AllCveContetTypes has all of CveContentTypes
var AllCveContetTypes = CveContentTypes{
Mitre,
Nvd,
Jvn,
Fortinet,
Expand Down Expand Up @@ -633,3 +673,10 @@ type Reference struct {
RefID string `json:"refID,omitempty"`
Tags []string `json:"tags,omitempty"`
}

// SSVC has SSVC decision points
type SSVC struct {
Exploitation string `json:"exploitation,omitempty"`
Automatable string `json:"automatable,omitempty"`
TechnicalImpact string `json:"technical_impact,omitempty"`
}
92 changes: 92 additions & 0 deletions models/cvecontents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,48 @@ func TestCveContents_Sort(t *testing.T) {
},
},
},
{
name: "sort CVSS v4.0",
v: CveContents{
Mitre: []CveContent{
{Cvss40Score: 0},
{Cvss40Score: 6.9},
},
},
want: CveContents{
Mitre: []CveContent{
{Cvss40Score: 6.9},
{Cvss40Score: 0},
},
},
},
{
name: "sort CVSS v4.0 and CVSS v3",
v: CveContents{
Mitre: []CveContent{
{
Cvss40Score: 0,
Cvss3Score: 7.3,
},
{
Cvss40Score: 0,
Cvss3Score: 9.8,
},
},
},
want: CveContents{
Mitre: []CveContent{
{
Cvss40Score: 0,
Cvss3Score: 9.8,
},
{
Cvss40Score: 0,
Cvss3Score: 7.3,
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -309,3 +351,53 @@ func TestGetCveContentTypes(t *testing.T) {
})
}
}

func TestCveContents_SSVC(t *testing.T) {
tests := []struct {
name string
v CveContents
want []CveContentSSVC
}{
{
name: "happy",
v: CveContents{
Mitre: []CveContent{
{
Type: Mitre,
CveID: "CVE-2024-5732",
Title: "Clash Proxy Port improper authentication",
Optional: map[string]string{"source": "CNA"},
},
{
Type: Mitre,
CveID: "CVE-2024-5732",
Title: "CISA ADP Vulnrichment",
SSVC: &SSVC{
Exploitation: "none",
Automatable: "no",
TechnicalImpact: "partial",
},
Optional: map[string]string{"source": "ADP:CISA-ADP"},
},
},
},
want: []CveContentSSVC{
{
Type: "mitre(ADP:CISA-ADP)",
Value: SSVC{
Exploitation: "none",
Automatable: "no",
TechnicalImpact: "partial",
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.v.SSVC(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("CveContents.SSVC() = %v, want %v", got, tt.want)
}
})
}
}
Loading

0 comments on commit a7e725a

Please sign in to comment.