cvss-suite 1.1.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +21 -0
  3. data/.github/ISSUE_TEMPLATE/custom.md +7 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +17 -0
  5. data/.github/workflows/rspec.yml +23 -0
  6. data/.gitignore +1 -0
  7. data/.rubocop.yml +39 -1
  8. data/.rubocop_todo.yml +124 -0
  9. data/CHANGES.md +63 -2
  10. data/PULL_REQUEST_TEMPLATE.md +24 -0
  11. data/README.md +32 -9
  12. data/_config.yml +1 -0
  13. data/bin/console +3 -3
  14. data/cvss_suite.gemspec +14 -13
  15. data/lib/cvss_suite.rb +12 -6
  16. data/lib/cvss_suite/cvss.rb +85 -61
  17. data/lib/cvss_suite/cvss2/cvss2.rb +34 -26
  18. data/lib/cvss_suite/cvss2/cvss2_base.rb +70 -73
  19. data/lib/cvss_suite/cvss2/cvss2_environmental.rb +49 -50
  20. data/lib/cvss_suite/cvss2/cvss2_temporal.rb +41 -39
  21. data/lib/cvss_suite/cvss3/cvss3.rb +34 -26
  22. data/lib/cvss_suite/cvss3/cvss3_base.rb +64 -65
  23. data/lib/cvss_suite/cvss3/cvss3_environmental.rb +159 -107
  24. data/lib/cvss_suite/cvss3/cvss3_temporal.rb +42 -40
  25. data/lib/cvss_suite/cvss31/cvss31.rb +61 -0
  26. data/lib/cvss_suite/cvss31/cvss31_base.rb +94 -0
  27. data/lib/cvss_suite/cvss31/cvss31_environmental.rb +196 -0
  28. data/lib/cvss_suite/cvss31/cvss31_temporal.rb +59 -0
  29. data/lib/cvss_suite/cvss_metric.rb +31 -31
  30. data/lib/cvss_suite/cvss_property.rb +56 -54
  31. data/lib/cvss_suite/helpers/cvss31_helper.rb +27 -0
  32. data/lib/cvss_suite/helpers/cvss3_helper.rb +20 -13
  33. data/lib/cvss_suite/invalid_cvss.rb +31 -32
  34. data/lib/cvss_suite/version.rb +2 -2
  35. metadata +20 -25
  36. data/.travis.yml +0 -4
  37. data/lib/cvss_suite/helpers/extensions.rb +0 -32
@@ -14,61 +14,60 @@ require_relative '../cvss_metric'
14
14
  ##
15
15
  # This class represents a CVSS Environmental metric in version 2.
16
16
 
17
- class Cvss2Environmental < CvssMetric
17
+ module CvssSuite
18
+ class Cvss2Environmental < CvssMetric
19
+ ##
20
+ # Property of this metric
18
21
 
19
- ##
20
- # Property of this metric
22
+ attr_reader :collateral_damage_potential, :target_distribution, :security_requirements_cr,
23
+ :security_requirements_ir, :security_requirements_ar
21
24
 
22
- attr_reader :collateral_damage_potential, :target_distribution, :security_requirements_cr,
23
- :security_requirements_ir, :security_requirements_ar
25
+ ##
26
+ # Returns score of this metric
24
27
 
25
- ##
26
- # Returns score of this metric
28
+ def score(base, temporal_score)
29
+ base_score = (base.score @security_requirements_cr.score, @security_requirements_ir.score, @security_requirements_ar.score).round(1)
27
30
 
28
- def score(base, temporal_score)
29
- base_score = (base.score @security_requirements_cr.score, @security_requirements_ir.score, @security_requirements_ar.score).round(1)
31
+ adjusted_temporal = (base_score * temporal_score).round(1)
32
+ (adjusted_temporal + (10 - adjusted_temporal) * @collateral_damage_potential.score) * @target_distribution.score
33
+ end
30
34
 
31
- adjusted_temporal = (base_score * temporal_score).round(1)
32
- (adjusted_temporal + (10 - adjusted_temporal) * @collateral_damage_potential.score) * @target_distribution.score
35
+ private
33
36
 
