cvss-suite 1.1.1 → 2.0.1

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 (38) 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/.github/workflows/rubocop.yml +21 -0
  7. data/.gitignore +1 -0
  8. data/.rubocop.yml +45 -1
  9. data/.rubocop_todo.yml +59 -0
  10. data/CHANGES.md +61 -1
  11. data/PULL_REQUEST_TEMPLATE.md +24 -0
  12. data/README.md +43 -16
  13. data/_config.yml +1 -0
  14. data/bin/console +3 -3
  15. data/cvss_suite.gemspec +14 -13
  16. data/lib/cvss_suite.rb +13 -11
  17. data/lib/cvss_suite/cvss.rb +85 -73
  18. data/lib/cvss_suite/cvss2/cvss2.rb +39 -36
  19. data/lib/cvss_suite/cvss2/cvss2_base.rb +69 -75
  20. data/lib/cvss_suite/cvss2/cvss2_environmental.rb +52 -54
  21. data/lib/cvss_suite/cvss2/cvss2_temporal.rb +40 -41
  22. data/lib/cvss_suite/cvss3/cvss3.rb +39 -36
  23. data/lib/cvss_suite/cvss3/cvss3_base.rb +72 -75
  24. data/lib/cvss_suite/cvss3/cvss3_environmental.rb +159 -109
  25. data/lib/cvss_suite/cvss3/cvss3_temporal.rb +41 -42
  26. data/lib/cvss_suite/cvss31/cvss31.rb +60 -0
  27. data/lib/cvss_suite/cvss31/cvss31_base.rb +93 -0
  28. data/lib/cvss_suite/cvss31/cvss31_environmental.rb +194 -0
  29. data/lib/cvss_suite/cvss31/cvss31_temporal.rb +56 -0
  30. data/lib/cvss_suite/cvss_metric.rb +31 -35
  31. data/lib/cvss_suite/cvss_property.rb +57 -56
  32. data/lib/cvss_suite/helpers/cvss31_helper.rb +27 -0
  33. data/lib/cvss_suite/helpers/cvss3_helper.rb +21 -15
  34. data/lib/cvss_suite/invalid_cvss.rb +37 -45
  35. data/lib/cvss_suite/version.rb +2 -2
  36. metadata +21 -25
  37. data/.travis.yml +0 -4
  38. 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,23 @@ 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']
22
23
 
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/"
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/'
27
28
 
29
+ spec.required_ruby_version = '>= 2.4.0'
28
30
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
29
31
  spec.bindir = 'exe'
30
32
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
33
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
32
- spec.require_paths = ["lib"]
34
+ spec.require_paths = ['lib']
33
35
 
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"
36
+ spec.add_development_dependency 'bundler', '>= 1.10'
37
+ spec.add_development_dependency 'rspec', '~> 3.4'
38
+ spec.add_development_dependency 'rspec-its', '~> 1.2'
39
+ spec.add_development_dependency 'simplecov', '~> 0.18'
39
40
  end
@@ -10,28 +10,33 @@
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
 
18
18
  ##
19
19
  # Module of this gem.
20
-
21
20
  module CvssSuite
22
- CVSS_VECTOR_BEGINNINGS = [{:string => 'AV:', :version => 2}, {:string => 'CVSS:3.0/', :version => 3}]
21
+ CVSS_VECTOR_BEGINNINGS = [
22
+ { string: 'AV:', version: 2 },
23
+ { string: 'CVSS:3.0/', version: 3.0 },
24
+ { string: 'CVSS:3.1/', version: 3.1 }
25
+ ].freeze
23
26
 
24
27
  ##
25
28
  # Returns a CVSS class by a +vector+.
26
-
27
29
  def self.new(vector)
28
30
  return InvalidCvss.new unless vector.is_a? String
31
+
29
32
  @vector_string = vector
30
33
  case version
31
34
  when 2
