CVE-2022-39213 (published 2022-09-15T22:15Z)

go-cvss is a Go module to manipulate Common Vulnerability Scoring System (CVSS). In affected versions when a full CVSS v2.0 vector string is parsed using `ParseVector`, an Out-of-Bounds Read is possible due to a lack of tests. The Go module will then panic. The problem is patched in tag `v0.4.0`, by the commit `d9d478ff0c13b8b09ace030db9262f3c2fe031f4`. Users are advised to upgrade. Users unable to upgrade may avoid this issue by parsing only CVSS v2.0 vector strings that do not have all attributes defined (e.g. `AV:N/AC:L/Au:N/C:P/I:P/A:C/E:U/RL:OF/RC:C/CDP:MH/TD:H/CR:M/IR:M/AR:M`). As stated in [SECURITY.md](https://github.com/pandatix/go-cvss/blob/master/SECURITY.md), the CPE v2.3 to refer to this Go module is `cpe:2.3:a:pandatix:go_cvss:*:*:*:*:*:*:*:*`. The entry has already been requested to the NVD CPE dictionary.

CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-125
cwe_name: Out-of-bounds Read
description: The product reads data past the end, or before the beginning, of the intended buffer.
extended_description: 
            
            
             
			 
         
         

Fix information:
repo_url: https://github.com/pandatix/go-cvss
commit_details: repo_url: https://github.com/pandatix/go-cvss
author: TESSON Lucas
author_date: 2022-09-13 19:46:05+02:00
msg: Improve CVSS v2.0 implementation (0/1 allocs/op)
repository: repo_url: https://github.com/pandatix/go-cvss
repo_name: pandatix/go-cvss
description: Common Vulnerability Scoring System (CVSS) made safe and highly efficient
repo_language: Go
owner: pandatix
file_changes: filename: cvss20.go
change_type: ModificationType.MODIFY
diff: @@ -3,55 +3,61 @@ package gocvss20
 import (
 	"math"
 	"strings"
+	"sync"
+	"unsafe"
 )
 
+var order = [][]string{
+	{"AV", "AC", "Au", "C", "I", "A"}, // Base metrics
+	{"E", "RL", "RC"},                 // Temporal metrics
+	{"CDP", "TD", "CR", "IR", "AR"},   // Environmental metrics
+}
+
 // ParseVector parses a CVSS v2.0 vector.
 func ParseVector(vector string) (*CVSS20, error) {
 	// Split parts
-	pts := strings.Split(vector, "/")
-	if len(pts) != 6 && len(pts) != 9 && len(pts) != 14 {
+	pts, l := split(vector)
+	if l != 6 && l != 9 && l != 14 {
 		return nil, ErrTooShortVector
 	}
+	pts = pts[:l]
 
 	// Work on each CVSS part
 	cvss20 := &CVSS20{
-		Base: Base{},
-		Temporal: Temporal{
-			Exploitability:   "ND",
-			RemediationLevel: "ND",
-			ReportConfidence: "ND",
+		base: base{},
+		temporal: temporal{
+			exploitability:   "ND",
+			remediationLevel: "ND",
+			reportConfidence: "ND",
 		},
-		Environmental: Environmental{
-			CollateralDamagePotential:  "ND",
-			TargetDistribution:         "ND",
-			ConfidentialityRequirement: "ND",
-			IntegrityRequirement:       "ND",
-			AvailabilityRequirement:    "ND",
+		environmental: environmental{
+			collateralDamagePotential:  "ND",
+			targetDistribution:         "ND",
+			confidentialityRequirement: "ND",
+			integrityRequirement:       "ND",
+			availabilityRequirement:    "ND",
 		},
 	}
 
-	// Parse metrics in order
-	slcs := [][]string{
-		{"AV", "AC", "Au", "C", "I", "A"}, // Base metrics
-		{"E", "RL", "RC"},                 // Temporal metrics
-		{"CDP", "TD", "CR", "IR", "AR"},   // Environmental metrics
-	}
 	slci := 0
-	currSlc := slcs[slci]
 	i := 0
 	for _, pt := range pts {
 		abv, v, _ := strings.Cut(pt, ":")
-		if abv != currSlc[i] {
+		if slci == 4 {
+			return nil, &ErrDefinedN{Abv: abv}
+		}
+		if abv != order[slci][i] {
 			return nil, ErrInvalidMetricOrder
 		}
+
 		if err := cvss20.Set(abv, v); err != nil {
 			return nil, err
 		}
+
 		// Go to next element in slice, or next slice if fully consumed
 		i++
-		if i == len(currSlc) {
+		if i == len(order[slci]) {
 			slci++
-			currSlc = slcs[slci]
 			i = 0
 		}
 	}
@@ -59,30 +65,102 @@ func ParseVector(vector string) (*CVSS20, error) {
 	return cvss20, nil
 }
 
+var splitPool = sync.Pool{
+	New: func() any {
+		return make([]string, 14)
+	},
+}
+
+func split(vector string) ([]string, int) {
+	partsPtr := splitPool.Get()
+	defer splitPool.Put(partsPtr)
+	parts := partsPtr.([]string)
+
+	start := 0
+	curr := 0
+	l := len(vector)
+	i := 0
+	for ; i < l; i++ {
+		if vector[i] == '/' {
+			parts[curr] = vector[start:i]
+
+			start = i + 1
+			curr++
+
+			if curr == 13 {
+				break
+			}
+		}
+	}
+	parts[curr] = vector[start:]
+	return parts, curr + 1
+}
+
 func (cvss20 CVSS20) Vector() string {
-	s := ""
+	l := lenVec(&cvss20)
+	b := make([]byte, 0, l)
+
 	// Base
-	s += "AV:" + cvss20.AccessVector
-	s += "/AC:" + cvss20.AccessComplexity
-	s += "/Au:" + cvss20.Authentication
-	s += "/C:" + cvss20.ConfidentialityImpact
-	s += "/I:" + cvss20.IntegrityImpact
-	s += "/A:" + cvss20.AvailabilityImpact
-	// Temporal, if any is defined
-	if cvss20.Exploitability != "ND" || cvss20.RemediationLevel != "ND" || cvss20.ReportConfidence != "ND" {
-		s += "/E:" + cvss20.Exploitability
-		s += "/RL:" + cvss20.RemediationLevel
-		s += "/RC:" + cvss20.ReportConfidence
+	app(&b, "AV:", cvss20.accessVector)
+	app(&b, "/AC:", cvss20.accessComplexity)
+	app(&b, "/Au:", cvss20.authentication)
+	app(&b, "/C:", cvss20.confidentialityImpact)
+	app(&b, "/I:", cvss20.integrityImpact)
+	app(&b, "/A:", cvss20.availabilityImpact)
+
+	// Temporal
+	if cvss20.exploitability != "ND" || cvss20.remediationLevel != "ND" || cvss20.reportConfidence != "ND" {
+		app(&b, "/E:", cvss20.exploitability)
+		app(&b, "/RL:", cvss20.remediationLevel)
+		app(&b, "/RC:", cvss20.reportConfidence)
 	}
-	// Environmental, if any is defined
-	if cvss20.CollateralDamagePotential != "ND" || cvss20.TargetDistribution != "ND" || cvss20.ConfidentialityRequirement != "ND" || cvss20.IntegrityRequirement != "ND" || cvss20.AvailabilityRequirement != "ND" {
-		s += "/CDP:" + cvss20.CollateralDamagePotential
-		s += "/TD:" + cvss20.TargetDistribution
-		s += "/CR:" + cvss20.ConfidentialityRequirement
-		s += "/IR:" + cvss20.IntegrityRequirement
-		s += "/AR:" + cvss20.AvailabilityRequirement
+
+	// Environmental
+	if cvss20.collateralDamagePotential != "ND" || cvss20.targetDistribution != "ND" || cvss20.confidentialityRequirement != "ND" || cvss20.integrityRequirement != "ND" || cvss20.availabilityRequirement != "ND" {
+		app(&b, "/CDP:", cvss20.collateralDamagePotential)
+		app(&b, "/TD:", cvss20.targetDistribution)
+		app(&b, "/CR:", cvss20.confidentialityRequirement)
+		app(&b, "/IR:", cvss20.integrityRequirement)
+		app(&b, "/AR:", cvss20.availabilityRequirement)
 	}
-	return s
+
+	return *(*string)(unsafe.Pointer(&b))
+}
+
+func lenVec(cvss20 *CVSS20) int {
+	// Base:
+	// - AV, AC, Au: 4
+	// - C, I, A: 3
+	// - separators: 5
+	// Total: 3*4 + 3*3 + 5 = 30
+	l := 26
+
+	// Temporal:
+	// - E: 2 + len(v)
+	// - RL: 3 + len(v)
+	// - RC: 3 + len(v)
+	// - separators: 3
+	// Total: 11 + 3*len(v)
+	if cvss20.exploitability != "ND" || cvss20.remediationLevel != "ND" || cvss20.reportConfidence != "ND" {
+		l += 11 + len(cvss20.exploitability) + len(cvss20.remediationLevel) + len(cvss20.reportConfidence)
+	}
+
+	// Environmental:
+	// - CDP: 4 + len(v)
+	// - TD: 3 + len(v)
+	// - CR, IR, AR: 3 + len(v)
+	// - separators: 5
+	// Total: 21 + 5*len(v)
+	if cvss20.collateralDamagePotential != "ND" || cvss20.targetDistribution != "ND" || cvss20.confidentialityRequirement != "ND" || cvss20.integrityRequirement != "ND" || cvss20.availabilityRequirement != "ND" {
+		l += 21 + len(cvss20.collateralDamagePotential) + len(cvss20.targetDistribution) + len(cvss20.confidentialityRequirement) + len(cvss20.integrityRequirement) + len(cvss20.availabilityRequirement)
+	}
+
+	return l
+}
+
+func app(b *[]byte, pre, v string) {
+	*b = append(*b, pre...)
+	*b = append(*b, v...)
 }
 
 // CVSS20 embeds all the metric values defined by the CVSS v2.0
@@ -90,85 +168,85 @@ func (cvss20 CVSS20) Vector() string {
 // Attributes values must not be manipulated directly. Use Get
 // and Set methods.
 type CVSS20 struct {
-	Base
-	Temporal
-	Environmental
+	base
+	temporal
+	environmental
 }
 
-// Base is the group of metrics defined with such name by the
+// base is the group of metrics defined with such name by the
 // first.org CVSS v2.0 rev2 specification.
 // Mandatory.
-type Base struct {
+type base struct {
 	// AV -> [L,A,N]
-	AccessVector string
+	accessVector string
 	// AC -> [H,M,L]
-	AccessComplexity string
+	accessComplexity string
 	// Au -> [M,S,N]
-	Authentication string
+	authentication string
 	// C -> [N,P,C]
-	ConfidentialityImpact string
+	confidentialityImpact string
 	// I -> [N,P,C]
-	IntegrityImpact string
+	integrityImpact string
 	// A -> [N,P,C]
-	AvailabilityImpact string
+	availabilityImpact string
 }
 
-// Temporal is the group of metrics defined with such name by the
+// temporal is the group of metrics defined with such name by the
 // first.org CVSS v2.0 rev2 specification.
 // Not mandatory.
-type Temporal struct {
+type temporal struct {
 	// E -> [U,POC,F,F,H,ND]
-	Exploitability string
+	exploitability string
 	// RL -> [OF,TF,W,U,ND]
-	RemediationLevel string
+	remediationLevel string
 	// RC -> [UC,UR,C,ND]
-	ReportConfidence string
+	reportConfidence string
 }
 
-// Environmental is the group of metrics defined with such name by the
+// environmental is the group of metrics defined with such name by the
 // first.org CVSS v2.0 rev2 specification.
 // Not mandatory.
-type Environmental struct {
+type environmental struct {
 	// CDP -> [N,L,LM,MH,H,ND]
-	CollateralDamagePotential string
+	collateralDamagePotential string
 	// TD -> [N,L,M,H,ND]
-	TargetDistribution string
+	targetDistribution string
 	// CR,IR,AR -> [L,M,H,ND]
-	ConfidentialityRequirement string
-	IntegrityRequirement       string
-	AvailabilityRequirement    string
+	confidentialityRequirement string
+	integrityRequirement       string
+	availabilityRequirement    string
 }
 
 func (cvss20 CVSS20) Get(abv string) (string, error) {
 	switch abv {
 	case "AV":
-		return cvss20.AccessVector, nil
+		return cvss20.accessVector, nil
 	case "AC":
-		return cvss20.AccessComplexity, nil
+		return cvss20.accessComplexity, nil
 	case "Au":
-		return cvss20.Authentication, nil
+		return cvss20.authentication, nil
 	case "C":
-		return cvss20.ConfidentialityImpact, nil
+		return cvss20.confidentialityImpact, nil
 	case "I":
-		return cvss20.IntegrityImpact, nil
+		return cvss20.integrityImpact, nil
 	case "A":
-		return cvss20.AvailabilityImpact, nil
+		return cvss20.availabilityImpact, nil
 	case "E":
-		return cvss20.Exploitability, nil
+		return cvss20.exploitability, nil
 	case "RL":
-		return cvss20.RemediationLevel, nil
+		return cvss20.remediationLevel, nil
 	case "RC":
-		return cvss20.ReportConfidence, nil
+		return cvss20.reportConfidence, nil
 	case "CDP":
-		return cvss20.CollateralDamagePotential, nil
+		return cvss20.collateralDamagePotential, nil
 	case "TD":
-		return cvss20.TargetDistribution, nil
+		return cvss20.targetDistribution, nil
 	case "CR":
-		return cvss20.ConfidentialityRequirement, nil
+		return cvss20.confidentialityRequirement, nil
 	case "IR":
-		return cvss20.IntegrityRequirement, nil
+		return cvss20.integrityRequirement, nil
 	case "AR":
-		return cvss20.AvailabilityRequirement, nil
+		return cvss20.availabilityRequirement, nil
 	default:
 		return "", &ErrInvalidMetric{Abv: abv}
 	}
@@ -181,74 +259,74 @@ func (cvss20 *CVSS20) Set(abv string, value string) error {
 		if err := validate(value, []string{"L", "A", "N"}); err != nil {
 			return err
 		}
-		cvss20.AccessVector = value
+		cvss20.accessVector = value
 	case "AC":
 		if err := validate(value, []string{"H", "M", "L"}); err != nil {
 			return err
 		}
-		cvss20.AccessComplexity = value
+		cvss20.accessComplexity = value
 	case "Au":
 		if err := validate(value, []string{"M", "S", "N"}); err != nil {
 			return err
 		}
-		cvss20.Authentication = value
+		cvss20.authentication = value
 	case "C":
 		if err := validate(value, []string{"N", "P", "C"}); err != nil {
 			return err
 		}
-		cvss20.ConfidentialityImpact = value
+		cvss20.confidentialityImpact = value
 	case "I":
 		if err := validate(value, []string{"N", "P", "C"}); err != nil {
 			return err
 		}
-		cvss20.IntegrityImpact = value
+		cvss20.integrityImpact = value
 	case "A":
 		if err := validate(value, []string{"N", "P", "C"}); err != nil {
 			return err
 		}
-		cvss20.AvailabilityImpact = value
+		cvss20.availabilityImpact = value
 	// Temporal
 	case "E":
 		if err := validate(value, []string{"U", "POC", "F", "H", "ND"}); err != nil {
 			return err
 		}
-		cvss20.Exploitability = value
+		cvss20.exploitability = value
 	case "RL":
 		if err := validate(value, []string{"OF", "TF", "W", "U", "ND"}); err != nil {
 			return err
 		}
-		cvss20.RemediationLevel = value
+		cvss20.remediationLevel = value
 	case "RC":
 		if err := validate(value, []string{"UC", "UR", "C", "ND"}); err != nil {
 			return err
 		}
-		cvss20.ReportConfidence = value
+		cvss20.reportConfidence = value
 	// Environmental
 	case "CDP":
 		if err := validate(value, []string{"N", "L", "LM", "MH", "H", "ND"}); err != nil {
 			return err
 		}
-		cvss20.CollateralDamagePotential = value
+		cvss20.collateralDamagePotential = value
 	case "TD":
 		if err := validate(value, []string{"N", "L", "M", "H", "ND"}); err != nil {
 			return err
 		}
-		cvss20.TargetDistribution = value
+		cvss20.targetDistribution = value
 	case "CR":
 		if err := validate(value, []string{"L", "M", "H", "ND"}); err != nil {
 			return err
 		}
-		cvss20.ConfidentialityRequirement = value
+		cvss20.confidentialityRequirement = value
 	case "IR":
 		if err := validate(value, []string{"L", "M", "H", "ND"}); err != nil {
 			return err
 		}
-		cvss20.IntegrityRequirement = value
+		cvss20.integrityRequirement = value
 	case "AR":
 		if err := validate(value, []string{"L", "M", "H", "ND"}); err != nil {
 			return err
 		}
-		cvss20.AvailabilityRequirement = value
+		cvss20.availabilityRequirement = value
 	default:
 		return &ErrInvalidMetric{Abv: abv}
 	}
@@ -267,32 +345,40 @@ func validate(value string, enabled []string) error {
 
 // BaseScore returns the CVSS v2.0's base score.
 func (cvss20 CVSS20) BaseScore() float64 {
-	impact := 10.41 * (1 - (1-cia(cvss20.ConfidentialityImpact))*(1-cia(cvss20.IntegrityImpact))*(1-cia(cvss20.AvailabilityImpact)))
+	impact := cvss20.Impact()
 	fimpact := 0.0
 	if impact != 0 {
 		fimpact = 1.176
 	}
-	exploitability := 20 * accessVector(cvss20.AccessVector) * accessComplexity(cvss20.AccessComplexity) * authentication(cvss20.Authentication)
+	exploitability := cvss20.Exploitability()
 	return roundTo1Decimal(((0.6 * impact) + (0.4 * exploitability) - 1.5) * fimpact)
 }
 
+func (cvss20 CVSS20) Impact() float64 {
+	return 10.41 * (1 - (1-cia(cvss20.confidentialityImpact))*(1-cia(cvss20.integrityImpact))*(1-cia(cvss20.availabilityImpact)))
+}
+
+func (cvss20 CVSS20) Exploitability() float64 {
+	return 20 * accessVector(cvss20.accessVector) * accessComplexity(cvss20.accessComplexity) * authentication(cvss20.authentication)
+}
+
 // TemporalScore returns the CVSS v2.0's temporal score.
 func (cvss20 CVSS20) TemporalScore() float64 {
-	return roundTo1Decimal(cvss20.BaseScore() * exploitability(cvss20.Exploitability) * remediationLevel(cvss20.RemediationLevel) * reportConfidence(cvss20.ReportConfidence))
+	return roundTo1Decimal(cvss20.BaseScore() * exploitability(cvss20.exploitability) * remediationLevel(cvss20.remediationLevel) * reportConfidence(cvss20.reportConfidence))
 }
 
 // EnvironmentalScore returns the CVSS v2.0's environmental score.
 func (cvss20 CVSS20) EnvironmentalScore() float64 {
 	// Recompute base score
-	adjustedImpact := math.Min(10, 10.41*(1-(1-cia(cvss20.ConfidentialityImpact)*ciar(cvss20.ConfidentialityRequirement))*(1-cia(cvss20.IntegrityImpact)*ciar(cvss20.IntegrityRequirement))*(1-cia(cvss20.AvailabilityImpact)*ciar(cvss20.AvailabilityRequirement))))
+	adjustedImpact := math.Min(10, 10.41*(1-(1-cia(cvss20.confidentialityImpact)*ciar(cvss20.confidentialityRequirement))*(1-cia(cvss20.integrityImpact)*ciar(cvss20.integrityRequirement))*(1-cia(cvss20.availabilityImpact)*ciar(cvss20.availabilityRequirement))))
 	fimpactBase := 0.0
 	if adjustedImpact != 0 {
 		fimpactBase = 1.176
 	}
-	expltBase := 20 * accessVector(cvss20.AccessVector) * accessComplexity(cvss20.AccessComplexity) * authentication(cvss20.Authentication)
+	expltBase := 20 * accessVector(cvss20.accessVector) * accessComplexity(cvss20.accessComplexity) * authentication(cvss20.authentication)
 	recBase := roundTo1Decimal(((0.6 * adjustedImpact) + (0.4 * expltBase) - 1.5) * fimpactBase)
-	adjustedTemporal := roundTo1Decimal(recBase * exploitability(cvss20.Exploitability) * remediationLevel(cvss20.RemediationLevel) * reportConfidence(cvss20.ReportConfidence))
-	return roundTo1Decimal((adjustedTemporal + (10-adjustedTemporal)*collateralDamagePotential(cvss20.CollateralDamagePotential)) * targetDistribution(cvss20.TargetDistribution))
+	adjustedTemporal := roundTo1Decimal(recBase * exploitability(cvss20.exploitability) * remediationLevel(cvss20.remediationLevel) * reportConfidence(cvss20.reportConfidence))
+	return roundTo1Decimal((adjustedTemporal + (10-adjustedTemporal)*collateralDamagePotential(cvss20.collateralDamagePotential)) * targetDistribution(cvss20.targetDistribution))
 }
 
 // Helpers to compute CVSS v2.0 scores.

programming_language: Go
filename: cvss20_bench_test.go
change_type: ModificationType.ADD
diff: @@ -0,0 +1,100 @@
+package gocvss20_test
+
+import (
+	"testing"
+
+	gocvss20 "github.com/pandatix/go-cvss/20"
+)
+
+var Gcvss20 *gocvss20.CVSS20
+var Gerr error
+
+func BenchmarkParseVector_Base(b *testing.B) {
+	benchmarkParseVector("AV:N/AC:L/Au:N/C:P/I:P/A:C", b)
+}
+
+func BenchmarkParseVector_WithTempAndEnv(b *testing.B) {
+	benchmarkParseVector("AV:N/AC:L/Au:N/C:P/I:P/A:C/E:U/RL:OF/RC:C/CDP:MH/TD:H/CR:M/IR:M/AR:M", b)
+}
+
+func benchmarkParseVector(vector string, b *testing.B) {
+	var cvss20 *gocvss20.CVSS20
+	var err error
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		cvss20, err = gocvss20.ParseVector(vector)
+	}
+	Gcvss20 = cvss20
+	Gerr = err
+}
+
+var Gstr string
+
+func BenchmarkCVSS20Vector(b *testing.B) {
+	cvss20, _ := gocvss20.ParseVector("AV:N/AC:L/Au:N/C:P/I:P/A:C/E:U/RL:OF/RC:C/CDP:MH/TD:H/CR:M/IR:M/AR:M")
+	var str string
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		str = cvss20.Vector()
+	}
+	Gstr = str
+}
+
+var Gget string
+
+func BenchmarkCVSS20Get(b *testing.B) {
+	const abv = "Au"
+	cvss20, _ := gocvss20.ParseVector("AV:N/AC:L/Au:N/C:P/I:P/A:C/E:U/RL:OF/RC:C/CDP:MH/TD:H/CR:M/IR:M/AR:M")
+	var get string
+	var err error
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		get, err = cvss20.Get(abv)
+	}
+	Gget = get
+	Gerr = err
+}
+
+func BenchmarkCVSS20Set(b *testing.B) {
+	const abv = "Au"
+	const value = "S"
+	cvss20, _ := gocvss20.ParseVector("AV:N/AC:L/Au:N/C:P/I:P/A:C/E:U/RL:OF/RC:C/CDP:MH/TD:H/CR:M/IR:M/AR:M")
+	var err error
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		err = cvss20.Set(abv, value)
+	}
+	Gerr = err
+}
+
+var Gscore float64
+
+func BenchmarkCVSS20BaseScore(b *testing.B) {
+	var score float64
+	cvss20, _ := gocvss20.ParseVector("AV:N/AC:L/Au:N/C:P/I:P/A:C/E:U/RL:OF/RC:C/CDP:MH/TD:H/CR:M/IR:M/AR:M")
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		score = cvss20.BaseScore()
+	}
+	Gscore = score
+}
+
+func BenchmarkCVSS20TemporalScore(b *testing.B) {
+	var score float64
+	cvss20, _ := gocvss20.ParseVector("AV:N/AC:L/Au:N/C:P/I:P/A:C/E:U/RL:OF/RC:C/CDP:MH/TD:H/CR:M/IR:M/AR:M")
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		score = cvss20.TemporalScore()
+	}
+	Gscore = score
+}
+
+func BenchmarkCVSS20EnvironmentalScore(b *testing.B) {
+	var score float64
+	cvss20, _ := gocvss20.ParseVector("AV:N/AC:L/Au:N/C:P/I:P/A:C/E:U/RL:OF/RC:C/CDP:MH/TD:H/CR:M/IR:M/AR:M")
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		score = cvss20.EnvironmentalScore()
+	}
+	Gscore = score
+}

programming_language: Go
filename: cvss20_fuzz_test.go
change_type: ModificationType.MODIFY
diff: @@ -1,9 +1,7 @@
-package gocvss20_test
+package gocvss20
 
 import (
 	"testing"
-
-	gocvss20 "github.com/pandatix/go-cvss/20"
 )
 
 func FuzzParseVector(f *testing.F) {
@@ -12,7 +10,7 @@ func FuzzParseVector(f *testing.F) {
 	}
 
 	f.Fuzz(func(t *testing.T, vector string) {
-		cvss20, err := gocvss20.ParseVector(vector)
+		cvss20, err := ParseVector(vector)
 
 		if err != nil {
 			if cvss20 != nil {

programming_language: Go
filename: cvss20_test.go
change_type: ModificationType.MODIFY
diff: @@ -1,95 +1,128 @@
-package gocvss20_test
+package gocvss20
 
 import (
 	"testing"
 
-	gocvss20 "github.com/pandatix/go-cvss/20"
 	"github.com/stretchr/testify/assert"
 )
 
 var testsParseVector = map[string]struct {
 	Vector         string
-	ExpectedCVSS20 *gocvss20.CVSS20
+	ExpectedCVSS20 *CVSS20
 	ExpectedErr    error
 }{
 	"CVSS v2.0 Guide Section 3.3.1 CVE-2002-0392": {
 		Vector: "AV:N/AC:L/Au:N/C:N/I:N/A:C/E:F/RL:OF/RC:C",
-		ExpectedCVSS20: &gocvss20.CVSS20{
-			Base: gocvss20.Base{
-				AccessVector:          "N",
-				AccessComplexity:      "L",
-				Authentication:        "N",
-				ConfidentialityImpact: "N",
-				IntegrityImpact:       "N",
-				AvailabilityImpact:    "C",
+		ExpectedCVSS20: &CVSS20{
+			base: base{
+				accessVector:          "N",
+				accessComplexity:      "L",
+				authentication:        "N",
+				confidentialityImpact: "N",
+				integrityImpact:       "N",
+				availabilityImpact:    "C",
 			},
-			Temporal: gocvss20.Temporal{
-				Exploitability:   "F",
-				RemediationLevel: "OF",
-				ReportConfidence: "C",
+			temporal: temporal{
+				exploitability:   "F",
+				remediationLevel: "OF",
+				reportConfidence: "C",
 			},
-			Environmental: gocvss20.Environmental{
-				CollateralDamagePotential:  "ND",
-				TargetDistribution:         "ND",
-				ConfidentialityRequirement: "ND",
-				IntegrityRequirement:       "ND",
-				AvailabilityRequirement:    "ND",
+			environmental: environmental{
+				collateralDamagePotential:  "ND",
+				targetDistribution:         "ND",
+				confidentialityRequirement: "ND",
+				integrityRequirement:       "ND",
+				availabilityRequirement:    "ND",
 			},
 		},
 		ExpectedErr: nil,
 	},
 	"CVSS v2.0 Guide Section 3.3.2 CVE-2003-0818": {
 		Vector: "AV:N/AC:L/Au:N/C:C/I:C/A:C/E:F/RL:OF/RC:C",
-		ExpectedCVSS20: &gocvss20.CVSS20{
-			Base: gocvss20.Base{
-				AccessVector:          "N",
-				AccessComplexity:      "L",
-				Authentication:        "N",
-				ConfidentialityImpact: "C",
-				IntegrityImpact:       "C",
-				AvailabilityImpact:    "C",
+		ExpectedCVSS20: &CVSS20{
+			base: base{
+				accessVector:          "N",
+				accessComplexity:      "L",
+				authentication:        "N",
+				confidentialityImpact: "C",
+				integrityImpact:       "C",
+				availabilityImpact:    "C",
 			},
-			Temporal: gocvss20.Temporal{
-				Exploitability:   "F",
-				RemediationLevel: "OF",
-				ReportConfidence: "C",
+			temporal: temporal{
+				exploitability:   "F",
+				remediationLevel: "OF",
+				reportConfidence: "C",
 			},
-			Environmental: gocvss20.Environmental{
-				CollateralDamagePotential:  "ND",
-				TargetDistribution:         "ND",
-				ConfidentialityRequirement: "ND",
-				IntegrityRequirement:       "ND",
-				AvailabilityRequirement:    "ND",
+			environmental: environmental{
+				collateralDamagePotential:  "ND",
+				targetDistribution:         "ND",
+				confidentialityRequirement: "ND",
+				integrityRequirement:       "ND",
+				availabilityRequirement:    "ND",
 			},
 		},
 		ExpectedErr: nil,
 	},
 	"CVSS v2.0 Guide Section 3.3.3 CVE-2003-0062": {
 		Vector: "AV:L/AC:H/Au:N/C:C/I:C/A:C/E:POC/RL:OF/RC:C",
-		ExpectedCVSS20: &gocvss20.CVSS20{
-			Base: gocvss20.Base{
-				AccessVector:          "L",
-				AccessComplexity:      "H",
-				Authentication:        "N",
-				ConfidentialityImpact: "C",
-				IntegrityImpact:       "C",
-				AvailabilityImpact:    "C",
+		ExpectedCVSS20: &CVSS20{
+			base: base{
+				accessVector:          "L",
+				accessComplexity:      "H",
+				authentication:        "N",
+				confidentialityImpact: "C",
+				integrityImpact:       "C",
+				availabilityImpact:    "C",
 			},
-			Temporal: gocvss20.Temporal{
-				Exploitability:   "POC",
-				RemediationLevel: "OF",
-				ReportConfidence: "C",
+			temporal: temporal{
+				exploitability:   "POC",
+				remediationLevel: "OF",
+				reportConfidence: "C",
 			},
-			Environmental: gocvss20.Environmental{
-				CollateralDamagePotential:  "ND",
-				TargetDistribution:         "ND",
-				ConfidentialityRequirement: "ND",
-				IntegrityRequirement:       "ND",
-				AvailabilityRequirement:    "ND",
+			environmental: environmental{
+				collateralDamagePotential:  "ND",
+				targetDistribution:         "ND",
+				confidentialityRequirement: "ND",
+				integrityRequirement:       "ND",
+				availabilityRequirement:    "ND",
 			},
 		},
 		ExpectedErr: nil,
 	},
+	"all-defined": {
+		Vector: "AV:N/AC:L/Au:N/C:P/I:P/A:C/E:U/RL:OF/RC:C/CDP:MH/TD:H/CR:M/IR:M/AR:M",
+		ExpectedCVSS20: &CVSS20{
+			base: base{
+				accessVector:          "N",
+				accessComplexity:      "L",
+				authentication:        "N",
+				confidentialityImpact: "P",
+				integrityImpact:       "P",
+				availabilityImpact:    "C",
+			},
+			temporal: temporal{
+				exploitability:   "U",
+				remediationLevel: "OF",
+				reportConfidence: "C",
+			},
+			environmental: environmental{
+				collateralDamagePotential:  "MH",
+				targetDistribution:         "H",
+				confidentialityRequirement: "M",
+				integrityRequirement:       "M",
+				availabilityRequirement:    "M",
+			},
+		},
+		ExpectedErr: nil,
+	},
+	"Fuzz_50620a37c4a7716a77a14602b4bcc7b02e6f751d0a714ed796d9b04402c745ac": {
+		// This fuzz crasher enabled detecting that the split function
+		// (comming from the optimization step) was doing an Out-Of-Bounds
+		// Write (CWE-787) if the vector was only composed of '/'.
+		Vector:         "//////////////",
+		ExpectedCVSS20: nil,
+		ExpectedErr:    ErrInvalidMetricOrder,
+	},
 }
 
 func TestParseVector(t *testing.T) {
@@ -99,7 +132,7 @@ func TestParseVector(t *testing.T) {
 		t.Run(testname, func(t *testing.T) {
 			assert := assert.New(t)
 
-			cvss20, err := gocvss20.ParseVector(tt.Vector)
+			cvss20, err := ParseVector(tt.Vector)
 
 			assert.Equal(tt.ExpectedCVSS20, cvss20)
 			assert.Equal(tt.ExpectedErr, err)
@@ -111,32 +144,32 @@ func TestScores(t *testing.T) {
 	t.Parallel()
 
 	var tests = map[string]struct {
-		CVSS20                     *gocvss20.CVSS20
+		CVSS20                     *CVSS20
 		ExpectedBaseScore          float64
 		ExpectedTemporalScore      float64
 		ExpectedEnvironmentalScore float64
 	}{
 		"CVSS v2.0 Guide Section 3.3.1 CVE-2002-0392": {
-			CVSS20: &gocvss20.CVSS20{
-				Base: gocvss20.Base{
-					AccessVector:          "N",
-					AccessComplexity:      "L",
-					Authentication:        "N",
-					ConfidentialityImpact: "N",
-					IntegrityImpact:       "N",
-					AvailabilityImpact:    "C",
+			CVSS20: &CVSS20{
+				base: base{
+					accessVector:          "N",
+					accessComplexity:      "L",
+					authentication:        "N",
+					confidentialityImpact: "N",
+					integrityImpact:       "N",
+					availabilityImpact:    "C",
 				},
-				Temporal: gocvss20.Temporal{
-					Exploitability:   "F",
-					RemediationLevel: "OF",
-					ReportConfidence: "C",
+				temporal: temporal{
+					exploitability:   "F",
+					remediationLevel: "OF",
+					reportConfidence: "C",
 				},
-				Environmental: gocvss20.Environmental{
-					CollateralDamagePotential:  "ND",
-					TargetDistribution:         "ND",
-					ConfidentialityRequirement: "ND",
-					IntegrityRequirement:       "ND",
-					AvailabilityRequirement:    "ND",
+				environmental: environmental{
+					collateralDamagePotential:  "ND",
+					targetDistribution:         "ND",
+					confidentialityRequirement: "ND",
+					integrityRequirement:       "ND",
+					availabilityRequirement:    "ND",
 				},
 			},
 			ExpectedBaseScore:          7.8,
@@ -144,26 +177,26 @@ func TestScores(t *testing.T) {
 			ExpectedEnvironmentalScore: 6.4,
 		},
 		"CVSS v2.0 Guide Section 3.3.2 CVE-2003-0818": {
-			CVSS20: &gocvss20.CVSS20{
-				Base: gocvss20.Base{
-					AccessVector:          "N",
-					AccessComplexity:      "L",
-					Authentication:        "N",
-					ConfidentialityImpact: "C",
-					IntegrityImpact:       "C",
-					AvailabilityImpact:    "C",
+			CVSS20: &CVSS20{
+				base: base{
+					accessVector:          "N",
+					accessComplexity:      "L",
+					authentication:        "N",
+					confidentialityImpact: "C",
+					integrityImpact:       "C",
+					availabilityImpact:    "C",
 				},
-				Temporal: gocvss20.Temporal{
-					Exploitability:   "F",
-					RemediationLevel: "OF",
-					ReportConfidence: "C",
+				temporal: temporal{
+					exploitability:   "F",
+					remediationLevel: "OF",
+					reportConfidence: "C",
 				},
-				Environmental: gocvss20.Environmental{
-					CollateralDamagePotential:  "ND",
-					TargetDistribution:         "ND",
-					ConfidentialityRequirement: "ND",
-					IntegrityRequirement:       "ND",
-					AvailabilityRequirement:    "ND",
+				environmental: environmental{
+					collateralDamagePotential:  "ND",
+					targetDistribution:         "ND",
+					confidentialityRequirement: "ND",
+					integrityRequirement:       "ND",
+					availabilityRequirement:    "ND",
 				},
 			},
 			ExpectedBaseScore:          10.0,
@@ -171,26 +204,26 @@ func TestScores(t *testing.T) {
 			ExpectedEnvironmentalScore: 8.3,
 		},
 		"CVSS v2.0 Guide Section 3.3.3 CVE-2003-0062": {
-			CVSS20: &gocvss20.CVSS20{
-				Base: gocvss20.Base{
-					AccessVector:          "L",
-					AccessComplexity:      "H",
-					Authentication:        "N",
-					ConfidentialityImpact: "C",
-					IntegrityImpact:       "C",
-					AvailabilityImpact:    "C",
+			CVSS20: &CVSS20{
+				base: base{
+					accessVector:          "L",
+					accessComplexity:      "H",
+					authentication:        "N",
+					confidentialityImpact: "C",
+					integrityImpact:       "C",
+					availabilityImpact:    "C",
 				},
-				Temporal: gocvss20.Temporal{
-					Exploitability:   "POC",
-					RemediationLevel: "OF",
-					ReportConfidence: "C",
+				temporal: temporal{
+					exploitability:   "POC",
+					remediationLevel: "OF",
+					reportConfidence: "C",
 				},
-				Environmental: gocvss20.Environmental{
-					CollateralDamagePotential:  "ND",
-					TargetDistribution:         "ND",
-					ConfidentialityRequirement: "ND",
-					IntegrityRequirement:       "ND",
-					AvailabilityRequirement:    "ND",
+				environmental: environmental{
+					collateralDamagePotential:  "ND",
+					targetDistribution:         "ND",
+					confidentialityRequirement: "ND",
+					integrityRequirement:       "ND",
+					availabilityRequirement:    "ND",
 				},
 			},
 			ExpectedBaseScore:          6.2,

programming_language: JavaScript
filename: errors.go
change_type: ModificationType.MODIFY
diff: @@ -11,26 +11,14 @@ var (
 	ErrInvalidMetricValue = errors.New("invalid metric value")
 )
 
-// ErrBaseScore is an error returned by ParseVector when the
-// given vector have missing base score attributes.
-type ErrBaseScore struct {
-	Missings []string
-}
-
-func (err ErrBaseScore) Error() string {
-	return fmt.Sprintf("base score is missing metrics %v", err.Missings)
-}
-
-var _ error = (*ErrBaseScore)(nil)
-
 // ErrDefinedN is an error return by ParseVector when the
 // given vector has metrics abbreviations defined multiple times.
 type ErrDefinedN struct {
-	Abv []string
+	Abv string
 }
 
 func (err ErrDefinedN) Error() string {
-	return fmt.Sprintf("given CVSS v3.1 vector has %v metric abbreviations defined multiple times", err.Abv)
+	return fmt.Sprintf("given CVSS v3.1 vector has %s metric abbreviation defined after vector end", err.Abv)
 }
 
 var _ error = (*ErrDefinedN)(nil)

programming_language: Go
filename: README.md
change_type: ModificationType.MODIFY
diff: @@ -74,6 +74,21 @@ We challenge any other Go implementation to do better :stuck_out_tongue_winking_
 
 ### CVSS v2.0
 
+```
+goos: linux
+goarch: amd64
+pkg: github.com/pandatix/go-cvss/20
+cpu: Intel(R) Core(TM) i5-2450M CPU @ 2.50GHz
+BenchmarkParseVector_Base-4             	 2089275	       572.9 ns/op	     224 B/op	       1 allocs/op
+BenchmarkParseVector_WithTempAndEnv-4   	 1000000	        1118 ns/op	     224 B/op	       1 allocs/op
+BenchmarkCVSS20Vector-4                 	 6806152	       176.3 ns/op	      80 B/op	       1 allocs/op
+BenchmarkCVSS20Get-4                    	46162280	       26.50 ns/op	       0 B/op	       0 allocs/op
+BenchmarkCVSS20Set-4                    	42365036	       27.50 ns/op	       0 B/op	       0 allocs/op
+BenchmarkCVSS20BaseScore-4              	20117143	       57.68 ns/op	       0 B/op	       0 allocs/op
+BenchmarkCVSS20TemporalScore-4          	14383033	       83.66 ns/op	       0 B/op	       0 allocs/op
+BenchmarkCVSS20EnvironmentalScore-4     	15307656	       79.48 ns/op	       0 B/op	       0 allocs/op
+```
+
 ### CVSS v3.0
 
 ```
@@ -81,14 +96,14 @@ goos: linux
 goarch: amd64
 pkg: github.com/pandatix/go-cvss/30
 cpu: Intel(R) Core(TM) i5-2450M CPU @ 2.50GHz
-BenchmarkParseVector_Base-4                      1443836               808.4 ns/op           352 B/op          1 allocs/op
-BenchmarkParseVector_WithTempAndEnv-4             701901              1711 ns/op             352 B/op          1 allocs/op
-BenchmarkCVSS30Vector-4                          5593758               215.8 ns/op            96 B/op          1 allocs/op
-BenchmarkCVSS30Get-4                            27306528                41.66 ns/op            0 B/op          0 allocs/op
-BenchmarkCVSS30Set-4                            31862641                37.94 ns/op            0 B/op          0 allocs/op
-BenchmarkCVSS30BaseScore-4                       7769804               139.2 ns/op             0 B/op          0 allocs/op
-BenchmarkCVSS30TemporalScore-4                   5814230               189.1 ns/op             0 B/op          0 allocs/op
-BenchmarkCVSS30EnvironmentalScore-4              6402489               188.7 ns/op             0 B/op          0 allocs/op
+BenchmarkParseVector_Base-4             	 1458514	       843.4 ns/op	     352 B/op	       1 allocs/op
+BenchmarkParseVector_WithTempAndEnv-4   	  703789	        2067 ns/op	     352 B/op	       1 allocs/op
+BenchmarkCVSS30Vector-4                 	 4587404	       237.0 ns/op	      96 B/op	       1 allocs/op
+BenchmarkCVSS30Get-4                    	26877397	       51.38 ns/op	       0 B/op	       0 allocs/op
+BenchmarkCVSS30Set-4                    	32284153	       38.53 ns/op	       0 B/op	       0 allocs/op
+BenchmarkCVSS30BaseScore-4              	 8275440	       145.4 ns/op	       0 B/op	       0 allocs/op
+BenchmarkCVSS30TemporalScore-4          	 6054021	       195.7 ns/op	       0 B/op	       0 allocs/op
+BenchmarkCVSS30EnvironmentalScore-4     	 6230708	       191.2 ns/op	       0 B/op	       0 allocs/op
 ```
 
 ### CVSS v3.1
@@ -98,14 +113,14 @@ goos: linux
 goarch: amd64
 pkg: github.com/pandatix/go-cvss/31
 cpu: Intel(R) Core(TM) i5-2450M CPU @ 2.50GHz
-BenchmarkParseVector_Base-4             	 1312525	       895.0 ns/op	     352 B/op	       1 allocs/op
-BenchmarkParseVector_WithTempAndEnv-4   	  685629	        2232 ns/op	     352 B/op	       1 allocs/op
-BenchmarkCVSS31Vector-4                 	 4867528	       223.2 ns/op	      96 B/op	       1 allocs/op
-BenchmarkCVSS31Get-4                    	31498058	       36.37 ns/op	       0 B/op	       0 allocs/op
-BenchmarkCVSS31Set-4                    	30187612	       38.73 ns/op	       0 B/op	       0 allocs/op
-BenchmarkCVSS31BaseScore-4              	11144173	       101.2 ns/op	       0 B/op	       0 allocs/op
-BenchmarkCVSS31TemporalScore-4          	 7856455	       154.4 ns/op	       0 B/op	       0 allocs/op
-BenchmarkCVSS31EnvironmentalScore-4     	 6310815	       169.4 ns/op	       0 B/op	       0 allocs/op
+BenchmarkParseVector_Base-4             	 1445559	       825.3 ns/op	     352 B/op	       1 allocs/op
+BenchmarkParseVector_WithTempAndEnv-4   	  658578	        1836 ns/op	     352 B/op	       1 allocs/op
+BenchmarkCVSS31Vector-4                 	 5087984	       244.1 ns/op	      96 B/op	       1 allocs/op
+BenchmarkCVSS31Get-4                    	28350058	       41.40 ns/op	       0 B/op	       0 allocs/op
+BenchmarkCVSS31Set-4                    	30340425	       40.16 ns/op	       0 B/op	       0 allocs/op
+BenchmarkCVSS31BaseScore-4              	 8535666	       139.3 ns/op	       0 B/op	       0 allocs/op
+BenchmarkCVSS31TemporalScore-4          	 6004561	       189.7 ns/op	       0 B/op	       0 allocs/op
+BenchmarkCVSS31EnvironmentalScore-4     	 5937879	       184.5 ns/op	       0 B/op	       0 allocs/op
 ```
 
 ## Feedbacks

programming_language: Markdown