34
- end
35
-
36
- private
37
-
38
- def init_properties
39
- @properties.push(@collateral_damage_potential =
40
- CvssProperty.new(name: 'Collateral Damage Potential', abbreviation: 'CDP', position: [6, 9],
41
- choices: [{ name: 'None', abbreviation: 'N', weight: 0.0 },
42
- { name: 'Low', abbreviation: 'L', weight: 0.1 },
43
- { name: 'Low-Medium', abbreviation: 'LM', weight: 0.3 },
44
- { name: 'Medium-High', abbreviation: 'MH', weight: 0.4 },
45
- { name: 'High', abbreviation: 'H', weight: 0.5 },
46
- { name: 'Not Defined', abbreviation: 'ND', weight: 0.0 }]))
47
- @properties.push(@target_distribution =
48
- CvssProperty.new(name: 'Target Distribution', abbreviation: 'TD', position: [7, 10],
49
- choices: [{ name: 'None', abbreviation: 'N', weight: 0.0 },
50
- { name: 'Low', abbreviation: 'L', weight: 0.25 },
51
- { name: 'Medium', abbreviation: 'M', weight: 0.75 },
52
- { name: 'High', abbreviation: 'H', weight: 1.0 },
53
- { name: 'Not Defined', abbreviation: 'ND', weight: 1.0 }]))
54
- @properties.push(@security_requirements_cr =
55
- CvssProperty.new(name: 'Confidentiality Requirement', abbreviation: 'CR', position: [8, 11],
56
- choices: [{ name: 'Low', abbreviation: 'L', weight: 0.5 },
57
- { name: 'Medium', abbreviation: 'M', weight: 1.0 },
58
- { name: 'High', abbreviation: 'H', weight: 1.51 },
59
- { name: 'Not Defined', abbreviation: 'ND', weight: 1.0 }]))
60
- @properties.push(@security_requirements_ir =
61
- CvssProperty.new(name: 'Integrity Requirement', abbreviation: 'IR', position: [9, 12],
62
- choices: [{ name: 'Low', abbreviation: 'L', weight: 0.5 },
63
- { name: 'Medium', abbreviation: 'M', weight: 1.0 },
64
- { name: 'High', abbreviation: 'H', weight: 1.51 },
65
- { name: 'Not Defined', abbreviation: 'ND', weight: 1.0 }]))
66
- @properties.push(@security_requirements_ar =
67
- CvssProperty.new(name: 'Availability Requirement', abbreviation: 'AR', position: [10, 13],
68
- choices: [{ name: 'Low', abbreviation: 'L', weight: 0.5 },
69
- { name: 'Medium', abbreviation: 'M', weight: 1.0 },
70
- { name: 'High', abbreviation: 'H', weight: 1.51 },
71
- { name: 'Not Defined', abbreviation: 'ND', weight: 1.0 }]))
37
+ def init_properties
38
+ @properties.push(@collateral_damage_potential =
39
+ CvssProperty.new(name: 'Collateral Damage Potential', abbreviation: 'CDP', position: [6, 9],
40
+ choices: [{ name: 'None', abbreviation: 'N', weight: 0.0 },
41
+ { name: 'Low', abbreviation: 'L', weight: 0.1 },
42
+ { name: 'Low-Medium', abbreviation: 'LM', weight: 0.3 },
43
+ { name: 'Medium-High', abbreviation: 'MH', weight: 0.4 },
44
+ { name: 'High', abbreviation: 'H', weight: 0.5 },
45
+ { name: 'Not Defined', abbreviation: 'ND', weight: 0.0 }]))
46
+ @properties.push(@target_distribution =
47
+ CvssProperty.new(name: 'Target Distribution', abbreviation: 'TD', position: [7, 10],
48
+ choices: [{ name: 'None', abbreviation: 'N', weight: 0.0 },
49
+ { name: 'Low', abbreviation: 'L', weight: 0.25 },
50
+ { name: 'Medium', abbreviation: 'M', weight: 0.75 },
51
+ { name: 'High', abbreviation: 'H', weight: 1.0 },
52
+ { name: 'Not Defined', abbreviation: 'ND', weight: 1.0 }]))
53
+ @properties.push(@security_requirements_cr =
54
+ CvssProperty.new(name: 'Confidentiality Requirement', abbreviation: 'CR', position: [8, 11],
55
+ choices: [{ name: 'Low', abbreviation: 'L', weight: 0.5 },
56
+ { name: 'Medium', abbreviation: 'M', weight: 1.0 },
57
+ { name: 'High', abbreviation: 'H', weight: 1.51 },
58
+ { name: 'Not Defined', abbreviation: 'ND', weight: 1.0 }]))
59
+ @properties.push(@security_requirements_ir =
60
+ CvssProperty.new(name: 'Integrity Requirement', abbreviation: 'IR', position: [9, 12],
61
+ choices: [{ name: 'Low', abbreviation: 'L', weight: 0.5 },
62
+ { name: 'Medium', abbreviation: 'M', weight: 1.0 },
63
+ { name: 'High', abbreviation: 'H', weight: 1.51 },
64
+ { name: 'Not Defined', abbreviation: 'ND', weight: 1.0 }]))
65
+ @properties.push(@security_requirements_ar =
66
+ CvssProperty.new(name: 'Availability Requirement', abbreviation: 'AR', position: [10, 13],
67
+ choices: [{ name: 'Low', abbreviation: 'L', weight: 0.5 },
68
+ { name: 'Medium', abbreviation: 'M', weight: 1.0 },
69
+ { name: 'High', abbreviation: 'H', weight: 1.51 },
70
+ { name: 'Not Defined', abbreviation: 'ND', weight: 1.0 }]))
71
+ end
72
72
  end
73
73
  end
74
-
@@ -14,44 +14,46 @@ require_relative '../cvss_metric'
14
14
  ##
15
15
  # This class represents a CVSS Temporal metric in version 2.
16
16
 
