diff options
| author | Paul Duncan <pabs@pablotron.org> | 2022-02-06 15:07:17 -0500 | 
|---|---|---|
| committer | Paul Duncan <pabs@pablotron.org> | 2022-02-06 15:07:17 -0500 | 
| commit | 336b2474298dda59807ff19aac184d0b1b69a611 (patch) | |
| tree | 2f72116ddafd53036e007aa3dbb94a6c78a614ac /cvss | |
| parent | 210c57382dfcd1e1d307609d7a34afd4d58b5c1d (diff) | |
| download | cvez-336b2474298dda59807ff19aac184d0b1b69a611.tar.xz cvez-336b2474298dda59807ff19aac184d0b1b69a611.zip  | |
cvss: add v2scores and tests (busted atm)
Diffstat (limited to 'cvss')
| -rw-r--r-- | cvss/v2scores.go | 253 | ||||
| -rw-r--r-- | cvss/v2scores_test.go | 80 | 
2 files changed, 333 insertions, 0 deletions
diff --git a/cvss/v2scores.go b/cvss/v2scores.go new file mode 100644 index 0000000..c4643d4 --- /dev/null +++ b/cvss/v2scores.go @@ -0,0 +1,253 @@ +package cvss + +import ( +  "math" +) + +// CVSS v2 base, temporal, and environmental scores. +type v2Scores struct { +  base      v2Score // base score +  temporal  v2Score // temporal score +  env       v2Score // environmental score +} + +// Create new v2 scores from v2 vector. +func newV2Scores(v v2Vector) (v2Scores, 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 +  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) +  temporalScore := 0.0 +  { +    temporalScore = baseScore * exploitability * remediationLevel * reportConfidence +    temporalScore = math.Round(10.0 * temporalScore) / 10.0 +  } + +  // calculate environmental score (3.2.4 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 +  { +    impact := 10.41 * (1 - (1 - confImpact) * (1 - integImpact) * (1 - availImpact)) +    adjImpact := math.Min( +      10.0, +      10.41 * (1 - (1 - confImpact * confReq) * (1 - integImpact * integReq) * (1 - availImpact * availReq)), +    ) + +    baseExpl := 20 * accessVector * accessComplexity * auth +    envBaseScore := ((0.6 * impact + 0.4 * baseExpl) - 1.5) * adjImpact +    envBaseScore = 10.0 * math.Round(envBaseScore) / 10.0 + +    adjTemporalScore := envBaseScore * exploitability * remediationLevel * reportConfidence +    adjTemporalScore = 10.0 * math.Round(adjTemporalScore) / 10.0 + +    envScore = adjTemporalScore + (10 - adjTemporalScore) * cdp * td +    envScore = math.Round(10.0 * envScore) / 10.0 +  } + +  // convert base score from float to v2score +  rBaseScore, err := newV2Score(baseScore) +  if err != nil { +    return v2Scores{}, err +  } + +  // convert temporal score from float to v2score +  rTemporalScore, err := newV2Score(temporalScore) +  if err != nil { +    return v2Scores{}, err +  } + +  // convert env score from float to v2score +  rEnvScore, err := newV2Score(envScore) +  if err != nil { +    return v2Scores{}, err +  } + +  // return success +  return v2Scores { +    base: rBaseScore, +    temporal: rTemporalScore, +    env: rEnvScore, +  }, nil +} diff --git a/cvss/v2scores_test.go b/cvss/v2scores_test.go new file mode 100644 index 0000000..b47cf05 --- /dev/null +++ b/cvss/v2scores_test.go @@ -0,0 +1,80 @@ +package cvss + +import ( +  "reflect" +  "testing" +) + +// build v2scores from slice of floats +func getTestScores(vals []float64) (v2Scores, error) { +  // build expected score list +  scores := make([]v2Score, 3) +  for i, val := range(vals) { +    if score, err := newV2Score(val); err != nil { +      return v2Scores{}, err +    } else { +      scores[i] = score +    } +  } + +  // build expected scores +  return v2Scores { +    scores[0], +    scores[1], +    scores[2], +  }, nil +} + +func TestNewV2Scores(t *testing.T) { +  passTests := []struct { +    name    string // test name +    val     string // test cvss v2 vector +    exps    []float64 // expected base, temporal, and env scores +  } {{ +    name: "CVE-2002-0392", +    val:  "AV:N/AC:L/Au:N/C:N/I:N/A:C", +    exps: []float64 { 7.8, 6.4, 9.2 }, +  }} + +  for _, test := range(passTests) { +    t.Run(test.name, func(t *testing.T) { +      // build expected score list +      expScores := make([]v2Score, 3) +      for i, val := range(test.exps) { +        if s, err := newV2Score(val); err != nil { +          t.Error(err) +          return +        } else { +          expScores[i] = s +        } +      } + +      // build expected scores +      exp := v2Scores { +        expScores[0], +        expScores[1], +        expScores[2], +      } + +      // 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 +      } + + +      if !reflect.DeepEqual(got, exp) { +        t.Errorf("got %v, exp %v", got, exp) +        return +      } +    }) +  } +}  | 
