feat(cve/mitre): support go-cve-dictionary:mitre (#1978)
* feat(cve/mitre): support go-cve-dictionary:mitre * chore: adopt reviewer comment * refactor(models): refactor CveContents method
This commit is contained in:
		@@ -1,10 +1,14 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sort"
 | 
			
		||||
	"cmp"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"slices"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/exp/maps"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -15,18 +19,14 @@ type CveContents map[CveContentType][]CveContent
 | 
			
		||||
func NewCveContents(conts ...CveContent) CveContents {
 | 
			
		||||
	m := CveContents{}
 | 
			
		||||
	for _, cont := range conts {
 | 
			
		||||
		if cont.Type == Jvn {
 | 
			
		||||
			found := false
 | 
			
		||||
			for _, cveCont := range m[cont.Type] {
 | 
			
		||||
				if cont.SourceLink == cveCont.SourceLink {
 | 
			
		||||
					found = true
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if !found {
 | 
			
		||||
		switch cont.Type {
 | 
			
		||||
		case Jvn:
 | 
			
		||||
			if !slices.ContainsFunc(m[cont.Type], func(e CveContent) bool {
 | 
			
		||||
				return cont.SourceLink == e.SourceLink
 | 
			
		||||
			}) {
 | 
			
		||||
				m[cont.Type] = append(m[cont.Type], cont)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
		default:
 | 
			
		||||
			m[cont.Type] = []CveContent{cont}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -43,14 +43,7 @@ type CveContentStr struct {
 | 
			
		||||
func (v CveContents) Except(exceptCtypes ...CveContentType) (values CveContents) {
 | 
			
		||||
	values = CveContents{}
 | 
			
		||||
	for ctype, content := range v {
 | 
			
		||||
		found := false
 | 
			
		||||
		for _, exceptCtype := range exceptCtypes {
 | 
			
		||||
			if ctype == exceptCtype {
 | 
			
		||||
				found = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !found {
 | 
			
		||||
		if !slices.Contains(exceptCtypes, ctype) {
 | 
			
		||||
			values[ctype] = content
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -63,43 +56,51 @@ func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string, confidences Co
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if conts, found := v[Nvd]; found {
 | 
			
		||||
		for _, cont := range conts {
 | 
			
		||||
			for _, r := range cont.References {
 | 
			
		||||
				for _, t := range r.Tags {
 | 
			
		||||
					if t == "Vendor Advisory" {
 | 
			
		||||
						values = append(values, CveContentStr{Nvd, r.Link})
 | 
			
		||||
	for _, ctype := range append(append(CveContentTypes{Mitre, Nvd, Jvn}, GetCveContentTypes(myFamily)...), GitHub) {
 | 
			
		||||
		for _, cont := range v[ctype] {
 | 
			
		||||
			switch ctype {
 | 
			
		||||
			case Nvd:
 | 
			
		||||
				for _, r := range cont.References {
 | 
			
		||||
					if slices.Contains(r.Tags, "Vendor Advisory") {
 | 
			
		||||
						if !slices.ContainsFunc(values, func(e CveContentStr) bool {
 | 
			
		||||
							return e.Type == ctype && e.Value == r.Link
 | 
			
		||||
						}) {
 | 
			
		||||
							values = append(values, CveContentStr{
 | 
			
		||||
								Type:  ctype,
 | 
			
		||||
								Value: r.Link,
 | 
			
		||||
							})
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	order := append(append(CveContentTypes{Nvd}, GetCveContentTypes(myFamily)...), GitHub)
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if conts, found := v[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if cont.SourceLink == "" {
 | 
			
		||||
					continue
 | 
			
		||||
				if cont.SourceLink != "" && !slices.ContainsFunc(values, func(e CveContentStr) bool {
 | 
			
		||||
					return e.Type == ctype && e.Value == cont.SourceLink
 | 
			
		||||
				}) {
 | 
			
		||||
					values = append(values, CveContentStr{
 | 
			
		||||
						Type:  ctype,
 | 
			
		||||
						Value: cont.SourceLink,
 | 
			
		||||
					})
 | 
			
		||||
				}
 | 
			
		||||
				values = append(values, CveContentStr{ctype, cont.SourceLink})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	jvnMatch := false
 | 
			
		||||
	for _, confidence := range confidences {
 | 
			
		||||
		if confidence.DetectionMethod == JvnVendorProductMatchStr {
 | 
			
		||||
			jvnMatch = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if lang == "ja" || jvnMatch {
 | 
			
		||||
		if conts, found := v[Jvn]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if 0 < len(cont.SourceLink) {
 | 
			
		||||
					values = append(values, CveContentStr{Jvn, cont.SourceLink})
 | 
			
		||||
			case Jvn:
 | 
			
		||||
				if lang == "ja" || slices.ContainsFunc(confidences, func(e Confidence) bool {
 | 
			
		||||
					return e.DetectionMethod == JvnVendorProductMatchStr
 | 
			
		||||
				}) {
 | 
			
		||||
					if cont.SourceLink != "" && !slices.ContainsFunc(values, func(e CveContentStr) bool {
 | 
			
		||||
						return e.Type == ctype && e.Value == cont.SourceLink
 | 
			
		||||
					}) {
 | 
			
		||||
						values = append(values, CveContentStr{
 | 
			
		||||
							Type:  ctype,
 | 
			
		||||
							Value: cont.SourceLink,
 | 
			
		||||
						})
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			default:
 | 
			
		||||
				if cont.SourceLink != "" && !slices.ContainsFunc(values, func(e CveContentStr) bool {
 | 
			
		||||
					return e.Type == ctype && e.Value == cont.SourceLink
 | 
			
		||||
				}) {
 | 
			
		||||
					values = append(values, CveContentStr{
 | 
			
		||||
						Type:  ctype,
 | 
			
		||||
						Value: cont.SourceLink,
 | 
			
		||||
					})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -108,7 +109,7 @@ func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string, confidences Co
 | 
			
		||||
	if len(values) == 0 && strings.HasPrefix(cveID, "CVE") {
 | 
			
		||||
		return []CveContentStr{{
 | 
			
		||||
			Type:  Nvd,
 | 
			
		||||
			Value: "https://nvd.nist.gov/vuln/detail/" + cveID,
 | 
			
		||||
			Value: fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", cveID),
 | 
			
		||||
		}}
 | 
			
		||||
	}
 | 
			
		||||
	return values
 | 
			
		||||
@@ -116,17 +117,10 @@ func (v CveContents) PrimarySrcURLs(lang, myFamily, cveID string, confidences Co
 | 
			
		||||
 | 
			
		||||
// PatchURLs returns link of patch
 | 
			
		||||
func (v CveContents) PatchURLs() (urls []string) {
 | 
			
		||||
	conts, found := v[Nvd]
 | 
			
		||||
	if !found {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, cont := range conts {
 | 
			
		||||
	for _, cont := range v[Nvd] {
 | 
			
		||||
		for _, r := range cont.References {
 | 
			
		||||
			for _, t := range r.Tags {
 | 
			
		||||
				if t == "Patch" {
 | 
			
		||||
					urls = append(urls, r.Link)
 | 
			
		||||
				}
 | 
			
		||||
			if slices.Contains(r.Tags, "Patch") && !slices.Contains(urls, r.Link) {
 | 
			
		||||
				urls = append(urls, r.Link)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -145,21 +139,24 @@ func (v CveContents) Cpes(myFamily string) (values []CveContentCpes) {
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(order...)...)
 | 
			
		||||
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if conts, found := v[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if 0 < len(cont.Cpes) {
 | 
			
		||||
					values = append(values, CveContentCpes{
 | 
			
		||||
						Type:  ctype,
 | 
			
		||||
						Value: cont.Cpes,
 | 
			
		||||
					})
 | 
			
		||||
				}
 | 
			
		||||
		for _, cont := range v[ctype] {
 | 
			
		||||
			if len(cont.Cpes) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if !slices.ContainsFunc(values, func(e CveContentCpes) bool {
 | 
			
		||||
				return e.Type == ctype && slices.Equal(e.Value, cont.Cpes)
 | 
			
		||||
			}) {
 | 
			
		||||
				values = append(values, CveContentCpes{
 | 
			
		||||
					Type:  ctype,
 | 
			
		||||
					Value: cont.Cpes,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CveContentRefs has CveContentType and Cpes
 | 
			
		||||
// CveContentRefs has CveContentType and References
 | 
			
		||||
type CveContentRefs struct {
 | 
			
		||||
	Type  CveContentType
 | 
			
		||||
	Value []Reference
 | 
			
		||||
@@ -171,14 +168,19 @@ func (v CveContents) References(myFamily string) (values []CveContentRefs) {
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(order...)...)
 | 
			
		||||
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if conts, found := v[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if 0 < len(cont.References) {
 | 
			
		||||
					values = append(values, CveContentRefs{
 | 
			
		||||
						Type:  ctype,
 | 
			
		||||
						Value: cont.References,
 | 
			
		||||
					})
 | 
			
		||||
				}
 | 
			
		||||
		for _, cont := range v[ctype] {
 | 
			
		||||
			if len(cont.References) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if !slices.ContainsFunc(values, func(e CveContentRefs) bool {
 | 
			
		||||
				return e.Type == ctype && slices.EqualFunc(e.Value, cont.References, func(e1, e2 Reference) bool {
 | 
			
		||||
					return e1.Link == e2.Link && e1.RefID == e2.RefID && e1.Source == e2.Source && slices.Equal(e1.Tags, e2.Tags)
 | 
			
		||||
				})
 | 
			
		||||
			}) {
 | 
			
		||||
				values = append(values, CveContentRefs{
 | 
			
		||||
					Type:  ctype,
 | 
			
		||||
					Value: cont.References,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -191,20 +193,18 @@ func (v CveContents) CweIDs(myFamily string) (values []CveContentStr) {
 | 
			
		||||
	order := GetCveContentTypes(myFamily)
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(order...)...)
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if conts, found := v[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if 0 < len(cont.CweIDs) {
 | 
			
		||||
					for _, cweID := range cont.CweIDs {
 | 
			
		||||
						for _, val := range values {
 | 
			
		||||
							if val.Value == cweID {
 | 
			
		||||
								continue
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
						values = append(values, CveContentStr{
 | 
			
		||||
							Type:  ctype,
 | 
			
		||||
							Value: cweID,
 | 
			
		||||
						})
 | 
			
		||||
					}
 | 
			
		||||
		for _, cont := range v[ctype] {
 | 
			
		||||
			if len(cont.CweIDs) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			for _, cweID := range cont.CweIDs {
 | 
			
		||||
				if !slices.ContainsFunc(values, func(e CveContentStr) bool {
 | 
			
		||||
					return e.Type == ctype && e.Value == cweID
 | 
			
		||||
				}) {
 | 
			
		||||
					values = append(values, CveContentStr{
 | 
			
		||||
						Type:  ctype,
 | 
			
		||||
						Value: cweID,
 | 
			
		||||
					})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -213,52 +213,55 @@ func (v CveContents) CweIDs(myFamily string) (values []CveContentStr) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UniqCweIDs returns Uniq CweIDs
 | 
			
		||||
func (v CveContents) UniqCweIDs(myFamily string) (values []CveContentStr) {
 | 
			
		||||
func (v CveContents) UniqCweIDs(myFamily string) []CveContentStr {
 | 
			
		||||
	uniq := map[string]CveContentStr{}
 | 
			
		||||
	for _, cwes := range v.CweIDs(myFamily) {
 | 
			
		||||
		uniq[cwes.Value] = cwes
 | 
			
		||||
	}
 | 
			
		||||
	for _, cwe := range uniq {
 | 
			
		||||
		values = append(values, cwe)
 | 
			
		||||
	return maps.Values(uniq)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CveContentSSVC has CveContentType and SSVC
 | 
			
		||||
type CveContentSSVC struct {
 | 
			
		||||
	Type  CveContentType
 | 
			
		||||
	Value SSVC
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v CveContents) SSVC() (value []CveContentSSVC) {
 | 
			
		||||
	for _, cont := range v[Mitre] {
 | 
			
		||||
		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 values
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sort elements for integration-testing
 | 
			
		||||
func (v CveContents) Sort() {
 | 
			
		||||
	for contType, contents := range v {
 | 
			
		||||
		// CVSS3 desc, CVSS2 desc, SourceLink asc
 | 
			
		||||
		sort.Slice(contents, func(i, j int) bool {
 | 
			
		||||
			if contents[i].Cvss3Score > contents[j].Cvss3Score {
 | 
			
		||||
				return true
 | 
			
		||||
			} else if contents[i].Cvss3Score == contents[i].Cvss3Score {
 | 
			
		||||
				if contents[i].Cvss2Score > contents[j].Cvss2Score {
 | 
			
		||||
					return true
 | 
			
		||||
				} else if contents[i].Cvss2Score == contents[i].Cvss2Score {
 | 
			
		||||
					if contents[i].SourceLink < contents[j].SourceLink {
 | 
			
		||||
						return true
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return false
 | 
			
		||||
		// CVSS40 desc, CVSS3 desc, CVSS2 desc, SourceLink asc
 | 
			
		||||
		slices.SortFunc(contents, func(a, b CveContent) int {
 | 
			
		||||
			return cmp.Or(
 | 
			
		||||
				cmp.Compare(b.Cvss40Score, a.Cvss40Score),
 | 
			
		||||
				cmp.Compare(b.Cvss3Score, a.Cvss3Score),
 | 
			
		||||
				cmp.Compare(b.Cvss2Score, a.Cvss2Score),
 | 
			
		||||
				cmp.Compare(a.SourceLink, b.SourceLink),
 | 
			
		||||
			)
 | 
			
		||||
		})
 | 
			
		||||
		v[contType] = contents
 | 
			
		||||
	}
 | 
			
		||||
	for contType, contents := range v {
 | 
			
		||||
		for cveID, cont := range contents {
 | 
			
		||||
			sort.Slice(cont.References, func(i, j int) bool {
 | 
			
		||||
				return cont.References[i].Link < cont.References[j].Link
 | 
			
		||||
			})
 | 
			
		||||
			sort.Slice(cont.CweIDs, func(i, j int) bool {
 | 
			
		||||
				return cont.CweIDs[i] < cont.CweIDs[j]
 | 
			
		||||
			})
 | 
			
		||||
			for i, ref := range cont.References {
 | 
			
		||||
				// sort v.CveContents[].References[].Tags
 | 
			
		||||
				sort.Slice(ref.Tags, func(j, k int) bool {
 | 
			
		||||
					return ref.Tags[j] < ref.Tags[k]
 | 
			
		||||
				})
 | 
			
		||||
				cont.References[i] = ref
 | 
			
		||||
			slices.SortFunc(cont.References, func(a, b Reference) int { return cmp.Compare(a.Link, b.Link) })
 | 
			
		||||
			for i := range cont.References {
 | 
			
		||||
				slices.Sort(cont.References[i].Tags)
 | 
			
		||||
			}
 | 
			
		||||
			slices.Sort(cont.CweIDs)
 | 
			
		||||
			contents[cveID] = cont
 | 
			
		||||
		}
 | 
			
		||||
		v[contType] = contents
 | 
			
		||||
@@ -267,23 +270,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
 | 
			
		||||
@@ -297,6 +304,8 @@ type CveContentType string
 | 
			
		||||
// NewCveContentType create CveContentType
 | 
			
		||||
func NewCveContentType(name string) CveContentType {
 | 
			
		||||
	switch name {
 | 
			
		||||
	case "mitre":
 | 
			
		||||
		return Mitre
 | 
			
		||||
	case "nvd":
 | 
			
		||||
		return Nvd
 | 
			
		||||
	case "jvn":
 | 
			
		||||
@@ -415,6 +424,9 @@ func GetCveContentTypes(family string) []CveContentType {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// Mitre is Mitre
 | 
			
		||||
	Mitre CveContentType = "mitre"
 | 
			
		||||
 | 
			
		||||
	// Nvd is Nvd JSON
 | 
			
		||||
	Nvd CveContentType = "nvd"
 | 
			
		||||
 | 
			
		||||
@@ -556,6 +568,7 @@ type CveContentTypes []CveContentType
 | 
			
		||||
 | 
			
		||||
// AllCveContetTypes has all of CveContentTypes
 | 
			
		||||
var AllCveContetTypes = CveContentTypes{
 | 
			
		||||
	Mitre,
 | 
			
		||||
	Nvd,
 | 
			
		||||
	Jvn,
 | 
			
		||||
	Fortinet,
 | 
			
		||||
@@ -603,14 +616,7 @@ var AllCveContetTypes = CveContentTypes{
 | 
			
		||||
// 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 {
 | 
			
		||||
		if !slices.Contains(excepts, ctype) {
 | 
			
		||||
			excepted = append(excepted, ctype)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -633,3 +639,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"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,26 +7,37 @@ import (
 | 
			
		||||
	"github.com/future-architect/vuls/constant"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestExcept(t *testing.T) {
 | 
			
		||||
	var tests = []struct {
 | 
			
		||||
		in  CveContents
 | 
			
		||||
		out CveContents
 | 
			
		||||
	}{{
 | 
			
		||||
		in: CveContents{
 | 
			
		||||
			RedHat: []CveContent{{Type: RedHat}},
 | 
			
		||||
			Ubuntu: []CveContent{{Type: Ubuntu}},
 | 
			
		||||
			Debian: []CveContent{{Type: Debian}},
 | 
			
		||||
func TestCveContents_Except(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		exceptCtypes []CveContentType
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name       string
 | 
			
		||||
		v          CveContents
 | 
			
		||||
		args       args
 | 
			
		||||
		wantValues CveContents
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "happy",
 | 
			
		||||
			v: CveContents{
 | 
			
		||||
				RedHat: []CveContent{{Type: RedHat}},
 | 
			
		||||
				Ubuntu: []CveContent{{Type: Ubuntu}},
 | 
			
		||||
				Debian: []CveContent{{Type: Debian}},
 | 
			
		||||
			},
 | 
			
		||||
			args: args{
 | 
			
		||||
				exceptCtypes: []CveContentType{Ubuntu, Debian},
 | 
			
		||||
			},
 | 
			
		||||
			wantValues: CveContents{
 | 
			
		||||
				RedHat: []CveContent{{Type: RedHat}},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		out: CveContents{
 | 
			
		||||
			RedHat: []CveContent{{Type: RedHat}},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		actual := tt.in.Except(Ubuntu, Debian)
 | 
			
		||||
		if !reflect.DeepEqual(tt.out, actual) {
 | 
			
		||||
			t.Errorf("\nexpected: %v\n  actual: %v\n", tt.out, actual)
 | 
			
		||||
		}
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if gotValues := tt.v.Except(tt.args.exceptCtypes...); !reflect.DeepEqual(gotValues, tt.wantValues) {
 | 
			
		||||
				t.Errorf("CveContents.Except() = %v, want %v", gotValues, tt.wantValues)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -84,14 +95,14 @@ func TestSourceLinks(t *testing.T) {
 | 
			
		||||
					Type:  Nvd,
 | 
			
		||||
					Value: "https://nvd.nist.gov/vuln/detail/CVE-2017-6074",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type:  RedHat,
 | 
			
		||||
					Value: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type:  Jvn,
 | 
			
		||||
					Value: "https://jvn.jp/vu/JVNVU93610402/",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type:  RedHat,
 | 
			
		||||
					Value: "https://access.redhat.com/security/cve/CVE-2017-6074",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		// lang: en
 | 
			
		||||
@@ -162,6 +173,294 @@ func TestSourceLinks(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCveContents_PatchURLs(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name     string
 | 
			
		||||
		v        CveContents
 | 
			
		||||
		wantUrls []string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "happy",
 | 
			
		||||
			v: CveContents{
 | 
			
		||||
				Nvd: []CveContent{
 | 
			
		||||
					{
 | 
			
		||||
						References: []Reference{
 | 
			
		||||
							{
 | 
			
		||||
								Link:   "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c52873e5a1ef72f845526d9f6a50704433f9c625",
 | 
			
		||||
								Source: "cve@mitre.org",
 | 
			
		||||
								Tags:   []string{"Patch", "Vendor Advisory"},
 | 
			
		||||
							},
 | 
			
		||||
							{
 | 
			
		||||
								Link:   "https://lists.debian.org/debian-lts-announce/2020/01/msg00013.html",
 | 
			
		||||
								Source: "cve@mitre.org",
 | 
			
		||||
								Tags:   []string{"Mailing List", "Third Party Advisory"},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						References: []Reference{
 | 
			
		||||
							{
 | 
			
		||||
								Link: "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c52873e5a1ef72f845526d9f6a50704433f9c625",
 | 
			
		||||
								Tags: []string{"Patch", "Vendor Advisory"},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantUrls: []string{"https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c52873e5a1ef72f845526d9f6a50704433f9c625"},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if gotUrls := tt.v.PatchURLs(); !reflect.DeepEqual(gotUrls, tt.wantUrls) {
 | 
			
		||||
				t.Errorf("CveContents.PatchURLs() = %v, want %v", gotUrls, tt.wantUrls)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCveContents_Cpes(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		myFamily string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name       string
 | 
			
		||||
		v          CveContents
 | 
			
		||||
		args       args
 | 
			
		||||
		wantValues []CveContentCpes
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "happy",
 | 
			
		||||
			v: CveContents{
 | 
			
		||||
				Nvd: []CveContent{{
 | 
			
		||||
					Cpes: []Cpe{{
 | 
			
		||||
						URI:             "cpe:/a:microsoft:internet_explorer:8.0.6001:beta",
 | 
			
		||||
						FormattedString: "cpe:2.3:a:microsoft:internet_explorer:8.0.6001:beta:*:*:*:*:*:*",
 | 
			
		||||
					}},
 | 
			
		||||
				}},
 | 
			
		||||
			},
 | 
			
		||||
			args: args{myFamily: "redhat"},
 | 
			
		||||
			wantValues: []CveContentCpes{{
 | 
			
		||||
				Type: Nvd,
 | 
			
		||||
				Value: []Cpe{{
 | 
			
		||||
					URI:             "cpe:/a:microsoft:internet_explorer:8.0.6001:beta",
 | 
			
		||||
					FormattedString: "cpe:2.3:a:microsoft:internet_explorer:8.0.6001:beta:*:*:*:*:*:*",
 | 
			
		||||
				}},
 | 
			
		||||
			}},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if gotValues := tt.v.Cpes(tt.args.myFamily); !reflect.DeepEqual(gotValues, tt.wantValues) {
 | 
			
		||||
				t.Errorf("CveContents.Cpes() = %v, want %v", gotValues, tt.wantValues)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
func TestCveContents_References(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		myFamily string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name       string
 | 
			
		||||
		v          CveContents
 | 
			
		||||
		args       args
 | 
			
		||||
		wantValues []CveContentRefs
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "happy",
 | 
			
		||||
			v: CveContents{
 | 
			
		||||
				Mitre: []CveContent{{CveID: "CVE-2024-0001"}},
 | 
			
		||||
				Nvd: []CveContent{
 | 
			
		||||
					{
 | 
			
		||||
						References: []Reference{
 | 
			
		||||
							{
 | 
			
		||||
								Link:   "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c52873e5a1ef72f845526d9f6a50704433f9c625",
 | 
			
		||||
								Source: "cve@mitre.org",
 | 
			
		||||
								Tags:   []string{"Patch", "Vendor Advisory"},
 | 
			
		||||
							},
 | 
			
		||||
							{
 | 
			
		||||
								Link:   "https://lists.debian.org/debian-lts-announce/2020/01/msg00013.html",
 | 
			
		||||
								Source: "cve@mitre.org",
 | 
			
		||||
								Tags:   []string{"Mailing List", "Third Party Advisory"},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						References: []Reference{
 | 
			
		||||
							{
 | 
			
		||||
								Link: "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c52873e5a1ef72f845526d9f6a50704433f9c625",
 | 
			
		||||
								Tags: []string{"Patch", "Vendor Advisory"},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			wantValues: []CveContentRefs{
 | 
			
		||||
				{
 | 
			
		||||
					Type: Nvd,
 | 
			
		||||
					Value: []Reference{
 | 
			
		||||
						{
 | 
			
		||||
							Link:   "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c52873e5a1ef72f845526d9f6a50704433f9c625",
 | 
			
		||||
							Source: "cve@mitre.org",
 | 
			
		||||
							Tags:   []string{"Patch", "Vendor Advisory"},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Link:   "https://lists.debian.org/debian-lts-announce/2020/01/msg00013.html",
 | 
			
		||||
							Source: "cve@mitre.org",
 | 
			
		||||
							Tags:   []string{"Mailing List", "Third Party Advisory"},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type: Nvd,
 | 
			
		||||
					Value: []Reference{
 | 
			
		||||
						{
 | 
			
		||||
							Link: "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c52873e5a1ef72f845526d9f6a50704433f9c625",
 | 
			
		||||
							Tags: []string{"Patch", "Vendor Advisory"},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if gotValues := tt.v.References(tt.args.myFamily); !reflect.DeepEqual(gotValues, tt.wantValues) {
 | 
			
		||||
				t.Errorf("CveContents.References() = %v, want %v", gotValues, tt.wantValues)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCveContents_CweIDs(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		myFamily string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name       string
 | 
			
		||||
		v          CveContents
 | 
			
		||||
		args       args
 | 
			
		||||
		wantValues []CveContentStr
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "happy",
 | 
			
		||||
			v: CveContents{
 | 
			
		||||
				Mitre: []CveContent{{CweIDs: []string{"CWE-001"}}},
 | 
			
		||||
				Nvd: []CveContent{
 | 
			
		||||
					{CweIDs: []string{"CWE-001"}},
 | 
			
		||||
					{CweIDs: []string{"CWE-001"}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			args: args{myFamily: "redhat"},
 | 
			
		||||
			wantValues: []CveContentStr{
 | 
			
		||||
				{
 | 
			
		||||
					Type:  Mitre,
 | 
			
		||||
					Value: "CWE-001",
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					Type:  Nvd,
 | 
			
		||||
					Value: "CWE-001",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if gotValues := tt.v.CweIDs(tt.args.myFamily); !reflect.DeepEqual(gotValues, tt.wantValues) {
 | 
			
		||||
				t.Errorf("CveContents.CweIDs() = %v, want %v", gotValues, tt.wantValues)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCveContents_UniqCweIDs(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		myFamily string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string
 | 
			
		||||
		v    CveContents
 | 
			
		||||
		args args
 | 
			
		||||
		want []CveContentStr
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "happy",
 | 
			
		||||
			v: CveContents{
 | 
			
		||||
				Mitre: []CveContent{{CweIDs: []string{"CWE-001"}}},
 | 
			
		||||
				Nvd: []CveContent{
 | 
			
		||||
					{CweIDs: []string{"CWE-001"}},
 | 
			
		||||
					{CweIDs: []string{"CWE-001"}},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			args: args{myFamily: "redhat"},
 | 
			
		||||
			want: []CveContentStr{
 | 
			
		||||
				{
 | 
			
		||||
					Type:  Nvd,
 | 
			
		||||
					Value: "CWE-001",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if got := tt.v.UniqCweIDs(tt.args.myFamily); !reflect.DeepEqual(got, tt.want) {
 | 
			
		||||
				t.Errorf("CveContents.UniqCweIDs() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCveContents_Sort(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string
 | 
			
		||||
@@ -241,6 +540,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) {
 | 
			
		||||
@@ -252,6 +593,47 @@ func TestCveContents_Sort(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCveContent_Empty(t *testing.T) {
 | 
			
		||||
	type fields struct {
 | 
			
		||||
		Type    CveContentType
 | 
			
		||||
		CveID   string
 | 
			
		||||
		Title   string
 | 
			
		||||
		Summary string
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name   string
 | 
			
		||||
		fields fields
 | 
			
		||||
		want   bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "empty",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				Summary: "",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "not empty",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				Summary: "summary",
 | 
			
		||||
			},
 | 
			
		||||
			want: false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if got := (CveContent{
 | 
			
		||||
				Type:    tt.fields.Type,
 | 
			
		||||
				CveID:   tt.fields.CveID,
 | 
			
		||||
				Title:   tt.fields.Title,
 | 
			
		||||
				Summary: tt.fields.Summary,
 | 
			
		||||
			}).Empty(); got != tt.want {
 | 
			
		||||
				t.Errorf("CveContent.Empty() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestNewCveContentType(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name string
 | 
			
		||||
@@ -309,3 +691,31 @@ func TestGetCveContentTypes(t *testing.T) {
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCveContentTypes_Except(t *testing.T) {
 | 
			
		||||
	type args struct {
 | 
			
		||||
		excepts []CveContentType
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name         string
 | 
			
		||||
		c            CveContentTypes
 | 
			
		||||
		args         args
 | 
			
		||||
		wantExcepted CveContentTypes
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "happy",
 | 
			
		||||
			c:    CveContentTypes{Ubuntu, UbuntuAPI},
 | 
			
		||||
			args: args{
 | 
			
		||||
				excepts: []CveContentType{Ubuntu},
 | 
			
		||||
			},
 | 
			
		||||
			wantExcepted: CveContentTypes{UbuntuAPI},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if gotExcepted := tt.c.Except(tt.args.excepts...); !reflect.DeepEqual(gotExcepted, tt.wantExcepted) {
 | 
			
		||||
				t.Errorf("CveContentTypes.Except() = %v, want %v", gotExcepted, tt.wantExcepted)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										120
									
								
								models/utils.go
									
									
									
									
									
								
							
							
						
						
									
										120
									
								
								models/utils.go
									
									
									
									
									
								
							@@ -6,6 +6,7 @@ package models
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	cvedict "github.com/vulsio/go-cve-dictionary/models"
 | 
			
		||||
)
 | 
			
		||||
@@ -178,3 +179,122 @@ func ConvertFortinetToModel(cveID string, fortinets []cvedict.Fortinet) []CveCon
 | 
			
		||||
	}
 | 
			
		||||
	return cves
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertMitreToModel convert Mitre to CveContent
 | 
			
		||||
func ConvertMitreToModel(cveID string, mitres []cvedict.Mitre) []CveContent {
 | 
			
		||||
	var cves []CveContent
 | 
			
		||||
	for _, mitre := range mitres {
 | 
			
		||||
		for _, c := range mitre.Containers {
 | 
			
		||||
			cve := CveContent{
 | 
			
		||||
				Type:  Mitre,
 | 
			
		||||
				CveID: cveID,
 | 
			
		||||
				Title: func() string {
 | 
			
		||||
					if c.Title != nil {
 | 
			
		||||
						return *c.Title
 | 
			
		||||
					}
 | 
			
		||||
					return ""
 | 
			
		||||
				}(),
 | 
			
		||||
				Summary: func() string {
 | 
			
		||||
					for _, d := range c.Descriptions {
 | 
			
		||||
						if d.Lang == "en" {
 | 
			
		||||
							return d.Value
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					return ""
 | 
			
		||||
				}(),
 | 
			
		||||
				SourceLink: fmt.Sprintf("https://www.cve.org/CVERecord?id=%s", cveID),
 | 
			
		||||
				Published: func() time.Time {
 | 
			
		||||
					if mitre.CVEMetadata.DatePublished != nil {
 | 
			
		||||
						return *mitre.CVEMetadata.DatePublished
 | 
			
		||||
					}
 | 
			
		||||
					return time.Time{}
 | 
			
		||||
				}(),
 | 
			
		||||
				LastModified: func() time.Time {
 | 
			
		||||
					if mitre.CVEMetadata.DateUpdated != nil {
 | 
			
		||||
						return *mitre.CVEMetadata.DateUpdated
 | 
			
		||||
					}
 | 
			
		||||
					if mitre.CVEMetadata.DatePublished != nil {
 | 
			
		||||
						return *mitre.CVEMetadata.DatePublished
 | 
			
		||||
					}
 | 
			
		||||
					return time.Time{}
 | 
			
		||||
				}(),
 | 
			
		||||
				Optional: map[string]string{"source": func() string {
 | 
			
		||||
					if c.ProviderMetadata.ShortName != nil {
 | 
			
		||||
						return fmt.Sprintf("%s:%s", c.ContainerType, *c.ProviderMetadata.ShortName)
 | 
			
		||||
					}
 | 
			
		||||
					return fmt.Sprintf("%s:%s", c.ContainerType, c.ProviderMetadata.OrgID)
 | 
			
		||||
				}()},
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, m := range c.Metrics {
 | 
			
		||||
				if m.CVSSv2 != nil {
 | 
			
		||||
					cve.Cvss2Score = m.CVSSv2.BaseScore
 | 
			
		||||
					cve.Cvss2Vector = m.CVSSv2.VectorString
 | 
			
		||||
				}
 | 
			
		||||
				if m.CVSSv30 != nil {
 | 
			
		||||
					if cve.Cvss3Vector == "" {
 | 
			
		||||
						cve.Cvss3Score = m.CVSSv30.BaseScore
 | 
			
		||||
						cve.Cvss3Vector = m.CVSSv30.VectorString
 | 
			
		||||
						cve.Cvss3Severity = m.CVSSv30.BaseSeverity
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				if m.CVSSv31 != nil {
 | 
			
		||||
					cve.Cvss3Score = m.CVSSv31.BaseScore
 | 
			
		||||
					cve.Cvss3Vector = m.CVSSv31.VectorString
 | 
			
		||||
					cve.Cvss3Severity = m.CVSSv31.BaseSeverity
 | 
			
		||||
				}
 | 
			
		||||
				if m.CVSSv40 != nil {
 | 
			
		||||
					cve.Cvss40Score = m.CVSSv40.BaseScore
 | 
			
		||||
					cve.Cvss40Vector = m.CVSSv40.VectorString
 | 
			
		||||
					cve.Cvss40Severity = m.CVSSv40.BaseSeverity
 | 
			
		||||
				}
 | 
			
		||||
				if m.SSVC != nil {
 | 
			
		||||
					cve.SSVC = &SSVC{
 | 
			
		||||
						Exploitation: func() string {
 | 
			
		||||
							if m.SSVC.Exploitation != nil {
 | 
			
		||||
								return *m.SSVC.Exploitation
 | 
			
		||||
							}
 | 
			
		||||
							return ""
 | 
			
		||||
						}(),
 | 
			
		||||
						Automatable: func() string {
 | 
			
		||||
							if m.SSVC.Automatable != nil {
 | 
			
		||||
								return *m.SSVC.Automatable
 | 
			
		||||
							}
 | 
			
		||||
							return ""
 | 
			
		||||
						}(),
 | 
			
		||||
						TechnicalImpact: func() string {
 | 
			
		||||
							if m.SSVC.TechnicalImpact != nil {
 | 
			
		||||
								return *m.SSVC.TechnicalImpact
 | 
			
		||||
							}
 | 
			
		||||
							return ""
 | 
			
		||||
						}(),
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, r := range c.References {
 | 
			
		||||
				cve.References = append(cve.References, Reference{
 | 
			
		||||
					Link:   r.Link,
 | 
			
		||||
					Source: r.Source,
 | 
			
		||||
					Tags: func() []string {
 | 
			
		||||
						if len(r.Tags) > 0 {
 | 
			
		||||
							return strings.Split(r.Tags, ",")
 | 
			
		||||
						}
 | 
			
		||||
						return nil
 | 
			
		||||
					}(),
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, p := range c.ProblemTypes {
 | 
			
		||||
				for _, d := range p.Descriptions {
 | 
			
		||||
					if d.CweID != nil {
 | 
			
		||||
						cve.CweIDs = append(cve.CweIDs, *d.CweID)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			cves = append(cves, cve)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return cves
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -123,8 +123,7 @@ func (v VulnInfos) FilterIgnorePkgs(ignorePkgsRegexps []string) (_ VulnInfos, nF
 | 
			
		||||
// FindScoredVulns return scored vulnerabilities
 | 
			
		||||
func (v VulnInfos) FindScoredVulns() (_ VulnInfos, nFiltered int) {
 | 
			
		||||
	return v.Find(func(vv VulnInfo) bool {
 | 
			
		||||
		if 0 < vv.MaxCvss2Score().Value.Score ||
 | 
			
		||||
			0 < vv.MaxCvss3Score().Value.Score {
 | 
			
		||||
		if 0 < vv.MaxCvss2Score().Value.Score || 0 < vv.MaxCvss3Score().Value.Score || 0 < vv.MaxCvss40Score().Value.Score {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		nFiltered++
 | 
			
		||||
@@ -152,7 +151,10 @@ func (v VulnInfos) ToSortedSlice() (sorted []VulnInfo) {
 | 
			
		||||
func (v VulnInfos) CountGroupBySeverity() map[string]int {
 | 
			
		||||
	m := map[string]int{}
 | 
			
		||||
	for _, vInfo := range v {
 | 
			
		||||
		score := vInfo.MaxCvss3Score().Value.Score
 | 
			
		||||
		score := vInfo.MaxCvss40Score().Value.Score
 | 
			
		||||
		if score < 0.1 {
 | 
			
		||||
			score = vInfo.MaxCvss3Score().Value.Score
 | 
			
		||||
		}
 | 
			
		||||
		if score < 0.1 {
 | 
			
		||||
			score = vInfo.MaxCvss2Score().Value.Score
 | 
			
		||||
		}
 | 
			
		||||
@@ -417,7 +419,7 @@ func (v VulnInfo) Titles(lang, myFamily string) (values []CveContentStr) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	order := append(GetCveContentTypes(string(Trivy)), append(CveContentTypes{Fortinet, Nvd}, GetCveContentTypes(myFamily)...)...)
 | 
			
		||||
	order := append(GetCveContentTypes(string(Trivy)), append(CveContentTypes{Fortinet, Nvd, Mitre}, GetCveContentTypes(myFamily)...)...)
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(append(order, Jvn)...)...)
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if conts, found := v.CveContents[ctype]; found {
 | 
			
		||||
@@ -464,7 +466,7 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	order := append(append(GetCveContentTypes(string(Trivy)), GetCveContentTypes(myFamily)...), Fortinet, Nvd, GitHub)
 | 
			
		||||
	order := append(append(GetCveContentTypes(string(Trivy)), GetCveContentTypes(myFamily)...), Fortinet, Nvd, Mitre, GitHub)
 | 
			
		||||
	order = append(order, AllCveContetTypes.Except(append(order, Jvn)...)...)
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if conts, found := v.CveContents[ctype]; found {
 | 
			
		||||
@@ -510,7 +512,7 @@ func (v VulnInfo) Summaries(lang, myFamily string) (values []CveContentStr) {
 | 
			
		||||
 | 
			
		||||
// Cvss2Scores returns CVSS V2 Scores
 | 
			
		||||
func (v VulnInfo) Cvss2Scores() (values []CveContentCvss) {
 | 
			
		||||
	order := append([]CveContentType{RedHatAPI, RedHat, Nvd, Jvn}, GetCveContentTypes(string(Trivy))...)
 | 
			
		||||
	order := append([]CveContentType{RedHatAPI, RedHat, Nvd, Mitre, Jvn}, GetCveContentTypes(string(Trivy))...)
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if conts, found := v.CveContents[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
@@ -535,7 +537,7 @@ func (v VulnInfo) Cvss2Scores() (values []CveContentCvss) {
 | 
			
		||||
 | 
			
		||||
// Cvss3Scores returns CVSS V3 Score
 | 
			
		||||
func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
 | 
			
		||||
	order := append([]CveContentType{RedHatAPI, RedHat, SUSE, Microsoft, Fortinet, Nvd, Jvn}, GetCveContentTypes(string(Trivy))...)
 | 
			
		||||
	order := append([]CveContentType{RedHatAPI, RedHat, SUSE, Microsoft, Fortinet, Nvd, Mitre, Jvn}, GetCveContentTypes(string(Trivy))...)
 | 
			
		||||
	for _, ctype := range order {
 | 
			
		||||
		if conts, found := v.CveContents[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
@@ -606,9 +608,37 @@ func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cvss40Scores returns CVSS V4 Score
 | 
			
		||||
func (v VulnInfo) Cvss40Scores() (values []CveContentCvss) {
 | 
			
		||||
	for _, ctype := range []CveContentType{Mitre} {
 | 
			
		||||
		if conts, found := v.CveContents[ctype]; found {
 | 
			
		||||
			for _, cont := range conts {
 | 
			
		||||
				if cont.Cvss40Score == 0 && cont.Cvss40Severity == "" {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				// https://nvd.nist.gov/vuln-metrics/cvss
 | 
			
		||||
				values = append(values, CveContentCvss{
 | 
			
		||||
					Type: ctype,
 | 
			
		||||
					Value: Cvss{
 | 
			
		||||
						Type:     CVSS40,
 | 
			
		||||
						Score:    cont.Cvss40Score,
 | 
			
		||||
						Vector:   cont.Cvss40Vector,
 | 
			
		||||
						Severity: strings.ToUpper(cont.Cvss40Severity),
 | 
			
		||||
					},
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MaxCvssScore returns max CVSS Score
 | 
			
		||||
// If there is no CVSS Score, return Severity as a numerical value.
 | 
			
		||||
func (v VulnInfo) MaxCvssScore() CveContentCvss {
 | 
			
		||||
	v40Max := v.MaxCvss40Score()
 | 
			
		||||
	if v40Max.Type != Unknown {
 | 
			
		||||
		return v40Max
 | 
			
		||||
	}
 | 
			
		||||
	v3Max := v.MaxCvss3Score()
 | 
			
		||||
	if v3Max.Type != Unknown {
 | 
			
		||||
		return v3Max
 | 
			
		||||
@@ -616,6 +646,20 @@ func (v VulnInfo) MaxCvssScore() CveContentCvss {
 | 
			
		||||
	return v.MaxCvss2Score()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MaxCvss40Score returns Max CVSS V4.0 Score
 | 
			
		||||
func (v VulnInfo) MaxCvss40Score() CveContentCvss {
 | 
			
		||||
	max := CveContentCvss{
 | 
			
		||||
		Type:  Unknown,
 | 
			
		||||
		Value: Cvss{Type: CVSS40},
 | 
			
		||||
	}
 | 
			
		||||
	for _, cvss := range v.Cvss40Scores() {
 | 
			
		||||
		if max.Value.Score < cvss.Value.Score {
 | 
			
		||||
			max = cvss
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return max
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MaxCvss3Score returns Max CVSS V3 Score
 | 
			
		||||
func (v VulnInfo) MaxCvss3Score() CveContentCvss {
 | 
			
		||||
	max := CveContentCvss{
 | 
			
		||||
@@ -648,17 +692,14 @@ func (v VulnInfo) MaxCvss2Score() CveContentCvss {
 | 
			
		||||
func (v VulnInfo) AttackVector() string {
 | 
			
		||||
	for _, conts := range v.CveContents {
 | 
			
		||||
		for _, cont := range conts {
 | 
			
		||||
			if strings.HasPrefix(cont.Cvss2Vector, "AV:N") ||
 | 
			
		||||
				strings.Contains(cont.Cvss3Vector, "AV:N") {
 | 
			
		||||
			switch {
 | 
			
		||||
			case strings.HasPrefix(cont.Cvss2Vector, "AV:N") || strings.Contains(cont.Cvss3Vector, "AV:N") || strings.Contains(cont.Cvss40Vector, "AV:N"):
 | 
			
		||||
				return "AV:N"
 | 
			
		||||
			} else if strings.HasPrefix(cont.Cvss2Vector, "AV:A") ||
 | 
			
		||||
				strings.Contains(cont.Cvss3Vector, "AV:A") {
 | 
			
		||||
			case strings.HasPrefix(cont.Cvss2Vector, "AV:A") || strings.Contains(cont.Cvss3Vector, "AV:A") || strings.Contains(cont.Cvss40Vector, "AV:A"):
 | 
			
		||||
				return "AV:A"
 | 
			
		||||
			} else if strings.HasPrefix(cont.Cvss2Vector, "AV:L") ||
 | 
			
		||||
				strings.Contains(cont.Cvss3Vector, "AV:L") {
 | 
			
		||||
			case strings.HasPrefix(cont.Cvss2Vector, "AV:L") || strings.Contains(cont.Cvss3Vector, "AV:L") || strings.Contains(cont.Cvss40Vector, "AV:L"):
 | 
			
		||||
				return "AV:L"
 | 
			
		||||
			} else if strings.Contains(cont.Cvss3Vector, "AV:P") {
 | 
			
		||||
				// no AV:P in CVSS v2
 | 
			
		||||
			case strings.Contains(cont.Cvss3Vector, "AV:P") || strings.Contains(cont.Cvss40Vector, "AV:P"): // no AV:P in CVSS v2
 | 
			
		||||
				return "AV:P"
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -724,6 +765,9 @@ const (
 | 
			
		||||
 | 
			
		||||
	// CVSS3 means CVSS version3
 | 
			
		||||
	CVSS3 CvssType = "3"
 | 
			
		||||
 | 
			
		||||
	// CVSS40 means CVSS version4.0
 | 
			
		||||
	CVSS40 CvssType = "4.0"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Cvss has CVSS Score
 | 
			
		||||
 
 | 
			
		||||
@@ -917,6 +917,50 @@ func TestMaxCvssScores(t *testing.T) {
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		// 6 : CVSSv4.0 and CVSSv3.1
 | 
			
		||||
		{
 | 
			
		||||
			in: VulnInfo{
 | 
			
		||||
				CveContents: CveContents{
 | 
			
		||||
					Mitre: []CveContent{
 | 
			
		||||
						{
 | 
			
		||||
							Type:           Mitre,
 | 
			
		||||
							Cvss40Score:    6.9,
 | 
			
		||||
							Cvss40Vector:   "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N",
 | 
			
		||||
							Cvss40Severity: "MEDIUM",
 | 
			
		||||
							Cvss3Score:     7.3,
 | 
			
		||||
							Cvss3Vector:    "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L",
 | 
			
		||||
							Cvss3Severity:  "HIGH",
 | 
			
		||||
							Optional:       map[string]string{"source": "CNA"},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					Nvd: []CveContent{
 | 
			
		||||
						{
 | 
			
		||||
							Type:          Nvd,
 | 
			
		||||
							Cvss3Score:    9.8,
 | 
			
		||||
							Cvss3Vector:   "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
 | 
			
		||||
							Cvss3Severity: "CRITICAL",
 | 
			
		||||
							Optional:      map[string]string{"source": "nvd@nist.gov"},
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
							Type:          Nvd,
 | 
			
		||||
							Cvss3Score:    7.3,
 | 
			
		||||
							Cvss3Vector:   "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L",
 | 
			
		||||
							Cvss3Severity: "HIGH",
 | 
			
		||||
							Optional:      map[string]string{"source": "cna@vuldb.com"},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			out: CveContentCvss{
 | 
			
		||||
				Type: Mitre,
 | 
			
		||||
				Value: Cvss{
 | 
			
		||||
					Type:     CVSS40,
 | 
			
		||||
					Score:    6.9,
 | 
			
		||||
					Severity: "MEDIUM",
 | 
			
		||||
					Vector:   "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		// Empty
 | 
			
		||||
		{
 | 
			
		||||
			in: VulnInfo{},
 | 
			
		||||
@@ -1859,3 +1903,109 @@ func TestVulnInfo_PatchStatus(t *testing.T) {
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestVulnInfo_Cvss40Scores(t *testing.T) {
 | 
			
		||||
	type fields struct {
 | 
			
		||||
		CveID       string
 | 
			
		||||
		CveContents CveContents
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name   string
 | 
			
		||||
		fields fields
 | 
			
		||||
		want   []CveContentCvss
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "happy",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				CveID: "CVE-2024-5732",
 | 
			
		||||
				CveContents: CveContents{
 | 
			
		||||
					Mitre: []CveContent{
 | 
			
		||||
						{
 | 
			
		||||
							Type:           Mitre,
 | 
			
		||||
							Cvss40Score:    6.9,
 | 
			
		||||
							Cvss40Vector:   "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N",
 | 
			
		||||
							Cvss40Severity: "MEDIUM",
 | 
			
		||||
							Cvss3Score:     7.3,
 | 
			
		||||
							Cvss3Vector:    "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L",
 | 
			
		||||
							Cvss3Severity:  "HIGH",
 | 
			
		||||
							Optional:       map[string]string{"source": "CNA"},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: []CveContentCvss{
 | 
			
		||||
				{
 | 
			
		||||
					Type: Mitre,
 | 
			
		||||
					Value: Cvss{
 | 
			
		||||
						Type:     CVSS40,
 | 
			
		||||
						Score:    6.9,
 | 
			
		||||
						Severity: "MEDIUM",
 | 
			
		||||
						Vector:   "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if got := (VulnInfo{
 | 
			
		||||
				CveID:       tt.fields.CveID,
 | 
			
		||||
				CveContents: tt.fields.CveContents,
 | 
			
		||||
			}).Cvss40Scores(); !reflect.DeepEqual(got, tt.want) {
 | 
			
		||||
				t.Errorf("VulnInfo.Cvss40Scores() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestVulnInfo_MaxCvss40Score(t *testing.T) {
 | 
			
		||||
	type fields struct {
 | 
			
		||||
		CveID       string
 | 
			
		||||
		CveContents CveContents
 | 
			
		||||
	}
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name   string
 | 
			
		||||
		fields fields
 | 
			
		||||
		want   CveContentCvss
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "happy",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				CveID: "CVE-2024-5732",
 | 
			
		||||
				CveContents: CveContents{
 | 
			
		||||
					Mitre: []CveContent{
 | 
			
		||||
						{
 | 
			
		||||
							Type:           Mitre,
 | 
			
		||||
							Cvss40Score:    6.9,
 | 
			
		||||
							Cvss40Vector:   "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N",
 | 
			
		||||
							Cvss40Severity: "MEDIUM",
 | 
			
		||||
							Cvss3Score:     7.3,
 | 
			
		||||
							Cvss3Vector:    "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L",
 | 
			
		||||
							Cvss3Severity:  "HIGH",
 | 
			
		||||
							Optional:       map[string]string{"source": "CNA"},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: CveContentCvss{
 | 
			
		||||
				Type: Mitre,
 | 
			
		||||
				Value: Cvss{
 | 
			
		||||
					Type:     CVSS40,
 | 
			
		||||
					Score:    6.9,
 | 
			
		||||
					Severity: "MEDIUM",
 | 
			
		||||
					Vector:   "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N",
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			if got := (VulnInfo{
 | 
			
		||||
				CveID:       tt.fields.CveID,
 | 
			
		||||
				CveContents: tt.fields.CveContents,
 | 
			
		||||
			}).MaxCvss40Score(); !reflect.DeepEqual(got, tt.want) {
 | 
			
		||||
				t.Errorf("VulnInfo.MaxsCvss40Score() = %v, want %v", got, tt.want)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user