17
- class Cvss2Temporal < CvssMetric
18
-
19
- ##
20
- # Property of this metric
21
-
22
- attr_reader :exploitability, :remediation_level, :report_confidence
23
-
24
- ##
25
- # Returns score of this metric
26
-
27
- def score
28
- return 1 unless valid?
29
- @exploitability.score * @remediation_level.score * @report_confidence.score
30
- end
31
-
32
- private
33
-
34
- def init_properties
35
- @properties.push(@exploitability =
36
- CvssProperty.new(name: 'Exploitability', abbreviation: 'E', position: [6],
37
- choices: [{ name: 'Not Defined', abbreviation: 'ND', weight: 1 },
38
- { name: 'Unproven', abbreviation: 'U', weight: 0.85 },
39
- { name: 'Proof-of-Concept', abbreviation: 'POC', weight: 0.9 },
40
- { name: 'Functional', abbreviation: 'F', weight: 0.95 },
41
- { name: 'High', abbreviation: 'H', weight: 1 }]))
42
- @properties.push(@remediation_level =
43
- CvssProperty.new(name: 'Remediation Level', abbreviation: 'RL', position: [7],
44
- choices: [{ name: 'Not Defined', abbreviation: 'ND', weight: 1 },
45
- { name: 'Official Fix', abbreviation: 'OF', weight: 0.87 },
46
- { name: 'Temporary Fix', abbreviation: 'TF', weight: 0.9 },
47
- { name: 'Workaround', abbreviation: 'W', weight: 0.95 },
48
- { name: 'Unavailable', abbreviation: 'U', weight: 1 }]))
49
-
50
- @properties.push(@report_confidence =
51
- CvssProperty.new(name: 'Report Confidence', abbreviation: 'RC', position: [8],
52
- choices: [{ name: 'Not Defined', abbreviation: 'ND', weight: 1 },
53
- { name: 'Unconfirmed', abbreviation: 'UC', weight: 0.9 },
54
- { name: 'Uncorroborated', abbreviation: 'UR', weight: 0.95 },
55
- { name: 'Confirmed', abbreviation: 'C', weight: 1 }]))
17
+ module CvssSuite
18
+ class Cvss2Temporal < CvssMetric
19
+ ##
20
+ # Property of this metric
21
+
22
+ attr_reader :exploitability, :remediation_level, :report_confidence
23
+
24
+ ##
25
+ # Returns score of this metric
26
+
27
+ def score
28
+ return 1 unless valid?
29
+
30
+ @exploitability.score * @remediation_level.score * @report_confidence.score
31
+ end
32
+
33
+ private
34
+
35
+ def init_properties
36
+ @properties.push(@exploitability =
37
+ CvssProperty.new(name: 'Exploitability', abbreviation: 'E', position: [6],
38
+ choices: [{ name: 'Not Defined', abbreviation: 'ND', weight: 1 },
39
+ { name: 'Unproven', abbreviation: 'U', weight: 0.85 },
40
+ { name: 'Proof-of-Concept', abbreviation: 'POC', weight: 0.9 },
41
+ { name: 'Functional', abbreviation: 'F', weight: 0.95 },
42
+ { name: 'High', abbreviation: 'H', weight: 1 }]))
43
+ @properties.push(@remediation_level =
44
+ CvssProperty.new(name: 'Remediation Level', abbreviation: 'RL', position: [7],
45
+ choices: [{ name: 'Not Defined', abbreviation: 'ND', weight: 1 },
46
+ { name: 'Official Fix', abbreviation: 'OF', weight: 0.87 },
47
+ { name: 'Temporary Fix', abbreviation: 'TF', weight: 0.9 },
48
+ { name: 'Workaround', abbreviation: 'W', weight: 0.95 },
49
+ { name: 'Unavailable', abbreviation: 'U', weight: 1 }]))
50
+
51
+ @properties.push(@report_confidence =
52
+ CvssProperty.new(name: 'Report Confidence', abbreviation: 'RC', position: [8],
53
+ choices: [{ name: 'Not Defined', abbreviation: 'ND', weight: 1 },
54
+ { name: 'Unconfirmed', abbreviation: 'UC', weight: 0.9 },
55
+ { name: 'Uncorroborated', abbreviation: 'UR', weight: 0.95 },
56
+ { name: 'Confirmed', abbreviation: 'C', weight: 1 }]))
57
+ end
56
58
  end
57
59
  end
@@ -8,7 +8,7 @@
8
8
  # This work is licensed under the terms of the MIT license.
9
9
  # See the LICENSE.md file in the top-level directory.
10
10
 
11
- require_relative '../../../lib/cvss_suite/cvss'
11
+ require_relative '../cvss'
12
12
  require_relative 'cvss3_base'
13
13
  require_relative 'cvss3_temporal'
14
14
  require_relative 'cvss3_environmental'
@@ -16,37 +16,45 @@ require_relative 'cvss3_environmental'
16
16
  ##
17
17
  # This class represents a CVSS vector in version 3.0.
18
18
 
19
- class Cvss3 < Cvss
19
+ module CvssSuite
20
+ class Cvss3 < Cvss
21
+ ##
22
+ # Returns the Version of the CVSS vector.
20
23
 
21
- ##
22
- # Returns the Base Score of the CVSS vector.
24
+ def version
25
+ 3.0
26
+ end
23
27
 
24
- def base_score
25
- check_validity
26
- @base.score.round_up(1)
27
- end
28
+ ##
29
+ # Returns the Base Score of the CVSS vector.
28
30
 
29
- ##
30
- # Returns the Temporal Score of the CVSS vector.
31
+ def base_score
32
+ check_validity
33
+ Cvss3Helper.round_up(@base.score)
34
+ end
31
35
 
32
- def temporal_score
33
- (@base.score.round_up(1) * @temporal.score).round_up(1)
34
- end
36
+ ##
37
+ # Returns the Temporal Score of the CVSS vector.
35
38
 
36
- ##
37
- # Returns the Environmental Score of the CVSS vector.
39
+ def temporal_score
40
+ Cvss3Helper.round_up(Cvss3Helper.round_up(@base.score) * @temporal.score)
41
+ end
38
42
 
39
- def environmental_score
40
- return temporal_score unless @environmental.valid?
41
- (@environmental.score @temporal.score).round_up(1)
42
- end
43
+ ##
44
+ # Returns the Environmental Score of the CVSS vector.
43
45
 
44
- private
46
+ def environmental_score
47
+ return temporal_score unless @environmental.valid?
45
48
 