32
- Cvss2.new(@vector_string, version)
33
- when 3
34
- Cvss3.new(@vector_string, version)
35
+ Cvss2.new(@vector_string)
36
+ when 3.0
37
+ Cvss3.new(@vector_string)
38
+ when 3.1
39
+ Cvss31.new(@vector_string)
35
40
  else
36
41
  InvalidCvss.new
37
42
  end
@@ -41,10 +46,7 @@ module CvssSuite
41
46
 
42
47
  def self.version
43
48
  CVSS_VECTOR_BEGINNINGS.each do |beginning|
44
- if @vector_string.start_with? beginning[:string]
45
- return beginning[:version]
46
- end
49
+ return beginning[:version] if @vector_string.start_with? beginning[:string]
47
50
  end
48
51
  end
49
-
50
52
  end
@@ -8,95 +8,107 @@
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
- ##
12
- # This class represents any CVSS vector. Do not instantiate this class!
13
-
14
- class Cvss
15
-
16
- ##
17
- # Metric of a CVSS vector.
18
-
19
- attr_reader :base, :temporal, :environmental
20
-
21
- ##
22
- # Returns version of current CVSS vector.
23
-
24
- attr_reader :version
25
-
26
- ##
27
- # Returns the vector itself.
28
-
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
44
-
11
+ module CvssSuite
45
12
  ##
46
- # Returns if CVSS vector is valid.
13
+ # This class represents any CVSS vector. Do not instantiate this class!
14
+ class Cvss
15
+ ##
16
+ # Metric of a CVSS vector.
17
+ attr_reader :base, :temporal, :environmental
18
+
19
+ ##
20
+ # Returns the vector itself.
21
+ attr_reader :vector
22
+
23
+ ##
24
+ # Creates a new CVSS vector by a +vector+.
25
+ #
26
+ # Raises an exception if it is called on Cvss class.
27
+ def initialize(vector)
28
+ raise CvssSuite::Errors::InvalidParentClass, 'Do not instantiate this class!' if self.class == Cvss
29
+
30
+ @vector = vector
31
+ @properties = []
32
+ extract_metrics
33
+ init_metrics
34
+ end
47
35
 
48
- def valid?
49
- if @amount_of_properties == required_amount_of_properties
36
+ ##
37
+ # Returns if CVSS vector is valid.
38
+ def valid?
39
+ if @amount_of_properties == required_amount_of_properties
50
40
  base = @base.valid?
51
41
  temporal = @base.valid? && @temporal.valid?
52
42
  environmental = @base.valid? && @environmental.valid?
53
43
  full = @base.valid? && @temporal.valid? && @environmental.valid?
54
44
  base || temporal || environmental || full
55
- else
56
- false
45
+ else
46
+ false
47
+ end
57
48
  end
58
- end
59
49
 
60
- ##
61
- # Returns the Overall Score of the CVSS vector.
50
+ ##
51
+ # Returns the severity of the CVSS vector.
52
+ def severity
53
+ check_validity
54
+
55
+ score = overall_score
56
+
57
+ if score == 0.0
58
+ 'None'
59
+ elsif (0.1..3.9).cover? score
60
+ 'Low'
61
+ elsif (4.0..6.9).cover? score
62
+ 'Medium'
63
+ elsif (7.0..8.9).cover? score
64
+ 'High'
65
+ elsif (9.0..10.0).cover? score
66
+ 'Critical'
67
+ else
68
+ 'None'
69
+ end
70
+ end
62
71
 
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
72
+ ##
73
+ # Returns the Overall Score of the CVSS vector.
74
+ def overall_score
75
+ check_validity
76
+ return temporal_score if @temporal.valid? && !@environmental.valid?
77
+ return environmental_score if @environmental.valid?
78
+
79
+ base_score
80
+ end
69
81
 
