Skip to content

Commit 96bb598

Browse files
MaineK00nclaude
andcommitted
feat(detector): enrich MITRE CVE v5 via vuls2 instead of go-cve-dictionary
Add MITRE CVE v5 (mitre-cve-v5) as a vuls2 enrich data source and remove the MITRE path from FillCvesWithGoCVEDictionary so MITRE CveContent is now sourced from the vuls2 DB. This mirrors the NVD migration in #2575. - vuls2.go: add sourceTypes.MitreCVEV5 to the enrich DataSources filter. - vendor.go: add enrichMitreCVE mapping unified content to models.Mitre CveContent, and a Mitre case in cveContentSourceLink. - detector.go / models/utils.go: drop ConvertMitreToModel and its usage. - testdata: add a positive mitre-cve-v5 enrich test (CVE-2023-44487); move the "datasource not in filter" case to a new nvd-api-cve fixture. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 024b0e2 commit 96bb598

8 files changed

Lines changed: 119 additions & 145 deletions

File tree

detector/detector.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,8 @@ func DetectWordPressCves(r *models.ScanResult, wpCnf config.WpScanConf) error {
405405
return nil
406406
}
407407

408-
// FillCvesWithGoCVEDictionary fills CVE detail with VulnCheck, JVN, EUVD, Fortinet, MITRE, Paloalto, Cisco
409-
// (NVD CveContent/exploits/mitigations are filled by the vuls2 enrich path instead; NVD cert alerts still come from here)
408+
// FillCvesWithGoCVEDictionary fills CVE detail with VulnCheck, JVN, EUVD, Fortinet, Paloalto, Cisco
409+
// (NVD and MITRE CveContent are filled by the vuls2 enrich path instead; JP-CERT alerts still come from here)
410410
func FillCvesWithGoCVEDictionary(r *models.ScanResult, cnf config.GoCveDictConf, logOpts logging.LogOpts) (err error) {
411411
cveIDs := make([]string, 0, len(r.ScannedCves))
412412
for _, v := range r.ScannedCves {
@@ -433,7 +433,6 @@ func FillCvesWithGoCVEDictionary(r *models.ScanResult, cnf config.GoCveDictConf,
433433
jvns := models.ConvertJvnToModel(d.CveID, d.Jvns)
434434
euvds := models.ConvertEuvdToModel(d.CveID, d.Euvds)
435435
fortinets := models.ConvertFortinetToModel(d.CveID, d.Fortinets)
436-
mitres := models.ConvertMitreToModel(d.CveID, d.Mitres)
437436
paloaltos := models.ConvertPaloaltoToModel(d.CveID, d.Paloaltos)
438437
ciscos := models.ConvertCiscoToModel(d.CveID, d.Ciscos)
439438

@@ -462,9 +461,6 @@ func FillCvesWithGoCVEDictionary(r *models.ScanResult, cnf config.GoCveDictConf,
462461
}
463462
}
464463
}
465-
for _, con := range mitres {
466-
vinfo.CveContents[con.Type] = append(vinfo.CveContents[con.Type], con)
467-
}
468464
// Set only JP-CERT; US-CERT is filled by the vuls2 enrich path
469465
// (vuls2.EnrichVulnInfos runs before this) and must be preserved.
470466
vinfo.AlertDict.JPCERT = alerts.JPCERT

detector/vuls2/testdata/fixtures/enrich/mitre-cve-v5/data/CVE-2024-1102.json

Lines changed: 0 additions & 18 deletions
This file was deleted.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"id": "CVE-2020-0001",
3+
"vulnerabilities": [
4+
{
5+
"content": {
6+
"id": "CVE-2020-0001",
7+
"title": "from nvd-api-cve",
8+
"description": "This data source is not in the enrich DataSources filter, so it should never reach enrichVulnerabilities."
9+
}
10+
}
11+
],
12+
"data_source": {
13+
"id": "nvd-api-cve",
14+
"raws": [
15+
"fixtures/CVE-2020-0001.json"
16+
]
17+
}
18+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"id": "nvd-api-cve",
3+
"name": "NVD CVE API"
4+
}

