From ee8432df59ac3a966e40b6e1378fa89750170bba Mon Sep 17 00:00:00 2001
From: Paul Duncan <pabs@pablotron.org>
Date: Mon, 7 Feb 2022 12:19:41 -0500
Subject: cvss: mv v2scores scores

---
 cvss/scores.go        | 266 ++++++++++++++++++++++++++++++++++++++++++++++++++
 cvss/scores_test.go   | 241 +++++++++++++++++++++++++++++++++++++++++++++
 cvss/v2scores.go      | 266 --------------------------------------------------
 cvss/v2scores_test.go | 241 ---------------------------------------------
 4 files changed, 507 insertions(+), 507 deletions(-)
 create mode 100644 cvss/scores.go
 create mode 100644 cvss/scores_test.go
 delete mode 100644 cvss/v2scores.go
 delete mode 100644 cvss/v2scores_test.go

(limited to 'cvss')

diff --git a/cvss/scores.go b/cvss/scores.go
new file mode 100644
index 0000000..3100b19
--- /dev/null
+++ b/cvss/scores.go
@@ -0,0 +1,266 @@
+package cvss
+
+import (
+  "math"
+)
+
+// CVSS score set.
+type Scores struct {
+  Base      Score `json:"base"`     // base score
+  Temporal  Score `json:"temporal"` // temporal score
+  Env       Score `json:"env"`      // environmental score
+}
+
+// Create new score set from floats.
+func NewScores(base, temporal, env float64) (Scores, error) {
+  // convert base from float to Score
+  baseScore, err := NewScore(base)
+  if err != nil {
+    return Scores{}, err
+  }
+
+  // convert temporal from float to Score
+  tempScore, err := NewScore(temporal)
+  if err != nil {
+    return Scores{}, err
+  }
+
+  // convert env from float to Score
+  envScore, err := NewScore(env)
+  if err != nil {
+    return Scores{}, err
+  }
+
+  // return success
+  return Scores {
+    Base: baseScore,
+    Temporal: tempScore,
+    Env: envScore,
+  }, nil
+}
+
+// Create new v2 scores from v2 vector.
+func newScoresFromV2Vector(v v2Vector) (Scores, error) {
+  // CVSS v2 (https://www.first.org/cvss/v2/guide 3.2.1)
+  //
+  // Impact = 10.41*(1-(1-ConfImpact)*(1-IntegImpact)*(1-AvailImpact))
+  // Exploitability = 20* AccessVector*AccessComplexity*Authentication
+  // f(impact)= 0 if Impact=0, 1.176 otherwise
+  // BaseScore = round_to_1_decimal(((0.6*Impact)+(0.4*Exploitability)-1.5)*f(Impact))
+
+  // base score values
+  confImpact := 0.0
+  integImpact := 0.0
+  availImpact := 0.0
+  accessVector := 0.0
+  accessComplexity := 0.0
+  auth := 0.0
+
+  // temporal score values
+  // (FIXME: should these be set to 1.0?)
+  exploitability := 0.0
+  remediationLevel := 0.0
+  reportConfidence := 0.0
+
+  // env score values
+  cdp := 0.0
+  td := 0.0
+  confReq := 0.0
+  integReq := 0.0
+  availReq := 0.0
+
+  for _, m := range([]v2Metric(v)) {
+    switch m {
+    case v2AVNetwork: // AV:N
+      accessVector = 1.0
+    case v2AVAdjacentNetwork: // AV:A
+      accessVector = 0.646
+    case v2AVLocal: // AV:L
+      accessVector = 0.395
+
+    case v2ACLow: // AC:L
+      accessComplexity = 0.71
+    case v2ACMedium: // AC:M
+      accessComplexity = 0.61
+    case v2ACHigh: // AC:H
+      accessComplexity = 0.35
+
+    case v2AuMultiple: // Au:M
+      auth = 0.45
+    case v2AuSingle: // Au:S
+      auth = 0.56
+    case v2AuNone: // Au:N
+      auth = 0.704
+
+    case v2CNone: // C:N
+      confImpact = 0.0
+    case v2CPartial: // C:P
+      confImpact = 0.275
+    case v2CComplete: // C:C
+      confImpact = 0.660
+
+    case v2INone: // I:N
+      integImpact = 0.0
+    case v2IPartial: // I:P
+      integImpact = 0.275
+    case v2IComplete: // I:C
+      integImpact = 0.660
+
+    case v2ANone: // A:N
+      availImpact = 0.0
+    case v2APartial: // A:P
+      availImpact = 0.275
+    case v2AComplete: // A:C
+      availImpact = 0.660
+
+    case v2ENotDefined: // E:ND
+      exploitability = 1.0
+    case v2EUnproven: // E:U
+      exploitability = 0.85
+    case v2EProofOfConcept: // E:POC
+      exploitability = 0.9
+    case v2EFunctional: // E:F
+      exploitability = 0.95
+    case v2EHigh: // E:H
+      exploitability = 1.0
+
+    case v2RLOfficialFix: // RL:OF
+      remediationLevel = 0.87
+    case v2RLTemporaryFix: // RL:TF
+      remediationLevel = 0.9
+    case v2RLWorkaround: // RL:W
+      remediationLevel = 0.95
+    case v2RLUnavailable: // RL:U
+      remediationLevel = 1.0
+    case v2RLNotDefined: // RL:ND
+      remediationLevel = 1.0
+
+    case v2RCUnconfirmed: // RC:UC
+      reportConfidence = 0.9
+    case v2RCUncorroborated: // RC:UR
+      reportConfidence = 0.95
+    case v2RCConfirmed: // RC:C
+      reportConfidence = 1.0
+    case v2RCNotDefined: // RC:ND
+      reportConfidence = 1.0
+
+    case v2CDPNone: // CDP:N
+      cdp = 0.0
+    case v2CDPLow: // CDP:L
+      cdp = 0.1
+    case v2CDPLowMedium: // CDP:LM
+      cdp = 0.3
+    case v2CDPMediumHigh: // CDP:MH
+      cdp = 0.4
+    case v2CDPHigh: // CDP:H
+      cdp = 0.5
+    case v2CDPNotDefined: // CDP:ND
+      cdp = 0.0
+
+    case v2TDNone: // TD:N
+      td = 0.0
+    case v2TDLow: // TD:L
+      td = 0.25
+    case v2TDMedium: // TD:M
+      td = 0.75
+    case v2TDHigh: // TD:H
+      td = 1.0
+    case v2TDNotDefined: // TD:ND
+      td = 1.0
+
+    case v2CRLow: // CR:L
+      confReq = 0.5
+    case v2CRMedium: // CR:M
+      confReq = 1.0
+    case v2CRHigh: // CR:H
+      confReq = 1.51
+    case v2CRNotDefined: // CR:ND
+      confReq = 1.0
+
+    case v2IRLow: // IR:L
+      integReq = 0.5
+    case v2IRMedium: // IR:M
+      integReq = 1.0
+    case v2IRHigh: // IR:H
+      integReq = 1.51
+    case v2IRNotDefined: // IR:ND
+      integReq = 1.0
+
+    case v2ARLow: // AR:L
+      availReq = 0.5
+    case v2ARMedium: // AR:M
+      availReq = 1.0
+    case v2ARHigh: // AR:H
+      availReq = 1.51
+    case v2ARNotDefined: // AR:ND
+      availReq = 1.0
+    }
+  }
+
+  // calculate base score (3.2.1 Base Equation)
+  //
+  // Impact = 10.41*(1-(1-ConfImpact)*(1-IntegImpact)*(1-AvailImpact))
+  // Exploitability = 20* AccessVector*AccessComplexity*Authentication
+  // f(impact)= 0 if Impact=0, 1.176 otherwise
+  // BaseScore = round_to_1_decimal(((0.6*Impact)+(0.4*Exploitability)-1.5)*f(Impact))
+  baseScore := 0.0
+  {
+    impact := 10.41 * (1 - (1 - confImpact) * (1 - integImpact) * (1 - availImpact))
+    fImpact := 0.0
+    if impact > 0.0 {
+      fImpact = 1.176
+    }
+    baseExpl := 20 * accessVector * accessComplexity * auth
+    baseScore = ((0.6 * impact + 0.4 * baseExpl) - 1.5) * fImpact
+    baseScore = math.Round(10.0 * baseScore) / 10.0
+  }
+
+  // calculate temporal score (3.2.2 Temporal Equation)
+  //
+  // TemporalScore = round_to_1_decimal(BaseScore*Exploitability
+  //                 *RemediationLevel*ReportConfidence)
+  tempScore := 0.0
+  {
+    tempScore = baseScore * exploitability * remediationLevel * reportConfidence
+    tempScore = math.Round(10.0 * tempScore) / 10.0
+  }
+
+  // calculate environmental score (3.2.3 Environmental Equation)
+  //
+  // AdjustedImpact = min(10,10.41*(1-(1-ConfImpact*ConfReq)*(1-IntegImpact*IntegReq)
+  //                      *(1-AvailImpact*AvailReq)))
+  //
+  // AdjustedTemporal = TemporalScore recomputed with the BaseScore's
+  // Impact sub-equation replaced with the AdjustedImpact equation
+  // 
+  // EnvironmentalScore = round_to_1_decimal((AdjustedTemporal+
+  // (10-AdjustedTemporal)*CollateralDamagePotential)*TargetDistribution)
+  // 
+  envScore := 0.0
+  {
+    // calc adjusted impact
+    adjImpact := math.Min(
+      10.0,
+      10.41 * (1 - (1 - confImpact * confReq) * (1 - integImpact * integReq) * (1 - availImpact * availReq)),
+    )
+    fImpact := 0.0
+    if adjImpact > 0.0 {
+      fImpact = 1.176
+    }
+
+    // calculate environmental base score using adjusted impact
+    baseExpl := 20 * accessVector * accessComplexity * auth
+    envBaseScore := ((0.6 * adjImpact + 0.4 * baseExpl) - 1.5) * fImpact
+    envBaseScore = (10.0 * envBaseScore) / 10.0
+
+    // calculate adjusted temporal score
+    adjTempScore := envBaseScore * exploitability * remediationLevel * reportConfidence
+    adjTempScore = math.Round(10.0 * adjTempScore) / 10.0
+
+    envScore = (adjTempScore + (10 - adjTempScore) * cdp) * td
+    envScore = math.Round(10.0 * envScore) / 10.0
+  }
+
+  // build and return result
+  return NewScores(baseScore, tempScore, envScore)
+}
diff --git a/cvss/scores_test.go b/cvss/scores_test.go
new file mode 100644
index 0000000..f35904c
--- /dev/null
+++ b/cvss/scores_test.go
@@ -0,0 +1,241 @@
+package cvss
+
+import (
+  "reflect"
+  "testing"
+)
+
+func TestNewScores(t *testing.T) {
+  // test out of bound errors
+  failTests := []struct {
+    name    string // test name
+    vals    []float64 // invalid base, temporal, and env scores
+  } {{
+    name: "invalid base",
+    vals: []float64 { 11.0, 0.0, 0.0 },
+  }, {
+    name: "invalid temporal",
+    vals: []float64 { 0.0, 11.0, 0.0 },
+  }, {
+    name: "invalid env",
+    vals: []float64 { 0.0, 0.0, 11.0 },
+  }}
+
+  for _, test := range(failTests) {
+    t.Run(test.name, func(t *testing.T) {
+      got, err := NewScores(test.vals[0], test.vals[1], test.vals[2])
+      if err == nil {
+        t.Errorf("got %v, exp error", got)
+      }
+    })
+  }
+}
+
+func TestNewScoresFromV2Vector(t *testing.T) {
+  // test vectors from section 3.3
+  passTests := []struct {
+    name    string // test name
+    val     string // test cvss v2 vector
+    exps    []float64 // expected base, temporal, and env scores
+  } {{
+    name: "CVE-2002-0392/base", // 3.3.1
+    val:  "AV:N/AC:L/Au:N/C:N/I:N/A:C",
+    exps: []float64 { 7.8, 0.0, 0.0 },
+  }, {
+    name: "CVE-2002-0392/temporal", // 3.3.1
+    val:  "AV:N/AC:L/Au:N/C:N/I:N/A:C/E:F/RL:OF/RC:C",
+    exps: []float64 { 7.8, 6.4, 0.0 },
+  }, {
+    name: "CVE-2002-0392/all", // 3.3.1
+    val:  "AV:N/AC:L/Au:N/C:N/I:N/A:C/E:F/RL:OF/RC:C/CDP:H/TD:H/CR:M/IR:M/AR:H",
+    exps: []float64 { 7.8, 6.4, 9.2 },
+  }, {
+    name: "CVE-2003-0818/base", // 3.3.2
+    val:  "AV:N/AC:L/Au:N/C:C/I:C/A:C",
+    exps: []float64 { 10.0, 0.0, 0.0 },
+  }, {
+    name: "CVE-2003-0818/temporal", // 3.3.2
+    val:  "AV:N/AC:L/Au:N/C:C/I:C/A:C/E:F/RL:OF/RC:C",
+    exps: []float64 { 10.0, 8.3, 0.0 },
+  }, {
+    name: "CVE-2003-0818/all", // 3.3.2
+    val:  "AV:N/AC:L/Au:N/C:C/I:C/A:C/E:F/RL:OF/RC:C/CDP:H/TD:H/CR:M/IR:M/AR:L",
+    exps: []float64 { 10.0, 8.3, 9.0 },
+  }, {
+    name: "CVE-2003-0062/base", // 3.3.3
+    val:  "AV:L/AC:H/Au:N/C:C/I:C/A:C",
+    exps: []float64 { 6.2, 0.0, 0.0 },
+  }, {
+    name: "CVE-2003-0062/temporal", // 3.3.3
+    val:  "AV:L/AC:H/Au:N/C:C/I:C/A:C/E:POC/RL:OF/RC:C",
+    exps: []float64 { 6.2, 4.9, 0.0 },
+  }, {
+    name: "CVE-2003-0062/all", // 3.3.3
+    val: "AV:L/AC:H/Au:N/C:C/I:C/A:C/E:POC/RL:OF/RC:C/CDP:H/TD:H/CR:M/IR:M/AR:M",
+    exps: []float64 { 6.2, 4.9, 7.5 },
+  }, {
+    name: "A:N", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:M/C:P/I:P/A:N",
+    exps: []float64 { 3.4, 0.0, 0.0 },
+  }, {
+    name: "Au:S", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P",
+    exps: []float64 { 4.9, 0.0, 0.0 },
+  }, {
+    name: "E:ND", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:ND",
+    exps: []float64 { 4.9, 0.0, 0.0 },
+  }, {
+    name: "E:U", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:U/RL:ND/RC:ND",
+    exps: []float64 { 4.9, 4.2, 0.0 },
+  }, {
+    name: "E:H", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:ND/RC:ND",
+    exps: []float64 { 4.9, 4.9, 0.0 },
+  }, {
+    name: "RL:TF", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:TF/RC:ND",
+    exps: []float64 { 4.9, 4.4, 0.0 },
+  }, {
+    name: "RL:W", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND",
+    exps: []float64 { 4.9, 4.7, 0.0 },
+  }, {
+    name: "RL:U", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:U/RC:ND",
+    exps: []float64 { 4.9, 4.9, 0.0 },
+  }, {
+    name: "RC:UC", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:U/RC:UC",
+    exps: []float64 { 4.9, 4.4, 0.0 },
+  }, {
+    name: "RC:UR", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:U/RC:UR",
+    exps: []float64 { 4.9, 4.7, 0.0 },
+  }, {
+    name: "CDP:ND", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:ND/TD:H/CR:L/IR:L/AR:L",
+    exps: []float64 { 4.9, 4.7, 2.8 },
+  }, {
+    name: "CDP:N", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:N/TD:H/CR:L/IR:L/AR:L",
+    exps: []float64 { 4.9, 4.7, 2.8 },
+  }, {
+    name: "CDP:L", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:L/TD:H/CR:L/IR:L/AR:L",
+    exps: []float64 { 4.9, 4.7, 3.5 },
+  }, {
+    name: "CDP:LM", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:LM/TD:H/CR:L/IR:L/AR:L",
+    exps: []float64 { 4.9, 4.7, 5.0 },
+  }, {
+    name: "CDP:MH", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:MH/TD:H/CR:L/IR:L/AR:L",
+    exps: []float64 { 4.9, 4.7, 5.7 },
+  }, {
+    name: "CDP:H", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:L/AR:L",
+    exps: []float64 { 4.9, 4.7, 6.4 },
+  }, {
+    name: "TD:ND", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:ND/CR:L/IR:L/AR:L",
+    exps: []float64 { 4.9, 4.7, 6.4 },
+  }, {
+    name: "TD:N", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:N/CR:L/IR:L/AR:L",
+    exps: []float64 { 4.9, 4.7, 0.0 },
+  }, {
+    name: "TD:L", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:L/CR:L/IR:L/AR:L",
+    exps: []float64 { 4.9, 4.7, 1.6 },
+  }, {
+    name: "TD:M", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:M/CR:L/IR:L/AR:L",
+    exps: []float64 { 4.9, 4.7, 4.8 },
+  }, {
+    name: "TD:H", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:L/AR:L",
+    exps: []float64 { 4.9, 4.7, 6.4 },
+  }, {
+    name: "CR:ND", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:ND/IR:L/AR:L",
+    exps: []float64 { 4.9, 4.7, 6.8 },
+  }, {
+    name: "CR:L", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:L/AR:L",
+    exps: []float64 { 4.9, 4.7, 6.4 },
+  }, {
+    name: "CR:M", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:M/IR:L/AR:L",
+    exps: []float64 { 4.9, 4.7, 6.8 },
+  }, {
+    name: "CR:H", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:H/IR:L/AR:L",
+    exps: []float64 { 4.9, 4.7, 7.1 },
+  }, {
+    name: "IR:ND", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:ND/AR:L",
+    exps: []float64 { 4.9, 4.7, 6.8 },
+  }, {
+    name: "IR:L", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:L/AR:L",
+    exps: []float64 { 4.9, 4.7, 6.4 },
+  }, {
+    name: "IR:M", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:M/AR:L",
+    exps: []float64 { 4.9, 4.7, 6.8 },
+  }, {
+    name: "IR:H", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:H/AR:L",
+    exps: []float64 { 4.9, 4.7, 7.1 },
+  }, {
+    name: "AR:ND", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:L/AR:ND",
+    exps: []float64 { 4.9, 4.7, 6.8 },
+  }, {
+    name: "AR:L", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:L/AR:L",
+    exps: []float64 { 4.9, 4.7, 6.4 },
+  }, {
+    name: "AR:M", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:L/AR:M",
+    exps: []float64 { 4.9, 4.7, 6.8 },
+  }, {
+    name: "AR:H", // from nvd v2 calc
+    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:L/AR:H",
+    exps: []float64 { 4.9, 4.7, 7.1 },
+  }}
+  // TODO: add additional test vectors using v2 calc
+
+  for _, test := range(passTests) {
+    t.Run(test.name, func(t *testing.T) {
+      // build expected result
+      exp, err := NewScores(test.exps[0], test.exps[1], test.exps[2])
+      if err != nil {
+        t.Error(err)
+        return
+      }
+
+      // create vector, check for error
+      vec, err := newV2Vector(test.val)
+      if err != nil {
+        t.Error(err)
+        return
+      }
+
+      // get scores
+      got, err := vec.Scores()
+      if err != nil {
+        t.Error(err)
+        return
+      }
+
+      // compare to expected scores
+      if !reflect.DeepEqual(got, exp) {
+        t.Errorf("got %v, exp %v", got, exp)
+        return
+      }
+    })
+  }
+}
diff --git a/cvss/v2scores.go b/cvss/v2scores.go
deleted file mode 100644
index 3100b19..0000000
--- a/cvss/v2scores.go
+++ /dev/null
@@ -1,266 +0,0 @@
-package cvss
-
-import (
-  "math"
-)
-
-// CVSS score set.
-type Scores struct {
-  Base      Score `json:"base"`     // base score
-  Temporal  Score `json:"temporal"` // temporal score
-  Env       Score `json:"env"`      // environmental score
-}
-
-// Create new score set from floats.
-func NewScores(base, temporal, env float64) (Scores, error) {
-  // convert base from float to Score
-  baseScore, err := NewScore(base)
-  if err != nil {
-    return Scores{}, err
-  }
-
-  // convert temporal from float to Score
-  tempScore, err := NewScore(temporal)
-  if err != nil {
-    return Scores{}, err
-  }
-
-  // convert env from float to Score
-  envScore, err := NewScore(env)
-  if err != nil {
-    return Scores{}, err
-  }
-
-  // return success
-  return Scores {
-    Base: baseScore,
-    Temporal: tempScore,
-    Env: envScore,
-  }, nil
-}
-
-// Create new v2 scores from v2 vector.
-func newScoresFromV2Vector(v v2Vector) (Scores, error) {
-  // CVSS v2 (https://www.first.org/cvss/v2/guide 3.2.1)
-  //
-  // Impact = 10.41*(1-(1-ConfImpact)*(1-IntegImpact)*(1-AvailImpact))
-  // Exploitability = 20* AccessVector*AccessComplexity*Authentication
-  // f(impact)= 0 if Impact=0, 1.176 otherwise
-  // BaseScore = round_to_1_decimal(((0.6*Impact)+(0.4*Exploitability)-1.5)*f(Impact))
-
-  // base score values
-  confImpact := 0.0
-  integImpact := 0.0
-  availImpact := 0.0
-  accessVector := 0.0
-  accessComplexity := 0.0
-  auth := 0.0
-
-  // temporal score values
-  // (FIXME: should these be set to 1.0?)
-  exploitability := 0.0
-  remediationLevel := 0.0
-  reportConfidence := 0.0
-
-  // env score values
-  cdp := 0.0
-  td := 0.0
-  confReq := 0.0
-  integReq := 0.0
-  availReq := 0.0
-
-  for _, m := range([]v2Metric(v)) {
-    switch m {
-    case v2AVNetwork: // AV:N
-      accessVector = 1.0
-    case v2AVAdjacentNetwork: // AV:A
-      accessVector = 0.646
-    case v2AVLocal: // AV:L
-      accessVector = 0.395
-
-    case v2ACLow: // AC:L
-      accessComplexity = 0.71
-    case v2ACMedium: // AC:M
-      accessComplexity = 0.61
-    case v2ACHigh: // AC:H
-      accessComplexity = 0.35
-
-    case v2AuMultiple: // Au:M
-      auth = 0.45
-    case v2AuSingle: // Au:S
-      auth = 0.56
-    case v2AuNone: // Au:N
-      auth = 0.704
-
-    case v2CNone: // C:N
-      confImpact = 0.0
-    case v2CPartial: // C:P
-      confImpact = 0.275
-    case v2CComplete: // C:C
-      confImpact = 0.660
-
-    case v2INone: // I:N
-      integImpact = 0.0
-    case v2IPartial: // I:P
-      integImpact = 0.275
-    case v2IComplete: // I:C
-      integImpact = 0.660
-
-    case v2ANone: // A:N
-      availImpact = 0.0
-    case v2APartial: // A:P
-      availImpact = 0.275
-    case v2AComplete: // A:C
-      availImpact = 0.660
-
-    case v2ENotDefined: // E:ND
-      exploitability = 1.0
-    case v2EUnproven: // E:U
-      exploitability = 0.85
-    case v2EProofOfConcept: // E:POC
-      exploitability = 0.9
-    case v2EFunctional: // E:F
-      exploitability = 0.95
-    case v2EHigh: // E:H
-      exploitability = 1.0
-
-    case v2RLOfficialFix: // RL:OF
-      remediationLevel = 0.87
-    case v2RLTemporaryFix: // RL:TF
-      remediationLevel = 0.9
-    case v2RLWorkaround: // RL:W
-      remediationLevel = 0.95
-    case v2RLUnavailable: // RL:U
-      remediationLevel = 1.0
-    case v2RLNotDefined: // RL:ND
-      remediationLevel = 1.0
-
-    case v2RCUnconfirmed: // RC:UC
-      reportConfidence = 0.9
-    case v2RCUncorroborated: // RC:UR
-      reportConfidence = 0.95
-    case v2RCConfirmed: // RC:C
-      reportConfidence = 1.0
-    case v2RCNotDefined: // RC:ND
-      reportConfidence = 1.0
-
-    case v2CDPNone: // CDP:N
-      cdp = 0.0
-    case v2CDPLow: // CDP:L
-      cdp = 0.1
-    case v2CDPLowMedium: // CDP:LM
-      cdp = 0.3
-    case v2CDPMediumHigh: // CDP:MH
-      cdp = 0.4
-    case v2CDPHigh: // CDP:H
-      cdp = 0.5
-    case v2CDPNotDefined: // CDP:ND
-      cdp = 0.0
-
-    case v2TDNone: // TD:N
-      td = 0.0
-    case v2TDLow: // TD:L
-      td = 0.25
-    case v2TDMedium: // TD:M
-      td = 0.75
-    case v2TDHigh: // TD:H
-      td = 1.0
-    case v2TDNotDefined: // TD:ND
-      td = 1.0
-
-    case v2CRLow: // CR:L
-      confReq = 0.5
-    case v2CRMedium: // CR:M
-      confReq = 1.0
-    case v2CRHigh: // CR:H
-      confReq = 1.51
-    case v2CRNotDefined: // CR:ND
-      confReq = 1.0
-
-    case v2IRLow: // IR:L
-      integReq = 0.5
-    case v2IRMedium: // IR:M
-      integReq = 1.0
-    case v2IRHigh: // IR:H
-      integReq = 1.51
-    case v2IRNotDefined: // IR:ND
-      integReq = 1.0
-
-    case v2ARLow: // AR:L
-      availReq = 0.5
-    case v2ARMedium: // AR:M
-      availReq = 1.0
-    case v2ARHigh: // AR:H
-      availReq = 1.51
-    case v2ARNotDefined: // AR:ND
-      availReq = 1.0
-    }
-  }
-
-  // calculate base score (3.2.1 Base Equation)
-  //
-  // Impact = 10.41*(1-(1-ConfImpact)*(1-IntegImpact)*(1-AvailImpact))
-  // Exploitability = 20* AccessVector*AccessComplexity*Authentication
-  // f(impact)= 0 if Impact=0, 1.176 otherwise
-  // BaseScore = round_to_1_decimal(((0.6*Impact)+(0.4*Exploitability)-1.5)*f(Impact))
-  baseScore := 0.0
-  {
-    impact := 10.41 * (1 - (1 - confImpact) * (1 - integImpact) * (1 - availImpact))
-    fImpact := 0.0
-    if impact > 0.0 {
-      fImpact = 1.176
-    }
-    baseExpl := 20 * accessVector * accessComplexity * auth
-    baseScore = ((0.6 * impact + 0.4 * baseExpl) - 1.5) * fImpact
-    baseScore = math.Round(10.0 * baseScore) / 10.0
-  }
-
-  // calculate temporal score (3.2.2 Temporal Equation)
-  //
-  // TemporalScore = round_to_1_decimal(BaseScore*Exploitability
-  //                 *RemediationLevel*ReportConfidence)
-  tempScore := 0.0
-  {
-    tempScore = baseScore * exploitability * remediationLevel * reportConfidence
-    tempScore = math.Round(10.0 * tempScore) / 10.0
-  }
-
-  // calculate environmental score (3.2.3 Environmental Equation)
-  //
-  // AdjustedImpact = min(10,10.41*(1-(1-ConfImpact*ConfReq)*(1-IntegImpact*IntegReq)
-  //                      *(1-AvailImpact*AvailReq)))
-  //
-  // AdjustedTemporal = TemporalScore recomputed with the BaseScore's
-  // Impact sub-equation replaced with the AdjustedImpact equation
-  // 
-  // EnvironmentalScore = round_to_1_decimal((AdjustedTemporal+
-  // (10-AdjustedTemporal)*CollateralDamagePotential)*TargetDistribution)
-  // 
-  envScore := 0.0
-  {
-    // calc adjusted impact
-    adjImpact := math.Min(
-      10.0,
-      10.41 * (1 - (1 - confImpact * confReq) * (1 - integImpact * integReq) * (1 - availImpact * availReq)),
-    )
-    fImpact := 0.0
-    if adjImpact > 0.0 {
-      fImpact = 1.176
-    }
-
-    // calculate environmental base score using adjusted impact
-    baseExpl := 20 * accessVector * accessComplexity * auth
-    envBaseScore := ((0.6 * adjImpact + 0.4 * baseExpl) - 1.5) * fImpact
-    envBaseScore = (10.0 * envBaseScore) / 10.0
-
-    // calculate adjusted temporal score
-    adjTempScore := envBaseScore * exploitability * remediationLevel * reportConfidence
-    adjTempScore = math.Round(10.0 * adjTempScore) / 10.0
-
-    envScore = (adjTempScore + (10 - adjTempScore) * cdp) * td
-    envScore = math.Round(10.0 * envScore) / 10.0
-  }
-
-  // build and return result
-  return NewScores(baseScore, tempScore, envScore)
-}
diff --git a/cvss/v2scores_test.go b/cvss/v2scores_test.go
deleted file mode 100644
index f35904c..0000000
--- a/cvss/v2scores_test.go
+++ /dev/null
@@ -1,241 +0,0 @@
-package cvss
-
-import (
-  "reflect"
-  "testing"
-)
-
-func TestNewScores(t *testing.T) {
-  // test out of bound errors
-  failTests := []struct {
-    name    string // test name
-    vals    []float64 // invalid base, temporal, and env scores
-  } {{
-    name: "invalid base",
-    vals: []float64 { 11.0, 0.0, 0.0 },
-  }, {
-    name: "invalid temporal",
-    vals: []float64 { 0.0, 11.0, 0.0 },
-  }, {
-    name: "invalid env",
-    vals: []float64 { 0.0, 0.0, 11.0 },
-  }}
-
-  for _, test := range(failTests) {
-    t.Run(test.name, func(t *testing.T) {
-      got, err := NewScores(test.vals[0], test.vals[1], test.vals[2])
-      if err == nil {
-        t.Errorf("got %v, exp error", got)
-      }
-    })
-  }
-}
-
-func TestNewScoresFromV2Vector(t *testing.T) {
-  // test vectors from section 3.3
-  passTests := []struct {
-    name    string // test name
-    val     string // test cvss v2 vector
-    exps    []float64 // expected base, temporal, and env scores
-  } {{
-    name: "CVE-2002-0392/base", // 3.3.1
-    val:  "AV:N/AC:L/Au:N/C:N/I:N/A:C",
-    exps: []float64 { 7.8, 0.0, 0.0 },
-  }, {
-    name: "CVE-2002-0392/temporal", // 3.3.1
-    val:  "AV:N/AC:L/Au:N/C:N/I:N/A:C/E:F/RL:OF/RC:C",
-    exps: []float64 { 7.8, 6.4, 0.0 },
-  }, {
-    name: "CVE-2002-0392/all", // 3.3.1
-    val:  "AV:N/AC:L/Au:N/C:N/I:N/A:C/E:F/RL:OF/RC:C/CDP:H/TD:H/CR:M/IR:M/AR:H",
-    exps: []float64 { 7.8, 6.4, 9.2 },
-  }, {
-    name: "CVE-2003-0818/base", // 3.3.2
-    val:  "AV:N/AC:L/Au:N/C:C/I:C/A:C",
-    exps: []float64 { 10.0, 0.0, 0.0 },
-  }, {
-    name: "CVE-2003-0818/temporal", // 3.3.2
-    val:  "AV:N/AC:L/Au:N/C:C/I:C/A:C/E:F/RL:OF/RC:C",
-    exps: []float64 { 10.0, 8.3, 0.0 },
-  }, {
-    name: "CVE-2003-0818/all", // 3.3.2
-    val:  "AV:N/AC:L/Au:N/C:C/I:C/A:C/E:F/RL:OF/RC:C/CDP:H/TD:H/CR:M/IR:M/AR:L",
-    exps: []float64 { 10.0, 8.3, 9.0 },
-  }, {
-    name: "CVE-2003-0062/base", // 3.3.3
-    val:  "AV:L/AC:H/Au:N/C:C/I:C/A:C",
-    exps: []float64 { 6.2, 0.0, 0.0 },
-  }, {
-    name: "CVE-2003-0062/temporal", // 3.3.3
-    val:  "AV:L/AC:H/Au:N/C:C/I:C/A:C/E:POC/RL:OF/RC:C",
-    exps: []float64 { 6.2, 4.9, 0.0 },
-  }, {
-    name: "CVE-2003-0062/all", // 3.3.3
-    val: "AV:L/AC:H/Au:N/C:C/I:C/A:C/E:POC/RL:OF/RC:C/CDP:H/TD:H/CR:M/IR:M/AR:M",
-    exps: []float64 { 6.2, 4.9, 7.5 },
-  }, {
-    name: "A:N", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:M/C:P/I:P/A:N",
-    exps: []float64 { 3.4, 0.0, 0.0 },
-  }, {
-    name: "Au:S", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P",
-    exps: []float64 { 4.9, 0.0, 0.0 },
-  }, {
-    name: "E:ND", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:ND",
-    exps: []float64 { 4.9, 0.0, 0.0 },
-  }, {
-    name: "E:U", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:U/RL:ND/RC:ND",
-    exps: []float64 { 4.9, 4.2, 0.0 },
-  }, {
-    name: "E:H", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:ND/RC:ND",
-    exps: []float64 { 4.9, 4.9, 0.0 },
-  }, {
-    name: "RL:TF", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:TF/RC:ND",
-    exps: []float64 { 4.9, 4.4, 0.0 },
-  }, {
-    name: "RL:W", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND",
-    exps: []float64 { 4.9, 4.7, 0.0 },
-  }, {
-    name: "RL:U", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:U/RC:ND",
-    exps: []float64 { 4.9, 4.9, 0.0 },
-  }, {
-    name: "RC:UC", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:U/RC:UC",
-    exps: []float64 { 4.9, 4.4, 0.0 },
-  }, {
-    name: "RC:UR", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:U/RC:UR",
-    exps: []float64 { 4.9, 4.7, 0.0 },
-  }, {
-    name: "CDP:ND", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:ND/TD:H/CR:L/IR:L/AR:L",
-    exps: []float64 { 4.9, 4.7, 2.8 },
-  }, {
-    name: "CDP:N", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:N/TD:H/CR:L/IR:L/AR:L",
-    exps: []float64 { 4.9, 4.7, 2.8 },
-  }, {
-    name: "CDP:L", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:L/TD:H/CR:L/IR:L/AR:L",
-    exps: []float64 { 4.9, 4.7, 3.5 },
-  }, {
-    name: "CDP:LM", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:LM/TD:H/CR:L/IR:L/AR:L",
-    exps: []float64 { 4.9, 4.7, 5.0 },
-  }, {
-    name: "CDP:MH", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:MH/TD:H/CR:L/IR:L/AR:L",
-    exps: []float64 { 4.9, 4.7, 5.7 },
-  }, {
-    name: "CDP:H", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:L/AR:L",
-    exps: []float64 { 4.9, 4.7, 6.4 },
-  }, {
-    name: "TD:ND", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:ND/CR:L/IR:L/AR:L",
-    exps: []float64 { 4.9, 4.7, 6.4 },
-  }, {
-    name: "TD:N", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:N/CR:L/IR:L/AR:L",
-    exps: []float64 { 4.9, 4.7, 0.0 },
-  }, {
-    name: "TD:L", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:L/CR:L/IR:L/AR:L",
-    exps: []float64 { 4.9, 4.7, 1.6 },
-  }, {
-    name: "TD:M", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:M/CR:L/IR:L/AR:L",
-    exps: []float64 { 4.9, 4.7, 4.8 },
-  }, {
-    name: "TD:H", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:L/AR:L",
-    exps: []float64 { 4.9, 4.7, 6.4 },
-  }, {
-    name: "CR:ND", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:ND/IR:L/AR:L",
-    exps: []float64 { 4.9, 4.7, 6.8 },
-  }, {
-    name: "CR:L", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:L/AR:L",
-    exps: []float64 { 4.9, 4.7, 6.4 },
-  }, {
-    name: "CR:M", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:M/IR:L/AR:L",
-    exps: []float64 { 4.9, 4.7, 6.8 },
-  }, {
-    name: "CR:H", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:H/IR:L/AR:L",
-    exps: []float64 { 4.9, 4.7, 7.1 },
-  }, {
-    name: "IR:ND", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:ND/AR:L",
-    exps: []float64 { 4.9, 4.7, 6.8 },
-  }, {
-    name: "IR:L", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:L/AR:L",
-    exps: []float64 { 4.9, 4.7, 6.4 },
-  }, {
-    name: "IR:M", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:M/AR:L",
-    exps: []float64 { 4.9, 4.7, 6.8 },
-  }, {
-    name: "IR:H", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:H/AR:L",
-    exps: []float64 { 4.9, 4.7, 7.1 },
-  }, {
-    name: "AR:ND", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:L/AR:ND",
-    exps: []float64 { 4.9, 4.7, 6.8 },
-  }, {
-    name: "AR:L", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:L/AR:L",
-    exps: []float64 { 4.9, 4.7, 6.4 },
-  }, {
-    name: "AR:M", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:L/AR:M",
-    exps: []float64 { 4.9, 4.7, 6.8 },
-  }, {
-    name: "AR:H", // from nvd v2 calc
-    val: "AV:A/AC:M/Au:S/C:P/I:P/A:P/E:H/RL:W/RC:ND/CDP:H/TD:H/CR:L/IR:L/AR:H",
-    exps: []float64 { 4.9, 4.7, 7.1 },
-  }}
-  // TODO: add additional test vectors using v2 calc
-
-  for _, test := range(passTests) {
-    t.Run(test.name, func(t *testing.T) {
-      // build expected result
-      exp, err := NewScores(test.exps[0], test.exps[1], test.exps[2])
-      if err != nil {
-        t.Error(err)
-        return
-      }
-
-      // create vector, check for error
-      vec, err := newV2Vector(test.val)
-      if err != nil {
-        t.Error(err)
-        return
-      }
-
-      // get scores
-      got, err := vec.Scores()
-      if err != nil {
-        t.Error(err)
-        return
-      }
-
-      // compare to expected scores
-      if !reflect.DeepEqual(got, exp) {
-        t.Errorf("got %v, exp %v", got, exp)
-        return
-      }
-    })
-  }
-}
-- 
cgit v1.2.3