46
- def init_metrics
47
- @base = Cvss3Base.new(@properties)
48
- @temporal = Cvss3Temporal.new(@properties)
49
- @environmental = Cvss3Environmental.new(@properties)
50
- end
49
+ Cvss3Helper.round_up(@environmental.score(@base, @temporal))
50
+ end
51
+
52
+ private
51
53
 
52
- end
54
+ def init_metrics
55
+ @base = Cvss3Base.new(@properties)
56
+ @temporal = Cvss3Temporal.new(@properties)
57
+ @environmental = Cvss3Environmental.new(@properties)
58
+ end
59
+ end
60
+ end
@@ -15,81 +15,80 @@ require_relative '../helpers/cvss3_helper'
15
15
  ##
16
16
  # This class represents a CVSS Base metric in version 3.
17
17
 
18
- class Cvss3Base < CvssMetric
18
+ module CvssSuite
19
+ class Cvss3Base < CvssMetric
20
+ ##
21
+ # Property of this metric
19
22
 
20
- ##
21
- # Property of this metric
23
+ attr_reader :attack_vector, :attack_complexity, :privileges_required, :user_interaction,
24
+ :scope, :confidentiality, :integrity, :availability
22
25
 
23
- attr_reader :attack_vector, :attack_complexity, :privileges_required, :user_interaction,
24
- :scope, :confidentiality, :integrity, :availability
26
+ ##
27
+ # Returns score of this metric
25
28
 
26
- ##
27
- # Returns score of this metric
29
+ def score
30
+ privilege_score = Cvss3Helper.privileges_required_score @privileges_required, @scope
28
31
 
29
- def score
32
+ exploitability = 8.22 * @attack_vector.score * @attack_complexity.score * privilege_score * @user_interaction.score
30
33
 
31
- privilege_score = Cvss3Helper.privileges_required_score @privileges_required, @scope
34
+ isc_base = 1 - ((1 - @confidentiality.score) * (1 - @integrity.score) * (1 - @availability.score))
32
35
 
33
- exploitability = 8.22 * @attack_vector.score * @attack_complexity.score * privilege_score * @user_interaction.score
36
+ impact_sub_score = if @scope.selected_choice[:name] == 'Changed'
37
+ 7.52 * (isc_base - 0.029) - 3.25 * (isc_base - 0.02)**15
38
+ else
39
+ 6.42 * isc_base
40
+ end
34
41
 
35
- isc_base = 1 - ((1 - @confidentiality.score) * (1 - @integrity.score) * (1 - @availability.score))
42
+ return 0 if impact_sub_score <= 0
36
43
 
37
- if @scope.selected_choice[:name] == 'Changed'
38
- impact_sub_score = 7.52 * (isc_base - 0.029) - 3.25 * (isc_base - 0.02)**15
39
- else
40
- impact_sub_score = 6.42 * isc_base
44
+ if @scope.selected_choice[:name] == 'Changed'
45
+ [10, 1.08 * (impact_sub_score + exploitability)].min
46
+ else
47
+ [10, impact_sub_score + exploitability].min
48
+ end
41
49
  end
42
50
 
43
- return 0 if impact_sub_score <= 0
44
-
45
- if @scope.selected_choice[:name] == 'Changed'
46
- [10, 1.08 * (impact_sub_score + exploitability)].min
47
- else
48
- [10, impact_sub_score + exploitability].min
51
+ private
52
+
53
+ def init_properties
54
+ @properties.push(@attack_vector =
55
+ CvssProperty.new(name: 'Attack Vector', abbreviation: 'AV', position: [0],
56
+ choices: [{ name: 'Network', abbreviation: 'N', weight: 0.85 },
57
+ { name: 'Adjacent', abbreviation: 'A', weight: 0.62 },
58
+ { name: 'Local', abbreviation: 'L', weight: 0.55 },
59
+ { name: 'Physical', abbreviation: 'P', weight: 0.2 }]))
60
+ @properties.push(@attack_complexity =
61
+ CvssProperty.new(name: 'Attack Complexity', abbreviation: 'AC', position: [1],
62
+ choices: [{ name: 'Low', abbreviation: 'L', weight: 0.77 },
63
+ { name: 'High', abbreviation: 'H', weight: 0.44 }]))
64
+ @properties.push(@privileges_required =
65
+ CvssProperty.new(name: 'Privileges Required', abbreviation: 'PR', position: [2],
66
+ choices: [{ name: 'None', abbreviation: 'N', weight: 0.85 },
67
+ { name: 'Low', abbreviation: 'L', weight: 0.62 },
68
+ { name: 'High', abbreviation: 'H', weight: 0.27 }]))
69
+ @properties.push(@user_interaction =
70
+ CvssProperty.new(name: 'User Interaction', abbreviation: 'UI', position: [3],
71
+ choices: [{ name: 'None', abbreviation: 'N', weight: 0.85 },
72
+ { name: 'Required', abbreviation: 'R', weight: 0.62 }]))
73
+ @properties.push(@scope =
74
+ CvssProperty.new(name: 'Scope', abbreviation: 'S', position: [4],
75
+ choices: [{ name: 'Unchanged', abbreviation: 'U' },
76
+ { name: 'Changed', abbreviation: 'C' }]))
77
+ @properties.push(@confidentiality =
78
+ CvssProperty.new(name: 'Confidentiality', abbreviation: 'C', position: [5],
79
+ choices: [{ name: 'None', abbreviation: 'N', weight: 0.0 },
80
+ { name: 'Low', abbreviation: 'L', weight: 0.22 },
81
+ { name: 'High', abbreviation: 'H', weight: 0.56 }]))
82
+ @properties.push(@integrity =
83
+ CvssProperty.new(name: 'Integrity', abbreviation: 'I', position: [6],
84
+ choices: [{ name: 'None', abbreviation: 'N', weight: 0.0 },
85
+ { name: 'Low', abbreviation: 'L', weight: 0.22 },
86
+ { name: 'High', abbreviation: 'H', weight: 0.56 }]))
87
+ @properties.push(@availability =
88
+ CvssProperty.new(name: 'Availability', abbreviation: 'A', position: [7],
89
+ choices: [{ name: 'None', abbreviation: 'N', weight: 0.0 },
90
+ { name: 'Low', abbreviation: 'L', weight: 0.22 },
91
+ { name: 'High', abbreviation: 'H', weight: 0.56 }]))
49
92
  end