70
- private
82
+ private
71
83
 
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 })
84
+ def extract_metrics
85
+ properties = prepared_vector.split('/')
86
+ @amount_of_properties = properties.size
87
+ properties.each_with_index do |property, index|
88
+ property = property.split(':')
89
+ @properties.push({ name: property[0], selected: property[1], position: index })
90
+ end
78
91
  end
79
- end
80
92
 
81
- def check_validity
82
- raise CvssSuite::Errors::InvalidVector, 'Vector is not valid!' unless valid?
83
- end
93
+ def check_validity
94
+ raise CvssSuite::Errors::InvalidVector, 'Vector is not valid!' unless valid?
95
+ end
84
96
 
85
- def prepared_vector
86
- start_of_vector = @vector.index('AV')
97
+ def prepared_vector
98
+ start_of_vector = @vector.index('AV')
87
99
 
88
- if start_of_vector.nil?
89
- String.new
90
- else
91
- @vector[start_of_vector..-1]
100
+ if start_of_vector.nil?
101
+ ''
102
+ else
103
+ @vector[start_of_vector..-1]
104
+ end
92
105
  end
93
- end
94
106
 
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
107
+ def required_amount_of_properties
108
+ total = @base.count if @base.valid?
109
+ total += @temporal.count if @temporal.valid?
110
+ total += @environmental.count if @environmental.valid?
111
+ total || 0
112
+ end
100
113
  end
101
-
102
- end
114
+ end
@@ -8,45 +8,48 @@
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'
15
15
 
16
- ##
17
- # This class represents a CVSS vector in version 2.
18
-
19
- class Cvss2 < Cvss
20
-
21
- ##
22
- # Returns the Base Score of the CVSS vector.
23
-
24
- def base_score
25
- check_validity
26
- @base.score.round(1)
27
- end
28
-
16
+ module CvssSuite
29
17
  ##
30
- # Returns the Temporal Score of the CVSS vector.
31
-
32
- def temporal_score
33
- (base_score * @temporal.score).round(1)
18
+ # This class represents a CVSS vector in version 2.
19
+ class Cvss2 < Cvss
20
+ ##
21
+ # Returns the Version of the CVSS vector.
22
+ def version
23
+ 2
24
+ end
25
+
26
+ ##
27
+ # Returns the Base Score of the CVSS vector.
28
+ def base_score
29
+ check_validity
30
+ @base.score.round(1)
31
+ end
32
+
33
+ ##
34
+ # Returns the Temporal Score of the CVSS vector.
35
+ def temporal_score
36
+ (base_score * @temporal.score).round(1)
37
+ end
38
+
39
+ ##
40
+ # Returns the Environmental Score of the CVSS vector.
41
+ def environmental_score
42
+ return temporal_score unless @environmental.valid?
43
+
44
+ (@environmental.score @base, @temporal.score).round(1)
45
+ end
46
+
47
+ private
48
+
49
+ def init_metrics
50
+ @base = Cvss2Base.new(@properties)
51
+ @temporal = Cvss2Temporal.new(@properties)
52
+ @environmental = Cvss2Environmental.new(@properties)
53
+ end
34
54
  end
35
-
36
- ##
37
- # Returns the Environmental Score of the CVSS vector.
38
-
39
- def environmental_score
40
- return temporal_score unless @environmental.valid?
41
- (@environmental.score @base, @temporal.score).round(1)
42
- end
43
-
44
- private
45
-
46
- def init_metrics
47
- @base = Cvss2Base.new(@properties)
48
- @temporal = Cvss2Temporal.new(@properties)
49
- @environmental = Cvss2Environmental.new(@properties)
50
- end
51
-
52
- end
55
+ end
@@ -11,81 +11,75 @@
11
11
  require_relative '../cvss_property'
12
12
  require_relative '../cvss_metric'
13
13
 
