diff options
| author | Paul Duncan <pabs@pablotron.org> | 2022-02-04 00:35:31 -0500 | 
|---|---|---|
| committer | Paul Duncan <pabs@pablotron.org> | 2022-02-04 00:35:31 -0500 | 
| commit | 9c17b97cd0f83be3fff9fa4e87fd1d29052ea616 (patch) | |
| tree | 0d97030a0d0c3ad983be281ce89f80571338887f /cvss | |
| parent | 92400d731546557d110c9c3cc3906d700f83dda8 (diff) | |
| download | cvez-9c17b97cd0f83be3fff9fa4e87fd1d29052ea616.tar.xz cvez-9c17b97cd0f83be3fff9fa4e87fd1d29052ea616.zip  | |
rename to github.com/pablotron/cvez, remove internal libs
Diffstat (limited to 'cvss')
| -rw-r--r-- | cvss/badkey.go | 20 | ||||
| -rw-r--r-- | cvss/badmetric.go | 19 | ||||
| -rw-r--r-- | cvss/badmetric_test.go | 25 | ||||
| -rw-r--r-- | cvss/category.go | 13 | ||||
| -rw-r--r-- | cvss/category_string.go | 26 | ||||
| -rw-r--r-- | cvss/category_test.go | 41 | ||||
| -rw-r--r-- | cvss/cvss.go | 23 | ||||
| -rw-r--r-- | cvss/v2key.go | 92 | ||||
| -rw-r--r-- | cvss/v2key_string.go | 37 | ||||
| -rw-r--r-- | cvss/v2key_test.go | 116 | ||||
| -rw-r--r-- | cvss/v2metric.go | 244 | ||||
| -rw-r--r-- | cvss/v2metric_string.go | 78 | ||||
| -rw-r--r-- | cvss/v2metric_test.go | 276 | ||||
| -rw-r--r-- | cvss/v2vector.go | 108 | ||||
| -rw-r--r-- | cvss/v2vector_test.go | 69 | ||||
| -rw-r--r-- | cvss/v30vector.go | 119 | ||||
| -rw-r--r-- | cvss/v30vector_test.go | 69 | ||||
| -rw-r--r-- | cvss/v31vector.go | 119 | ||||
| -rw-r--r-- | cvss/v31vector_test.go | 561 | ||||
| -rw-r--r-- | cvss/v3key.go | 116 | ||||
| -rw-r--r-- | cvss/v3key_string.go | 45 | ||||
| -rw-r--r-- | cvss/v3key_test.go | 142 | ||||
| -rw-r--r-- | cvss/v3metric.go | 331 | ||||
| -rw-r--r-- | cvss/v3metric_string.go | 100 | ||||
| -rw-r--r-- | cvss/v3metric_test.go | 366 | ||||
| -rw-r--r-- | cvss/vector.go | 36 | ||||
| -rw-r--r-- | cvss/vector_test.go | 659 | ||||
| -rw-r--r-- | cvss/version.go | 12 | ||||
| -rw-r--r-- | cvss/version_string.go | 25 | ||||
| -rw-r--r-- | cvss/version_test.go | 24 | 
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) +      } +    }) +  } +}  | 