50
93
  end
51
-
52
- private
53
-
54
- def init_properties
55
- @properties.push(@attack_vector =
56
- CvssProperty.new(name: 'Attack Vector', abbreviation: 'AV', position: [0],
57
- choices: [{ name: 'Network', abbreviation: 'N', weight: 0.85 },
58
- { name: 'Adjacent', abbreviation: 'A', weight: 0.62 },
59
- { name: 'Local', abbreviation: 'L', weight: 0.55 },
60
- { name: 'Physical', abbreviation: 'P', weight: 0.2 }]))
61
- @properties.push(@attack_complexity =
62
- CvssProperty.new(name: 'Attack Complexity', abbreviation: 'AC', position: [1],
63
- choices: [{ name: 'Low', abbreviation: 'L', weight: 0.77 },
64
- { name: 'High', abbreviation: 'H', weight: 0.44 }]))
65
- @properties.push(@privileges_required =
66
- CvssProperty.new(name: 'Privileges Required', abbreviation: 'PR', position: [2],
67
- choices: [{ name: 'None', abbreviation: 'N', weight: 0.85 },
68
- { name: 'Low', abbreviation: 'L', weight: 0.62 },
69
- { name: 'High', abbreviation: 'H', weight: 0.27 }]))
70
- @properties.push(@user_interaction =
71
- CvssProperty.new(name: 'User Interaction', abbreviation: 'UI', position: [3],
72
- choices: [{ name: 'None', abbreviation: 'N', weight: 0.85 },
73
- { name: 'Required', abbreviation: 'R', weight: 0.62 }]))
74
- @properties.push(@scope =
75
- CvssProperty.new(name: 'Scope', abbreviation: 'S', position: [4],
76
- choices: [{ name: 'Unchanged', abbreviation: 'U' },
77
- { name: 'Changed', abbreviation: 'C' }]))
78
- @properties.push(@confidentiality =
79
- CvssProperty.new(name: 'Confidentiality', abbreviation: 'C', position: [5],
80
- choices: [{ name: 'None', abbreviation: 'N', weight: 0.0 },
81
- { name: 'Low', abbreviation: 'L', weight: 0.22 },
82
- { name: 'High', abbreviation: 'H', weight: 0.56 }]))
83
- @properties.push(@integrity =
84
- CvssProperty.new(name: 'Integrity', abbreviation: 'I', position: [6],
85
- choices: [{ name: 'None', abbreviation: 'N', weight: 0.0 },
86
- { name: 'Low', abbreviation: 'L', weight: 0.22 },
87
- { name: 'High', abbreviation: 'H', weight: 0.56 }]))
88
- @properties.push(@availability =
89
- CvssProperty.new(name: 'Availability', abbreviation: 'A', position: [7],
90
- choices: [{ name: 'None', abbreviation: 'N', weight: 0.0 },
91
- { name: 'Low', abbreviation: 'L', weight: 0.22 },
92
- { name: 'High', abbreviation: 'H', weight: 0.56 }]))
93
- end
94
94
  end
95
-
@@ -15,130 +15,182 @@ require_relative '../helpers/cvss3_helper'
15
15
  ##
16
16
  # This class represents a CVSS Environmental metric in version 3.
17
17
 
18
- class Cvss3Environmental < CvssMetric
18
+ module CvssSuite
19
+ class Cvss3Environmental < CvssMetric
20
+ ##
21
+ # Property of this metric
19
22
 
20
- ##
21
- # Property of this metric
23
+ attr_reader :confidentiality_requirement, :integrity_requirement, :availability_requirement,
24
+ :modified_attack_vector, :modified_attack_complexity, :modified_privileges_required,
25
+ :modified_user_interaction, :modified_scope, :modified_confidentiality,
26
+ :modified_integrity, :modified_availability
22
27
 
23
- attr_reader :confidentiality_requirement, :integrity_requirement, :availability_requirement,
24
- :modified_attack_vector, :modified_attack_complexity, :modified_privileges_required,
25
- :modified_user_interaction, :modified_scope, :modified_confidentiality,
26
- :modified_integrity, :modified_availability
28
+ ##
29
+ # Returns score of this metric
27
30
 
28
- ##
29
- # Returns score of this metric
31
+ def score(base, temporal)
32
+ @base = base
30
33
 
31
- def score(temporal_score)
34
+ merged_modified_privileges_required = @modified_privileges_required
35
+ if @modified_privileges_required.selected_choice[:name] == 'Not Defined'
36
+ merged_modified_privileges_required = @base.privileges_required
37
+ end
32
38
 