14
- ##
15
- # This class represents a CVSS Base metric in version 2.
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
-
14
+ module CvssSuite
25
15
  ##
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
-
16
+ # This class represents a CVSS Base metric in version 2.
17
+ class Cvss2Base < CvssMetric
18
+ ##
19
+ # Property of this metric
20
+ attr_reader :access_vector, :access_complexity, :authentication,
21
+ :confidentiality_impact, :integrity_impact, :availability_impact
22
+
23
+ ##
24
+ # Returns the base score of the CVSS vector. The calculation is based on formula version 2.10 .
25
+ # See CVSS documentation for further information https://www.first.org/cvss/v2/guide#i3.2.1 .
26
+ #
27
+ # Takes +Security+ +Requirement+ +Impacts+ for calculating environmental score.
28
+ def score(sr_cr_score = 1, sr_ir_score = 1, sr_ar_score = 1)
29
+ impact = calc_impact(sr_cr_score, sr_ir_score, sr_ar_score)
30
+
31
+ exploitability = calc_exploitability
32
+
33
+ additional_impact = (impact.zero? ? 0 : 1.176)
34
+
35
+ ((0.6 * impact) + (0.4 * exploitability) - 1.5) * additional_impact
36
+ end
37
+
38
+ private
39
+
40
+ def init_properties
41
+ @properties.push(@access_vector =
42
+ CvssProperty.new(name: 'Access Vector', abbreviation: 'AV', position: [0],
43
+ values: [{ name: 'Network', abbreviation: 'N', weight: 1.0 },
44
+ { name: 'Adjacent Network', abbreviation: 'A', weight: 0.646 },
45
+ { name: 'Local', abbreviation: 'L', weight: 0.395 }]))
46
+ @properties.push(@access_complexity =
47
+ CvssProperty.new(name: 'Access Complexity', abbreviation: 'AC', position: [1],
48
+ values: [{ name: 'Low', abbreviation: 'L', weight: 0.71 },
49
+ { name: 'Medium', abbreviation: 'M', weight: 0.61 },
50
+ { name: 'High', abbreviation: 'H', weight: 0.35 }]))
51
+ @properties.push(@authentication =
52
+ CvssProperty.new(name: 'Authentication', abbreviation: 'Au', position: [2],
53
+ values: [{ name: 'None', abbreviation: 'N', weight: 0.704 },
54
+ { name: 'Single', abbreviation: 'S', weight: 0.56 },
55
+ { name: 'Multiple', abbreviation: 'M', weight: 0.45 }]))
56
+ @properties.push(@confidentiality_impact =
57
+ CvssProperty.new(name: 'Confidentiality Impact', abbreviation: 'C', position: [3],
58
+ values: [{ name: 'None', abbreviation: 'N', weight: 0.0 },
59
+ { name: 'Partial', abbreviation: 'P', weight: 0.275 },
60
+ { name: 'Complete', abbreviation: 'C', weight: 0.66 }]))
61
+ @properties.push(@integrity_impact =
62
+ CvssProperty.new(name: 'Integrity Impact', abbreviation: 'I', position: [4],
63
+ values: [{ 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(@availability_impact =
67
+ CvssProperty.new(name: 'Availability Impact', abbreviation: 'A', position: [5],
68
+ values: [{ name: 'None', abbreviation: 'N', weight: 0.0 },
69
+ { name: 'Partial', abbreviation: 'P', weight: 0.275 },
70
+ { name: 'Complete', abbreviation: 'C', weight: 0.66 }]))
71
+ end
72
+
73
+ def calc_impact(sr_cr_score, sr_ir_score, sr_ar_score)
74
+ confidentiality_score = 1 - @confidentiality_impact.score * sr_cr_score
75
+ integrity_score = 1 - @integrity_impact.score * sr_ir_score
76
+ availability_score = 1 - @availability_impact.score * sr_ar_score
77
+
78
+ [10, 10.41 * (1 - confidentiality_score * integrity_score * availability_score)].min
79
+ end
80
+
81
+ def calc_exploitability
82
+ 20 * @access_vector.score * @access_complexity.score * @authentication.score
83
+ end
41
84
  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
85
  end
91
-