detector/vuls2/vendor.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,8 @@ func cveContentSourceLink(ccType models.CveContentType, v vulnerabilityTypes.Vul
649649
return fmt.Sprintf("https://security.alpinelinux.org/vuln/%s", v.Content.ID)
650650
case models.Nvd:
651651
return fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", v.Content.ID)
652+
case models.Mitre:
653+
return fmt.Sprintf("https://www.cve.org/CVERecord?id=%s", v.Content.ID)
652654
case models.Microsoft:
653655
return fmt.Sprintf("https://msrc.microsoft.com/update-guide/vulnerability/%s", v.Content.ID)
654656
default:
@@ -1168,6 +1170,8 @@ func enrichVulnerabilities(vi *models.VulnInfo, vulns []dbTypes.VulnerabilityDat
11681170
enrichRedHatCVE(vi, rootMap)
11691171
case sourceTypes.NVDFeedCVEv2:
11701172
enrichNVD(vi, rootMap)
1173+
case sourceTypes.MitreCVEV5:
1174+
enrichMitreCVE(vi, rootMap)
11711175
case sourceTypes.Metasploit:
11721176
enrichMetasploit(vi, rootMap)
11731177
case sourceTypes.ExploitExploitDB, sourceTypes.ExploitGitHub, sourceTypes.ExploitInTheWild, sourceTypes.ExploitTrickest, sourceTypes.NucleiRepository:
@@ -1378,6 +1382,60 @@ func enrichNVD(vi *models.VulnInfo, rootMap map[dataTypes.RootID][]vulnerability
13781382
}
13791383
}
13801384

1385+
// enrichMitreCVE adds MITRE CVE v5 data as CveContent.
1386+
func enrichMitreCVE(vi *models.VulnInfo, rootMap map[dataTypes.RootID][]vulnerabilityTypes.Vulnerability) {
1387+
if _, ok := vi.CveContents[models.Mitre]; ok {
1388+
return
1389+
}
1390+
for _, vulns := range rootMap {
1391+
for _, v := range vulns {
1392+
cvss2, cvss3, cvss40 := enrichCvss(v.Content.Severity)
1393+
1394+
var rs models.References
1395+
for _, r := range v.Content.References {
1396+
rs = append(rs, toReference(r.URL))
1397+
}
1398+
1399+
vi.CveContents[models.Mitre] = append(vi.CveContents[models.Mitre], models.CveContent{
1400+
Type: models.Mitre,
1401+
CveID: string(v.Content.ID),
1402+
Title: v.Content.Title,
1403+
Summary: v.Content.Description,
1404+
Cvss2Score: cvss2.BaseScore,
1405+
Cvss2Vector: cvss2.Vector,
1406+
Cvss2Severity: cvss2.NVDBaseSeverity,
1407+
Cvss3Score: cvss3.BaseScore,
1408+
Cvss3Vector: cvss3.Vector,
1409+
Cvss3Severity: cvss3.BaseSeverity,
1410+
Cvss40Score: cvss40.Score,
1411+
Cvss40Vector: cvss40.Vector,
1412+
Cvss40Severity: cvss40.Severity,
1413+
SourceLink: cveContentSourceLink(models.Mitre, v),
1414+
References: rs,
1415+
CweIDs: func() []string {
1416+
var cs []string //nolint:prealloc
1417+
for _, cwe := range v.Content.CWE {
1418+
cs = append(cs, cwe.CWE...)
1419+
}
1420+
return cs
1421+
}(),
1422+
Published: func() time.Time {
1423+
if v.Content.Published != nil {
1424+
return *v.Content.Published
1425+
}
1426+
return time.Date(1000, time.January, 1, 0, 0, 0, 0, time.UTC)
1427+
}(),
1428+
LastModified: func() time.Time {
1429+
if v.Content.Modified != nil {
1430+
return *v.Content.Modified
1431+
}
1432+
return time.Date(1000, time.January, 1, 0, 0, 0, 0, time.UTC)
1433+
}(),
1434+
})
1435+
}
1436+
}
1437+
}
1438+
13811439
// enrichVulnerabilityKEV extracts KEV data from vulnerability content and maps it to models.KEV.
13821440
// Handles CISA and VulnCheck KEV sources where KEV data is stored in vulnerability content.
13831441
func enrichVulnerabilityKEV(sourceID sourceTypes.SourceID, rootMap map[dataTypes.RootID][]vulnerabilityTypes.Vulnerability) []models.KEV {

detector/vuls2/vuls2.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1806,6 +1806,7 @@ func enrich(sesh *session.Session, vim models.VulnInfos) error {
18061806
sourceTypes.ExploitInTheWild,
18071807
sourceTypes.ExploitTrickest,
18081808
sourceTypes.Metasploit,
1809+
sourceTypes.MitreCVEV5,
18091810
sourceTypes.NVDFeedCVEv2,
18101811
sourceTypes.NucleiRepository,
18111812
sourceTypes.RedHatCVE,

detector/vuls2/vuls2_test.go

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11146,7 +11146,7 @@ func Test_enrich(t *testing.T) {
1114611146
want: models.VulnInfos{},
1114711147
},
1114811148
{
11149-
name: "datasource not in enrich filter is filtered out",
11149+
name: "enrich with mitre-cve-v5 data",
1115011150
args: args{
1115111151
vim: models.VulnInfos{
1115211152
"CVE-2023-44487": models.VulnInfo{
@@ -11156,7 +11156,41 @@ func Test_enrich(t *testing.T) {
1115611156
},
1115711157
want: models.VulnInfos{
1115811158
"CVE-2023-44487": models.VulnInfo{
11159-
CveID: "CVE-2023-44487",
11159+
CveID: "CVE-2023-44487",
11160+
CveContents: models.CveContents{
11161+
models.Mitre: []models.CveContent{
11162+
{
11163+
Type: models.Mitre,
11164+
CveID: "CVE-2023-44487",
11165+
Title: "HTTP/2 Rapid Reset Attack",
11166+
Summary: "The HTTP/2 protocol allows a denial of service (server resource consumption) because request cancellation can reset many streams quickly, as exploited in the wild in August through October 2023.",
11167+
Cvss3Score: 7.5,
11168+
Cvss3Vector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
11169+
Cvss3Severity: "HIGH",
11170+
SourceLink: "https://www.cve.org/CVERecord?id=CVE-2023-44487",
11171+
References: models.References{
11172+
{Link: "https://www.cve.org/CVERecord?id=CVE-2023-44487", Source: "CVE", RefID: "CVE-2023-44487"},
11173+
},
11174+
Published: time.Date(2023, 10, 10, 0, 0, 0, 0, time.UTC),
11175+
LastModified: time.Date(1000, 1, 1, 0, 0, 0, 0, time.UTC),
11176+
},
11177+
},
11178+
},
11179+
},
11180+
},
11181+
},
11182+
{
11183+
name: "datasource not in enrich filter is filtered out",
11184+
args: args{
11185+
vim: models.VulnInfos{
11186+
"CVE-2020-0001": models.VulnInfo{
11187+
CveID: "CVE-2020-0001",
11188+
},
11189+
},
11190+
},
11191+
want: models.VulnInfos{
11192+
"CVE-2020-0001": models.VulnInfo{
11193+
CveID: "CVE-2020-0001",
1116011194
CveContents: models.CveContents{},
1116111195
},
1116211196
},

models/utils.go

Lines changed: 0 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -350,125 +350,6 @@ func ConvertFortinetToModel(cveID string, fortinets []cvedict.Fortinet) []CveCon
350350
return cves
351351
}
352352

353-
// ConvertMitreToModel convert Mitre to CveContent
354-
func ConvertMitreToModel(cveID string, mitres []cvedict.Mitre) []CveContent {
355-
var cves []CveContent
356-
for _, mitre := range mitres {
357-
for _, c := range mitre.Containers {
358-
cve := CveContent{
359-
Type: Mitre,
360-
CveID: cveID,
361-
Title: func() string {
362-
if c.Title != nil {
363-
return *c.Title
364-
}
365-
return ""
366-
}(),
367-
Summary: func() string {
368-
for _, d := range c.Descriptions {
369-
if d.Lang == "en" {
370-
return d.Value
371-
}
372-
}
373-
return ""
374-
}(),
375-
SourceLink: fmt.Sprintf("https://www.cve.org/CVERecord?id=%s", cveID),
376-
Published: func() time.Time {
377-
if mitre.CVEMetadata.DatePublished != nil {
378-
return *mitre.CVEMetadata.DatePublished
379-
}
380-
return time.Time{}
381-
}(),
382-
LastModified: func() time.Time {
383-
if mitre.CVEMetadata.DateUpdated != nil {
384-
return *mitre.CVEMetadata.DateUpdated
385-
}
386-
if mitre.CVEMetadata.DatePublished != nil {
387-
return *mitre.CVEMetadata.DatePublished
388-
}
389-
return time.Time{}
390-
}(),
391-
Optional: map[string]string{"source": func() string {
392-
if c.ProviderMetadata.ShortName != nil {
393-
return fmt.Sprintf("%s:%s", c.ContainerType, *c.ProviderMetadata.ShortName)
394-
}
395-
return fmt.Sprintf("%s:%s", c.ContainerType, c.ProviderMetadata.OrgID)
396-
}()},
397-
}
398-
399-
for _, m := range c.Metrics {
400-
if m.CVSSv2 != nil {
401-
cve.Cvss2Score = m.CVSSv2.BaseScore
402-
cve.Cvss2Vector = m.CVSSv2.VectorString
403-
}
404-
if m.CVSSv30 != nil {
405-
if cve.Cvss3Vector == "" {
406-
cve.Cvss3Score = m.CVSSv30.BaseScore
407-
cve.Cvss3Vector = m.CVSSv30.VectorString
408-
cve.Cvss3Severity = m.CVSSv30.BaseSeverity
409-
}
410-
}
411-
if m.CVSSv31 != nil {
412-
cve.Cvss3Score = m.CVSSv31.BaseScore
413-
cve.Cvss3Vector = m.CVSSv31.VectorString
414-
cve.Cvss3Severity = m.CVSSv31.BaseSeverity
415-
}
416-
if m.CVSSv40 != nil {
417-
cve.Cvss40Score = m.CVSSv40.BaseScore
418-
cve.Cvss40Vector = m.CVSSv40.VectorString
419-
cve.Cvss40Severity = m.CVSSv40.BaseSeverity
420-
}
421-
if m.SSVC != nil {
422-
cve.SSVC = &SSVC{
423-
Exploitation: func() string {
424-
if m.SSVC.Exploitation != nil {
425-
return *m.SSVC.Exploitation
426-
}
427-
return ""
428-
}(),
429-
Automatable: func() string {
430-
if m.SSVC.Automatable != nil {
431-
return *m.SSVC.Automatable
432-
}
433-
return ""
434-
}(),
435-
TechnicalImpact: func() string {
436-
if m.SSVC.TechnicalImpact != nil {
437-
return *m.SSVC.TechnicalImpact
438-
}
439-
return ""
440-
}(),
441-
}
442-
}
443-
}
444-
445-
for _, r := range c.References {
446-
cve.References = append(cve.References, Reference{
447-
Link: r.Link,
448-
Source: r.Source,
449-
Tags: func() []string {
450-
if len(r.Tags) > 0 {
451-
return strings.Split(r.Tags, ",")
452-
}
453-
return nil
454-
}(),
455-
})
456-
}
457-
458-
for _, p := range c.ProblemTypes {
459-
for _, d := range p.Descriptions {
460-
if d.CweID != nil {
461-
cve.CweIDs = append(cve.CweIDs, *d.CweID)
462-
}
463-
}
464-
}
465-
466-
cves = append(cves, cve)
467-
}
468-
}
469-
return cves
470-
}
471-
472353
// ConvertPaloaltoToModel convert Paloalto to CveContent
473354
func ConvertPaloaltoToModel(cveID string, paloaltos []cvedict.Paloalto) []CveContent {
474355
cves := make([]CveContent, 0, len(paloaltos))

0 commit comments

Comments
 (0)