33
- privilege_score = Cvss3Helper.privileges_required_score @modified_privileges_required, @modified_scope
39
+ merged_modified_scope = @modified_scope
40
+ if @modified_scope.selected_choice[:name] == 'Not Defined'
41
+ merged_modified_scope = @base.scope
42
+ end
34
43
 
35
- modified_exploitability_sub_score = modified_exploitability_sub privilege_score
44
+ privilege_score = Cvss3Helper.privileges_required_score(merged_modified_privileges_required, merged_modified_scope)
36
45
 
37
- isc_modified_score = isc_modified
46
+ modified_exploitability_sub_score = modified_exploitability_sub(privilege_score)
38
47
 
39
- modified_impact_sub_score = modified_impact_sub isc_modified_score
48
+ modified_impact_sub_score = modified_impact_sub(isc_modified)
40
49
 
41
- return 0 if modified_impact_sub_score <= 0
50
+ return 0 if modified_impact_sub_score <= 0
42
51
 
43
- calculate_score modified_impact_sub_score, modified_exploitability_sub_score, temporal_score
44
- end
52
+ calculate_score modified_impact_sub_score, modified_exploitability_sub_score, temporal.score
53
+ end
45
54
 
46
- private
47
-
48
- def init_properties
49
- @properties.push(@confidentiality_requirement =
50
- CvssProperty.new(name: 'Confidentiality Requirement', abbreviation: 'CR', position: [8, 11],
51
- choices: [{name: 'Low', abbreviation: 'L', weight: 0.5},
52
- {name: 'Medium', abbreviation: 'M', weight: 1.0},
53
- {name: 'High', abbreviation: 'H', weight: 1.5},
54
- {name: 'Not Defined', abbreviation: 'X', weight: 1}]))
55
- @properties.push(@integrity_requirement =
56
- CvssProperty.new(name: 'Integrity Requirement', abbreviation: 'IR', position: [9, 12],
57
- choices: [{name: 'Low', abbreviation: 'L', weight: 0.5},
58
- {name: 'Medium', abbreviation: 'M', weight: 1.0},
59
- {name: 'High', abbreviation: 'H', weight: 1.5},
60
- {name: 'Not Defined', abbreviation: 'X', weight: 1}]))
61
-
62
- @properties.push(@availability_requirement =
63
- CvssProperty.new(name: 'Availability Requirement', abbreviation: 'AR', position: [10, 13],
64
- choices: [{name: 'Low', abbreviation: 'L', weight: 0.5},
65
- {name: 'Medium', abbreviation: 'M', weight: 1.0},
66
- {name: 'High', abbreviation: 'H', weight: 1.5},
67
- {name: 'Not Defined', abbreviation: 'X', weight: 1}]))
68
- @properties.push(@modified_attack_vector =
69
- CvssProperty.new(name: 'Modified Attack Vector', abbreviation: 'MAV', position: [11, 14],
70
- choices: [{name: 'Network', abbreviation: 'N', weight: 0.85},
71
- {name: 'Adjacent Network', abbreviation: 'A', weight: 0.62},
72
- {name: 'Local', abbreviation: 'L', weight: 0.55},
73
- {name: 'Physical', abbreviation: 'P', weight: 0.2},
74
- {name: 'Not Defined', abbreviation: 'X', weight: 1}]))
75
- @properties.push(@modified_attack_complexity =
76
- CvssProperty.new(name: 'Modified Attack Complexity', abbreviation: 'MAC', position: [12, 15],
77
- choices: [{name: 'Low', abbreviation: 'L', weight: 0.77},
78
- {name: 'High', abbreviation: 'H', weight: 0.44},
79
- {name: 'Not Defined', abbreviation: 'X', weight: 1}]))
80
- @properties.push(@modified_privileges_required =
81
- CvssProperty.new(name: 'Modified Privileges Required', abbreviation: 'MPR', position: [13, 16],
82
- choices: [{name: 'None', abbreviation: 'N', weight: 0.85},
83
- {name: 'Low', abbreviation: 'L', weight: 0.62},
84
- {name: 'High', abbreviation: 'H', weight: 0.27},
85
- {name: 'Not Defined', abbreviation: 'X', weight: 1}]))
86
- @properties.push(@modified_user_interaction =
87
- CvssProperty.new(name: 'Modified User Interaction', abbreviation: 'MUI', position: [14, 17],
88
- choices: [{name: 'None', abbreviation: 'N', weight: 0.85},
89
- {name: 'Required', abbreviation: 'R', weight: 0.62},
90
- {name: 'Not Defined', abbreviation: 'X', weight: 1}]))
91
- @properties.push(@modified_scope =
92
- CvssProperty.new(name: 'Modified Scope', abbreviation: 'MS', position: [15, 18],
93
- choices: [{name: 'Changed', abbreviation: 'C'},
94
- {name: 'Unchanged', abbreviation: 'U'}]))
95
- @properties.push(@modified_confidentiality =
96
- CvssProperty.new(name: 'Modified Confidentiality', abbreviation: 'MC', position: [16, 19],
97
- choices: [{name: 'None', abbreviation: 'N', weight: 0},
98
- {name: 'Low', abbreviation: 'L', weight: 0.22},
99
- {name: 'High', abbreviation: 'H', weight: 0.56},
100
- {name: 'Not Defined', abbreviation: 'X', weight: 1}]))
101
- @properties.push(@modified_integrity =
102
- CvssProperty.new(name: 'Modified Integrity', abbreviation: 'MI', position: [17, 20],
103
- choices: [{name: 'None', abbreviation: 'N', weight: 0},
104
- {name: 'Low', abbreviation: 'L', weight: 0.22},
105
- {name: 'High', abbreviation: 'H', weight: 0.56},
106
- {name: 'Not Defined', abbreviation: 'X', weight: 1}]))
107
- @properties.push(@modified_availability =
108
- CvssProperty.new(name: 'Modified Availability', abbreviation: 'MA', position: [18, 21],
109
- choices: [{name: 'None', abbreviation: 'N', weight: 0},
110
- {name: 'Low', abbreviation: 'L', weight: 0.22},
111
- {name: 'High', abbreviation: 'H', weight: 0.56},
112
- {name: 'Not Defined', abbreviation: 'X', weight: 1}]))
113
- end
55
+ private
56
+
57
+ def init_properties
58
+ @properties.push(@confidentiality_requirement =
59
+ CvssProperty.new(name: 'Confidentiality Requirement', abbreviation: 'CR', position: [8, 11],
60
+ choices: [{ name: 'Low', abbreviation: 'L', weight: 0.5 },
61
+ { name: 'Medium', abbreviation: 'M', weight: 1.0 },
62
+ { name: 'High', abbreviation: 'H', weight: 1.5 },
63
+ { name: 'Not Defined', abbreviation: 'X', weight: 1 }]))
64
+ @properties.push(@integrity_requirement =
65
+ CvssProperty.new(name: 'Integrity Requirement', abbreviation: 'IR', position: [9, 12],
66
+ choices: [{ name: 'Low', abbreviation: 'L', weight: 0.5 },
67
+ { name: 'Medium', abbreviation: 'M', weight: 1.0 },
68
+ { name: 'High', abbreviation: 'H', weight: 1.5 },
69
+ { name: 'Not Defined', abbreviation: 'X', weight: 1 }]))
70
+
71
+ @properties.push(@availability_requirement =
72
+ CvssProperty.new(name: 'Availability Requirement', abbreviation: 'AR', position: [10, 13],
73
+ choices: [{ name: 'Low', abbreviation: 'L', weight: 0.5 },
74
+ { name: 'Medium', abbreviation: 'M', weight: 1.0 },
75
+ { name: 'High', abbreviation: 'H', weight: 1.5 },
76
+ { name: 'Not Defined', abbreviation: 'X', weight: 1 }]))
77
+ @properties.push(@modified_attack_vector =
78
+ CvssProperty.new(name: 'Modified Attack Vector', abbreviation: 'MAV', position: [11, 14],
79
+ choices: [{ name: 'Network', abbreviation: 'N', weight: 0.85 },
80
+ { name: 'Adjacent Network', abbreviation: 'A', weight: 0.62 },
81
+ { name: 'Local', abbreviation: 'L', weight: 0.55 },
82
+ { name: 'Physical', abbreviation: 'P', weight: 0.2 },
83
+ { name: 'Not Defined', abbreviation: 'X', weight: 1 }]))
84
+ @properties.push(@modified_attack_complexity =
85
+ CvssProperty.new(name: 'Modified Attack Complexity', abbreviation: 'MAC', position: [12, 15],
86
+ choices: [{ name: 'Low', abbreviation: 'L', weight: 0.77 },
87
+ { name: 'High', abbreviation: 'H', weight: 0.44 },
88
+ { name: 'Not Defined', abbreviation: 'X', weight: 1 }]))
89
+ @properties.push(@modified_privileges_required =
90
+ CvssProperty.new(name: 'Modified Privileges Required', abbreviation: 'MPR', position: [13, 16],
91
+ choices: [{ name: 'None', abbreviation: 'N', weight: 0.85 },
92
+ { name: 'Low', abbreviation: 'L', weight: 0.62 },
93
+ { name: 'High', abbreviation: 'H', weight: 0.27 },
94
+ { name: 'Not Defined', abbreviation: 'X', weight: 1 }]))
95
+ @properties.push(@modified_user_interaction =
96
+ CvssProperty.new(name: 'Modified User Interaction', abbreviation: 'MUI', position: [14, 17],
97
+ choices: [{ name: 'None', abbreviation: 'N', weight: 0.85 },
98
+ { name: 'Required', abbreviation: 'R', weight: 0.62 },
99
+ { name: 'Not Defined', abbreviation: 'X', weight: 1 }]))
100
+ @properties.push(@modified_scope =
101
+ CvssProperty.new(name: 'Modified Scope', abbreviation: 'MS', position: [15, 18],
102
+ choices: [{ name: 'Changed', abbreviation: 'C' },
103
+ { name: 'Unchanged', abbreviation: 'U' },
104
+ { name: 'Not Defined', abbreviation: 'X' }]))
105
+ @properties.push(@modified_confidentiality =
106
+ CvssProperty.new(name: 'Modified Confidentiality', abbreviation: 'MC', position: [16, 19],
107
+ choices: [{ name: 'None', abbreviation: 'N', weight: 0 },
108
+ { name: 'Low', abbreviation: 'L', weight: 0.22 },
109
+ { name: 'High', abbreviation: 'H', weight: 0.56 },
110
+ { name: 'Not Defined', abbreviation: 'X', weight: 1 }]))
111
+ @properties.push(@modified_integrity =
112
+ CvssProperty.new(name: 'Modified Integrity', abbreviation: 'MI', position: [17, 20],
113
+ choices: [{ name: 'None', abbreviation: 'N', weight: 0 },
114
+ { name: 'Low', abbreviation: 'L', weight: 0.22 },
115
+ { name: 'High', abbreviation: 'H', weight: 0.56 },
116
+ { name: 'Not Defined', abbreviation: 'X', weight: 1 }]))
117
+ @properties.push(@modified_availability =
118
+ CvssProperty.new(name: 'Modified Availability', abbreviation: 'MA', position: [18, 21],
119
+ choices: [{ name: 'None', abbreviation: 'N', weight: 0 },
120
+ { name: 'Low', abbreviation: 'L', weight: 0.22 },
121
+ { name: 'High', abbreviation: 'H', weight: 0.56 },
122
+ { name: 'Not Defined', abbreviation: 'X', weight: 1 }]))
123
+ end
114
124
 
