aboutsummaryrefslogtreecommitdiff
path: root/cvss
diff options
context:
space:
mode:
Diffstat (limited to 'cvss')
-rw-r--r--cvss/badkey.go20
-rw-r--r--cvss/badmetric.go19
-rw-r--r--cvss/badmetric_test.go25
-rw-r--r--cvss/category.go13
-rw-r--r--cvss/category_string.go26
-rw-r--r--cvss/category_test.go41
-rw-r--r--cvss/cvss.go23
-rw-r--r--cvss/v2key.go92
-rw-r--r--cvss/v2key_string.go37
-rw-r--r--cvss/v2key_test.go116
-rw-r--r--cvss/v2metric.go244
-rw-r--r--cvss/v2metric_string.go78
-rw-r--r--cvss/v2metric_test.go276
-rw-r--r--cvss/v2vector.go108
-rw-r--r--cvss/v2vector_test.go69
-rw-r--r--cvss/v30vector.go119
-rw-r--r--cvss/v30vector_test.go69
-rw-r--r--cvss/v31vector.go119
-rw-r--r--cvss/v31vector_test.go561
-rw-r--r--cvss/v3key.go116
-rw-r--r--cvss/v3key_string.go45
-rw-r--r--cvss/v3key_test.go142
-rw-r--r--cvss/v3metric.go331
-rw-r--r--cvss/v3metric_string.go100
-rw-r--r--cvss/v3metric_test.go366
-rw-r--r--cvss/vector.go36
-rw-r--r--cvss/vector_test.go659
-rw-r--r--cvss/version.go12
-rw-r--r--cvss/version_string.go25
-rw-r--r--cvss/version_test.go24
30 files changed, 3911 insertions, 0 deletions
diff --git a/cvss/badkey.go b/cvss/badkey.go
new file mode 100644
index 0000000..d4bc845
--- /dev/null
+++ b/cvss/badkey.go
@@ -0,0 +1,20 @@
+package cvss
+
+// disabled for now (unused)
+// import "fmt"
+//
+// // Bad metric key error.
+// type badKey struct {
+// version Version
+// key string
+// }
+//
+// // Create new bad key error.
+// func newBadKey(version Version, key string) error {
+// return &badKey { version, key }
+// }
+//
+// // Return printable error string
+// func (e badKey) Error() string {
+// return fmt.Sprintf("invalid CVSS %s metric key: %s", e.version, e.key)
+// }
diff --git a/cvss/badmetric.go b/cvss/badmetric.go
new file mode 100644
index 0000000..8f0bf5f
--- /dev/null
+++ b/cvss/badmetric.go
@@ -0,0 +1,19 @@
+package cvss
+
+import "fmt"
+
+// Bad metric error.
+type badMetric struct {
+ version Version // CVSS version
+ val string // metric value
+}
+
+// Create new bad key error.
+func newBadMetric(version Version, val string) error {
+ return &badMetric { version, val }
+}
+
+// Return printable error string.
+func (e badMetric) Error() string {
+ return fmt.Sprintf("invalid CVSS %s metric: %s", e.version, e.val)
+}
diff --git a/cvss/badmetric_test.go b/cvss/badmetric_test.go
new file mode 100644
index 0000000..87316c0
--- /dev/null
+++ b/cvss/badmetric_test.go
@@ -0,0 +1,25 @@
+package cvss
+
+import "testing"
+
+func TestNewBadMetric(t *testing.T) {
+ tests := []struct {
+ name string
+ version Version
+ val string
+ exp string
+ } {
+ { "v20-foo", V20, "foo", "invalid CVSS 2.0 metric: foo" },
+ { "v30-foo", V30, "foo", "invalid CVSS 3.0 metric: foo" },
+ { "v31-foo", V31, "foo", "invalid CVSS 3.1 metric: foo" },
+ }
+
+ for _, test := range(tests) {
+ t.Run(test.name, func(t *testing.T) {
+ err := newBadMetric(test.version, test.val)
+ if err.Error() != test.exp {
+ t.Errorf("got: %s, exp: %s", err.Error(), test.exp)
+ }
+ })
+ }
+}
diff --git a/cvss/category.go b/cvss/category.go
new file mode 100644
index 0000000..54781dc
--- /dev/null
+++ b/cvss/category.go
@@ -0,0 +1,13 @@
+package cvss
+
+//go:generate stringer -linecomment -type=Category
+
+// CVSS metric category.
+type Category byte
+
+const (
+ Base Category = iota // Base
+ Temporal // Temporal
+ Environmental // Environmental
+ InvalidCategory // invalid
+)
diff --git a/cvss/category_string.go b/cvss/category_string.go
new file mode 100644
index 0000000..782f8fb
--- /dev/null
+++ b/cvss/category_string.go
@@ -0,0 +1,26 @@
+// Code generated by "stringer -linecomment -type=Category"; DO NOT EDIT.
+
+package cvss
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[Base-0]
+ _ = x[Temporal-1]
+ _ = x[Environmental-2]
+ _ = x[InvalidCategory-3]
+}
+
+const _Category_name = "BaseTemporalEnvironmentalinvalid"
+
+var _Category_index = [...]uint8{0, 4, 12, 25, 32}
+
+func (i Category) String() string {
+ if i >= Category(len(_Category_index)-1) {
+ return "Category(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _Category_name[_Category_index[i]:_Category_index[i+1]]
+}
diff --git a/cvss/category_test.go b/cvss/category_test.go
new file mode 100644
index 0000000..7f69689
--- /dev/null
+++ b/cvss/category_test.go
@@ -0,0 +1,41 @@
+package cvss
+
+import "testing"
+
+func TestCategoryString(t *testing.T) {
+ tests := []struct {
+ cat Category
+ exp string
+ } {
+ { Base, "Base" },
+ { Temporal, "Temporal" },
+ { Environmental, "Environmental" },
+ }
+
+ for _, test := range(tests) {
+ t.Run(test.exp, func(t *testing.T) {
+ got := test.cat.String()
+ if got != test.exp {
+ t.Errorf("got: %s, exp: %s", got, test.exp)
+ }
+ })
+ }
+}
+
+func TestInvalidCategory(t *testing.T) {
+ tests := []struct {
+ cat Category
+ exp string
+ } {
+ { Category(byte(255)), "Category(255)" },
+ }
+
+ for _, test := range(tests) {
+ t.Run(test.exp, func(t *testing.T) {
+ got := test.cat.String()
+ if got != test.exp {
+ t.Errorf("got: %s, exp: %s", got, test.exp)
+ }
+ })
+ }
+}
diff --git a/cvss/cvss.go b/cvss/cvss.go
new file mode 100644
index 0000000..2bae0d7
--- /dev/null
+++ b/cvss/cvss.go
@@ -0,0 +1,23 @@
+// CVSS vector parser.
+package cvss
+
+// Metric key.
+type Key interface {
+ // Get full name.
+ Name() string
+
+ // Get category.
+ Category() Category
+
+ // Return string representation.
+ String() string
+}
+
+// CVSS metric.
+type Metric interface {
+ // Get metric key.
+ Key() Key
+
+ // Return string representation of metric.
+ String() string
+}
diff --git a/cvss/v2key.go b/cvss/v2key.go
new file mode 100644
index 0000000..740d26d
--- /dev/null
+++ b/cvss/v2key.go
@@ -0,0 +1,92 @@
+package cvss
+
+//go:generate stringer -linecomment -type=v2Key
+
+// CVSS 2.0 metric key.
+type v2Key byte
+
+const (
+ v2AccessVector v2Key = iota // AV
+ v2AccessComplexity // AC
+ v2Authentication // Au
+ v2ConfidentialityImpact // C
+ v2IntegrityImpact // I
+ v2AvailabilityImpact // A
+ v2Exploitability // E
+ v2RemediationLevel // RL
+ v2ReportConfidence // RC
+ v2CollateralDamagePotential // CDP
+ v2TargetDistribution // TD
+ v2ConfidentialityRequirement // CR
+ v2IntegrityRequirement // IR
+ v2AvailabilityRequirement // AR
+
+ v2InvalidKey // invalid
+)
+
+// CVSS V2 metric key info lut
+var v2Keys = map[v2Key]struct {
+ Name string
+ Category Category
+} {
+ v2AccessVector: { "Access Vector", Base },
+ v2AccessComplexity: { "Access Complexity", Base },
+ v2Authentication: { "Authentication", Base },
+ v2ConfidentialityImpact: { "Confidentiality Impact", Base },
+ v2IntegrityImpact: { "Integrity Impact", Base },
+ v2AvailabilityImpact: { "Availability Impact", Base },
+ v2Exploitability: { "Exploitability", Temporal },
+ v2RemediationLevel: { "Remediation Level", Temporal },
+ v2ReportConfidence: { "Report Confidence", Temporal },
+ v2CollateralDamagePotential: { "Collateral Damage Potential", Environmental },
+ v2TargetDistribution: { "Target Distribution", Environmental },
+ v2ConfidentialityRequirement: { "Confidentiality Requirement", Environmental },
+ v2IntegrityRequirement: { "Integrity Requirement", Environmental },
+ v2AvailabilityRequirement: { "Availability Requirement", Environmental },
+}
+
+// // v2 metric key IDs lut
+// var v2KeyIds = map[string]v2Key {
+// "AV": v2AccessVector,
+// "AC": v2AccessComplexity,
+// "Au": v2Authentication,
+// "C": v2ConfidentialityImpact,
+// "I": v2IntegrityImpact,
+// "A": v2AvailabilityImpact,
+// "E": v2Exploitability,
+// "RL": v2RemediationLevel,
+// "RC": v2ReportConfidence,
+// "CDP": v2CollateralDamagePotential,
+// "TD": v2TargetDistribution,
+// "CR": v2ConfidentialityRequirement,
+// "IR": v2IntegrityRequirement,
+// "AR": v2AvailabilityRequirement,
+// }
+//
+// // Get metric key from string.
+// func getV2KeyFromString(s string) (v2Key, error) {
+// k, ok := v2KeyIds[s]
+// if ok {
+// return k, nil
+// } else {
+// return v2InvalidKey, newBadKey(V20, s)
+// }
+// }
+
+// Get metric key name.
+func (k v2Key) Name() string {
+ if data, ok := v2Keys[k]; ok {
+ return data.Name
+ } else {
+ return "invalid"
+ }
+}
+
+// Get metric key category.
+func (k v2Key) Category() Category {
+ if data, ok := v2Keys[k]; ok {
+ return data.Category
+ } else {
+ return InvalidCategory
+ }
+}
diff --git a/cvss/v2key_string.go b/cvss/v2key_string.go
new file mode 100644
index 0000000..8e945fd
--- /dev/null
+++ b/cvss/v2key_string.go
@@ -0,0 +1,37 @@
+// Code generated by "stringer -linecomment -type=v2Key"; DO NOT EDIT.
+
+package cvss
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[v2AccessVector-0]
+ _ = x[v2AccessComplexity-1]
+ _ = x[v2Authentication-2]
+ _ = x[v2ConfidentialityImpact-3]
+ _ = x[v2IntegrityImpact-4]
+ _ = x[v2AvailabilityImpact-5]
+ _ = x[v2Exploitability-6]
+ _ = x[v2RemediationLevel-7]
+ _ = x[v2ReportConfidence-8]
+ _ = x[v2CollateralDamagePotential-9]
+ _ = x[v2TargetDistribution-10]
+ _ = x[v2ConfidentialityRequirement-11]
+ _ = x[v2IntegrityRequirement-12]
+ _ = x[v2AvailabilityRequirement-13]
+ _ = x[v2InvalidKey-14]
+}
+
+const _v2Key_name = "AVACAuCIAERLRCCDPTDCRIRARinvalid"
+
+var _v2Key_index = [...]uint8{0, 2, 4, 6, 7, 8, 9, 10, 12, 14, 17, 19, 21, 23, 25, 32}
+
+func (i v2Key) String() string {
+ if i >= v2Key(len(_v2Key_index)-1) {
+ return "v2Key(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _v2Key_name[_v2Key_index[i]:_v2Key_index[i+1]]
+}
diff --git a/cvss/v2key_test.go b/cvss/v2key_test.go
new file mode 100644
index 0000000..a54f4e7
--- /dev/null
+++ b/cvss/v2key_test.go
@@ -0,0 +1,116 @@
+package cvss
+
+import "testing"
+
+func TestV2KeyString(t *testing.T) {
+ tests := []struct {
+ key v2Key
+ exp string
+ } {
+ { v2AccessVector, "AV" },
+ { v2AccessComplexity, "AC" },
+ { v2Authentication, "Au" },
+ { v2ConfidentialityImpact, "C" },
+ { v2IntegrityImpact, "I" },
+ { v2AvailabilityImpact, "A" },
+ { v2Exploitability, "E" },
+ { v2RemediationLevel, "RL" },
+ { v2ReportConfidence, "RC" },
+ { v2CollateralDamagePotential, "CDP" },
+ { v2TargetDistribution, "TD" },
+ { v2ConfidentialityRequirement, "CR" },
+ { v2IntegrityRequirement, "IR" },
+ { v2AvailabilityRequirement, "AR" },
+
+ { v2Key(255), "v2Key(255)" },
+ }
+
+ for _, test := range(tests) {
+ t.Run(test.exp, func(t *testing.T) {
+ got := test.key.String()
+ if got != test.exp {
+ t.Errorf("got: \"%s\", exp: \"%s\"", got, test.exp)
+ }
+ })
+ }
+}
+
+func TestV2KeyName(t *testing.T) {
+ tests := []struct {
+ key v2Key
+ exp string
+ } {
+ { v2AccessVector, "Access Vector" },
+ { v2AccessComplexity, "Access Complexity" },
+ { v2Authentication, "Authentication" },
+ { v2ConfidentialityImpact, "Confidentiality Impact" },
+ { v2IntegrityImpact, "Integrity Impact" },
+ { v2AvailabilityImpact, "Availability Impact" },
+ { v2Exploitability, "Exploitability" },
+ { v2RemediationLevel, "Remediation Level" },
+ { v2ReportConfidence, "Report Confidence" },
+ { v2CollateralDamagePotential, "Collateral Damage Potential" },
+ { v2TargetDistribution, "Target Distribution" },
+ { v2ConfidentialityRequirement, "Confidentiality Requirement" },
+ { v2IntegrityRequirement, "Integrity Requirement" },
+ { v2AvailabilityRequirement, "Availability Requirement" },
+ }
+
+ for _, test := range(tests) {
+ t.Run(test.exp, func(t *testing.T) {
+ got := test.key.Name()
+ if got != test.exp {
+ t.Errorf("got: \"%s\", exp: \"%s\"", got, test.exp)
+ }
+ })
+ }
+}
+
+func TestV2KeyCategory(t *testing.T) {
+ tests := []struct {
+ key v2Key
+ exp Category
+ } {
+ { v2AccessVector, Base },
+ { v2AccessComplexity, Base },
+ { v2Authentication, Base },
+ { v2ConfidentialityImpact, Base },
+ { v2IntegrityImpact, Base },
+ { v2AvailabilityImpact, Base },
+ { v2Exploitability, Temporal },
+ { v2RemediationLevel, Temporal },
+ { v2ReportConfidence, Temporal },
+ { v2CollateralDamagePotential, Environmental },
+ { v2TargetDistribution, Environmental},
+ { v2ConfidentialityRequirement, Environmental},
+ { v2IntegrityRequirement, Environmental},
+ { v2AvailabilityRequirement, Environmental},
+ }
+
+ for _, test := range(tests) {
+ t.Run(test.key.String(), func(t *testing.T) {
+ got := test.key.Category()
+ if got != test.exp {
+ t.Errorf("got: \"%s\", exp: \"%s\"", got, test.exp)
+ }
+ })
+ }
+}
+
+func TestInvalidV2KeyName(t *testing.T) {
+ exp := "invalid"
+ got := v2Key(255).Name()
+
+ if got != exp {
+ t.Errorf("got: \"%s\", exp: \"%s\"", got, exp)
+ }
+}
+
+func TestInvalidV2KeyCategory(t *testing.T) {
+ exp := InvalidCategory
+ got := v2Key(255).Category()
+
+ if got != exp {
+ t.Errorf("got: \"%s\", exp: \"%s\"", got, exp)
+ }
+}
diff --git a/cvss/v2metric.go b/cvss/v2metric.go
new file mode 100644
index 0000000..2dee41a
--- /dev/null
+++ b/cvss/v2metric.go
@@ -0,0 +1,244 @@
+package cvss
+
+//go:generate stringer -linecomment -type=v2Metric
+
+// CVSS v2 metric value
+type v2Metric byte
+
+const (
+ v2AVNetwork v2Metric = iota // AV:N
+ v2AVAdjacentNetwork // AV:A
+ v2AVLocal // AV:L
+
+ v2ACLow // AC:L
+ v2ACMedium // AC:M
+ v2ACHigh // AC:H
+
+ v2AuMultiple // Au:M
+ v2AuSingle // Au:S
+ v2AuNone // Au:N
+
+ v2CNone // C:N
+ v2CPartial // C:P
+ v2CComplete // C:C
+
+ v2INone // I:N
+ v2IPartial // I:P
+ v2IComplete // I:C
+
+ v2ANone // A:N
+ v2APartial // A:P
+ v2AComplete // A:C
+
+ v2ENotDefined // E:ND
+ v2EUnproven // E:U
+ v2EProofOfConcept // E:POC
+ v2EFunctional // E:F
+ v2EHigh // E:H
+
+ v2RLOfficialFix // RL:OF
+ v2RLTemporaryFix // RL:TF
+ v2RLWorkaround // RL:W
+ v2RLUnavailable // RL:U
+ v2RLNotDefined // RL:ND
+
+ v2RCUnconfirmed // RC:UC
+ v2RCUncorroborated // RC:UR
+ v2RCConfirmed // RC:C
+ v2RCNotDefined // RC:ND
+
+ v2CDPNone // CDP:N
+ v2CDPLow // CDP:L
+ v2CDPLowMedium // CDP:LM
+ v2CDPMediumHigh // CDP:MH
+ v2CDPHigh // CDP:H
+ v2CDPNotDefined // CDP:ND
+
+ v2TDNone // TD:N
+ v2TDLow // TD:L
+ v2TDMedium // TD:M
+ v2TDHigh // TD:H
+ v2TDNotDefined // TD:ND
+
+ v2CRLow // CR:L
+ v2CRMedium // CR:M
+ v2CRHigh // CR:H
+ v2CRNotDefined // CR:ND
+
+ v2IRLow // IR:L
+ v2IRMedium // IR:M
+ v2IRHigh // IR:H
+ v2IRNotDefined // IR:ND
+
+ v2ARLow // AR:L
+ v2ARMedium // AR:M
+ v2ARHigh // AR:H
+ v2ARNotDefined // AR:ND
+
+ v2InvalidMetric // invalid
+)
+
+// map of metrics to metric keys
+var v2KeyLut = map[v2Metric]v2Key {
+ v2AVNetwork: v2AccessVector,
+ v2AVAdjacentNetwork: v2AccessVector,
+ v2AVLocal: v2AccessVector,
+
+ v2ACLow: v2AccessComplexity,
+ v2ACMedium: v2AccessComplexity,
+ v2ACHigh: v2AccessComplexity,
+
+ v2AuMultiple: v2Authentication,
+ v2AuSingle: v2Authentication,
+ v2AuNone: v2Authentication,
+
+ v2CNone: v2ConfidentialityImpact,
+ v2CPartial: v2ConfidentialityImpact,
+ v2CComplete: v2ConfidentialityImpact,
+
+ v2INone: v2IntegrityImpact,
+ v2IPartial: v2IntegrityImpact,
+ v2IComplete: v2IntegrityImpact,
+
+ v2ANone: v2AvailabilityImpact,
+ v2APartial: v2AvailabilityImpact,
+ v2AComplete: v2AvailabilityImpact,
+
+ v2ENotDefined: v2Exploitability,
+ v2EUnproven: v2Exploitability,
+ v2EProofOfConcept: v2Exploitability,
+ v2EFunctional: v2Exploitability,
+ v2EHigh: v2Exploitability,
+
+ v2RLOfficialFix: v2RemediationLevel,
+ v2RLTemporaryFix: v2RemediationLevel,
+ v2RLWorkaround: v2RemediationLevel,
+ v2RLUnavailable: v2RemediationLevel,
+ v2RLNotDefined: v2RemediationLevel,
+
+ v2RCUnconfirmed: v2ReportConfidence,
+ v2RCUncorroborated: v2ReportConfidence,
+ v2RCConfirmed: v2ReportConfidence,
+ v2RCNotDefined: v2ReportConfidence,
+
+ v2CDPNone: v2CollateralDamagePotential,
+ v2CDPLow: v2CollateralDamagePotential,
+ v2CDPLowMedium: v2CollateralDamagePotential,
+ v2CDPMediumHigh: v2CollateralDamagePotential,
+ v2CDPHigh: v2CollateralDamagePotential,
+ v2CDPNotDefined: v2CollateralDamagePotential,
+
+ v2TDNone: v2TargetDistribution,
+ v2TDLow: v2TargetDistribution,
+ v2TDMedium: v2TargetDistribution,
+ v2TDHigh: v2TargetDistribution,
+ v2TDNotDefined: v2TargetDistribution,
+
+ v2CRLow: v2ConfidentialityRequirement,
+ v2CRMedium: v2ConfidentialityRequirement,
+ v2CRHigh: v2ConfidentialityRequirement,
+ v2CRNotDefined: v2ConfidentialityRequirement,
+
+ v2IRLow: v2IntegrityRequirement,
+ v2IRMedium: v2IntegrityRequirement,
+ v2IRHigh: v2IntegrityRequirement,
+ v2IRNotDefined: v2IntegrityRequirement,
+
+ v2ARLow: v2AvailabilityRequirement,
+ v2ARMedium: v2AvailabilityRequirement,
+ v2ARHigh: v2AvailabilityRequirement,
+ v2ARNotDefined: v2AvailabilityRequirement,
+}
+
+// map of metric strings to metrics
+var v2MetricStrLut = map[string]v2Metric {
+ "AV:N": v2AVNetwork,
+ "AV:A": v2AVAdjacentNetwork,
+ "AV:L": v2AVLocal,
+
+ "AC:L": v2ACLow,
+ "AC:M": v2ACMedium,
+ "AC:H": v2ACHigh,
+
+ "Au:M": v2AuMultiple,
+ "Au:S": v2AuSingle,
+ "Au:N": v2AuNone,
+
+ "C:N": v2CNone,
+ "C:P": v2CPartial,
+ "C:C": v2CComplete,
+
+ "I:N": v2INone,
+ "I:P": v2IPartial,
+ "I:C": v2IComplete,
+
+ "A:N": v2ANone,
+ "A:P": v2APartial,
+ "A:C": v2AComplete,
+
+ "E:ND": v2ENotDefined,
+ "E:U": v2EUnproven,
+ "E:POC": v2EProofOfConcept,
+ "E:F": v2EFunctional,
+ "E:H": v2EHigh,
+
+ "RL:OF": v2RLOfficialFix,
+ "RL:TF": v2RLTemporaryFix,
+ "RL:W": v2RLWorkaround,
+ "RL:U": v2RLUnavailable,
+ "RL:ND": v2RLNotDefined,
+
+ "RC:UC": v2RCUnconfirmed,
+ "RC:UR": v2RCUncorroborated,
+ "RC:C": v2RCConfirmed,
+ "RC:ND": v2RCNotDefined,
+
+ "CDP:N": v2CDPNone,
+ "CDP:L": v2CDPLow,
+ "CDP:LM": v2CDPLowMedium,
+ "CDP:MH": v2CDPMediumHigh,
+ "CDP:H": v2CDPHigh,
+ "CDP:ND": v2CDPNotDefined,
+
+ "TD:N": v2TDNone,
+ "TD:L": v2TDLow,
+ "TD:M": v2TDMedium,
+ "TD:H": v2TDHigh,
+ "TD:ND": v2TDNotDefined,
+
+ "CR:L": v2CRLow,
+ "CR:M": v2CRMedium,
+ "CR:H": v2CRHigh,
+ "CR:ND": v2CRNotDefined,
+
+ "IR:L": v2IRLow,
+ "IR:M": v2IRMedium,
+ "IR:H": v2IRHigh,
+ "IR:ND": v2IRNotDefined,
+
+ "AR:L": v2ARLow,
+ "AR:M": v2ARMedium,
+ "AR:H": v2ARHigh,
+ "AR:ND": v2ARNotDefined,
+}
+
+// Convert string to CVSS 2.0 metric.
+func getV2Metric(s string) (v2Metric, error) {
+ // get metric
+ m, ok := v2MetricStrLut[s]
+ if !ok {
+ return v2InvalidMetric, newBadMetric(V20, s)
+ }
+
+ // return success
+ return m, nil
+}
+
+// Get CVSS 2.0 metric key.
+func (m v2Metric) Key() Key {
+ if k, ok := v2KeyLut[m]; ok {
+ return k
+ } else {
+ return v2InvalidKey
+ }
+}
diff --git a/cvss/v2metric_string.go b/cvss/v2metric_string.go
new file mode 100644
index 0000000..6ad2f93
--- /dev/null
+++ b/cvss/v2metric_string.go
@@ -0,0 +1,78 @@
+// Code generated by "stringer -linecomment -type=v2Metric"; DO NOT EDIT.
+
+package cvss
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[v2AVNetwork-0]
+ _ = x[v2AVAdjacentNetwork-1]
+ _ = x[v2AVLocal-2]
+ _ = x[v2ACLow-3]
+ _ = x[v2ACMedium-4]
+ _ = x[v2ACHigh-5]
+ _ = x[v2AuMultiple-6]
+ _ = x[v2AuSingle-7]
+ _ = x[v2AuNone-8]
+ _ = x[v2CNone-9]
+ _ = x[v2CPartial-10]
+ _ = x[v2CComplete-11]
+ _ = x[v2INone-12]
+ _ = x[v2IPartial-13]
+ _ = x[v2IComplete-14]
+ _ = x[v2ANone-15]
+ _ = x[v2APartial-16]
+ _ = x[v2AComplete-17]
+ _ = x[v2ENotDefined-18]
+ _ = x[v2EUnproven-19]
+ _ = x[v2EProofOfConcept-20]
+ _ = x[v2EFunctional-21]
+ _ = x[v2EHigh-22]
+ _ = x[v2RLOfficialFix-23]
+ _ = x[v2RLTemporaryFix-24]
+ _ = x[v2RLWorkaround-25]
+ _ = x[v2RLUnavailable-26]
+ _ = x[v2RLNotDefined-27]
+ _ = x[v2RCUnconfirmed-28]
+ _ = x[v2RCUncorroborated-29]
+ _ = x[v2RCConfirmed-30]
+ _ = x[v2RCNotDefined-31]
+ _ = x[v2CDPNone-32]
+ _ = x[v2CDPLow-33]
+ _ = x[v2CDPLowMedium-34]
+ _ = x[v2CDPMediumHigh-35]
+ _ = x[v2CDPHigh-36]
+ _ = x[v2CDPNotDefined-37]
+ _ = x[v2TDNone-38]
+ _ = x[v2TDLow-39]
+ _ = x[v2TDMedium-40]
+ _ = x[v2TDHigh-41]
+ _ = x[v2TDNotDefined-42]
+ _ = x[v2CRLow-43]
+ _ = x[v2CRMedium-44]
+ _ = x[v2CRHigh-45]
+ _ = x[v2CRNotDefined-46]
+ _ = x[v2IRLow-47]
+ _ = x[v2IRMedium-48]
+ _ = x[v2IRHigh-49]
+ _ = x[v2IRNotDefined-50]
+ _ = x[v2ARLow-51]
+ _ = x[v2ARMedium-52]
+ _ = x[v2ARHigh-53]
+ _ = x[v2ARNotDefined-54]
+ _ = x[v2InvalidMetric-55]
+}
+
+const _v2Metric_name = "AV:NAV:AAV:LAC:LAC:MAC:HAu:MAu:SAu:NC:NC:PC:CI:NI:PI:CA:NA:PA:CE:NDE:UE:POCE:FE:HRL:OFRL:TFRL:WRL:URL:NDRC:UCRC:URRC:CRC:NDCDP:NCDP:LCDP:LMCDP:MHCDP:HCDP:NDTD:NTD:LTD:MTD:HTD:NDCR:LCR:MCR:HCR:NDIR:LIR:MIR:HIR:NDAR:LAR:MAR:HAR:NDinvalid"
+
+var _v2Metric_index = [...]uint8{0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 67, 70, 75, 78, 81, 86, 91, 95, 99, 104, 109, 114, 118, 123, 128, 133, 139, 145, 150, 156, 160, 164, 168, 172, 177, 181, 185, 189, 194, 198, 202, 206, 211, 215, 219, 223, 228, 235}
+
+func (i v2Metric) String() string {
+ if i >= v2Metric(len(_v2Metric_index)-1) {
+ return "v2Metric(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _v2Metric_name[_v2Metric_index[i]:_v2Metric_index[i+1]]
+}
diff --git a/cvss/v2metric_test.go b/cvss/v2metric_test.go
new file mode 100644
index 0000000..f606ed5
--- /dev/null
+++ b/cvss/v2metric_test.go
@@ -0,0 +1,276 @@
+package cvss
+
+import "testing"
+
+func TestGetV2Metric(t *testing.T) {
+ tests := []struct {
+ val string
+ exp v2Metric
+ ok bool
+ } {
+ { "AV:N", v2AVNetwork, true },
+ { "AV:A", v2AVAdjacentNetwork, true },
+ { "AV:L", v2AVLocal, true },
+
+ { "AC:L", v2ACLow, true },
+ { "AC:M", v2ACMedium, true },
+ { "AC:H", v2ACHigh, true },
+
+ { "Au:M", v2AuMultiple, true },
+ { "Au:S", v2AuSingle, true },
+ { "Au:N", v2AuNone, true },
+
+ { "C:N", v2CNone, true },
+ { "C:P", v2CPartial, true },
+ { "C:C", v2CComplete, true },
+
+ { "I:N", v2INone, true },
+ { "I:P", v2IPartial, true },
+ { "I:C", v2IComplete, true },
+
+ { "A:N", v2ANone, true },
+ { "A:P", v2APartial, true },
+ { "A:C", v2AComplete, true },
+
+ { "E:ND", v2ENotDefined, true },
+ { "E:U", v2EUnproven, true },
+ { "E:POC", v2EProofOfConcept, true },
+ { "E:F", v2EFunctional, true },
+ { "E:H", v2EHigh, true },
+
+ { "RL:OF", v2RLOfficialFix, true },
+ { "RL:TF", v2RLTemporaryFix, true },
+ { "RL:W", v2RLWorkaround, true },
+ { "RL:U", v2RLUnavailable, true },
+ { "RL:ND", v2RLNotDefined, true },
+
+ { "RC:UC", v2RCUnconfirmed, true },
+ { "RC:UR", v2RCUncorroborated, true },
+ { "RC:C", v2RCConfirmed, true },
+ { "RC:ND", v2RCNotDefined, true },
+
+ { "CDP:N", v2CDPNone, true },
+ { "CDP:L", v2CDPLow, true },
+ { "CDP:LM", v2CDPLowMedium, true },
+ { "CDP:MH", v2CDPMediumHigh, true },
+ { "CDP:H", v2CDPHigh, true },
+ { "CDP:ND", v2CDPNotDefined, true },
+
+ { "TD:N", v2TDNone, true },
+ { "TD:L", v2TDLow, true },
+ { "TD:M", v2TDMedium, true },
+ { "TD:H", v2TDHigh, true },
+ { "TD:ND", v2TDNotDefined, true },
+
+ { "CR:L", v2CRLow, true },
+ { "CR:M", v2CRMedium, true },
+ { "CR:H", v2CRHigh, true },
+ { "CR:ND", v2CRNotDefined, true },
+
+ { "IR:L", v2IRLow, true },
+ { "IR:M", v2IRMedium, true },
+ { "IR:H", v2IRHigh, true },
+ { "IR:ND", v2IRNotDefined, true },
+
+ { "AR:L", v2ARLow, true },
+ { "AR:M", v2ARMedium, true },
+ { "AR:H", v2ARHigh, true },
+ { "AR:ND", v2ARNotDefined, true },
+
+ { "asdf", v2InvalidMetric, false },
+ }
+
+ for _, test := range(tests) {
+ t.Run(test.val, func(t *testing.T) {
+ got, err := getV2Metric(test.val)
+ if test.ok && err == nil && got != test.exp {
+ t.Errorf("got: \"%s\", exp: \"%s\"", got, test.exp)
+ } else if test.ok && err != nil {
+ t.Error(err)
+ } else if !test.ok && err == nil {
+ t.Errorf("got: \"%s\", exp: error", got)
+ }
+ })
+ }
+}
+
+func TestGetV2MetricKey(t *testing.T) {
+ tests := []struct {
+ val v2Metric
+ exp v2Key
+ } {
+ { v2AVNetwork, v2AccessVector },
+ { v2AVAdjacentNetwork, v2AccessVector },
+ { v2AVLocal, v2AccessVector },
+
+ { v2ACLow, v2AccessComplexity },
+ { v2ACMedium, v2AccessComplexity },
+ { v2ACHigh, v2AccessComplexity },
+
+ { v2AuMultiple, v2Authentication },
+ { v2AuSingle, v2Authentication },
+ { v2AuNone, v2Authentication },
+
+ { v2CNone, v2ConfidentialityImpact },
+ { v2CPartial, v2ConfidentialityImpact },
+ { v2CComplete, v2ConfidentialityImpact },
+
+ { v2INone, v2IntegrityImpact },
+ { v2IPartial, v2IntegrityImpact },
+ { v2IComplete, v2IntegrityImpact },
+
+ { v2ANone, v2AvailabilityImpact },
+ { v2APartial, v2AvailabilityImpact },
+ { v2AComplete, v2AvailabilityImpact },
+
+ { v2ENotDefined, v2Exploitability },
+ { v2EUnproven, v2Exploitability },
+ { v2EProofOfConcept, v2Exploitability },
+ { v2EFunctional, v2Exploitability },
+ { v2EHigh, v2Exploitability },
+
+ { v2RLOfficialFix, v2RemediationLevel },
+ { v2RLTemporaryFix, v2RemediationLevel },
+ { v2RLWorkaround, v2RemediationLevel },
+ { v2RLUnavailable, v2RemediationLevel },
+ { v2RLNotDefined, v2RemediationLevel },
+
+ { v2RCUnconfirmed, v2ReportConfidence },
+ { v2RCUncorroborated, v2ReportConfidence },
+ { v2RCConfirmed, v2ReportConfidence },
+ { v2RCNotDefined, v2ReportConfidence },
+
+ { v2CDPNone, v2CollateralDamagePotential },
+ { v2CDPLow, v2CollateralDamagePotential },
+ { v2CDPLowMedium, v2CollateralDamagePotential },
+ { v2CDPMediumHigh, v2CollateralDamagePotential },
+ { v2CDPHigh, v2CollateralDamagePotential },
+ { v2CDPNotDefined, v2CollateralDamagePotential },
+
+ { v2TDNone, v2TargetDistribution },
+ { v2TDLow, v2TargetDistribution },
+ { v2TDMedium, v2TargetDistribution },
+ { v2TDHigh, v2TargetDistribution },
+ { v2TDNotDefined, v2TargetDistribution },
+
+ { v2CRLow, v2ConfidentialityRequirement },
+ { v2CRMedium, v2ConfidentialityRequirement },
+ { v2CRHigh, v2ConfidentialityRequirement },
+ { v2CRNotDefined, v2ConfidentialityRequirement },
+
+ { v2IRLow, v2IntegrityRequirement },
+ { v2IRMedium, v2IntegrityRequirement },
+ { v2IRHigh, v2IntegrityRequirement },
+ { v2IRNotDefined, v2IntegrityRequirement },
+
+ { v2ARLow, v2AvailabilityRequirement },
+ { v2ARMedium, v2AvailabilityRequirement },
+ { v2ARHigh, v2AvailabilityRequirement },
+ { v2ARNotDefined, v2AvailabilityRequirement },
+ }
+
+ for _, test := range(tests) {
+ t.Run(test.val.String(), func(t *testing.T) {
+ got := test.val.Key()
+ if got != test.exp {
+ t.Errorf("got: \"%s\", exp: \"%s\"", got, test.exp)
+ }
+ })
+ }
+}
+
+func TestV2MetricString(t *testing.T) {
+ tests := []struct {
+ val v2Metric
+ exp string
+ } {
+ { v2AVNetwork, "AV:N" },
+ { v2AVAdjacentNetwork, "AV:A" },
+ { v2AVLocal, "AV:L" },
+
+ { v2ACLow, "AC:L" },
+ { v2ACMedium, "AC:M" },
+ { v2ACHigh, "AC:H" },
+
+ { v2AuMultiple, "Au:M" },
+ { v2AuSingle, "Au:S" },
+ { v2AuNone, "Au:N" },
+
+ { v2CNone, "C:N" },
+ { v2CPartial, "C:P" },
+ { v2CComplete, "C:C" },
+
+ { v2INone, "I:N" },
+ { v2IPartial, "I:P" },
+ { v2IComplete, "I:C" },
+
+ { v2ANone, "A:N" },
+ { v2APartial, "A:P" },
+ { v2AComplete, "A:C" },
+
+ { v2ENotDefined, "E:ND" },
+ { v2EUnproven, "E:U" },
+ { v2EProofOfConcept, "E:POC" },
+ { v2EFunctional, "E:F" },
+ { v2EHigh, "E:H" },
+
+ { v2RLOfficialFix, "RL:OF" },
+ { v2RLTemporaryFix, "RL:TF" },
+ { v2RLWorkaround, "RL:W" },
+ { v2RLUnavailable, "RL:U" },
+ { v2RLNotDefined, "RL:ND" },
+
+ { v2RCUnconfirmed, "RC:UC" },
+ { v2RCUncorroborated, "RC:UR" },
+ { v2RCConfirmed, "RC:C" },
+ { v2RCNotDefined, "RC:ND" },
+
+ { v2CDPNone, "CDP:N" },
+ { v2CDPLow, "CDP:L" },
+ { v2CDPLowMedium, "CDP:LM" },
+ { v2CDPMediumHigh, "CDP:MH" },
+ { v2CDPHigh, "CDP:H" },
+ { v2CDPNotDefined, "CDP:ND" },
+
+ { v2TDNone, "TD:N" },
+ { v2TDLow, "TD:L" },
+ { v2TDMedium, "TD:M" },
+ { v2TDHigh, "TD:H" },
+ { v2TDNotDefined, "TD:ND" },
+
+ { v2CRLow, "CR:L" },
+ { v2CRMedium, "CR:M" },
+ { v2CRHigh, "CR:H" },
+ { v2CRNotDefined, "CR:ND" },
+
+ { v2IRLow, "IR:L" },
+ { v2IRMedium, "IR:M" },
+ { v2IRHigh, "IR:H" },
+ { v2IRNotDefined, "IR:ND" },
+
+ { v2ARLow, "AR:L" },
+ { v2ARMedium, "AR:M" },
+ { v2ARHigh, "AR:H" },
+ { v2ARNotDefined, "AR:ND" },
+
+ { v2Metric(255), "v2Metric(255)" },
+ }
+
+ for _, test := range(tests) {
+ t.Run(test.val.String(), func(t *testing.T) {
+ got := test.val.String()
+ if got != test.exp {
+ t.Errorf("got: \"%s\", exp: \"%s\"", got, test.exp)
+ }
+ })
+ }
+}
+
+func TestInvalidV2MetricKey(t *testing.T) {
+ got := v2Metric(255).Key()
+ exp := v2InvalidKey
+
+ if got != exp {
+ t.Errorf("got: \"%s\", exp: \"%s\"", got, exp)
+ }
+}
diff --git a/cvss/v2vector.go b/cvss/v2vector.go
new file mode 100644
index 0000000..b1fca1f
--- /dev/null
+++ b/cvss/v2vector.go
@@ -0,0 +1,108 @@
+package cvss
+
+import (
+ // "encoding/json"
+ "regexp"
+ "strings"
+)
+
+// CVSS 2.0 vector.
+type v2Vector []v2Metric
+
+// Convert vector to string.
+func (v v2Vector) String() string {
+ // convert to slice of metrics
+ metrics := []v2Metric(v)
+
+ // build vector
+ r := make([]string, len(metrics))
+ for i, m := range(metrics) {
+ r[i] = m.String()
+ }
+
+ // build and return string
+ return strings.Join(r, "/")
+}
+
+// Return CVSS version.
+func (v2Vector) Version() Version {
+ return V20
+}
+
+// Return metrics in this vector.
+func (v v2Vector) Metrics() []Metric {
+ // build result
+ r := make([]Metric, len(v))
+ for i, m := range(v) {
+ r[i] = m
+ }
+
+ // return result
+ return r
+}
+
+// Create CVSS 2.0 vector from string.
+func newV2Vector(s string) (v2Vector, error) {
+ strs := strings.Split(s, "/")
+ r := make([]v2Metric, len(strs))
+
+ // walk metric strings
+ for i, ms := range(strs) {
+ // convert string to vector
+ m, err := getV2Metric(ms)
+ if err != nil {
+ return nil, err
+ }
+
+ // add to results
+ r[i] = m
+ }
+
+ // build and return vector
+ return v2Vector(r), nil
+}
+
+// // Unmarshal CVSS 2.0 vector from JSON string.
+// func (me *v2Vector) UnmarshalJSON(b []byte) error {
+// // decode string, check for error
+// var s string
+// if err := json.Unmarshal(b, &s); err != nil {
+// return err
+// }
+//
+// // parse vector, check for error
+// r, err := newV2Vector(s)
+// if err != nil {
+// // return error
+// return err
+// }
+//
+// // save result, return success
+// *me = r
+// return nil
+// }
+
+var v2MetricRe = "(?:" + strings.Join([]string {
+ "(?:AV:[NAL])",
+ "(?:AC:[LMH])",
+ "(?:Au:[MSN])",
+ "(?:C:[NPC])",
+ "(?:I:[NPC])",
+ "(?:A:[NPC])",
+ "(?:E:(?:ND|U|POC|F|H))",
+ "(?:RL:(?:OF|TF|W|U|ND))",
+ "(?:RC:(?:UC|UR|C|ND))",
+ "(?:CDP:(?:N|L|LM|MH|H|ND))",
+ "(?:TD:(?:N|L|M|H|ND))",
+ "(?:CR:(?:L|M|H|ND))",
+ "(?:IR:(?:L|M|H|ND))",
+}, "|") + ")"
+
+var v2VecRe = regexp.MustCompile(
+ "\\A" + v2MetricRe + "(?:/" + v2MetricRe + ")*\\z",
+)
+
+// Is the given string a CVSS v2 vector string?
+func isV2VectorString(s string) bool {
+ return len(s) > 0 && v2VecRe.MatchString(s)
+}
diff --git a/cvss/v2vector_test.go b/cvss/v2vector_test.go
new file mode 100644
index 0000000..f297ba6
--- /dev/null
+++ b/cvss/v2vector_test.go
@@ -0,0 +1,69 @@
+package cvss
+
+import (
+ "testing"
+)
+
+func TestNewV2Vector(t *testing.T) {
+ passTests := []struct {
+ val string
+ exp []string
+ } {
+ {
+ val: "AV:L/AC:L/Au:N/C:N/I:N/A:P",
+ exp: []string { "AV:L", "AC:L", "Au:N", "C:N", "I:N", "A:P" },
+ },
+ }
+
+ for _, test := range(passTests) {
+ t.Run(test.val, func(t *testing.T) {
+ // parse vector
+ vec, err := newV2Vector(test.val)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ // check version
+ if vec.Version() != V20 {
+ t.Errorf("got %s, exp %s", vec.Version(), V20)
+ return
+ }
+
+ // check metric length
+ if len(vec.Metrics()) != len(test.exp) {
+ t.Errorf("got %d, exp %d", len(vec.Metrics()), len(test.exp))
+ return
+ }
+
+ // check metrics
+ for i, m := range(vec.Metrics()) {
+ got := m.String()
+ if got != test.exp[i] {
+ t.Errorf("got %s, exp %s", got, test.exp[i])
+ }
+ }
+ })
+ }
+
+ failTests := []struct {
+ val string
+ exp string
+ } {
+ {
+ val: "AV:L/junk/Au:N/C:N/I:N/A:P",
+ exp: "invalid CVSS 2.0 metric: junk",
+ },
+ }
+
+ for _, test := range(failTests) {
+ t.Run(test.val, func(t *testing.T) {
+ got, err := newV2Vector(test.val)
+ if err != nil && err.Error() != test.exp {
+ t.Errorf("got \"%s\", exp \"%s\"", err.Error(), test.exp)
+ } else if err == nil {
+ t.Errorf("got \"%s\", exp badMetric", got)
+ }
+ })
+ }
+}
diff --git a/cvss/v30vector.go b/cvss/v30vector.go
new file mode 100644
index 0000000..5ef1ae8
--- /dev/null
+++ b/cvss/v30vector.go
@@ -0,0 +1,119 @@
+package cvss
+
+import (
+ // "encoding/json"
+ "regexp"
+ "strings"
+)
+
+// CVSS v3.0 prefix
+var v30Prefix = "CVSS:3.0/"
+
+// CVSS 3.0 vector.
+type v30Vector []v3Metric
+
+// Convert vector to string
+func (v v30Vector) String() string {
+ // convert to slice of metrics
+ metrics := []v3Metric(v)
+
+ // build vector
+ r := make([]string, len(metrics))
+ for i, m := range(metrics) {
+ r[i] = m.String()
+ }
+
+ // build and return string
+ return v30Prefix + strings.Join(r, "/")
+}
+
+// Return CVSS version.
+func (v30Vector) Version() Version {
+ return V30
+}
+
+// Return metrics in this vector.
+func (v v30Vector) Metrics() []Metric {
+ // build result
+ r := make([]Metric, len(v))
+ for i, m := range(v) {
+ r[i] = m
+ }
+
+ // return result
+ return r
+}
+
+// Create CVSS 3.0 vector from string.
+func newV30Vector(s string) (v30Vector, error) {
+ // strip version prefix, split into metric strings
+ strs := strings.Split(s[len(v30Prefix):], "/")
+ r := make([]v3Metric, len(strs))
+
+ // build results
+ for i, ms := range(strs) {
+ // get metric from string
+ m, err := getV3Metric(V30, ms)
+ if err != nil {
+ return nil, err
+ }
+
+ r[i] = m
+ }
+
+ // return result
+ return v30Vector(r), nil
+}
+
+// // Unmarshal CVSS 3.0 vector from JSON string.
+// func (me *v30Vector) UnmarshalJSON(b []byte) error {
+// // decode string, check for error
+// var s string
+// if err := json.Unmarshal(b, &s); err != nil {
+// return err
+// }
+//
+// // parse vector, check for error
+// r, err := newV30Vector(s)
+// if err != nil {
+// return err
+// }
+//
+// // save result, return success
+// *me = r
+// return nil
+// }
+
+var v30VecRe = regexp.MustCompile(
+ "\\ACVSS:3\\.0(?:/(?:" + strings.Join([]string {
+ "(?:AV:[NALP])",
+ "(?:AC:[LH])",
+ "(?:PR:[NLH])",
+ "(?:UI:[NR])",
+ "(?:S:[UC])",
+ "(?:C:[HLN])",
+ "(?:I:[HLN])",
+ "(?:A:[HLN])",
+ "(?:E:[XHFPU])",
+ "(?:RL:[XUWTO])",
+ "(?:RC:[XCRU])",
+ "(?:CR:[XHML])",
+ "(?:IR:[XHML])",
+ "(?:AR:[XHML])",
+ "(?:MAV:[XNALP])",
+ "(?:MAC:[XLH])",
+ "(?:MPR:[XNLH])",
+ "(?:MUI:[XNR])",
+ "(?:MS:[XUC])",
+ "(?:MC:[XNLH])",
+ "(?:MI:[XNLH])",
+ "(?:MA:[XNLH])",
+ }, "|") + "))+\\z",
+)
+
+// Is the given string a CVSSv3.1 vector string?
+func isV30VectorString(s string) bool {
+ return (len(s) > len(v30Prefix)) &&
+ (s[:len(v30Prefix)] == v30Prefix) &&
+ v30VecRe.MatchString(s)
+}
diff --git a/cvss/v30vector_test.go b/cvss/v30vector_test.go
new file mode 100644
index 0000000..1d44d35
--- /dev/null
+++ b/cvss/v30vector_test.go
@@ -0,0 +1,69 @@
+package cvss
+
+import (
+ "testing"
+)
+
+func TestNewV30Vector(t *testing.T) {
+ passTests := []struct {
+ val string
+ exp []string
+ } {
+ {
+ val: "CVSS:3.0/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N",
+ exp: []string { "AV:L", "AC:H", "PR:L", "UI:N", "S:U", "C:H", "I:N", "A:N" },
+ },
+ }
+
+ for _, test := range(passTests) {
+ t.Run(test.val, func(t *testing.T) {
+ // parse vector
+ vec, err := newV30Vector(test.val)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ // check version
+ if vec.Version() != V30 {
+ t.Errorf("got %s, exp %s", vec.Version(), V30)
+ return
+ }
+
+ // check metric length
+ if len(vec.Metrics()) != len(test.exp) {
+ t.Errorf("got %d, exp %d", len(vec.Metrics()), len(test.exp))
+ return
+ }
+
+ // check metrics
+ for i, m := range(vec.Metrics()) {
+ got := m.String()
+ if got != test.exp[i] {
+ t.Errorf("got %s, exp %s", got, test.exp[i])
+ }
+ }
+ })
+ }
+
+ failTests := []struct {
+ val string
+ exp string
+ } {
+ {
+ val: "CVSS:3.0/AV:L/junk/PR:L/UI:N/S:U/C:H/I:N/A:N",
+ exp: "invalid CVSS 3.0 metric: junk",
+ },
+ }
+
+ for _, test := range(failTests) {
+ t.Run(test.val, func(t *testing.T) {
+ got, err := newV30Vector(test.val)
+ if err != nil && err.Error() != test.exp {
+ t.Errorf("got \"%s\", exp \"%s\"", err.Error(), test.exp)
+ } else if err == nil {
+ t.Errorf("got \"%s\", exp badMetric", got)
+ }
+ })
+ }
+}
diff --git a/cvss/v31vector.go b/cvss/v31vector.go
new file mode 100644
index 0000000..f286ea0
--- /dev/null
+++ b/cvss/v31vector.go
@@ -0,0 +1,119 @@
+package cvss
+
+import (
+ "regexp"
+ "strings"
+)
+
+// CVSS v3.1 prefix
+var v31Prefix = "CVSS:3.1/"
+
+// CVSS 3.1 vector.
+type v31Vector []v3Metric
+
+// Convert vector to string
+func (v v31Vector) String() string {
+ // convert to slice of metrics
+ metrics := []v3Metric(v)
+
+ // build vector
+ r := make([]string, len(metrics))
+ for i, m := range(metrics) {
+ r[i] = m.String()
+ }
+
+ // build and return string
+ return v31Prefix + strings.Join(r, "/")
+}
+
+// Return CVSS version.
+func (v31Vector) Version() Version {
+ return V31
+}
+
+// Return metrics in this vector.
+func (v v31Vector) Metrics() []Metric {
+ // build slice of metrics
+ r := make([]Metric, len(v))
+ for i, m := range(v) {
+ r[i] = m
+ }
+
+ // return result
+ return r
+}
+
+// create CVSS 3.1 vector from string.
+func newV31Vector(s string) (v31Vector, error) {
+ // strip version prefix, split into metric strings
+ strs := strings.Split(s[len(v31Prefix):], "/")
+ r := make([]v3Metric, len(strs))
+
+ // build results
+ for i, ms := range(strs) {
+ // get metric from string
+ m, err := getV3Metric(V31, ms)
+ if err != nil {
+ return nil, err
+ }
+
+ // add to results
+ r[i] = m
+ }
+
+ // return result
+ return v31Vector(r), nil
+}
+
+// // Unmarshal CVSS 3.1 vector from JSON string.
+// func (me *v31Vector) UnmarshalJSON(b []byte) error {
+// // decode string, check for error
+// var s string
+// if err := json.Unmarshal(b, &s); err != nil {
+// return err
+// }
+//
+// // parse vector, check for error
+// r, err := newV31Vector(s)
+// if err != nil {
+// return err
+// }
+//
+// // save result, return success
+// *me = r
+// return nil
+// }
+
+var v31VecRe = regexp.MustCompile(
+ "\\ACVSS:3\\.1(?:/(?:" + strings.Join([]string {
+ "(?:AV:[NALP])",
+ "(?:AC:[LH])",
+ "(?:PR:[NLH])",
+ "(?:UI:[NR])",
+ "(?:S:[UC])",
+ "(?:C:[HLN])",
+ "(?:I:[HLN])",
+ "(?:A:[HLN])",
+ "(?:E:[XHFPU])",
+ "(?:RL:[XUWTO])",
+ "(?:RC:[XCRU])",
+ "(?:CR:[XHML])",
+ "(?:IR:[XHML])",
+ "(?:AR:[XHML])",
+ "(?:MAV:[XNALP])",
+ "(?:MAC:[XLH])",
+ "(?:MPR:[XNLH])",
+ "(?:MUI:[XNR])",
+ "(?:MS:[XUC])",
+ "(?:MC:[XNLH])",
+ "(?:MI:[XNLH])",
+ "(?:MA:[XNLH])",
+ }, "|") + "))+\\z",
+)
+
+// Is the given string a CVSSv3.1 vector string?
+func isV31VectorString(s string) bool {
+ return (len(s) > len(v31Prefix)) &&
+ (s[:len(v31Prefix)] == v31Prefix) &&
+ v31VecRe.MatchString(s)
+}
diff --git a/cvss/v31vector_test.go b/cvss/v31vector_test.go
new file mode 100644
index 0000000..d05edca
--- /dev/null
+++ b/cvss/v31vector_test.go
@@ -0,0 +1,561 @@
+package cvss
+
+import (
+ "testing"
+)
+
+func TestNewV31Vector(t *testing.T) {
+ passTests := []struct {
+ val string
+ exp []string
+ } {
+ {
+ val: "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N",
+ exp: []string { "AV:L", "AC:H", "PR:L", "UI:N", "S:U", "C:H", "I:N", "A:N" },
+ },
+ }
+
+ for _, test := range(passTests) {
+ t.Run(test.val, func(t *testing.T) {
+ // parse vector
+ vec, err := newV31Vector(test.val)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ // check version
+ if vec.Version() != V31 {
+ t.Errorf("got %s, exp %s", vec.Version(), V31)
+ return
+ }
+
+ // check metric length
+ if len(vec.Metrics()) != len(test.exp) {
+ t.Errorf("got %d, exp %d", len(vec.Metrics()), len(test.exp))
+ return
+ }
+
+ // check metrics
+ for i, m := range(vec.Metrics()) {
+ got := m.String()
+ if got != test.exp[i] {
+ t.Errorf("got %s, exp %s", got, test.exp[i])
+ }
+ }
+ })
+ }
+
+ failTests := []struct {
+ val string
+ exp string
+ } {
+ {
+ val: "CVSS:3.1/AV:L/junk/PR:L/UI:N/S:U/C:H/I:N/A:N",
+ exp: "invalid CVSS 3.1 metric: junk",
+ },
+ }
+
+ for _, test := range(failTests) {
+ t.Run(test.val, func(t *testing.T) {
+ got, err := newV31Vector(test.val)
+ if err != nil && err.Error() != test.exp {
+ t.Errorf("got \"%s\", exp \"%s\"", err.Error(), test.exp)
+ } else if err == nil {
+ t.Errorf("got \"%s\", exp badMetric", got)
+ }
+ })
+ }
+}
+
+func TestIsV31VectorString(t *testing.T) {
+ // test non v31 strings
+ passTests := []string {
+ "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:L",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:L",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:L",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:L",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:R/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:R/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:L/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:H/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:N/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:C/C:N/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:L/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:N/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:L/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:C/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:H/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:P/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:P/AC:H/PR:L/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:P/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:C/C:L/I:H/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
+ }
+
+ for _, test := range(passTests) {
+ t.Run(test, func(t *testing.T) {
+ if !isV31VectorString(test) {
+ t.Error("got false, exp true")
+ }
+ })
+ }
+
+ // test non v31 strings
+ failTests := []string {
+ "AV:N/AC:M/Au:S/C:N/I:P/A:C",
+ "AV:N/AC:M/Au:S/C:N/I:P/A:N",
+ "AV:N/AC:M/Au:S/C:N/I:P/A:P",
+ "AV:N/AC:M/Au:S/C:P/I:N/A:N",
+ "AV:N/AC:M/Au:S/C:P/I:N/A:P",
+ "AV:N/AC:M/Au:S/C:P/I:P/A:C",
+ "AV:N/AC:M/Au:S/C:P/I:P/A:N",
+ "AV:N/AC:M/Au:S/C:P/I:P/A:P",
+
+ "CVSS:3.0/AV:A/AC:H/PR:H/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.0/AV:A/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:L",
+ "CVSS:3.0/AV:A/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.0/AV:A/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.0/AV:A/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H",
+ }
+
+ for _, test := range(failTests) {
+ t.Run(test, func(t *testing.T) {
+ if isV31VectorString(test) {
+ t.Error("got true, exp false")
+ }
+ })
+ }
+}
diff --git a/cvss/v3key.go b/cvss/v3key.go
new file mode 100644
index 0000000..653c2da
--- /dev/null
+++ b/cvss/v3key.go
@@ -0,0 +1,116 @@
+package cvss
+
+//go:generate stringer -linecomment -type=v3Key
+
+// CVSS v3 metric key
+type v3Key byte
+
+const (
+ v3AttackVector v3Key = iota // AV
+ v3AttackComplexity // AC
+ v3PrivilegesRequired // PR
+ v3UserInteraction // UI
+ v3Scope // S
+ v3Confidentiality // C
+ v3Integrity // I
+ v3Availability // A
+ v3ExploitCodeMaturity // E
+ v3RemediationLevel // RL
+ v3ReportConfidence // RC
+ v3ConfidentialityRequirement // CR
+ v3IntegrityRequirement // IR
+ v3AvailabilityRequirement // AR
+ v3ModifiedAttackVector // MAV
+ v3ModifiedAttackComplexity // MAC
+ v3ModifiedPrivilegesRequired // MPR
+ v3ModifiedUserInteraction // MUI
+ v3ModifiedScope // MS
+ v3ModifiedConfidentiality // MC
+ v3ModifiedIntegrity // MI
+ v3ModifiedAvailability // MA
+
+ v3InvalidKey // invalid
+)
+
+// CVSS v3 metric key info lut
+var v3Keys = map[v3Key]struct {
+ Name string
+ Category Category
+} {
+ v3AttackVector: { "Attack Vector", Base },
+ v3AttackComplexity: { "Attack Complexity", Base },
+ v3PrivilegesRequired: { "Privileges Required", Base },
+ v3UserInteraction: { "User Interaction", Base },
+ v3Scope: { "Scope", Base },
+ v3Confidentiality: { "Confidentiality", Base },
+ v3Integrity: { "Integrity", Base },
+ v3Availability: { "Availability", Base },
+ v3ExploitCodeMaturity: { "Exploit Code Maturity", Temporal },
+ v3RemediationLevel: { "Remediation Level", Temporal },
+ v3ReportConfidence: { "Report Confidence", Temporal },
+ v3ConfidentialityRequirement: { "Confidentiality Requirement", Environmental },
+ v3IntegrityRequirement: { "Integrity Requirement", Environmental },
+ v3AvailabilityRequirement: { "Availability Requirement", Environmental },
+ v3ModifiedAttackVector: { "Modified Attack Vector", Environmental },
+ v3ModifiedAttackComplexity: { "Modified Attack Complexity", Environmental },
+ v3ModifiedPrivilegesRequired: { "Modified Privileges Required", Environmental },
+ v3ModifiedUserInteraction: { "Modified User Interaction", Environmental },
+ v3ModifiedScope: { "Modified Scope", Environmental },
+ v3ModifiedConfidentiality: { "Modified Confidentiality", Environmental },
+ v3ModifiedIntegrity: { "Modified Integrity", Environmental },
+ v3ModifiedAvailability: { "Modified Availability", Environmental },
+}
+
+// // metric key IDs lut
+// var v3KeyIds = map[string]v3Key {
+// "AV": v3AttackVector,
+// "AC": v3AttackComplexity,
+// "PR": v3PrivilegesRequired,
+// "UI": v3UserInteraction,
+// "S": v3Scope,
+// "C": v3Confidentiality,
+// "I": v3Integrity,
+// "A": v3Availability,
+// "E": v3ExploitCodeMaturity,
+// "RL": v3RemediationLevel,
+// "RC": v3ReportConfidence,
+// "CR": v3ConfidentialityRequirement,
+// "IR": v3IntegrityRequirement,
+// "AR": v3AvailabilityRequirement,
+// "MAV": v3ModifiedAttackVector,
+// "MAC": v3ModifiedAttackComplexity,
+// "MPR": v3ModifiedPrivilegesRequired,
+// "MUI": v3ModifiedUserInteraction,
+// "MS": v3ModifiedScope,
+// "MC": v3ModifiedConfidentiality,
+// "MI": v3ModifiedIntegrity,
+// "MA": v3ModifiedAvailability,
+// }
+//
+// // Get metric key from string.
+// func getV3KeyFromString(s string) (v3Key, error) {
+// k, ok := v3KeyIds[s]
+// if ok {
+// return k, nil
+// } else {
+// return v3InvalidKey, newBadKey(V30, s)
+// }
+// }
+
+// Get metric key name.
+func (k v3Key) Name() string {
+ if data, ok := v3Keys[k]; ok {
+ return data.Name
+ } else {
+ return "invalid"
+ }
+}
+
+// Get metric key category.
+func (k v3Key) Category() Category {
+ if data, ok := v3Keys[k]; ok {
+ return data.Category
+ } else {
+ return InvalidCategory
+ }
+}
diff --git a/cvss/v3key_string.go b/cvss/v3key_string.go
new file mode 100644
index 0000000..0644117
--- /dev/null
+++ b/cvss/v3key_string.go
@@ -0,0 +1,45 @@
+// Code generated by "stringer -linecomment -type=v3Key"; DO NOT EDIT.
+
+package cvss
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[v3AttackVector-0]
+ _ = x[v3AttackComplexity-1]
+ _ = x[v3PrivilegesRequired-2]
+ _ = x[v3UserInteraction-3]
+ _ = x[v3Scope-4]
+ _ = x[v3Confidentiality-5]
+ _ = x[v3Integrity-6]
+ _ = x[v3Availability-7]
+ _ = x[v3ExploitCodeMaturity-8]
+ _ = x[v3RemediationLevel-9]
+ _ = x[v3ReportConfidence-10]
+ _ = x[v3ConfidentialityRequirement-11]
+ _ = x[v3IntegrityRequirement-12]
+ _ = x[v3AvailabilityRequirement-13]
+ _ = x[v3ModifiedAttackVector-14]
+ _ = x[v3ModifiedAttackComplexity-15]
+ _ = x[v3ModifiedPrivilegesRequired-16]
+ _ = x[v3ModifiedUserInteraction-17]
+ _ = x[v3ModifiedScope-18]
+ _ = x[v3ModifiedConfidentiality-19]
+ _ = x[v3ModifiedIntegrity-20]
+ _ = x[v3ModifiedAvailability-21]
+ _ = x[v3InvalidKey-22]
+}
+
+const _v3Key_name = "AVACPRUISCIAERLRCCRIRARMAVMACMPRMUIMSMCMIMAinvalid"
+
+var _v3Key_index = [...]uint8{0, 2, 4, 6, 8, 9, 10, 11, 12, 13, 15, 17, 19, 21, 23, 26, 29, 32, 35, 37, 39, 41, 43, 50}
+
+func (i v3Key) String() string {
+ if i >= v3Key(len(_v3Key_index)-1) {
+ return "v3Key(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _v3Key_name[_v3Key_index[i]:_v3Key_index[i+1]]
+}
diff --git a/cvss/v3key_test.go b/cvss/v3key_test.go
new file mode 100644
index 0000000..517eebd
--- /dev/null
+++ b/cvss/v3key_test.go
@@ -0,0 +1,142 @@
+package cvss
+
+import (
+ "testing"
+)
+
+func TestV3KeyString(t *testing.T) {
+ tests := []struct {
+ val v3Key
+ exp string
+ } {
+ { v3AttackVector, "AV" },
+ { v3AttackComplexity, "AC" },
+ { v3PrivilegesRequired, "PR" },
+ { v3UserInteraction, "UI" },
+ { v3Scope, "S" },
+ { v3Confidentiality, "C" },
+ { v3Integrity, "I" },
+ { v3Availability, "A" },
+ { v3ExploitCodeMaturity, "E" },
+ { v3RemediationLevel, "RL" },
+ { v3ReportConfidence, "RC" },
+ { v3ConfidentialityRequirement, "CR" },
+ { v3IntegrityRequirement, "IR" },
+ { v3AvailabilityRequirement, "AR" },
+ { v3ModifiedAttackVector, "MAV" },
+ { v3ModifiedAttackComplexity, "MAC" },
+ { v3ModifiedPrivilegesRequired, "MPR" },
+ { v3ModifiedUserInteraction, "MUI" },
+ { v3ModifiedScope, "MS" },
+ { v3ModifiedConfidentiality, "MC" },
+ { v3ModifiedIntegrity, "MI" },
+ { v3ModifiedAvailability, "MA" },
+
+ { v3Key(255), "v3Key(255)" },
+ }
+
+ for _, test := range(tests) {
+ t.Run(test.exp, func(t *testing.T) {
+ got := test.val.String()
+ if got != test.exp {
+ t.Errorf("got \"%s\", exp \"%s\"", got, test.exp)
+ }
+ })
+ }
+}
+
+func TestV3KeyName(t *testing.T) {
+ tests := []struct {
+ val v3Key
+ exp string
+ } {
+ { v3AttackVector, "Attack Vector" },
+ { v3AttackComplexity, "Attack Complexity" },
+ { v3PrivilegesRequired, "Privileges Required" },
+ { v3UserInteraction, "User Interaction" },
+ { v3Scope, "Scope" },
+ { v3Confidentiality, "Confidentiality" },
+ { v3Integrity, "Integrity" },
+ { v3Availability, "Availability" },
+ { v3ExploitCodeMaturity, "Exploit Code Maturity" },
+ { v3RemediationLevel, "Remediation Level" },
+ { v3ReportConfidence, "Report Confidence" },
+ { v3ConfidentialityRequirement, "Confidentiality Requirement" },
+ { v3IntegrityRequirement, "Integrity Requirement" },
+ { v3AvailabilityRequirement, "Availability Requirement" },
+ { v3ModifiedAttackVector, "Modified Attack Vector" },
+ { v3ModifiedAttackComplexity, "Modified Attack Complexity" },
+ { v3ModifiedPrivilegesRequired, "Modified Privileges Required" },
+ { v3ModifiedUserInteraction, "Modified User Interaction" },
+ { v3ModifiedScope, "Modified Scope" },
+ { v3ModifiedConfidentiality, "Modified Confidentiality" },
+ { v3ModifiedIntegrity, "Modified Integrity" },
+ { v3ModifiedAvailability, "Modified Availability" },
+ }
+
+ for _, test := range(tests) {
+ t.Run(test.exp, func(t *testing.T) {
+ got := test.val.Name()
+ if got != test.exp {
+ t.Errorf("got \"%s\", exp \"%s\"", got, test.exp)
+ }
+ })
+ }
+}
+
+func TestV3KeyCategory(t *testing.T) {
+ tests := []struct {
+ val v3Key
+ exp Category
+ } {
+ { v3AttackVector, Base },
+ { v3AttackComplexity, Base },
+ { v3PrivilegesRequired, Base },
+ { v3UserInteraction, Base },
+ { v3Scope, Base },
+ { v3Confidentiality, Base },
+ { v3Integrity, Base },
+ { v3Availability, Base },
+ { v3ExploitCodeMaturity, Temporal },
+ { v3RemediationLevel, Temporal },
+ { v3ReportConfidence, Temporal },
+ { v3ConfidentialityRequirement, Environmental },
+ { v3IntegrityRequirement, Environmental },
+ { v3AvailabilityRequirement, Environmental },
+ { v3ModifiedAttackVector, Environmental },
+ { v3ModifiedAttackComplexity, Environmental },
+ { v3ModifiedPrivilegesRequired, Environmental },
+ { v3ModifiedUserInteraction, Environmental },
+ { v3ModifiedScope, Environmental },
+ { v3ModifiedConfidentiality, Environmental },
+ { v3ModifiedIntegrity, Environmental },
+ { v3ModifiedAvailability, Environmental },
+ }
+
+ for _, test := range(tests) {
+ t.Run(test.val.String(), func(t *testing.T) {
+ got := test.val.Category()
+ if got != test.exp {
+ t.Errorf("got \"%s\", exp \"%s\"", got, test.exp)
+ }
+ })
+ }
+}
+
+func TestInvalidV3KeyName(t *testing.T) {
+ exp := "invalid"
+ got := v3Key(255).Name()
+
+ if got != exp {
+ t.Errorf("got: \"%s\", exp: \"%s\"", got, exp)
+ }
+}
+
+func TestInvalidV3KeyCategory(t *testing.T) {
+ exp := InvalidCategory
+ got := v3Key(255).Category()
+
+ if got != exp {
+ t.Errorf("got: \"%s\", exp: \"%s\"", got, exp)
+ }
+}
diff --git a/cvss/v3metric.go b/cvss/v3metric.go
new file mode 100644
index 0000000..fc4c860
--- /dev/null
+++ b/cvss/v3metric.go
@@ -0,0 +1,331 @@
+package cvss
+
+//go:generate stringer -linecomment -type=v3Metric
+
+// metric value
+type v3Metric byte
+
+const (
+ v3AVNetwork v3Metric = iota // AV:N
+ v3AVAdjacentNetwork // AV:A
+ v3AVLocal // AV:L
+ v3AVPhysical // AV:P
+
+ v3ACLow // AC:L
+ v3ACHigh // AC:H
+
+ v3PRNone // PR:N
+ v3PRLow // PR:L
+ v3PRHigh // PR:H
+
+ v3UINone // UI:N
+ v3UIRequired // UI:R
+
+ v3SUnchanged // S:U
+ v3SChanged // S:C
+
+ v3CHigh // C:H
+ v3CLow // C:L
+ v3CNone // C:N
+
+ v3IHigh // I:H
+ v3ILow // I:L
+ v3INone // I:N
+
+ v3AHigh // A:H
+ v3ALow // A:L
+ v3ANone // A:N
+
+ v3ENotDefined // E:X
+ v3EHigh // E:H
+ v3EFunctional // E:F
+ v3EProofOfConcept // E:P
+ v3EUnproven // E:U
+
+ v3RLNotDefined // RL:X
+ v3RLUnavailable // RL:U
+ v3RLWorkaround // RL:W
+ v3RLTemporaryFix // RL:T
+ v3RLOfficialFix // RL:O
+
+ v3RCNotDefined // RC:X
+ v3RCConfirmed // RC:C
+ v3RCReasonable // RC:R
+ v3RCUnknown // RC:U
+
+ v3CRNotDefined // CR:X
+ v3CRHigh // CR:H
+ v3CRMedium // CR:M
+ v3CRLow // CR:L
+
+ v3IRNotDefined // IR:X
+ v3IRHigh // IR:H
+ v3IRMedium // IR:M
+ v3IRLow // IR:L
+
+ v3ARNotDefined // AR:X
+ v3ARHigh // AR:H
+ v3ARMedium // AR:M
+ v3ARLow // AR:L
+
+ v3MAVNotDefined // MAV:X
+ v3MAVNetwork // MAV:N
+ v3MAVAdjacentNetwork // MAV:A
+ v3MAVLocal // MAV:L
+ v3MAVPhysical // MAV:P
+
+ v3MACNotDefined // MAC:X
+ v3MACLow // MAC:L
+ v3MACHigh // MAC:H
+
+ v3MMRNotDefined // MPR:X
+ v3MPRLow // MPR:L
+ v3MPRHigh // MPR:H
+
+ v3MUINotDefined // MUI:X
+ v3MUINone // MUI:N
+ v3MUIRequired // MUI:R
+
+ v3MSNotDefined // MMS:X
+ v3MSUnchanged // MMS:U
+ v3MSChanged // MMS:C
+
+ v3MCNotDefined // MC:X
+ v3MCHigh // MC:H
+ v3MCLow // MC:L
+ v3MCNone // MC:N
+
+ v3MINotDefined // MI:X
+ v3MIHigh // MI:H
+ v3MILow // MI:L
+ v3MINone // MI:N
+
+ v3MANotDefined // MA:X
+ v3MAHigh // MA:H
+ v3MALow // MA:L
+ v3MANone // MA:N
+
+ v3InvalidMetric // invalid
+)
+
+// map of metrics to metric keys
+var v3KeyLut = map[v3Metric]v3Key {
+ v3AVNetwork: v3AttackVector, // AV:N
+ v3AVAdjacentNetwork: v3AttackVector, // AV:A
+ v3AVLocal: v3AttackVector, // AV:L
+ v3AVPhysical: v3AttackVector, // AV:P
+
+ v3ACLow: v3AttackComplexity, // AC:L
+ v3ACHigh: v3AttackComplexity, // AC:H
+
+ v3PRNone: v3PrivilegesRequired, // PR:N
+ v3PRLow: v3PrivilegesRequired, // PR:L
+ v3PRHigh: v3PrivilegesRequired, // PR:H
+
+ v3UINone: v3UserInteraction, // UI:N
+ v3UIRequired: v3UserInteraction, // UI:R
+
+ v3SUnchanged: v3Scope, // S:U
+ v3SChanged: v3Scope, // S:C
+
+ v3CHigh: v3Confidentiality, // C:H
+ v3CLow: v3Confidentiality, // C:L
+ v3CNone: v3Confidentiality, // C:N
+
+ v3IHigh: v3Integrity, // I:H
+ v3ILow: v3Integrity, // I:L
+ v3INone: v3Integrity, // I:N
+
+ v3AHigh: v3Availability, // A:H
+ v3ALow: v3Availability, // A:L
+ v3ANone: v3Availability, // A:N
+
+ v3ENotDefined: v3ExploitCodeMaturity, // E:X
+ v3EHigh: v3ExploitCodeMaturity, // E:H
+ v3EFunctional: v3ExploitCodeMaturity, // E:F
+ v3EProofOfConcept: v3ExploitCodeMaturity, // E:P
+ v3EUnproven: v3ExploitCodeMaturity, // E:U
+
+ v3RLNotDefined: v3RemediationLevel, // RL:X
+ v3RLUnavailable: v3RemediationLevel, // RL:U
+ v3RLWorkaround: v3RemediationLevel, // RL:W
+ v3RLTemporaryFix: v3RemediationLevel, // RL:T
+ v3RLOfficialFix: v3RemediationLevel, // RL:O
+
+ v3RCNotDefined: v3ReportConfidence, // RC:X
+ v3RCConfirmed: v3ReportConfidence, // RC:C
+ v3RCReasonable: v3ReportConfidence, // RC:R
+ v3RCUnknown: v3ReportConfidence, // RC:U
+
+ v3CRNotDefined: v3ConfidentialityRequirement, // CR:X
+ v3CRHigh: v3ConfidentialityRequirement, // CR:H
+ v3CRMedium: v3ConfidentialityRequirement, // CR:M
+ v3CRLow: v3ConfidentialityRequirement, // CR:L
+
+ v3IRNotDefined: v3IntegrityRequirement, // IR:X
+ v3IRHigh: v3IntegrityRequirement, // IR:H
+ v3IRMedium: v3IntegrityRequirement, // IR:M
+ v3IRLow: v3IntegrityRequirement, // IR:L
+
+ v3ARNotDefined: v3AvailabilityRequirement, // AR:X
+ v3ARHigh: v3AvailabilityRequirement, // AR:H
+ v3ARMedium: v3AvailabilityRequirement, // AR:M
+ v3ARLow: v3AvailabilityRequirement, // AR:L
+
+ v3MAVNotDefined: v3ModifiedAttackVector, // MAV:X
+ v3MAVNetwork: v3ModifiedAttackVector, // MAV:N
+ v3MAVAdjacentNetwork: v3ModifiedAttackVector, // MAV:A
+ v3MAVLocal: v3ModifiedAttackVector, // MAV:L
+ v3MAVPhysical: v3ModifiedAttackVector, // MAV:P
+
+ v3MACNotDefined: v3ModifiedAttackComplexity, // MAC:X
+ v3MACLow: v3ModifiedAttackComplexity, // MAC:L
+ v3MACHigh: v3ModifiedAttackComplexity, // MAC:H
+
+ v3MMRNotDefined: v3ModifiedPrivilegesRequired, // MPR:X
+ v3MPRLow: v3ModifiedPrivilegesRequired, // MPR:L
+ v3MPRHigh: v3ModifiedPrivilegesRequired, // MPR:H
+
+ v3MUINotDefined: v3ModifiedUserInteraction, // MUI:X
+ v3MUINone: v3ModifiedUserInteraction, // MUI:N
+ v3MUIRequired: v3ModifiedUserInteraction, // MUI:R
+
+ v3MSNotDefined: v3ModifiedScope, // MMS:X
+ v3MSUnchanged: v3ModifiedConfidentiality, // MMS:U
+ v3MSChanged: v3ModifiedIntegrity, // MMS:C
+
+ v3MCNotDefined: v3ModifiedConfidentiality, // MC:X
+ v3MCHigh: v3ModifiedConfidentiality, // MC:H
+ v3MCLow: v3ModifiedConfidentiality, // MC:L
+ v3MCNone: v3ModifiedConfidentiality, // MC:N
+
+ v3MINotDefined: v3ModifiedIntegrity, // MI:X
+ v3MIHigh: v3ModifiedIntegrity, // MI:H
+ v3MILow: v3ModifiedIntegrity, // MI:L
+ v3MINone: v3ModifiedIntegrity, // MI:N
+
+ v3MANotDefined: v3ModifiedAvailability, // MA:X
+ v3MAHigh: v3ModifiedAvailability, // MA:H
+ v3MALow: v3ModifiedAvailability, // MA:L
+ v3MANone: v3ModifiedAvailability, // MA:N
+}
+
+// map of metric strings to metrics
+var v3MetricStrLut = map[string]v3Metric {
+ "AV:N": v3AVNetwork,
+ "AV:A": v3AVAdjacentNetwork,
+ "AV:L": v3AVLocal,
+ "AV:P": v3AVPhysical,
+
+ "AC:L": v3ACLow,
+ "AC:H": v3ACHigh,
+
+ "PR:N": v3PRNone,
+ "PR:L": v3PRLow,
+ "PR:H": v3PRHigh,
+
+ "UI:N": v3UINone,
+ "UI:R": v3UIRequired,
+
+ "S:U": v3SUnchanged,
+ "S:C": v3SChanged,
+
+ "C:H": v3CHigh,
+ "C:L": v3CLow,
+ "C:N": v3CNone,
+
+ "I:H": v3IHigh,
+ "I:L": v3ILow,
+ "I:N": v3INone,
+
+ "A:H": v3AHigh,
+ "A:L": v3ALow,
+ "A:N": v3ANone,
+
+ "E:X": v3ENotDefined,
+ "E:H": v3EHigh,
+ "E:F": v3EFunctional,
+ "E:P": v3EProofOfConcept,
+ "E:U": v3EUnproven,
+
+ "RL:X": v3RLNotDefined,
+ "RL:U": v3RLUnavailable,
+ "RL:W": v3RLWorkaround,
+ "RL:T": v3RLTemporaryFix,
+ "RL:O": v3RLOfficialFix,
+
+ "RC:X": v3RCNotDefined,
+ "RC:C": v3RCConfirmed,
+ "RC:R": v3RCReasonable,
+ "RC:U": v3RCUnknown,
+
+ "CR:X": v3CRNotDefined,
+ "CR:H": v3CRHigh,
+ "CR:M": v3CRMedium,
+ "CR:L": v3CRLow,
+
+ "IR:X": v3IRNotDefined,
+ "IR:H": v3IRHigh,
+ "IR:M": v3IRMedium,
+ "IR:L": v3IRLow,
+
+ "AR:X": v3ARNotDefined,
+ "AR:H": v3ARHigh,
+ "AR:M": v3ARMedium,
+ "AR:L": v3ARLow,
+
+ "MAV:X": v3MAVNotDefined,
+ "MAV:N": v3MAVNetwork,
+ "MAV:A": v3MAVAdjacentNetwork,
+ "MAV:L": v3MAVLocal,
+ "MAV:P": v3MAVPhysical,
+
+ "MAC:X": v3MACNotDefined,
+ "MAC:L": v3MACLow,
+ "MAC:H": v3MACHigh,
+
+ "MPR:X": v3MMRNotDefined,
+ "MPR:L": v3MPRLow,
+ "MPR:H": v3MPRHigh,
+
+ "MUI:X": v3MUINotDefined,
+ "MUI:N": v3MUINone,
+ "MUI:R": v3MUIRequired,
+
+ "MMS:X": v3MSNotDefined,
+ "MMS:U": v3MSUnchanged,
+ "MMS:C": v3MSChanged,
+
+ "MC:X": v3MCNotDefined,
+ "MC:H": v3MCHigh,
+ "MC:L": v3MCLow,
+ "MC:N": v3MCNone,
+
+ "MI:X": v3MINotDefined,
+ "MI:H": v3MIHigh,
+ "MI:L": v3MILow,
+ "MI:N": v3MINone,
+
+ "MA:X": v3MANotDefined,
+ "MA:H": v3MAHigh,
+ "MA:L": v3MALow,
+ "MA:N": v3MANone,
+}
+
+// Get CVSS 3.x metric key.
+func (m v3Metric) Key() Key {
+ if k, ok := v3KeyLut[m]; ok {
+ return k
+ } else {
+ return v3InvalidKey
+ }
+}
+
+// Convert string to CVSS 3.1 metric.
+func getV3Metric(version Version, s string) (v3Metric, error) {
+ if m, ok := v3MetricStrLut[s]; ok {
+ return m, nil
+ } else {
+ return v3InvalidMetric, newBadMetric(version, s)
+ }
+}
diff --git a/cvss/v3metric_string.go b/cvss/v3metric_string.go
new file mode 100644
index 0000000..612ed83
--- /dev/null
+++ b/cvss/v3metric_string.go
@@ -0,0 +1,100 @@
+// Code generated by "stringer -linecomment -type=v3Metric"; DO NOT EDIT.
+
+package cvss
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[v3AVNetwork-0]
+ _ = x[v3AVAdjacentNetwork-1]
+ _ = x[v3AVLocal-2]
+ _ = x[v3AVPhysical-3]
+ _ = x[v3ACLow-4]
+ _ = x[v3ACHigh-5]
+ _ = x[v3PRNone-6]
+ _ = x[v3PRLow-7]
+ _ = x[v3PRHigh-8]
+ _ = x[v3UINone-9]
+ _ = x[v3UIRequired-10]
+ _ = x[v3SUnchanged-11]
+ _ = x[v3SChanged-12]
+ _ = x[v3CHigh-13]
+ _ = x[v3CLow-14]
+ _ = x[v3CNone-15]
+ _ = x[v3IHigh-16]
+ _ = x[v3ILow-17]
+ _ = x[v3INone-18]
+ _ = x[v3AHigh-19]
+ _ = x[v3ALow-20]
+ _ = x[v3ANone-21]
+ _ = x[v3ENotDefined-22]
+ _ = x[v3EHigh-23]
+ _ = x[v3EFunctional-24]
+ _ = x[v3EProofOfConcept-25]
+ _ = x[v3EUnproven-26]
+ _ = x[v3RLNotDefined-27]
+ _ = x[v3RLUnavailable-28]
+ _ = x[v3RLWorkaround-29]
+ _ = x[v3RLTemporaryFix-30]
+ _ = x[v3RLOfficialFix-31]
+ _ = x[v3RCNotDefined-32]
+ _ = x[v3RCConfirmed-33]
+ _ = x[v3RCReasonable-34]
+ _ = x[v3RCUnknown-35]
+ _ = x[v3CRNotDefined-36]
+ _ = x[v3CRHigh-37]
+ _ = x[v3CRMedium-38]
+ _ = x[v3CRLow-39]
+ _ = x[v3IRNotDefined-40]
+ _ = x[v3IRHigh-41]
+ _ = x[v3IRMedium-42]
+ _ = x[v3IRLow-43]
+ _ = x[v3ARNotDefined-44]
+ _ = x[v3ARHigh-45]
+ _ = x[v3ARMedium-46]
+ _ = x[v3ARLow-47]
+ _ = x[v3MAVNotDefined-48]
+ _ = x[v3MAVNetwork-49]
+ _ = x[v3MAVAdjacentNetwork-50]
+ _ = x[v3MAVLocal-51]
+ _ = x[v3MAVPhysical-52]
+ _ = x[v3MACNotDefined-53]
+ _ = x[v3MACLow-54]
+ _ = x[v3MACHigh-55]
+ _ = x[v3MMRNotDefined-56]
+ _ = x[v3MPRLow-57]
+ _ = x[v3MPRHigh-58]
+ _ = x[v3MUINotDefined-59]
+ _ = x[v3MUINone-60]
+ _ = x[v3MUIRequired-61]
+ _ = x[v3MSNotDefined-62]
+ _ = x[v3MSUnchanged-63]
+ _ = x[v3MSChanged-64]
+ _ = x[v3MCNotDefined-65]
+ _ = x[v3MCHigh-66]
+ _ = x[v3MCLow-67]
+ _ = x[v3MCNone-68]
+ _ = x[v3MINotDefined-69]
+ _ = x[v3MIHigh-70]
+ _ = x[v3MILow-71]
+ _ = x[v3MINone-72]
+ _ = x[v3MANotDefined-73]
+ _ = x[v3MAHigh-74]
+ _ = x[v3MALow-75]
+ _ = x[v3MANone-76]
+ _ = x[v3InvalidMetric-77]
+}
+
+const _v3Metric_name = "AV:NAV:AAV:LAV:PAC:LAC:HPR:NPR:LPR:HUI:NUI:RS:US:CC:HC:LC:NI:HI:LI:NA:HA:LA:NE:XE:HE:FE:PE:URL:XRL:URL:WRL:TRL:ORC:XRC:CRC:RRC:UCR:XCR:HCR:MCR:LIR:XIR:HIR:MIR:LAR:XAR:HAR:MAR:LMAV:XMAV:NMAV:AMAV:LMAV:PMAC:XMAC:LMAC:HMPR:XMPR:LMPR:HMUI:XMUI:NMUI:RMMS:XMMS:UMMS:CMC:XMC:HMC:LMC:NMI:XMI:HMI:LMI:NMA:XMA:HMA:LMA:Ninvalid"
+
+var _v3Metric_index = [...]uint16{0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 47, 50, 53, 56, 59, 62, 65, 68, 71, 74, 77, 80, 83, 86, 89, 92, 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 148, 152, 156, 160, 164, 168, 172, 176, 181, 186, 191, 196, 201, 206, 211, 216, 221, 226, 231, 236, 241, 246, 251, 256, 261, 265, 269, 273, 277, 281, 285, 289, 293, 297, 301, 305, 309, 316}
+
+func (i v3Metric) String() string {
+ if i >= v3Metric(len(_v3Metric_index)-1) {
+ return "v3Metric(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _v3Metric_name[_v3Metric_index[i]:_v3Metric_index[i+1]]
+}
diff --git a/cvss/v3metric_test.go b/cvss/v3metric_test.go
new file mode 100644
index 0000000..432d34a
--- /dev/null
+++ b/cvss/v3metric_test.go
@@ -0,0 +1,366 @@
+package cvss
+
+import "testing"
+
+func TestGetV3Metric(t *testing.T) {
+ tests := []struct {
+ val string
+ exp v3Metric
+ ok bool
+ } {
+ { "AV:N", v3AVNetwork, true },
+ { "AV:A", v3AVAdjacentNetwork, true },
+ { "AV:L", v3AVLocal, true },
+ { "AV:P", v3AVPhysical, true },
+
+ { "AC:L", v3ACLow, true },
+ { "AC:H", v3ACHigh, true },
+
+ { "PR:N", v3PRNone, true },
+ { "PR:L", v3PRLow, true },
+ { "PR:H", v3PRHigh, true },
+
+ { "UI:N", v3UINone, true },
+ { "UI:R", v3UIRequired, true },
+
+ { "S:U", v3SUnchanged, true },
+ { "S:C", v3SChanged, true },
+
+ { "C:H", v3CHigh, true },
+ { "C:L", v3CLow, true },
+ { "C:N", v3CNone, true },
+
+ { "I:H", v3IHigh, true },
+ { "I:L", v3ILow, true },
+ { "I:N", v3INone, true },
+
+ { "A:H", v3AHigh, true },
+ { "A:L", v3ALow, true },
+ { "A:N", v3ANone, true },
+
+ { "E:X", v3ENotDefined, true },
+ { "E:H", v3EHigh, true },
+ { "E:F", v3EFunctional, true },
+ { "E:P", v3EProofOfConcept, true },
+ { "E:U", v3EUnproven, true },
+
+ { "RL:X", v3RLNotDefined, true },
+ { "RL:U", v3RLUnavailable, true },
+ { "RL:W", v3RLWorkaround, true },
+ { "RL:T", v3RLTemporaryFix, true },
+ { "RL:O", v3RLOfficialFix, true },
+
+ { "RC:X", v3RCNotDefined, true },
+ { "RC:C", v3RCConfirmed, true },
+ { "RC:R", v3RCReasonable, true },
+ { "RC:U", v3RCUnknown, true },
+
+ { "CR:X", v3CRNotDefined, true },
+ { "CR:H", v3CRHigh, true },
+ { "CR:M", v3CRMedium, true },
+ { "CR:L", v3CRLow, true },
+
+ { "IR:X", v3IRNotDefined, true },
+ { "IR:H", v3IRHigh, true },
+ { "IR:M", v3IRMedium, true },
+ { "IR:L", v3IRLow, true },
+
+ { "AR:X", v3ARNotDefined, true },
+ { "AR:H", v3ARHigh, true },
+ { "AR:M", v3ARMedium, true },
+ { "AR:L", v3ARLow, true },
+
+ { "MAV:X", v3MAVNotDefined, true },
+ { "MAV:N", v3MAVNetwork, true },
+ { "MAV:A", v3MAVAdjacentNetwork, true },
+ { "MAV:L", v3MAVLocal, true },
+ { "MAV:P", v3MAVPhysical, true },
+
+ { "MAC:X", v3MACNotDefined, true },
+ { "MAC:L", v3MACLow, true },
+ { "MAC:H", v3MACHigh, true },
+
+ { "MPR:X", v3MMRNotDefined, true },
+ { "MPR:L", v3MPRLow, true },
+ { "MPR:H", v3MPRHigh, true },
+
+ { "MUI:X", v3MUINotDefined, true },
+ { "MUI:N", v3MUINone, true },
+ { "MUI:R", v3MUIRequired, true },
+
+ { "MMS:X", v3MSNotDefined, true },
+ { "MMS:U", v3MSUnchanged, true },
+ { "MMS:C", v3MSChanged, true },
+
+ { "MC:X", v3MCNotDefined, true },
+ { "MC:H", v3MCHigh, true },
+ { "MC:L", v3MCLow, true },
+ { "MC:N", v3MCNone, true },
+
+ { "MI:X", v3MINotDefined, true },
+ { "MI:H", v3MIHigh, true },
+ { "MI:L", v3MILow, true },
+ { "MI:N", v3MINone, true },
+
+ { "MA:X", v3MANotDefined, true },
+ { "MA:H", v3MAHigh, true },
+ { "MA:L", v3MALow, true },
+ { "MA:N", v3MANone, true },
+
+ { "invalid", v3InvalidMetric, false },
+ }
+
+ for _, test := range(tests) {
+ t.Run(test.val, func(t *testing.T) {
+ got, err := getV3Metric(V31, test.val)
+ if test.ok && err == nil && got != test.exp {
+ t.Errorf("got: \"%s\", exp: \"%s\"", got, test.exp)
+ } else if test.ok && err != nil {
+ t.Error(err)
+ } else if !test.ok && err == nil {
+ t.Errorf("got: \"%s\", exp: error", got)
+ }
+ })
+ }
+}
+
+func TestGetV3MetricKey(t *testing.T) {
+ tests := []struct {
+ val v3Metric
+ exp v3Key
+ } {
+ { v3AVNetwork, v3AttackVector }, // AV:N
+ { v3AVAdjacentNetwork, v3AttackVector }, // AV:A
+ { v3AVLocal, v3AttackVector }, // AV:L
+ { v3AVPhysical, v3AttackVector }, // AV:P
+
+ { v3ACLow, v3AttackComplexity }, // AC:L
+ { v3ACHigh, v3AttackComplexity }, // AC:H
+
+ { v3PRNone, v3PrivilegesRequired }, // PR:N
+ { v3PRLow, v3PrivilegesRequired }, // PR:L
+ { v3PRHigh, v3PrivilegesRequired }, // PR:H
+
+ { v3UINone, v3UserInteraction }, // UI:N
+ { v3UIRequired, v3UserInteraction }, // UI:R
+
+ { v3SUnchanged, v3Scope }, // S:U
+ { v3SChanged, v3Scope }, // S:C
+
+ { v3CHigh, v3Confidentiality }, // C:H
+ { v3CLow, v3Confidentiality }, // C:L
+ { v3CNone, v3Confidentiality }, // C:N
+
+ { v3IHigh, v3Integrity }, // I:H
+ { v3ILow, v3Integrity }, // I:L
+ { v3INone, v3Integrity }, // I:N
+
+ { v3AHigh, v3Availability }, // A:H
+ { v3ALow, v3Availability }, // A:L
+ { v3ANone, v3Availability }, // A:N
+
+ { v3ENotDefined, v3ExploitCodeMaturity }, // E:X
+ { v3EHigh, v3ExploitCodeMaturity }, // E:H
+ { v3EFunctional, v3ExploitCodeMaturity }, // E:F
+ { v3EProofOfConcept, v3ExploitCodeMaturity }, // E:P
+ { v3EUnproven, v3ExploitCodeMaturity }, // E:U
+
+ { v3RLNotDefined, v3RemediationLevel }, // RL:X
+ { v3RLUnavailable, v3RemediationLevel }, // RL:U
+ { v3RLWorkaround, v3RemediationLevel }, // RL:W
+ { v3RLTemporaryFix, v3RemediationLevel }, // RL:T
+ { v3RLOfficialFix, v3RemediationLevel }, // RL:O
+
+ { v3RCNotDefined, v3ReportConfidence }, // RC:X
+ { v3RCConfirmed, v3ReportConfidence }, // RC:C
+ { v3RCReasonable, v3ReportConfidence }, // RC:R
+ { v3RCUnknown, v3ReportConfidence }, // RC:U
+
+ { v3CRNotDefined, v3ConfidentialityRequirement }, // CR:X
+ { v3CRHigh, v3ConfidentialityRequirement }, // CR:H
+ { v3CRMedium, v3ConfidentialityRequirement }, // CR:M
+ { v3CRLow, v3ConfidentialityRequirement }, // CR:L
+
+ { v3IRNotDefined, v3IntegrityRequirement }, // IR:X
+ { v3IRHigh, v3IntegrityRequirement }, // IR:H
+ { v3IRMedium, v3IntegrityRequirement }, // IR:M
+ { v3IRLow, v3IntegrityRequirement }, // IR:L
+
+ { v3ARNotDefined, v3AvailabilityRequirement }, // AR:X
+ { v3ARHigh, v3AvailabilityRequirement }, // AR:H
+ { v3ARMedium, v3AvailabilityRequirement }, // AR:M
+ { v3ARLow, v3AvailabilityRequirement }, // AR:L
+
+ { v3MAVNotDefined, v3ModifiedAttackVector }, // MAV:X
+ { v3MAVNetwork, v3ModifiedAttackVector }, // MAV:N
+ { v3MAVAdjacentNetwork, v3ModifiedAttackVector }, // MAV:A
+ { v3MAVLocal, v3ModifiedAttackVector }, // MAV:L
+ { v3MAVPhysical, v3ModifiedAttackVector }, // MAV:P
+
+ { v3MACNotDefined, v3ModifiedAttackComplexity }, // MAC:X
+ { v3MACLow, v3ModifiedAttackComplexity }, // MAC:L
+ { v3MACHigh, v3ModifiedAttackComplexity }, // MAC:H
+
+ { v3MMRNotDefined, v3ModifiedPrivilegesRequired }, // MPR:X
+ { v3MPRLow, v3ModifiedPrivilegesRequired }, // MPR:L
+ { v3MPRHigh, v3ModifiedPrivilegesRequired }, // MPR:H
+
+ { v3MUINotDefined, v3ModifiedUserInteraction }, // MUI:X
+ { v3MUINone, v3ModifiedUserInteraction }, // MUI:N
+ { v3MUIRequired, v3ModifiedUserInteraction }, // MUI:R
+
+ { v3MSNotDefined, v3ModifiedScope }, // MMS:X
+ { v3MSUnchanged, v3ModifiedConfidentiality }, // MMS:U
+ { v3MSChanged, v3ModifiedIntegrity }, // MMS:C
+
+ { v3MCNotDefined, v3ModifiedConfidentiality }, // MC:X
+ { v3MCHigh, v3ModifiedConfidentiality }, // MC:H
+ { v3MCLow, v3ModifiedConfidentiality }, // MC:L
+ { v3MCNone, v3ModifiedConfidentiality }, // MC:N
+
+ { v3MINotDefined, v3ModifiedIntegrity }, // MI:X
+ { v3MIHigh, v3ModifiedIntegrity }, // MI:H
+ { v3MILow, v3ModifiedIntegrity }, // MI:L
+ { v3MINone, v3ModifiedIntegrity }, // MI:N
+
+ { v3MANotDefined, v3ModifiedAvailability }, // MA:X
+ { v3MAHigh, v3ModifiedAvailability }, // MA:H
+ { v3MALow, v3ModifiedAvailability }, // MA:L
+ { v3MANone, v3ModifiedAvailability }, // MA:N
+ }
+
+ for _, test := range(tests) {
+ t.Run(test.val.String(), func(t *testing.T) {
+ got := test.val.Key()
+ if got != test.exp {
+ t.Errorf("got: \"%s\", exp: \"%s\"", got, test.exp)
+ }
+ })
+ }
+}
+
+func TestV3MetricString(t *testing.T) {
+ tests := []struct {
+ val v3Metric
+ exp string
+ } {
+ { v3AVNetwork, "AV:N" },
+ { v3AVAdjacentNetwork, "AV:A" },
+ { v3AVLocal, "AV:L" },
+ { v3AVPhysical, "AV:P" },
+
+ { v3ACLow, "AC:L" },
+ { v3ACHigh, "AC:H" },
+
+ { v3PRNone, "PR:N" },
+ { v3PRLow, "PR:L" },
+ { v3PRHigh, "PR:H" },
+
+ { v3UINone, "UI:N" },
+ { v3UIRequired, "UI:R" },
+
+ { v3SUnchanged, "S:U" },
+ { v3SChanged, "S:C" },
+
+ { v3CHigh, "C:H" },
+ { v3CLow, "C:L" },
+ { v3CNone, "C:N" },
+
+ { v3IHigh, "I:H" },
+ { v3ILow, "I:L" },
+ { v3INone, "I:N" },
+
+ { v3AHigh, "A:H" },
+ { v3ALow, "A:L" },
+ { v3ANone, "A:N" },
+
+ { v3ENotDefined, "E:X" },
+ { v3EHigh, "E:H" },
+ { v3EFunctional, "E:F" },
+ { v3EProofOfConcept, "E:P" },
+ { v3EUnproven, "E:U" },
+
+ { v3RLNotDefined, "RL:X" },
+ { v3RLUnavailable, "RL:U" },
+ { v3RLWorkaround, "RL:W" },
+ { v3RLTemporaryFix, "RL:T" },
+ { v3RLOfficialFix, "RL:O" },
+
+ { v3RCNotDefined, "RC:X" },
+ { v3RCConfirmed, "RC:C" },
+ { v3RCReasonable, "RC:R" },
+ { v3RCUnknown, "RC:U" },
+
+ { v3CRNotDefined, "CR:X" },
+ { v3CRHigh, "CR:H" },
+ { v3CRMedium, "CR:M" },
+ { v3CRLow, "CR:L" },
+
+ { v3IRNotDefined, "IR:X" },
+ { v3IRHigh, "IR:H" },
+ { v3IRMedium, "IR:M" },
+ { v3IRLow, "IR:L" },
+
+ { v3ARNotDefined, "AR:X" },
+ { v3ARHigh, "AR:H" },
+ { v3ARMedium, "AR:M" },
+ { v3ARLow, "AR:L" },
+
+ { v3MAVNotDefined, "MAV:X" },
+ { v3MAVNetwork, "MAV:N" },
+ { v3MAVAdjacentNetwork, "MAV:A" },
+ { v3MAVLocal, "MAV:L" },
+ { v3MAVPhysical, "MAV:P" },
+
+ { v3MACNotDefined, "MAC:X" },
+ { v3MACLow, "MAC:L" },
+ { v3MACHigh, "MAC:H" },
+
+ { v3MMRNotDefined, "MPR:X" },
+ { v3MPRLow, "MPR:L" },
+ { v3MPRHigh, "MPR:H" },
+
+ { v3MUINotDefined, "MUI:X" },
+ { v3MUINone, "MUI:N" },
+ { v3MUIRequired, "MUI:R" },
+
+ { v3MSNotDefined, "MMS:X" },
+ { v3MSUnchanged, "MMS:U" },
+ { v3MSChanged, "MMS:C" },
+
+ { v3MCNotDefined, "MC:X" },
+ { v3MCHigh, "MC:H" },
+ { v3MCLow, "MC:L" },
+ { v3MCNone, "MC:N" },
+
+ { v3MINotDefined, "MI:X" },
+ { v3MIHigh, "MI:H" },
+ { v3MILow, "MI:L" },
+ { v3MINone, "MI:N" },
+
+ { v3MANotDefined, "MA:X" },
+ { v3MAHigh, "MA:H" },
+ { v3MALow, "MA:L" },
+ { v3MANone, "MA:N" },
+
+ { v3Metric(255), "v3Metric(255)" },
+ }
+
+ for _, test := range(tests) {
+ t.Run(test.val.String(), func(t *testing.T) {
+ got := test.val.String()
+ if got != test.exp {
+ t.Errorf("got: \"%s\", exp: \"%s\"", got, test.exp)
+ }
+ })
+ }
+}
+
+func TestInvalidV3MetricKey(t *testing.T) {
+ got := v3Metric(255).Key()
+ exp := v3InvalidKey
+
+ if got != exp {
+ t.Errorf("got: \"%s\", exp: \"%s\"", got, exp)
+ }
+}
diff --git a/cvss/vector.go b/cvss/vector.go
new file mode 100644
index 0000000..3e465c5
--- /dev/null
+++ b/cvss/vector.go
@@ -0,0 +1,36 @@
+package cvss
+
+import (
+ "fmt"
+)
+
+// CVSS metric vector.
+type Vector interface {
+ // Get CVSS version.
+ Version() Version
+
+ // Get CVSS vector string.
+ String() string
+
+ // Return metrics in this vector.
+ Metrics() []Metric
+
+ // Unmarshal vector from JSON.
+ // UnmarshalJSON(b []byte) error
+}
+
+// Create new CVSS vector from vector string.
+func NewVector(s string) (Vector, error) {
+ if isV31VectorString(s) {
+ // create CVSS v3.1 vector.
+ return newV31Vector(s)
+ } else if isV30VectorString(s) {
+ // create CVSS v3.0 vector.
+ return newV30Vector(s)
+ } else if isV2VectorString(s) {
+ // create CVSS v2 vector.
+ return newV2Vector(s)
+ } else {
+ return nil, fmt.Errorf("invalid CVSS vector: %s", s)
+ }
+}
diff --git a/cvss/vector_test.go b/cvss/vector_test.go
new file mode 100644
index 0000000..a9ea820
--- /dev/null
+++ b/cvss/vector_test.go
@@ -0,0 +1,659 @@
+package cvss
+
+import "testing"
+
+func TestNewVector(t *testing.T) {
+ passTests := []string {
+ "AV:A/AC:H/Au:S/C:C/I:C/A:C",
+ "AV:A/AC:H/Au:S/C:N/I:N/A:P",
+ "AV:A/AC:H/Au:S/C:P/I:P/A:P",
+ "AV:A/AC:L/Au:N/C:C/I:C/A:C",
+ "AV:A/AC:L/Au:N/C:N/I:N/A:C",
+ "AV:A/AC:L/Au:N/C:N/I:N/A:P",
+ "AV:A/AC:L/Au:N/C:N/I:P/A:N",
+ "AV:A/AC:L/Au:N/C:P/I:N/A:C",
+ "AV:A/AC:L/Au:N/C:P/I:N/A:N",
+ "AV:A/AC:L/Au:N/C:P/I:P/A:N",
+ "AV:A/AC:L/Au:N/C:P/I:P/A:P",
+ "AV:A/AC:L/Au:S/C:C/I:C/A:C",
+ "AV:A/AC:L/Au:S/C:C/I:N/A:C",
+ "AV:A/AC:L/Au:S/C:N/I:N/A:P",
+ "AV:A/AC:L/Au:S/C:N/I:P/A:N",
+ "AV:A/AC:L/Au:S/C:P/I:N/A:N",
+ "AV:A/AC:L/Au:S/C:P/I:N/A:P",
+ "AV:A/AC:L/Au:S/C:P/I:P/A:N",
+ "AV:A/AC:L/Au:S/C:P/I:P/A:P",
+ "AV:A/AC:M/Au:N/C:C/I:C/A:C",
+ "AV:A/AC:M/Au:N/C:N/I:N/A:C",
+ "AV:A/AC:M/Au:N/C:N/I:N/A:P",
+ "AV:A/AC:M/Au:N/C:N/I:P/A:N",
+ "AV:A/AC:M/Au:N/C:N/I:P/A:P",
+ "AV:A/AC:M/Au:N/C:P/I:N/A:N",
+ "AV:A/AC:M/Au:N/C:P/I:P/A:N",
+ "AV:A/AC:M/Au:N/C:P/I:P/A:P",
+ "AV:A/AC:M/Au:S/C:C/I:C/A:C",
+ "AV:A/AC:M/Au:S/C:N/I:N/A:P",
+ "AV:A/AC:M/Au:S/C:N/I:P/A:N",
+ "AV:A/AC:M/Au:S/C:P/I:N/A:N",
+ "AV:A/AC:M/Au:S/C:P/I:P/A:P",
+ "AV:L/AC:H/Au:N/C:C/I:C/A:C",
+ "AV:L/AC:H/Au:N/C:N/I:N/A:C",
+ "AV:L/AC:H/Au:N/C:N/I:N/A:P",
+ "AV:L/AC:H/Au:N/C:N/I:P/A:N",
+ "AV:L/AC:H/Au:N/C:P/I:N/A:N",
+ "AV:L/AC:H/Au:N/C:P/I:P/A:N",
+ "AV:L/AC:H/Au:S/C:P/I:P/A:P",
+ "AV:L/AC:L/Au:N/C:C/I:C/A:C",
+ "AV:L/AC:L/Au:N/C:C/I:C/A:N",
+ "AV:L/AC:L/Au:N/C:C/I:N/A:C",
+ "AV:L/AC:L/Au:N/C:C/I:N/A:N",
+ "AV:L/AC:L/Au:N/C:C/I:P/A:N",
+ "AV:L/AC:L/Au:N/C:N/I:C/A:C",
+ "AV:L/AC:L/Au:N/C:N/I:C/A:N",
+ "AV:L/AC:L/Au:N/C:N/I:N/A:C",
+ "AV:L/AC:L/Au:N/C:N/I:N/A:P",
+ "AV:L/AC:L/Au:N/C:N/I:P/A:C",
+ "AV:L/AC:L/Au:N/C:N/I:P/A:N",
+ "AV:L/AC:L/Au:N/C:N/I:P/A:P",
+ "AV:L/AC:L/Au:N/C:P/I:N/A:C",
+ "AV:L/AC:L/Au:N/C:P/I:N/A:N",
+ "AV:L/AC:L/Au:N/C:P/I:N/A:P",
+ "AV:L/AC:L/Au:N/C:P/I:P/A:C",
+ "AV:L/AC:L/Au:N/C:P/I:P/A:N",
+ "AV:L/AC:L/Au:N/C:P/I:P/A:P",
+ "AV:L/AC:L/Au:S/C:P/I:N/A:N",
+ "AV:L/AC:M/Au:N/C:C/I:C/A:C",
+ "AV:L/AC:M/Au:N/C:C/I:N/A:N",
+ "AV:L/AC:M/Au:N/C:N/I:C/A:C",
+ "AV:L/AC:M/Au:N/C:N/I:C/A:N",
+ "AV:L/AC:M/Au:N/C:N/I:N/A:C",
+ "AV:L/AC:M/Au:N/C:N/I:N/A:P",
+ "AV:L/AC:M/Au:N/C:N/I:P/A:C",
+ "AV:L/AC:M/Au:N/C:N/I:P/A:N",
+ "AV:L/AC:M/Au:N/C:N/I:P/A:P",
+ "AV:L/AC:M/Au:N/C:P/I:N/A:N",
+ "AV:L/AC:M/Au:N/C:P/I:N/A:P",
+ "AV:L/AC:M/Au:N/C:P/I:P/A:C",
+ "AV:L/AC:M/Au:N/C:P/I:P/A:N",
+ "AV:L/AC:M/Au:N/C:P/I:P/A:P",
+ "AV:N/AC:H/Au:N/C:C/I:C/A:C",
+ "AV:N/AC:H/Au:N/C:N/I:N/A:C",
+ "AV:N/AC:H/Au:N/C:N/I:N/A:P",
+ "AV:N/AC:H/Au:N/C:N/I:P/A:N",
+ "AV:N/AC:H/Au:N/C:N/I:P/A:P",
+ "AV:N/AC:H/Au:N/C:P/I:N/A:N",
+ "AV:N/AC:H/Au:N/C:P/I:N/A:P",
+ "AV:N/AC:H/Au:N/C:P/I:P/A:N",
+ "AV:N/AC:H/Au:N/C:P/I:P/A:P",
+ "AV:N/AC:H/Au:S/C:N/I:P/A:N",
+ "AV:N/AC:H/Au:S/C:P/I:N/A:N",
+ "AV:N/AC:H/Au:S/C:P/I:P/A:N",
+ "AV:N/AC:H/Au:S/C:P/I:P/A:P",
+ "AV:N/AC:L/Au:N/C:C/I:C/A:C",
+ "AV:N/AC:L/Au:N/C:C/I:C/A:N",
+ "AV:N/AC:L/Au:N/C:C/I:N/A:C",
+ "AV:N/AC:L/Au:N/C:C/I:N/A:N",
+ "AV:N/AC:L/Au:N/C:N/I:C/A:C",
+ "AV:N/AC:L/Au:N/C:N/I:C/A:N",
+ "AV:N/AC:L/Au:N/C:N/I:N/A:C",
+ "AV:N/AC:L/Au:N/C:N/I:N/A:P",
+ "AV:N/AC:L/Au:N/C:N/I:P/A:N",
+ "AV:N/AC:L/Au:N/C:N/I:P/A:P",
+ "AV:N/AC:L/Au:N/C:P/I:N/A:C",
+ "AV:N/AC:L/Au:N/C:P/I:N/A:N",
+ "AV:N/AC:L/Au:N/C:P/I:N/A:P",
+ "AV:N/AC:L/Au:N/C:P/I:P/A:C",
+ "AV:N/AC:L/Au:N/C:P/I:P/A:N",
+ "AV:N/AC:L/Au:N/C:P/I:P/A:P",
+ "AV:N/AC:L/Au:S/C:C/I:C/A:C",
+ "AV:N/AC:L/Au:S/C:C/I:C/A:N",
+ "AV:N/AC:L/Au:S/C:C/I:C/A:P",
+ "AV:N/AC:L/Au:S/C:C/I:N/A:N",
+ "AV:N/AC:L/Au:S/C:C/I:P/A:C",
+ "AV:N/AC:L/Au:S/C:C/I:P/A:N",
+ "AV:N/AC:L/Au:S/C:N/I:C/A:C",
+ "AV:N/AC:L/Au:S/C:N/I:C/A:N",
+ "AV:N/AC:L/Au:S/C:N/I:N/A:C",
+ "AV:N/AC:L/Au:S/C:N/I:N/A:P",
+ "AV:N/AC:L/Au:S/C:N/I:P/A:C",
+ "AV:N/AC:L/Au:S/C:N/I:P/A:N",
+ "AV:N/AC:L/Au:S/C:N/I:P/A:P",
+ "AV:N/AC:L/Au:S/C:P/I:N/A:C",
+ "AV:N/AC:L/Au:S/C:P/I:N/A:N",
+ "AV:N/AC:L/Au:S/C:P/I:N/A:P",
+ "AV:N/AC:L/Au:S/C:P/I:P/A:C",
+ "AV:N/AC:L/Au:S/C:P/I:P/A:N",
+ "AV:N/AC:L/Au:S/C:P/I:P/A:P",
+ "AV:N/AC:M/Au:N/C:C/I:C/A:C",
+ "AV:N/AC:M/Au:N/C:C/I:C/A:N",
+ "AV:N/AC:M/Au:N/C:C/I:N/A:N",
+ "AV:N/AC:M/Au:N/C:C/I:P/A:P",
+ "AV:N/AC:M/Au:N/C:N/I:C/A:C",
+ "AV:N/AC:M/Au:N/C:N/I:C/A:N",
+ "AV:N/AC:M/Au:N/C:N/I:N/A:C",
+ "AV:N/AC:M/Au:N/C:N/I:N/A:P",
+ "AV:N/AC:M/Au:N/C:N/I:P/A:N",
+ "AV:N/AC:M/Au:N/C:N/I:P/A:P",
+ "AV:N/AC:M/Au:N/C:P/I:C/A:C",
+ "AV:N/AC:M/Au:N/C:P/I:N/A:N",
+ "AV:N/AC:M/Au:N/C:P/I:N/A:P",
+ "AV:N/AC:M/Au:N/C:P/I:P/A:C",
+ "AV:N/AC:M/Au:N/C:P/I:P/A:N",
+ "AV:N/AC:M/Au:N/C:P/I:P/A:P",
+ "AV:N/AC:M/Au:S/C:C/I:C/A:C",
+ "AV:N/AC:M/Au:S/C:C/I:N/A:C",
+ "AV:N/AC:M/Au:S/C:C/I:N/A:N",
+ "AV:N/AC:M/Au:S/C:N/I:C/A:C",
+ "AV:N/AC:M/Au:S/C:N/I:N/A:C",
+ "AV:N/AC:M/Au:S/C:N/I:N/A:P",
+ "AV:N/AC:M/Au:S/C:N/I:P/A:C",
+ "AV:N/AC:M/Au:S/C:N/I:P/A:N",
+ "AV:N/AC:M/Au:S/C:N/I:P/A:P",
+ "AV:N/AC:M/Au:S/C:P/I:N/A:N",
+ "AV:N/AC:M/Au:S/C:P/I:N/A:P",
+ "AV:N/AC:M/Au:S/C:P/I:P/A:C",
+ "AV:N/AC:M/Au:S/C:P/I:P/A:N",
+ "AV:N/AC:M/Au:S/C:P/I:P/A:P",
+
+ "CVSS:3.0/AV:A/AC:H/PR:H/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.0/AV:A/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:L",
+ "CVSS:3.0/AV:A/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.0/AV:A/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.0/AV:A/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H",
+
+ "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:H/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:L",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:L",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:L",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:L",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:A/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:R/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:H/UI:R/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:C/C:L/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:H/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:L/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:N/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:C/C:N/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:L/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:N/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:L/I:N/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:C/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:H/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:L",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L",
+ "CVSS:3.1/AV:P/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:P/AC:H/PR:L/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:P/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:C/C:L/I:H/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
+ "CVSS:3.1/AV:P/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
+ }
+
+ for _, test := range(passTests) {
+ t.Run(test, func(t *testing.T) {
+ vec, err := NewVector(test)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ got := vec.String()
+ if got != test {
+ t.Errorf("got: \"%s\", exp: \"%s\"", got, test)
+ }
+ })
+ }
+
+ failTests := []struct {
+ val string
+ exp string
+ } {
+ {
+ val: "AV:A/junk/Au:S/C:C/I:C/A:C",
+ exp: "invalid CVSS vector: AV:A/junk/Au:S/C:C/I:C/A:C",
+ }, {
+ val: "CVSS:3.0/junk/AC:H/PR:H/UI:R/S:U/C:H/I:H/A:H",
+ exp: "invalid CVSS vector: CVSS:3.0/junk/AC:H/PR:H/UI:R/S:U/C:H/I:H/A:H",
+ }, {
+ val: "CVSS:3.1/AV:A/AC:H/junk/UI:N/S:U/C:H/I:H/A:H",
+ exp: "invalid CVSS vector: CVSS:3.1/AV:A/AC:H/junk/UI:N/S:U/C:H/I:H/A:H",
+ },
+ }
+
+ for _, test := range(failTests) {
+ t.Run(test.val, func(t *testing.T) {
+ got, err := NewVector(test.val)
+ if err != nil && err.Error() != test.exp {
+ t.Errorf("got \"%s\", exp \"%s\"", err.Error(), test.exp)
+ } else if err == nil {
+ t.Errorf("got \"%s\", exp error", got)
+ }
+ })
+ }
+}
diff --git a/cvss/version.go b/cvss/version.go
new file mode 100644
index 0000000..c46c893
--- /dev/null
+++ b/cvss/version.go
@@ -0,0 +1,12 @@
+package cvss
+
+//go:generate stringer -linecomment -type=Version
+
+// CVSS version
+type Version byte
+
+const (
+ V20 Version = iota // 2.0
+ V30 // 3.0
+ V31 // 3.1
+)
diff --git a/cvss/version_string.go b/cvss/version_string.go
new file mode 100644
index 0000000..03e8904
--- /dev/null
+++ b/cvss/version_string.go
@@ -0,0 +1,25 @@
+// Code generated by "stringer -linecomment -type=Version"; DO NOT EDIT.
+
+package cvss
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[V20-0]
+ _ = x[V30-1]
+ _ = x[V31-2]
+}
+
+const _Version_name = "2.03.03.1"
+
+var _Version_index = [...]uint8{0, 3, 6, 9}
+
+func (i Version) String() string {
+ if i >= Version(len(_Version_index)-1) {
+ return "Version(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _Version_name[_Version_index[i]:_Version_index[i+1]]
+}
diff --git a/cvss/version_test.go b/cvss/version_test.go
new file mode 100644
index 0000000..e010094
--- /dev/null
+++ b/cvss/version_test.go
@@ -0,0 +1,24 @@
+package cvss
+
+import "testing"
+
+func TestVersionString(t *testing.T) {
+ tests := []struct {
+ val Version
+ exp string
+ } {
+ { V20, "2.0" },
+ { V30, "3.0" },
+ { V31, "3.1" },
+ { Version(255), "Version(255)" },
+ }
+
+ for _, test := range(tests) {
+ t.Run(test.val.String(), func(t *testing.T) {
+ got := test.val.String()
+ if got != test.exp {
+ t.Errorf("got: \"%s\", exp: \"%s\"", got, test.exp)
+ }
+ })
+ }
+}