aboutsummaryrefslogtreecommitdiff
path: root/cvss/v2scores.go
blob: 990b805256ecc0780747c24144f92c47e8cd3efa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
package cvss

import (
  "math"
)

// CVSS v2 base, temporal, and environmental scores.
type v2Scores struct {
  base      Score // base score
  temporal  Score // temporal score
  env       Score // environmental score
}

// Create new CVSS v2Scores from floats.
func newV2ScoresFromFloats(base, temporal, env float64) (v2Scores, error) {
  // convert base from float to Score
  baseScore, err := NewScore(base)
  if err != nil {
    return v2Scores{}, err
  }

  // convert temporal from float to Score
  tempScore, err := NewScore(temporal)
  if err != nil {
    return v2Scores{}, err
  }

  // convert env from float to Score
  envScore, err := NewScore(env)
  if err != nil {
    return v2Scores{}, err
  }

  // return success
  return v2Scores {
    base: baseScore,
    temporal: tempScore,
    env: envScore,
  }, nil
}

// 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
  // (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 newV2ScoresFromFloats(baseScore, tempScore, envScore)
}