115
- def modified_impact_sub(isc_modified)
116
- if @modified_scope.selected_choice[:name] == 'Changed'
117
- 7.52 * (isc_modified - 0.029) - 3.25 * (isc_modified - 0.02)**15
118
- else
119
- 6.42 * isc_modified
125
+ def modified_impact_sub(isc_modified)
126
+ if @modified_scope.selected_choice[:name] == 'Not Defined'
127
+ if @base.scope.selected_choice[:name] == 'Changed'
128
+ return 7.52 * (isc_modified - 0.029) - 3.25 * (isc_modified - 0.02)**15
129
+ else
130
+ return 6.42 * isc_modified
131
+ end
132
+ end
133
+
134
+ if @modified_scope.selected_choice[:name] == 'Changed'
135
+ 7.52 * (isc_modified - 0.029) - 3.25 * (isc_modified - 0.02)**15
136
+ else
137
+ 6.42 * isc_modified
138
+ end
120
139
  end
121
- end
122
140
 
123
- def isc_modified
124
- confidentiality_score = 1 - @modified_confidentiality.score * @confidentiality_requirement.score
125
- integrity_score = 1 - @modified_integrity.score * @integrity_requirement.score
126
- availability_score = 1 - @modified_availability.score * @availability_requirement.score
141
+ def isc_modified
142
+ merged_modified_confidentiality = @modified_confidentiality
143
+ if @modified_confidentiality.selected_choice[:name] == 'Not Defined'
144
+ merged_modified_confidentiality = @base.confidentiality
145
+ end
127
146
 
