cvss-suite 1.1.1 → 1.2.3

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 +62 -0
  10. data/PULL_REQUEST_TEMPLATE.md +24 -0
  11. data/README.md +37 -9
  12. data/_config.yml +1 -0
  13. data/bin/console +3 -3
  14. data/cvss_suite.gemspec +16 -13
  15. data/lib/cvss_suite.rb +13 -6
  16. data/lib/cvss_suite/cvss.rb +94 -64
  17. data/lib/cvss_suite/cvss2/cvss2.rb +52 -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 +22 -26
  36. data/.travis.yml +0 -4
  37. data/lib/cvss_suite/helpers/extensions.rb +0 -32
@@ -9,7 +9,8 @@
9
9
  # See the LICENSE.md file in the top-level directory.
10
10
 
11
11
  # coding: utf-8
12
- lib = File.expand_path('../lib', __FILE__)
12
+
13
+ lib = File.expand_path('lib', __dir__)
13
14
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
14
15
  require 'cvss_suite/version'
15
16
 
@@ -17,23 +18,25 @@ Gem::Specification.new do |spec|
17
18
  spec.name = 'cvss-suite'
18
19
  spec.version = CvssSuite::VERSION
19
20
  spec.license = 'MIT'
20
- spec.authors = ["Oliver Hamboerger"]
21
- spec.email = ["oliver.hamboerger@siemens.com"]
21
+ spec.authors = ['Oliver Hamboerger']
22
+ spec.email = ['oliver.hamboerger@siemens.com']
23
+
24
+ spec.summary = 'Ruby gem for processing cvss vectors.'
25
+ spec.description = 'This Ruby gem helps you to process the vector of the Common Vulnerability Scoring System (https://www.first.org/cvss/specification-document).
26
+ Besides calculating the Base, Temporal and Environmental Score, you are able to extract the selected option.'
27
+ spec.homepage = 'https://siemens.github.io/cvss-suite/'
22
28
 
