dradis-calculator_cvss 4.11.0 → 4.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/app/assets/javascripts/dradis/plugins/calculators/cvss/cvss.js +32 -0
  4. data/app/assets/javascripts/dradis/plugins/calculators/cvss/manifests/application.js +16 -5
  5. data/app/assets/javascripts/dradis/plugins/calculators/cvss/manifests/tylium.js +16 -5
  6. data/app/assets/javascripts/dradis/plugins/calculators/cvss/{calculator.js.coffee → v3/calculator.js.coffee} +10 -32
  7. data/app/assets/javascripts/dradis/plugins/calculators/cvss/v4/calculator.js +168 -0
  8. data/app/assets/javascripts/dradis/plugins/calculators/cvss/v4/vendor/app.js +435 -0
  9. data/app/assets/javascripts/dradis/plugins/calculators/cvss/v4/vendor/cvss_config.js +858 -0
  10. data/app/assets/javascripts/dradis/plugins/calculators/cvss/v4/vendor/cvss_details.js +18 -0
  11. data/app/assets/javascripts/dradis/plugins/calculators/cvss/v4/vendor/cvss_lookup.js +275 -0
  12. data/app/assets/javascripts/dradis/plugins/calculators/cvss/v4/vendor/max_composed.js +35 -0
  13. data/app/assets/javascripts/dradis/plugins/calculators/cvss/v4/vendor/max_severity.js +30 -0
  14. data/app/assets/javascripts/dradis/plugins/calculators/cvss/v4/vendor/metrics.js +42 -0
  15. data/app/assets/stylesheets/dradis/plugins/calculators/cvss/manifests/application.css.scss +2 -1
  16. data/app/assets/stylesheets/dradis/plugins/calculators/cvss/manifests/tylium.scss +1 -2
  17. data/app/controllers/dradis/plugins/calculators/cvss/base_controller.rb +3 -1
  18. data/app/controllers/dradis/plugins/calculators/cvss/issues_controller.rb +35 -9
  19. data/app/models/dradis/plugins/calculators/cvss/v4.rb +89 -0
  20. data/app/views/dradis/plugins/calculators/cvss/_version_menu.html.erb +8 -0
  21. data/app/views/dradis/plugins/calculators/cvss/base/index.html.erb +9 -70
  22. data/app/views/dradis/plugins/calculators/cvss/base/v3/_base.html.erb +123 -0
  23. data/app/views/dradis/plugins/calculators/cvss/base/v3/_environmental.html.erb +192 -0
  24. data/app/views/dradis/plugins/calculators/cvss/base/v3/_index.html.erb +69 -0
  25. data/app/views/dradis/plugins/calculators/cvss/base/v3/_temporal.html.erb +67 -0
  26. data/app/views/dradis/plugins/calculators/cvss/base/v4/_base.html.erb +143 -0
  27. data/app/views/dradis/plugins/calculators/cvss/base/v4/_environmental.html.erb +220 -0
  28. data/app/views/dradis/plugins/calculators/cvss/base/v4/_index.html.erb +82 -0
  29. data/app/views/dradis/plugins/calculators/cvss/base/v4/_supplemental.html.erb +85 -0
  30. data/app/views/dradis/plugins/calculators/cvss/base/v4/_threat.html.erb +19 -0
  31. data/app/views/dradis/plugins/calculators/cvss/issues/_show-content.html.erb +21 -7
  32. data/app/views/dradis/plugins/calculators/cvss/issues/edit/_v3.html.erb +91 -0
  33. data/app/views/dradis/plugins/calculators/cvss/issues/edit/_v4.html.erb +103 -0
  34. data/app/views/dradis/plugins/calculators/cvss/issues/edit.html.erb +3 -93
  35. data/lib/dradis/plugins/calculators/cvss/gem_version.rb +1 -1
  36. metadata +30 -13
  37. data/app/assets/stylesheets/dradis/plugins/calculators/cvss/_version_switch.scss +0 -26
  38. data/app/views/dradis/plugins/calculators/cvss/_version_switch.html.erb +0 -10
  39. data/app/views/dradis/plugins/calculators/cvss/base/_base.html.erb +0 -123
  40. data/app/views/dradis/plugins/calculators/cvss/base/_environmental.html.erb +0 -192
  41. data/app/views/dradis/plugins/calculators/cvss/base/_temporal.html.erb +0 -67
  42. /data/app/assets/javascripts/dradis/plugins/calculators/cvss/{vendor → v3/vendor}/cvsscalc30.js +0 -0
  43. /data/app/assets/javascripts/dradis/plugins/calculators/cvss/{vendor → v3/vendor}/cvsscalc30_helptext.js +0 -0
  44. /data/app/assets/javascripts/dradis/plugins/calculators/cvss/{vendor → v3/vendor}/cvsscalc31.js +0 -0
  45. /data/app/assets/javascripts/dradis/plugins/calculators/cvss/{vendor → v3/vendor}/cvsscalc31_helptext.js +0 -0