128
- [0.915, (1 - confidentiality_score * integrity_score * availability_score)].min
129
- end
147
+ merged_modified_integrity = @modified_integrity
148
+ if @modified_integrity.selected_choice[:name] == 'Not Defined'
149
+ merged_modified_integrity = @base.integrity
150
+ end
130
151
 
131
- def modified_exploitability_sub(privilege_score)
132
- modified_exploitability_sub_score = 8.22 * @modified_attack_vector.score
133
- modified_exploitability_sub_score *= @modified_attack_complexity.score
134
- modified_exploitability_sub_score *= privilege_score
135
- modified_exploitability_sub_score *= @modified_user_interaction.score
136
- end
152
+ merged_modified_availability = @modified_availability
153
+ if @modified_availability.selected_choice[:name] == 'Not Defined'
154
+ merged_modified_availability = @base.availability
155
+ end
137
156
 
138
- def calculate_score(modified_impact_sub_score, modified_exploitability_sub_score, temporal_score)
139
- factor = @modified_scope.selected_choice[:name] == 'Changed' ? 1.08 : 1.0
157
+ confidentiality_score = 1 - merged_modified_confidentiality.score * @confidentiality_requirement.score
158
+ integrity_score = 1 - merged_modified_integrity.score * @integrity_requirement.score
159
+ availability_score = 1 - merged_modified_availability.score * @availability_requirement.score
140
160
 
141
- ([factor * (modified_impact_sub_score + modified_exploitability_sub_score), 10].min.round_up(1) * temporal_score)
161
+ [0.915, (1 - confidentiality_score * integrity_score * availability_score)].min
162
+ end
142
163
 
164
+ def modified_exploitability_sub(privilege_score)
165
+ merged_modified_attack_vector = @modified_attack_vector
166
+ if @modified_attack_vector.selected_choice[:name] == 'Not Defined'
167
+ merged_modified_attack_vector = @base.attack_vector
168
+ end
169
+
170
+ merged_modified_attack_complexity = @modified_attack_complexity
171
+ if @modified_attack_complexity.selected_choice[:name] == 'Not Defined'
172
+ merged_modified_attack_complexity = @base.attack_complexity
173
+ end
174
+
175
+ merged_modified_user_interaction = @modified_user_interaction
176
+ if @modified_user_interaction.selected_choice[:name] == 'Not Defined'
177
+ merged_modified_user_interaction = @base.user_interaction
178
+ end
179
+
180
+ 8.22 * merged_modified_attack_vector.score * merged_modified_attack_complexity.score *
181
+ privilege_score * merged_modified_user_interaction.score
182
+ end
183
+
184
+ def calculate_score(modified_impact_sub_score, modified_exploitability_sub_score, temporal_score)
185
+ if @modified_scope.selected_choice[:name] == 'Not Defined'
186
+ factor = @base.scope.selected_choice[:name] == 'Changed' ? 1.08 : 1.0
187
+ else
188
+ factor = @modified_scope.selected_choice[:name] == 'Changed' ? 1.08 : 1.0
189
+ end
190
+
191
+ Cvss3Helper.round_up(
192
+ [factor * (modified_impact_sub_score + modified_exploitability_sub_score), 10].min
193
+ ) * temporal_score
194
+ end
143
195
  end
144
- end
196
+ end