23
- spec.summary = %q{Ruby gem for processing cvss vectors.}
24
- spec.description = %q{This Ruby gem helps you to process the vector of the Common Vulnerability Scoring System (https://www.first.org/cvss/specification-document).
25
- Besides calculating the Base, Temporal and Environmental Score, you are able to extract the selected option.}
26
- spec.homepage = "https://siemens.github.io/cvss-suite/"
29
+ spec.post_install_message = 'Version 1.x of this gem is no longer supported, please update to a supported version.'
27
30
 
31
+ spec.required_ruby_version = '>= 2.0.0'
28
32
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
29
33
  spec.bindir = 'exe'
30
34
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
35
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
32
- spec.require_paths = ["lib"]
36
+ spec.require_paths = ['lib']
33
37
 
34
- spec.add_development_dependency "bundler", "~> 1.10"
35
- spec.add_development_dependency "rspec", "~> 3.4"
36
- spec.add_development_dependency "rspec-its", "~> 1.2"
37
- spec.add_development_dependency "rdoc", "~> 4.2"
38
- spec.add_development_dependency "simplecov", "~> 0.11.2"
38
+ spec.add_development_dependency 'bundler', '>= 1.10'
39
+ spec.add_development_dependency 'rspec', '~> 3.4'
40
+ spec.add_development_dependency 'rspec-its', '~> 1.2'
41
+ spec.add_development_dependency 'simplecov', '~> 0.11'
39
42
  end
@@ -10,8 +10,8 @@
10
10
 
11
11
  require 'cvss_suite/cvss2/cvss2'
12
12
  require 'cvss_suite/cvss3/cvss3'
13
+ require 'cvss_suite/cvss31/cvss31'
13
14
  require 'cvss_suite/version'
14
- require 'cvss_suite/helpers/extensions'
15
15
  require 'cvss_suite/errors'
16
16
  require 'cvss_suite/invalid_cvss'
17
17
 
@@ -19,19 +19,27 @@ require 'cvss_suite/invalid_cvss'
19
19
  # Module of this gem.
20
20
 
21
21
  module CvssSuite
22
- CVSS_VECTOR_BEGINNINGS = [{:string => 'AV:', :version => 2}, {:string => 'CVSS:3.0/', :version => 3}]
22
+ CVSS_VECTOR_BEGINNINGS = [
23
+ { string: 'AV:', version: 2 },
24
+ { string: '(AV:', version: 2 },
25
+ { string: 'CVSS:3.0/', version: 3.0 },
26
+ { string: 'CVSS:3.1/', version: 3.1 }
27
+ ]
23
28
 
24
29
  ##
25
30
  # Returns a CVSS class by a +vector+.
26
31
 
27
32
  def self.new(vector)
28
33
  return InvalidCvss.new unless vector.is_a? String
34
+
29
35
  @vector_string = vector
30
36
  case version
31
37
  when 2
32
- Cvss2.new(@vector_string, version)
33
- when 3
34
- Cvss3.new(@vector_string, version)
38
+ Cvss2.new(@vector_string)
39
+ when 3.0
40
+ Cvss3.new(@vector_string)
41
+ when 3.1
42
+ Cvss31.new(@vector_string)
35
43
  else
36
44
  InvalidCvss.new
37
45
  end
@@ -46,5 +54,4 @@ module CvssSuite
46
54
  end
47
55
  end
48
56
  end
49
-
50
57
  end
@@ -11,92 +11,122 @@
11
11
  ##
12
12
  # This class represents any CVSS vector. Do not instantiate this class!
13
13
 
14
- class Cvss
14
+ module CvssSuite
15
+ class Cvss
16
+ ##
17
+ # Metric of a CVSS vector.
15
18
 
16
- ##
17
- # Metric of a CVSS vector.
19
+ attr_reader :base, :temporal, :environmental
18
20
 
19
- attr_reader :base, :temporal, :environmental
21
+ ##
22
+ # Returns the vector itself.
20
23
 
21
- ##
22
- # Returns version of current CVSS vector.
24
+ attr_reader :vector
23
25
 
24
- attr_reader :version
26
+ ##
27
+ # Creates a new CVSS vector by a +vector+.
28
+ #
29
+ # Raises an exception if it is called on Cvss class.
25
30
 
26
- ##
27
- # Returns the vector itself.
31
+ def initialize(vector)
32
+ raise CvssSuite::Errors::InvalidParentClass, 'Do not instantiate this class!' if self.class == Cvss
28
33
 
29
- attr_reader :vector
30
-
31
- ##
32
- # Creates a new CVSS vector by a +vector+ and a +version+.
33
- #
34
- # Raises an exception if it is called on Cvss class.
35
-
36
- def initialize(vector, version)
37
- raise CvssSuite::Errors::InvalidParentClass, 'Do not instantiate this class!' if self.class == Cvss
38
- @version = version
39
- @vector = vector
40
- @properties = []
41
- extract_metrics
42
- init_metrics
43
- end
34
+ @vector = vector
35
+ @properties = []
36
+ extract_metrics
37
+ init_metrics
38
+ end
44
39
 
45
- ##
46
- # Returns if CVSS vector is valid.
40
+ ##
41
+ # Returns if CVSS vector is valid.
47
42
 
48
- def valid?
49
- if @amount_of_properties == required_amount_of_properties
43
+ def valid?
44
+ if @amount_of_properties == required_amount_of_properties
50
45
  base = @base.valid?
51
46
  temporal = @base.valid? && @temporal.valid?
52
47
  environmental = @base.valid? && @environmental.valid?
53
48
  full = @base.valid? && @temporal.valid? && @environmental.valid?
54
49
  base || temporal || environmental || full
55
- else
56
- false
50
+ else
51
+ false
52
+ end
57
53
  end
58
- end
59
54
 
60
- ##
61
- # Returns the Overall Score of the CVSS vector.
55
+ ##
56
+ # Returns the severity of the CVSS vector.
57
+
58
+ def severity
59
+ check_validity
60
+
61
+ score = overall_score
62
+
63
+ if score == 0.0
64
+ 'None'
65
+ elsif (0.1..3.9).include? score
66
+ 'Low'
67
+ elsif (4.0..6.9).include? score
68
+ 'Medium'
69
+ elsif (7.0..8.9).include? score
70
+ 'High'
71
+ elsif (9.0..10.0).include? score
72
+ 'Critical'
73
+ else
74
+ 'None'
75
+ end
76
+ end
62
77
 
63
- def overall_score
64
- check_validity
65
- return temporal_score if @temporal.valid? && !@environmental.valid?
66
- return environmental_score if @environmental.valid?
67
- base_score
68
- end
78
+ ##
79
+ # Returns the Overall Score of the CVSS vector.
69
80
 
70
- private
81
+ def overall_score
82
+ check_validity
83
+ return temporal_score if @temporal.valid? && !@environmental.valid?
84
+ return environmental_score if @environmental.valid?
71
85
 
72
- def extract_metrics
73
- properties = prepared_vector.split('/')
74
- @amount_of_properties = properties.size
75
- properties.each_with_index do |property, index|
76
- property = property.split(':')
77
- @properties.push({ name: property[0], selected: property[1], position: index })
86
+ base_score
78
87
  end
79
- end
80
88
 
81
- def check_validity
82
- raise CvssSuite::Errors::InvalidVector, 'Vector is not valid!' unless valid?
83
- end
89
+ private
84
90
 
85
- def prepared_vector
86
- start_of_vector = @vector.index('AV')
91
+ def extract_metrics
92
+ properties = prepared_vector.split('/')
93
+ @amount_of_properties = properties.size
94
+ properties.each_with_index do |property, index|
95
+ property = property.split(':')
96
+ @properties.push({ name: property[0], selected: property[1], position: index })
97
+ end
98
+ end
87
99
 
88
- if start_of_vector.nil?
89
- String.new
90
- else
91
- @vector[start_of_vector..-1]
100
+ def check_validity
101
+ raise CvssSuite::Errors::InvalidVector, 'Vector is not valid!' unless valid?
92
102
  end
93
- end
94
103
 
95
- def required_amount_of_properties
96
- total = @base.count if @base.valid?
97
- total += @temporal.count if @temporal.valid?
98
- total += @environmental.count if @environmental.valid?
99
- total ||= 0
100
- end
104
+ def prepared_vector
105
+ start_of_vector = @vector.index('AV')
106
+
107
+ if start_of_vector.nil?
108
+ ''
109
+ else
110
+ if start_of_vector == 1
111
+ matchArray = @vector.scan(/\((?>[^)(]+|\g<0>)*\)/)
112
+ if matchArray.length == 1 && matchArray[0] == @vector
113
+ @vector.slice!(0)
114
+ @vector.slice!(@vector.length - 1)
115
+ @vector
116
+ else
117
+ ''
118
+ end
119
+ else
120
+ @vector[start_of_vector..-1]
121
+ end
122
+ end
123
+ end
101
124
 
102
- end
125
+ def required_amount_of_properties
126
+ total = @base.count if @base.valid?
127
+ total += @temporal.count if @temporal.valid?
128
+ total += @environmental.count if @environmental.valid?
129
+ total ||= 0
130
+ end
131
+ end
132
+ 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 'cvss2_base'
13
13
  require_relative 'cvss2_temporal'
14
14
  require_relative 'cvss2_environmental'
@@ -16,37 +16,63 @@ require_relative 'cvss2_environmental'
16
16
  ##
17
17
  # This class represents a CVSS vector in version 2.
18
18
 
19
- class Cvss2 < Cvss
19
+ module CvssSuite
20
+ class Cvss2 < 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
+ 2
26
+ end
23
27
 
24
- def base_score
25
- check_validity
26
- @base.score.round(1)
27
- end
28
+ # Returns the severity of the CVSSv2 vector.
29
+ # https://nvd.nist.gov/vuln-metrics/cvss
30
+ def severity
31
+ check_validity
28
32
 
29
- ##
30
- # Returns the Temporal Score of the CVSS vector.
33
+ score = overall_score
31
34
 
32
- def temporal_score
33
- (base_score * @temporal.score).round(1)
34
- end
35
+ if (0.0..3.9).include? score
36
+ 'Low'
37
+ elsif (4.0..6.9).include? score
38
+ 'Medium'
39
+ elsif (7.0..10.0).include? score
40
+ 'High'
41
+ else
42
+ 'None'
43
+ end
44
+ end
35
45
 
36
- ##
37
- # Returns the Environmental Score of the CVSS vector.
46
+ ##
47
+ # Returns the Base Score of the CVSS vector.
38
48
 
39
- def environmental_score
40
- return temporal_score unless @environmental.valid?
41
- (@environmental.score @base, @temporal.score).round(1)
42
- end
49
+ def base_score
50
+ check_validity
51
+ @base.score.round(1)
52
+ end
43
53
 
44
- private
54
+ ##
55
+ # Returns the Temporal Score of the CVSS vector.
45
56
 
46
- def init_metrics
47
- @base = Cvss2Base.new(@properties)
48
- @temporal = Cvss2Temporal.new(@properties)
49
- @environmental = Cvss2Environmental.new(@properties)
50
- end
57
+ def temporal_score
58
+ (base_score * @temporal.score).round(1)
59
+ end
60
+
61
+ ##
62
+ # Returns the Environmental Score of the CVSS vector.
51
63
 
52
- end
64
+ def environmental_score
65
+ return temporal_score unless @environmental.valid?
66
+
67
+ (@environmental.score @base, @temporal.score).round(1)
68
+ end
69
+
70
+ private
71
+
72
+ def init_metrics
73
+ @base = Cvss2Base.new(@properties)
74
+ @temporal = Cvss2Temporal.new(@properties)
75
+ @environmental = Cvss2Environmental.new(@properties)
76
+ end
77
+ end
78
+ end
@@ -14,78 +14,75 @@ require_relative '../cvss_metric'
14
14
  ##
15
15
  # This class represents a CVSS Base metric in version 2.
16
16
 
17
- class Cvss2Base < CvssMetric
18
-
19
- ##
20
- # Property of this metric
21
-
22
- attr_reader :access_vector, :access_complexity, :authentication,
23
- :confidentiality_impact, :integrity_impact, :availability_impact
24
-
25
- ##
26
- # Returns the base score of the CVSS vector. The calculation is based on formula version 2.10 .
27
- # See CVSS documentation for further information https://www.first.org/cvss/v2/guide#i3.2.1 .
28
- #
29
- # Takes +Security+ +Requirement+ +Impacts+ for calculating environmental score.
30
-
31
- def score(sr_cr_score = 1, sr_ir_score = 1, sr_ar_score = 1)
32
-
33
- impact = calc_impact sr_cr_score, sr_ir_score, sr_ar_score
34
-
35
- exploitability = calc_exploitability
36
-
37
- additional_impact = (impact == 0 ? 0 : 1.176)
38
-
39
- ((0.6 * impact) + (0.4 * exploitability) - 1.5) * additional_impact
40
-
17
+ module CvssSuite
18
+ class Cvss2Base < CvssMetric
19
+ ##
20
+ # Property of this metric
21
+
22
+ attr_reader :access_vector, :access_complexity, :authentication,
23
+ :confidentiality_impact, :integrity_impact, :availability_impact
24
+
25
+ ##
26
+ # Returns the base score of the CVSS vector. The calculation is based on formula version 2.10 .
27
+ # See CVSS documentation for further information https://www.first.org/cvss/v2/guide#i3.2.1 .
28
+ #
29
+ # Takes +Security+ +Requirement+ +Impacts+ for calculating environmental score.
30
+
31
+ def score(sr_cr_score = 1, sr_ir_score = 1, sr_ar_score = 1)
32
+ impact = calc_impact(sr_cr_score, sr_ir_score, sr_ar_score)
33
+
34
+ exploitability = calc_exploitability
35
+
36
+ additional_impact = (impact == 0 ? 0 : 1.176)
37
+
38
+ ((0.6 * impact) + (0.4 * exploitability) - 1.5) * additional_impact
39
+ end
40
+
41
+ private
42
+
43
+ def init_properties
44
+ @properties.push(@access_vector =
45
+ CvssProperty.new(name: 'Access Vector', abbreviation: 'AV', position: [0],
46
+ choices: [{ name: 'Network', abbreviation: 'N', weight: 1.0 },
47
+ { name: 'Adjacent Network', abbreviation: 'A', weight: 0.646 },
48
+ { name: 'Local', abbreviation: 'L', weight: 0.395 }]))
49
+ @properties.push(@access_complexity =
50
+ CvssProperty.new(name: 'Access Complexity', abbreviation: 'AC', position: [1],
51
+ choices: [{ name: 'Low', abbreviation: 'L', weight: 0.71 },
52
+ { name: 'Medium', abbreviation: 'M', weight: 0.61 },
53
+ { name: 'High', abbreviation: 'H', weight: 0.35 }]))
54
+ @properties.push(@authentication =
55
+ CvssProperty.new(name: 'Authentication', abbreviation: 'Au', position: [2],
56
+ choices: [{ name: 'None', abbreviation: 'N', weight: 0.704 },
57
+ { name: 'Single', abbreviation: 'S', weight: 0.56 },
58
+ { name: 'Multiple', abbreviation: 'M', weight: 0.45 }]))
59
+ @properties.push(@confidentiality_impact =
60
+ CvssProperty.new(name: 'Confidentiality Impact', abbreviation: 'C', position: [3],
61
+ choices: [{ name: 'None', abbreviation: 'N', weight: 0.0 },
62
+ { name: 'Partial', abbreviation: 'P', weight: 0.275 },
63
+ { name: 'Complete', abbreviation: 'C', weight: 0.66 }]))
64
+ @properties.push(@integrity_impact =
65
+ CvssProperty.new(name: 'Integrity Impact', abbreviation: 'I', position: [4],
66
+ choices: [{ name: 'None', abbreviation: 'N', weight: 0.0 },
67
+ { name: 'Partial', abbreviation: 'P', weight: 0.275 },
68
+ { name: 'Complete', abbreviation: 'C', weight: 0.66 }]))
69
+ @properties.push(@availability_impact =
70
+ CvssProperty.new(name: 'Availability Impact', abbreviation: 'A', position: [5],
71
+ choices: [{ name: 'None', abbreviation: 'N', weight: 0.0 },
72
+ { name: 'Partial', abbreviation: 'P', weight: 0.275 },
73
+ { name: 'Complete', abbreviation: 'C', weight: 0.66 }]))
74
+ end
75
+
76
+ def calc_impact(sr_cr_score, sr_ir_score, sr_ar_score)
77
+ confidentiality_score = 1 - @confidentiality_impact.score * sr_cr_score
78
+ integrity_score = 1 - @integrity_impact.score * sr_ir_score
79
+ availability_score = 1 - @availability_impact.score * sr_ar_score
80
+
81
+ [10, 10.41 * (1 - confidentiality_score * integrity_score * availability_score)].min
82
+ end
83
+
84
+ def calc_exploitability
85
+ 20 * @access_vector.score * @access_complexity.score * @authentication.score
86
+ end
41
87
  end
42
-
43
- private
44
-
45
- def init_properties
46
- @properties.push(@access_vector =
47
- CvssProperty.new(name: 'Access Vector', abbreviation: 'AV', position: [0],
48
- choices: [{ name: 'Network', abbreviation: 'N', weight: 1.0 },
49
- { name: 'Adjacent Network', abbreviation: 'A', weight: 0.646 },
50
- { name: 'Local', abbreviation: 'L', weight: 0.395 }]))
51
- @properties.push(@access_complexity =
52
- CvssProperty.new(name: 'Access Complexity', abbreviation: 'AC', position: [1],
53
- choices: [{ name: 'Low', abbreviation: 'L', weight: 0.71 },
54
- { name: 'Medium', abbreviation: 'M', weight: 0.61 },
55
- { name: 'High', abbreviation: 'H', weight: 0.35 }]))
56
- @properties.push(@authentication =
57
- CvssProperty.new(name: 'Authentication', abbreviation: 'Au', position: [2],
58
- choices: [{ name: 'None', abbreviation: 'N', weight: 0.704 },
59
- { name: 'Single', abbreviation: 'S', weight: 0.56 },
60
- { name: 'Multiple', abbreviation: 'M', weight: 0.45 }]))
61
- @properties.push(@confidentiality_impact =
62
- CvssProperty.new(name: 'Confidentiality Impact', abbreviation: 'C', position: [3],
63
- choices: [{ name: 'None', abbreviation: 'N', weight: 0.0 },
64
- { name: 'Partial', abbreviation: 'P', weight: 0.275 },
65
- { name: 'Complete', abbreviation: 'C', weight: 0.66 }]))
66
- @properties.push(@integrity_impact =
67
- CvssProperty.new(name: 'Integrity Impact', abbreviation: 'I', position: [4],
68
- choices: [{ name: 'None', abbreviation: 'N', weight: 0.0 },
69
- { name: 'Partial', abbreviation: 'P', weight: 0.275 },
70
- { name: 'Complete', abbreviation: 'C', weight: 0.66 }]))
71
- @properties.push(@availability_impact =
72
- CvssProperty.new(name: 'Availability Impact', abbreviation: 'A', position: [5],
73
- choices: [{ name: 'None', abbreviation: 'N', weight: 0.0},
74
- { name: 'Partial', abbreviation: 'P', weight: 0.275},
75
- { name: 'Complete', abbreviation: 'C', weight: 0.66}]))
76
- end
77
-
78
- def calc_impact(sr_cr_score, sr_ir_score, sr_ar_score)
79
- confidentiality_score = 1 - @confidentiality_impact.score * sr_cr_score
80
- integrity_score = 1 - @integrity_impact.score * sr_ir_score
81
- availability_score = 1 - @availability_impact.score * sr_ar_score
82
-
83
- [10, 10.41 * (1-confidentiality_score*integrity_score*availability_score)].min
84
- end
85
-
86
- def calc_exploitability
87
- 20 * @access_vector.score * @access_complexity.score * @authentication.score
88
- end
89
-
90
88
  end
91
-