@@ -0,0 +1,435 @@
1
+ function cvss_v4_app() {
2
+ // See: https://github.com/RedHatProductSecurity/cvss-v4-calculator/blob/main/app.js
3
+ app = {
4
+ cvssSelected: {},
5
+ cvssSelectedValue: {},
6
+ lookup: cvssLookup_global,
7
+ maxComposedData: maxComposed,
8
+ maxSeverityData: maxSeverity,
9
+
10
+ getEQMaxes(lookup, eq) {
11
+ return maxComposed["eq" + eq][lookup[eq - 1]];
12
+ },
13
+
14
+ extractValueMetric(metric, str) {
15
+ // indexOf gives first index of the metric, we then need to go over its size
16
+ extracted = str.slice(str.indexOf(metric) + metric.length + 1)
17
+ // remove what follow
18
+ if (extracted.indexOf('/') > 0) {
19
+ metric_val = extracted.substring(0, extracted.indexOf('/'));
20
+ }
21
+ else {
22
+ // case where it is the last metric so no ending /
23
+ metric_val = extracted
24
+ }
25
+ return metric_val
26
+ },
27
+
28
+ m(metric) {
29
+ selected = this.cvssSelected[metric]
30
+
31
+ // If E=X it will default to the worst case i.e. E=A
32
+ if (metric == "E" && selected == "X") {
33
+ return "A"
34
+ }
35
+ // If CR=X, IR=X or AR=X they will default to the worst case i.e. CR=H, IR=H and AR=H
36
+ if (metric == "CR" && selected == "X") {
37
+ return "H";
38
+ }
39
+ // IR:X is the same as IR:H
40
+ if (metric == "IR" && selected == "X") {
41
+ return "H"
42
+ }
43
+ // AR:X is the same as AR:H
44
+ if (metric == "AR" && selected == "X") {
45
+ return "H"
46
+ }
47
+
48
+ // All other environmental metrics just overwrite base score values,
49
+ // so if they’re not defined just use the base score value.
50
+ if (Object.keys(this.cvssSelected).includes("M" + metric)) {
51
+ modified_selected = this.cvssSelected["M" + metric]
52
+ if (modified_selected != "X") {
53
+ return modified_selected
54
+ }
55
+ }
56
+
57
+ return selected
58
+ },
59
+
60
+ vector() {
61
+ value = "CVSS:4.0"
62
+ for (metric in this.expectedMetricOrder) {
63
+ selected = this.cvssSelected[metric]
64
+ if (selected != "X") {
65
+ value = value.concat("/" + metric + ":" + selected)
66
+ }
67
+ }
68
+ return value
69
+ },
70
+
71
+ macroVector() {
72
+ // EQ1: 0-AV:N and PR:N and UI:N
73
+ // 1-(AV:N or PR:N or UI:N) and not (AV:N and PR:N and UI:N) and not AV:P
74
+ // 2-AV:P or not(AV:N or PR:N or UI:N)
75
+
76
+ if (this.m("AV") == "N" && this.m("PR") == "N" && this.m("UI") == "N") {
77
+ eq1 = "0"
78
+ }
79
+ else if ((this.m("AV") == "N" || this.m("PR") == "N" || this.m("UI") == "N")
80
+ && !(this.m("AV") == "N" && this.m("PR") == "N" && this.m("UI") == "N")
81
+ && !(this.m("AV") == "P")) {
82
+ eq1 = "1"
83
+ }
84
+ else if (this.m("AV") == "P"
85
+ || !(this.m("AV") == "N" || this.m("PR") == "N" || this.m("UI") == "N")) {
86
+ eq1 = "2"
87
+ }
88
+
89
+ // EQ2: 0-(AC:L and AT:N)
90
+ // 1-(not(AC:L and AT:N))
91
+
92
+ if (this.m("AC") == "L" && this.m("AT") == "N") {
93
+ eq2 = "0"
94
+ }
95
+ else if (!(this.m("AC") == "L" && this.m("AT") == "N")) {
96
+ eq2 = "1"
97
+ }
98
+
99
+ // EQ3: 0-(VC:H and VI:H)
100
+ // 1-(not(VC:H and VI:H) and (VC:H or VI:H or VA:H))
101
+ // 2-not (VC:H or VI:H or VA:H)
102
+ if (this.m("VC") == "H" && this.m("VI") == "H") {
103
+ eq3 = 0
104
+ }
105
+ else if (!(this.m("VC") == "H" && this.m("VI") == "H")
106
+ && (this.m("VC") == "H" || this.m("VI") == "H" || this.m("VA") == "H")) {
107
+ eq3 = 1
108
+ }
109
+ else if (!(this.m("VC") == "H" || this.m("VI") == "H" || this.m("VA") == "H")) {
110
+ eq3 = 2
111
+ }
112
+
113
+ // EQ4: 0-(MSI:S or MSA:S)
114
+ // 1-not (MSI:S or MSA:S) and (SC:H or SI:H or SA:H)
115
+ // 2-not (MSI:S or MSA:S) and not (SC:H or SI:H or SA:H)
116
+
117
+ if (this.m("MSI") == "S" || this.m("MSA") == "S") {
118
+ eq4 = 0
119
+ }
120
+ else if (!(this.m("MSI") == "S" || this.m("MSA") == "S") &&
121
+ (this.m("SC") == "H" || this.m("SI") == "H" || this.m("SA") == "H")) {
122
+ eq4 = 1
123
+ }
124
+ else if (!(this.m("MSI") == "S" || this.m("MSA") == "S") &&
125
+ !((this.m("SC") == "H" || this.m("SI") == "H" || this.m("SA") == "H"))) {
126
+ eq4 = 2
127
+ }
128
+
129
+ // EQ5: 0-E:A
130
+ // 1-E:P
131
+ // 2-E:U
132
+
133
+ if (this.m("E") == "A") {
134
+ eq5 = 0
135
+ }
136
+ else if (this.m("E") == "P") {
137
+ eq5 = 1
138
+ }
139
+ else if (this.m("E") == "U") {
140
+ eq5 = 2
141
+ }
142
+ // if E:X
143
+ else {
144
+ eq5 = 0
145
+ }
146
+
147
+ // EQ6: 0-(CR:H and VC:H) or (IR:H and VI:H) or (AR:H and VA:H)
148
+ // 1-not[(CR:H and VC:H) or (IR:H and VI:H) or (AR:H and VA:H)]
149
+
150
+ if ((this.m("CR") == "H" && this.m("VC") == "H")
151
+ || (this.m("IR") == "H" && this.m("VI") == "H")
152
+ || (this.m("AR") == "H" && this.m("VA") == "H")) {
153
+ eq6 = 0
154
+ }
155
+ else if (!((this.m("CR") == "H" && this.m("VC") == "H")
156
+ || (this.m("IR") == "H" && this.m("VI") == "H")
157
+ || (this.m("AR") == "H" && this.m("VA") == "H"))) {
158
+ eq6 = 1
159
+ }
160
+
161
+ return eq1 + eq2 + eq3 + eq4 + eq5 + eq6
162
+ },
163
+
164
+ score() {
165
+ // The following defines the index of each metric's values.
166
+ // It is used when looking for the highest vector part of the
167
+ // combinations produced by the MacroVector respective highest vectors.
168
+ AV_levels = { "N": 0.0, "A": 0.1, "L": 0.2, "P": 0.3 }
169
+ PR_levels = { "N": 0.0, "L": 0.1, "H": 0.2 }
170
+ UI_levels = { "N": 0.0, "P": 0.1, "A": 0.2 }
171
+
172
+ AC_levels = { 'L': 0.0, 'H': 0.1 }
173
+ AT_levels = { 'N': 0.0, 'P': 0.1 }
174
+
175
+ VC_levels = { 'H': 0.0, 'L': 0.1, 'N': 0.2 }
176
+ VI_levels = { 'H': 0.0, 'L': 0.1, 'N': 0.2 }
177
+ VA_levels = { 'H': 0.0, 'L': 0.1, 'N': 0.2 }
178
+
179
+ SC_levels = { 'H': 0.1, 'L': 0.2, 'N': 0.3 }
180
+ SI_levels = { 'S': 0.0, 'H': 0.1, 'L': 0.2, 'N': 0.3 }
181
+ SA_levels = { 'S': 0.0, 'H': 0.1, 'L': 0.2, 'N': 0.3 }
182
+
183
+ CR_levels = { 'H': 0.0, 'M': 0.1, 'L': 0.2 }
184
+ IR_levels = { 'H': 0.0, 'M': 0.1, 'L': 0.2 }
185
+ AR_levels = { 'H': 0.0, 'M': 0.1, 'L': 0.2 }
186
+
187
+ E_levels = { 'U': 0.2, 'P': 0.1, 'A': 0 }
188
+
189
+
190
+
191
+ macroVector = this.macroVector()
192
+
193
+ // Exception for no impact on system (shortcut)
194
+ if (["VC", "VI", "VA", "SC", "SI", "SA"].every((metric) => this.m(metric) == "N")) {
195
+ return 0.0
196
+ }
197
+ value = this.lookup[macroVector]
198
+
199
+ // 1. For each of the EQs:
200
+ // a. The maximal scoring difference is determined as the difference
201
+ // between the current MacroVector and the lower MacroVector.
202
+ // i. If there is no lower MacroVector the available distance is
203
+ // set to NaN and then ignored in the further calculations.
204
+ eq1_val = parseInt(macroVector[0])
205
+ eq2_val = parseInt(macroVector[1])
206
+ eq3_val = parseInt(macroVector[2])
207
+ eq4_val = parseInt(macroVector[3])
208
+ eq5_val = parseInt(macroVector[4])
209
+ eq6_val = parseInt(macroVector[5])
210
+
211
+ // compute next lower macro, it can also not exist
212
+ eq1_next_lower_macro = "".concat(eq1_val + 1, eq2_val, eq3_val, eq4_val, eq5_val, eq6_val)
213
+ eq2_next_lower_macro = "".concat(eq1_val, eq2_val + 1, eq3_val, eq4_val, eq5_val, eq6_val)
214
+
215
+ // eq3 and eq6 are related
216
+ if (eq3 == 1 && eq6 == 1) {
217
+ // 11 --> 21
218
+ eq3eq6_next_lower_macro = "".concat(eq1_val, eq2_val, eq3_val + 1, eq4_val, eq5_val, eq6_val)
219
+ }
220
+ else if (eq3 == 0 && eq6 == 1) {
221
+ // 01 --> 11
222
+ eq3eq6_next_lower_macro = "".concat(eq1_val, eq2_val, eq3_val + 1, eq4_val, eq5_val, eq6_val)
223
+ }
224
+ else if (eq3 == 1 && eq6 == 0) {
225
+ // 10 --> 11
226
+ eq3eq6_next_lower_macro = "".concat(eq1_val, eq2_val, eq3_val, eq4_val, eq5_val, eq6_val + 1)
227
+ }
228
+ else if (eq3 == 0 && eq6 == 0) {
229
+ // 00 --> 01
230
+ // 00 --> 10
231
+ eq3eq6_next_lower_macro_left = "".concat(eq1_val, eq2_val, eq3_val, eq4_val, eq5_val, eq6_val + 1)
232
+ eq3eq6_next_lower_macro_right = "".concat(eq1_val, eq2_val, eq3_val + 1, eq4_val, eq5_val, eq6_val)
233
+ }
234
+ else {
235
+ // 21 --> 32 (do not exist)
236
+ eq3eq6_next_lower_macro = "".concat(eq1_val, eq2_val, eq3_val + 1, eq4_val, eq5_val, eq6_val + 1)
237
+ }
238
+
239
+
240
+ eq4_next_lower_macro = "".concat(eq1_val, eq2_val, eq3_val, eq4_val + 1, eq5_val, eq6_val)
241
+ eq5_next_lower_macro = "".concat(eq1_val, eq2_val, eq3_val, eq4_val, eq5_val + 1, eq6_val)
242
+
243
+
244
+ // get their score, if the next lower macro score do not exist the result is NaN
245
+ score_eq1_next_lower_macro = this.lookup[eq1_next_lower_macro]
246
+ score_eq2_next_lower_macro = this.lookup[eq2_next_lower_macro]
247
+
248
+ if (eq3 == 0 && eq6 == 0) {
249
+ // multiple path take the one with higher score
250
+ score_eq3eq6_next_lower_macro_left = this.lookup[eq3eq6_next_lower_macro_left]
251
+ score_eq3eq6_next_lower_macro_right = this.lookup[eq3eq6_next_lower_macro_right]
252
+
253
+ if (score_eq3eq6_next_lower_macro_left > score_eq3eq6_next_lower_macro_right) {
254
+ score_eq3eq6_next_lower_macro = score_eq3eq6_next_lower_macro_left
255
+ }
256
+ else {
257
+ score_eq3eq6_next_lower_macro = score_eq3eq6_next_lower_macro_right
258
+ }
259
+ }
260
+ else {
261
+ score_eq3eq6_next_lower_macro = this.lookup[eq3eq6_next_lower_macro]
262
+ }
263
+
264
+
265
+ score_eq4_next_lower_macro = this.lookup[eq4_next_lower_macro]
266
+ score_eq5_next_lower_macro = this.lookup[eq5_next_lower_macro]
267
+
268
+ // b. The severity distance of the to-be scored vector from a
269
+ // highest severity vector in the same MacroVector is determined.
270
+ eq1_maxes = this.getEQMaxes(macroVector, 1)
271
+ eq2_maxes = this.getEQMaxes(macroVector, 2)
272
+ eq3_eq6_maxes = this.getEQMaxes(macroVector, 3)[macroVector[5]]
273
+ eq4_maxes = this.getEQMaxes(macroVector, 4)
274
+ eq5_maxes = this.getEQMaxes(macroVector, 5)
275
+
276
+ // compose them
277
+ max_vectors = []
278
+ for (eq1_max of eq1_maxes) {
279
+ for (eq2_max of eq2_maxes) {
280
+ for (eq3_eq6_max of eq3_eq6_maxes) {
281
+ for (eq4_max of eq4_maxes) {
282
+ for (eq5max of eq5_maxes) {
283
+ max_vectors.push(eq1_max + eq2_max + eq3_eq6_max + eq4_max + eq5max)
284
+ }
285
+ }
286
+ }
287
+ }
288
+ }
289
+
290
+ // Find the max vector to use i.e. one in the combination of all the highests
291
+ // that is greater or equal (severity distance) than the to-be scored vector.
292
+ for (let i = 0; i < max_vectors.length; i++) {
293
+ max_vector = max_vectors[i]
294
+ severity_distance_AV = AV_levels[this.m("AV")] - AV_levels[this.extractValueMetric("AV", max_vector)]
295
+ severity_distance_PR = PR_levels[this.m("PR")] - PR_levels[this.extractValueMetric("PR", max_vector)]
296
+ severity_distance_UI = UI_levels[this.m("UI")] - UI_levels[this.extractValueMetric("UI", max_vector)]
297
+
298
+ severity_distance_AC = AC_levels[this.m("AC")] - AC_levels[this.extractValueMetric("AC", max_vector)]
299
+ severity_distance_AT = AT_levels[this.m("AT")] - AT_levels[this.extractValueMetric("AT", max_vector)]
300
+
301
+ severity_distance_VC = VC_levels[this.m("VC")] - VC_levels[this.extractValueMetric("VC", max_vector)]
302
+ severity_distance_VI = VI_levels[this.m("VI")] - VI_levels[this.extractValueMetric("VI", max_vector)]
303
+ severity_distance_VA = VA_levels[this.m("VA")] - VA_levels[this.extractValueMetric("VA", max_vector)]
304
+
305
+ severity_distance_SC = SC_levels[this.m("SC")] - SC_levels[this.extractValueMetric("SC", max_vector)]
306
+ severity_distance_SI = SI_levels[this.m("SI")] - SI_levels[this.extractValueMetric("SI", max_vector)]
307
+ severity_distance_SA = SA_levels[this.m("SA")] - SA_levels[this.extractValueMetric("SA", max_vector)]
308
+
309
+ severity_distance_CR = CR_levels[this.m("CR")] - CR_levels[this.extractValueMetric("CR", max_vector)]
310
+ severity_distance_IR = IR_levels[this.m("IR")] - IR_levels[this.extractValueMetric("IR", max_vector)]
311
+ severity_distance_AR = AR_levels[this.m("AR")] - AR_levels[this.extractValueMetric("AR", max_vector)]
312
+
313
+
314
+ // if any is less than zero this is not the right max
315
+ if ([severity_distance_AV, severity_distance_PR, severity_distance_UI, severity_distance_AC, severity_distance_AT, severity_distance_VC, severity_distance_VI, severity_distance_VA, severity_distance_SC, severity_distance_SI, severity_distance_SA, severity_distance_CR, severity_distance_IR, severity_distance_AR].some((met) => met < 0)) {
316
+ continue
317
+ }
318
+ // if multiple maxes exist to reach it it is enough the first one
319
+ break
320
+ }
321
+
322
+ current_severity_distance_eq1 = severity_distance_AV + severity_distance_PR + severity_distance_UI
323
+ current_severity_distance_eq2 = severity_distance_AC + severity_distance_AT
324
+ current_severity_distance_eq3eq6 = severity_distance_VC + severity_distance_VI + severity_distance_VA + severity_distance_CR + severity_distance_IR + severity_distance_AR
325
+ current_severity_distance_eq4 = severity_distance_SC + severity_distance_SI + severity_distance_SA
326
+ current_severity_distance_eq5 = 0
327
+
328
+ step = 0.1
329
+
330
+ // if the next lower macro score do not exist the result is Nan
331
+ // Rename to maximal scoring difference (aka MSD)
332
+ available_distance_eq1 = value - score_eq1_next_lower_macro
333
+ available_distance_eq2 = value - score_eq2_next_lower_macro
334
+ available_distance_eq3eq6 = value - score_eq3eq6_next_lower_macro
335
+ available_distance_eq4 = value - score_eq4_next_lower_macro
336
+ available_distance_eq5 = value - score_eq5_next_lower_macro
337
+
338
+ percent_to_next_eq1_severity = 0
339
+ percent_to_next_eq2_severity = 0
340
+ percent_to_next_eq3eq6_severity = 0
341
+ percent_to_next_eq4_severity = 0
342
+ percent_to_next_eq5_severity = 0
343
+
344
+ // some of them do not exist, we will find them by retrieving the score. If score null then do not exist
345
+ n_existing_lower = 0
346
+
347
+ normalized_severity_eq1 = 0
348
+ normalized_severity_eq2 = 0
349
+ normalized_severity_eq3eq6 = 0
350
+ normalized_severity_eq4 = 0
351
+ normalized_severity_eq5 = 0
352
+
353
+ // multiply by step because distance is pure
354
+ maxSeverity_eq1 = this.maxSeverityData["eq1"][eq1_val] * step
355
+ maxSeverity_eq2 = this.maxSeverityData["eq2"][eq2_val] * step
356
+ maxSeverity_eq3eq6 = this.maxSeverityData["eq3eq6"][eq3_val][eq6_val] * step
357
+ maxSeverity_eq4 = this.maxSeverityData["eq4"][eq4_val] * step
358
+
359
+ // c. The proportion of the distance is determined by dividing
360
+ // the severity distance of the to-be-scored vector by the depth
361
+ // of the MacroVector.
362
+ // d. The maximal scoring difference is multiplied by the proportion of
363
+ // distance.
364
+ if (!isNaN(available_distance_eq1)) {
365
+ n_existing_lower = n_existing_lower + 1
366
+ percent_to_next_eq1_severity = (current_severity_distance_eq1) / maxSeverity_eq1
367
+ normalized_severity_eq1 = available_distance_eq1 * percent_to_next_eq1_severity
368
+ }
369
+
370
+ if (!isNaN(available_distance_eq2)) {
371
+ n_existing_lower = n_existing_lower + 1
372
+ percent_to_next_eq2_severity = (current_severity_distance_eq2) / maxSeverity_eq2
373
+ normalized_severity_eq2 = available_distance_eq2 * percent_to_next_eq2_severity
374
+ }
375
+
376
+ if (!isNaN(available_distance_eq3eq6)) {
377
+ n_existing_lower = n_existing_lower + 1
378
+ percent_to_next_eq3eq6_severity = (current_severity_distance_eq3eq6) / maxSeverity_eq3eq6
379
+ normalized_severity_eq3eq6 = available_distance_eq3eq6 * percent_to_next_eq3eq6_severity
380
+ }
381
+
382
+ if (!isNaN(available_distance_eq4)) {
383
+ n_existing_lower = n_existing_lower + 1
384
+ percent_to_next_eq4_severity = (current_severity_distance_eq4) / maxSeverity_eq4
385
+ normalized_severity_eq4 = available_distance_eq4 * percent_to_next_eq4_severity
386
+ }
387
+
388
+ if (!isNaN(available_distance_eq5)) {
389
+ // for eq5 is always 0 the percentage
390
+ n_existing_lower = n_existing_lower + 1
391
+ percent_to_next_eq5_severity = 0
392
+ normalized_severity_eq5 = available_distance_eq5 * percent_to_next_eq5_severity
393
+ }
394
+
395
+ // 2. The mean of the above computed proportional distances is computed.
396
+ if (n_existing_lower == 0) {
397
+ mean_distance = 0
398
+ } else { // sometimes we need to go up but there is nothing there, or down but there is nothing there so it's a change of 0.
399
+ mean_distance = (normalized_severity_eq1 + normalized_severity_eq2 + normalized_severity_eq3eq6 + normalized_severity_eq4 + normalized_severity_eq5) / n_existing_lower
400
+ }
401
+
402
+ // 3. The score of the vector is the score of the MacroVector
403
+ // (i.e. the score of the highest severity vector) minus the mean
404
+ // distance so computed. This score is rounded to one decimal place.
405
+ value -= mean_distance;
406
+ if (value < 0) {
407
+ value = 0.0
408
+ }
409
+ if (value > 10) {
410
+ value = 10.0
411
+ }
412
+ return value.toFixed(1)
413
+ },
414
+
415
+ qualScore() {
416
+ if (this.score() == 0) {
417
+ return "None"
418
+ }
419
+ else if (this.score() < 4.0) {
420
+ return "Low"
421
+ }
422
+ else if (this.score() < 7.0) {
423
+ return "Medium"
424
+ }
425
+ else if (this.score() < 9.0) {
426
+ return "High"
427
+ }
428
+ else {
429
+ return "Critical"
430
+ }
431
+ }
432
+ }
433
+
434
+ return app;
435
+ }