cvss-suite 1.2.0 → 3.1.0

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.
@@ -1,9 +1,10 @@
1
1
  # CVSS-Suite, a Ruby gem to manage the CVSS vector
2
2
  #
3
- # Copyright (c) Siemens AG, 2019
3
+ # Copyright (c) 2019-2022 Siemens AG
4
+ # Copyright (c) 2022 0llirocks
4
5
  #
5
6
  # Authors:
6
- # Oliver Hambörger <oliver.hamboerger@siemens.com>
7
+ # 0llirocks <http://0lli.rocks>
7
8
  #
8
9
  # This work is licensed under the terms of the MIT license.
9
10
  # See the LICENSE.md file in the top-level directory.
@@ -11,47 +12,46 @@
11
12
  require_relative '../cvss_property'
12
13
  require_relative '../cvss_metric'
13
14
 
14
- ##
15
- # This class represents a CVSS Temporal metric in version 3.1.
16
-
17
- class Cvss31Temporal < CvssMetric
18
-
19
- ##
20
- # Property of this metric
21
-
22
- attr_reader :exploit_code_maturity, :remediation_level, :report_confidence
23
-
15
+ module CvssSuite
24
16
  ##
25
- # Returns score of this metric
26
-
27
- def score
28
- return 1.0 unless valid?
29
- @exploit_code_maturity.score * @remediation_level.score * @report_confidence.score
30
- end
31
-
32
- private
33
-
34
- def init_properties
35
- @properties.push(@exploit_code_maturity =
36
- CvssProperty.new(name: 'Exploit Code Maturity', abbreviation: 'E', position: [8],
37
- choices: [{ name: 'Not Defined', abbreviation: 'X', weight: 1.0 },
38
- { name: 'Unproven', abbreviation: 'U', weight: 0.91 },
39
- { name: 'Proof-of-Concept', abbreviation: 'P', weight: 0.94 },
40
- { name: 'Functional', abbreviation: 'F', weight: 0.97 },
41
- { name: 'High', abbreviation: 'H', weight: 1.0 }]))
42
- @properties.push(@remediation_level =
43
- CvssProperty.new(name: 'Remediation Level', abbreviation: 'RL', position: [9],
44
- choices: [{ name: 'Not Defined', abbreviation: 'X', weight: 1.0 },
45
- { name: 'Official Fix', abbreviation: 'O', weight: 0.95 },
46
- { name: 'Temporary Fix', abbreviation: 'T', weight: 0.96 },
47
- { name: 'Workaround', abbreviation: 'W', weight: 0.97 },
48
- { name: 'Unavailable', abbreviation: 'U', weight: 1.0 }]))
49
-
50
- @properties.push(@report_confidence =
51
- CvssProperty.new(name: 'Report Confidence', abbreviation: 'RC', position: [10],
52
- choices: [{ name: 'Not Defined', abbreviation: 'X', weight: 1.0 },
53
- { name: 'Unknown', abbreviation: 'U', weight: 0.92 },
54
- { name: 'Reasonable', abbreviation: 'R', weight: 0.96 },
55
- { name: 'Confirmed', abbreviation: 'C', weight: 1.0 }]))
17
+ # This class represents a CVSS Temporal metric in version 3.1.
18
+ class Cvss31Temporal < CvssMetric
19
+ ##
20
+ # Property of this metric
21
+ attr_reader :exploit_code_maturity, :remediation_level, :report_confidence
22
+
23
+ ##
24
+ # Returns score of this metric
25
+ def score
26
+ return 1.0 unless valid?
27
+
28
+ @exploit_code_maturity.score * @remediation_level.score * @report_confidence.score
29
+ end
30
+
31
+ private
32
+
33
+ def init_properties
34
+ @properties.push(@exploit_code_maturity =
35
+ CvssProperty.new(name: 'Exploit Code Maturity', abbreviation: 'E',
36
+ values: [{ name: 'Not Defined', abbreviation: 'X', weight: 1.0 },
37
+ { name: 'Unproven', abbreviation: 'U', weight: 0.91 },
38
+ { name: 'Proof-of-Concept', abbreviation: 'P', weight: 0.94 },
39
+ { name: 'Functional', abbreviation: 'F', weight: 0.97 },
40
+ { name: 'High', abbreviation: 'H', weight: 1.0 }]))
41
+ @properties.push(@remediation_level =
42
+ CvssProperty.new(name: 'Remediation Level', abbreviation: 'RL',
43
+ values: [{ name: 'Not Defined', abbreviation: 'X', weight: 1.0 },
44
+ { name: 'Official Fix', abbreviation: 'O', weight: 0.95 },
45
+ { name: 'Temporary Fix', abbreviation: 'T', weight: 0.96 },
46
+ { name: 'Workaround', abbreviation: 'W', weight: 0.97 },
47
+ { name: 'Unavailable', abbreviation: 'U', weight: 1.0 }]))
48
+
49
+ @properties.push(@report_confidence =
50
+ CvssProperty.new(name: 'Report Confidence', abbreviation: 'RC',
51
+ values: [{ name: 'Not Defined', abbreviation: 'X', weight: 1.0 },
52
+ { name: 'Unknown', abbreviation: 'U', weight: 0.92 },
53
+ { name: 'Reasonable', abbreviation: 'R', weight: 0.96 },
54
+ { name: 'Confirmed', abbreviation: 'C', weight: 1.0 }]))
55
+ end
56
56
  end
57
- end
57
+ end
@@ -1,53 +1,52 @@
1
1
  # CVSS-Suite, a Ruby gem to manage the CVSS vector
2
2
  #
3
- # Copyright (c) Siemens AG, 2016
3
+ # Copyright (c) 2016-2022 Siemens AG
4
+ # Copyright (c) 2022 0llirocks
4
5
  #
5
6
  # Authors:
6
- # Oliver Hambörger <oliver.hamboerger@siemens.com>
7
+ # 0llirocks <http://0lli.rocks>
7
8
  #
8
9
  # This work is licensed under the terms of the MIT license.
9
10
  # See the LICENSE.md file in the top-level directory.
10
11
 
11
- ##
12
- # This class represents any CVSS metric.
13
-
14
- class CvssMetric
15
-
12
+ module CvssSuite
16
13
  ##
17
- # Creates a new CVSS metric by +properties+
18
-
19
- def initialize(selected_properties)
20
- @properties = []
21
- init_properties
22
- extract_selected_choices_from selected_properties
23
- end
24
-
25
- ##
26
- # Returns if the metric is valid.
27
-
28
- def valid?
29
- @properties.each do |property|
30
- return false unless property.valid?
14
+ # This class represents any CVSS metric.
15
+ class CvssMetric
16
+ ##
17
+ # Creates a new CVSS metric by +properties+
18
+ def initialize(selected_properties)
19
+ @properties = []
20
+ init_properties
21
+ extract_selected_values_from selected_properties
31
22
  end
32
- true
33
- end
34
-
35
- ##
36
- # Returns number of properties for this metric.
37
23
 
38
- def count
39
- @properties.count
40
- end
24
+ ##
25
+ # Returns if the metric is valid.
26
+ def valid?
27
+ @properties.each do |property|
28
+ return false unless property.valid?
29
+ end
30
+ true
31
+ end
41
32
 
42
- private
33
+ ##
34
+ # Returns number of properties for this metric.
35
+ def count
36
+ @properties.count
37
+ end
43
38
 
44
- def extract_selected_choices_from(selected_properties)
45
- selected_properties.each do |selected_property|
46
- property = @properties.detect {
47
- |p| p.abbreviation == selected_property[:name] && p.position.include?(selected_property[:position])
48
- }
49
- property.set_selected_choice selected_property[:selected] unless property.nil?
39
+ private
40
+
41
+ def extract_selected_values_from(selected_properties)
42
+ selected_properties.each do |selected_property|
43
+ property = @properties.detect do |p|
44
+ p.abbreviation == selected_property[:name] &&
45
+ (p.position&.include?(selected_property[:position]) || p.position.nil?)
46
+ end
47
+ property&.set_selected_value selected_property[:selected]
48
+ end
49
+ @properties.reject(&:valid?).each(&:set_default_value)
50
50
  end
51
51
  end
52
-
53
- end
52
+ end
@@ -1,85 +1,97 @@
1
1
  # CVSS-Suite, a Ruby gem to manage the CVSS vector
2
2
  #
3
- # Copyright (c) Siemens AG, 2016
3
+ # Copyright (c) 2016-2022 Siemens AG
4
+ # Copyright (c) 2022 0llirocks
4
5
  #
5
6
  # Authors:
6
- # Oliver Hambörger <oliver.hamboerger@siemens.com>
7
+ # 0llirocks <http://0lli.rocks>
7
8
  #
8
9
  # This work is licensed under the terms of the MIT license.
9
10
  # See the LICENSE.md file in the top-level directory.
10
11
 
11
- ##
12
- # This class represents a CVSS property of a CVSS metric.
12
+ module CvssSuite
13
+ ##
14
+ # This class represents a CVSS property of a CVSS metric.
15
+ class CvssProperty
16
+ ##
17
+ # Creates a new CVSS property by a +property+.
18
+ #
19
+ # +Property+ needs to consist of a name, a abbreviation,
20
+ # the possible positions in the CVSS vector, a weight, and the
21
+ # available values for the property.
22
+
23
+ def initialize(property)
24
+ @property = property
25
+ @property[:default_value] ||= 'Not Defined'
26
+ end
13
27
 
14
- class CvssProperty
28
+ ##
29
+ # Returns the full name of the property.
15
30
 
16
- ##
17
- # Creates a new CVSS property by a +property+.
18
- #
19
- # +Property+ needs to consist of a name, a abbreviation, the possible positions in the CVSS vector, a weight, and the
20
- # available choices for the property.
21
-
22
- def initialize(property)
23
- @property = property
24
- @property[:default_choice] ||= 'Not Available'
25
- end
31
+ def name
32
+ @property[:name]
33
+ end
26
34
 
27
- ##
28
- # Returns the full name of the property.
35
+ ##
36
+ # Returns the abbreviation of the property.
29
37
 
30
- def name
31
- @property[:name]
32
- end
38
+ def abbreviation
39
+ @property[:abbreviation]
40
+ end
33
41
 
34
- ##
35
- # Returns the abbreviation of the property.
42
+ ##
43
+ # Returns all available values of the property.
36
44
 
37
- def abbreviation
38
- @property[:abbreviation]
39
- end
45
+ def values
46
+ @property[:values]
47
+ end
40
48
 
41
- ##
42
- # Returns all available choices of the property.
49
+ ##
50
+ # Returns the possible positions in the CVSS vector of the property.
43
51
 
44
- def choices
45
- @property[:choices]
46
- end
52
+ def position
53
+ @property[:position]
54
+ end
47
55
 
48
- ##
49
- # Returns the possible positions in the CVSS vector of the property.
56
+ ##
57
+ # Returns the selected value of the property.
50
58
 
51
- def position
52
- @property[:position]
53
- end
59
+ def selected_value
60
+ @selected_value || @property[:default_value]
61
+ end
54
62
 
55
- ##
56
- # Returns the selected choice of the property.
63
+ ##
64
+ # Returns true if the property is valid.
57
65
 
58
- def selected_choice
59
- @selected_choice || @property[:default_choice]
60
- end
66
+ def valid?
67
+ !@selected_value.nil?
68
+ end
61
69
 
62
- ##
63
- # Returns true if the property is valid.
70
+ ##
71
+ # Returns the score of the selected value.
64
72
 
65
- def valid?
66
- !@selected_choice.nil?
67
- end
73
+ def score
74
+ @selected_value[:weight]
75
+ end
68
76
 
69
- ##
70
- # Returns the score of the selected choice.
77
+ ##
78
+ # Sets the selected value by a +value+.
71
79
 
72
- def score
73
- @selected_choice[:weight]
74
- end
80
+ def set_selected_value(selected_value)
81
+ values.each do |value|
82
+ value[:selected] = selected_value.eql?(value[:abbreviation])
83
+ end
84
+ @selected_value = values.detect { |value| value[:selected] }
85
+ end
75
86
 
76
- ##
77
- # Sets the selected choice by a +choice+.
87
+ ##
88
+ # Sets the default value.
78
89
 
79
- def set_selected_choice(selected_choice)
80
- choices.each do |choice|
81
- choice[:selected] = selected_choice.eql?(choice[:abbreviation])
90
+ def set_default_value
91
+ values.each do |value|
92
+ value[:selected] = value[:abbreviation].eql?('X')
93
+ end
94
+ @selected_value = values.detect { |value| value[:selected] }
82
95
  end
83
- @selected_choice = choices.detect { |choice| choice[:selected] }
84
96
  end
85
- end
97
+ end
@@ -1,6 +1,7 @@
1
1
  # CVSS-Suite, a Ruby gem to manage the CVSS vector
2
2
  #
3
- # Copyright (c) Siemens AG, 2016
3
+ # Copyright (c) 2016-2022 Siemens AG
4
+ # Copyright (c) 2022 0llirocks
4
5
  #
5
6
  # Authors:
6
7
  # Adam David <adamrdavid@gmail.com>
@@ -19,10 +20,12 @@ module CvssSuite
19
20
 
20
21
  def initialize(message)
21
22
  @message = message
23
+ super
22
24
  end
23
25
  end
24
26
 
25
27
  class InvalidVector < RuntimeError; end
28
+
26
29
  class InvalidParentClass < ArgumentError; end
27
30
  end
28
31
  end
@@ -0,0 +1,28 @@
1
+ # CVSS-Suite, a Ruby gem to manage the CVSS vector
2
+ #
3
+ # Copyright (c) 2016-2022 Siemens AG
4
+ # Copyright (c) 2022 0llirocks
5
+ #
6
+ # Authors:
7
+ # 0llirocks <http://0lli.rocks>
8
+ #
9
+ # This work is licensed under the terms of the MIT license.
10
+ # See the LICENSE.md file in the top-level directory.
11
+
12
+ module CvssSuite
13
+ ##
14
+ # This module includes methods which are used by the CVSS 3 classes.
15
+ module Cvss31Helper
16
+ ##
17
+ # Since CVSS 3 all float values are rounded up, therefore this method is used
18
+ # instead of the mathematically correct method round().
19
+ def self.round_up(float)
20
+ output = (float * 100_000).round
21
+ if (output % 10_000).zero?
22
+ output / 100_000.0
23
+ else
24
+ ((output / 10_000).floor + 1) / 10.0
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,29 +1,36 @@
1
1
  # CVSS-Suite, a Ruby gem to manage the CVSS vector
2
2
  #
3
- # Copyright (c) Siemens AG, 2016
3
+ # Copyright (c) 2016-2022 Siemens AG
4
+ # Copyright (c) 2022 0llirocks
4
5
  #
5
6
  # Authors:
6
- # Oliver Hambörger <oliver.hamboerger@siemens.com>
7
+ # 0llirocks <http://0lli.rocks>
7
8
  #
8
9
  # This work is licensed under the terms of the MIT license.
9
10
  # See the LICENSE.md file in the top-level directory.
10
11
 
11
- ##
12
- # This module includes methods which are used by the CVSS 3 classes.
13
-
14
- module Cvss3Helper
15
-
12
+ module CvssSuite
16
13
  ##
17
- # Since CVSS 3 the Privilege Required score depends on the selected choice of the Scope metric.
18
- # This method takes a +Privilege+ +Required+ and a +Scope+ metric and returns the newly calculated score.
14
+ # This module includes methods which are used by the CVSS 3 classes.
15
+ module Cvss3Helper
16
+ ##
17
+ # Since CVSS 3 all float values are rounded up, therefore this method is used
18
+ # instead of the mathematically correct method round().
19
+ def self.round_up(float)
20
+ float.ceil(1).to_f
21
+ end
19
22
 
20
- def self.privileges_required_score(privileges_required, scope)
21
- changed = scope.selected_choice[:name] == 'Changed'
22
- privilege_score = privileges_required.score
23
- if changed
24
- privilege_score = 0.68 if privileges_required.selected_choice[:name] == 'Low'
25
- privilege_score = 0.50 if privileges_required.selected_choice[:name] == 'High'
23
+ ##
24
+ # Since CVSS 3 the Privilege Required score depends on the selected value of the Scope metric.
25
+ # This method takes a +Privilege+ +Required+ and a +Scope+ metric and returns the newly calculated score.
26
+ def self.privileges_required_score(privileges_required, scope)
27
+ changed = scope.selected_value[:name] == 'Changed'
28
+ privilege_score = privileges_required.score
29
+ if changed
30
+ privilege_score = 0.68 if privileges_required.selected_value[:name] == 'Low'
31
+ privilege_score = 0.50 if privileges_required.selected_value[:name] == 'High'
32
+ end
33
+ privilege_score
26
34
  end
27
- privilege_score
28
35
  end
29
- end
36
+ end
@@ -1,57 +1,52 @@
1
1
  # CVSS-Suite, a Ruby gem to manage the CVSS vector
2
2
  #
3
- # Copyright (c) Siemens AG, 2018
3
+ # Copyright (c) 2018-2022 Siemens AG
4
+ # Copyright (c) 2022 0llirocks
4
5
  #
5
6
  # Authors:
6
- # Oliver Hambörger <oliver.hamboerger@siemens.com>
7
+ # 0llirocks <http://0lli.rocks>
7
8
  #
8
9
  # This work is licensed under the terms of the MIT license.
9
10
  # See the LICENSE.md file in the top-level directory.
10
11
 
11
- # ##
12
- # # This class represents a invalid CVSS vector.
13
-
14
- class InvalidCvss < Cvss
15
-
16
- ##
17
- # Creates a new invalid CVSS vector.
18
-
19
- def initialize
20
- end
21
-
22
- ##
23
- # Since this is an invalid CVSS vector, it always returns false.
24
-
25
- def valid?
26
- false
27
- end
28
-
29
- ##
30
- # Since this is an invalid CVSS vector, it always throws an exception.
31
-
32
- def version
33
- check_validity
34
- end
35
-
36
- ##
37
- # Since this is an invalid CVSS vector, it always throws an exception.
38
-
39
- def base_score
40
- check_validity
41
- end
42
-
12
+ module CvssSuite
43
13
  ##
44
- # Since this is an invalid CVSS vector, it always throws an exception.
45
-
46
- def temporal_score
47
- check_validity
48
- end
49
-
50
- ##
51
- # Since this is an invalid CVSS vector, it always throws an exception.
52
-
53
- def environmental_score
54
- check_validity
14
+ # This class represents a invalid CVSS vector.
15
+ class InvalidCvss < Cvss
16
+ # rubocop:disable Lint/MissingSuper
17
+ ##
18
+ # Creates a new invalid CVSS vector.
19
+ def initialize; end
20
+ # rubocop:enable Lint/MissingSuper
21
+
22
+ ##
23
+ # Since this is an invalid CVSS vector, it always returns false.
24
+ def valid?
25
+ false
26
+ end
27
+
28
+ ##
29
+ # Since this is an invalid CVSS vector, it always throws an exception.
30
+ def version
31
+ check_validity
32
+ end
33
+
34
+ ##
35
+ # Since this is an invalid CVSS vector, it always throws an exception.
36
+ def base_score
37
+ check_validity
38
+ end
39
+
40
+ ##
41
+ # Since this is an invalid CVSS vector, it always throws an exception.
42
+ def temporal_score
43
+ check_validity
44
+ end
45
+
46
+ ##
47
+ # Since this is an invalid CVSS vector, it always throws an exception.
48
+ def environmental_score
49
+ check_validity
50
+ end
55
51
  end
56
-
57
- end
52
+ end
@@ -1,13 +1,14 @@
1
1
  # CVSS-Suite, a Ruby gem to manage the CVSS vector
2
2
  #
3
- # Copyright (c) Siemens AG, 2019
3
+ # Copyright (c) 2016-2022 Siemens AG
4
+ # Copyright (c) 2022 0llirocks
4
5
  #
5
6
  # Authors:
6
- # Oliver Hambörger <oliver.hamboerger@siemens.com>
7
+ # 0llirocks <http://0lli.rocks>
7
8
  #
8
9
  # This work is licensed under the terms of the MIT license.
9
10
  # See the LICENSE.md file in the top-level directory.
10
11
 
11
12
  module CvssSuite
12
- VERSION = "1.2.0"
13
+ VERSION = '3.1.0'.freeze
13
14
  end
data/lib/cvss_suite.rb CHANGED
@@ -1,9 +1,10 @@
1
1
  # CVSS-Suite, a Ruby gem to manage the CVSS vector
2
2
  #
3
- # Copyright (c) Siemens AG, 2016
3
+ # Copyright (c) 2016-2022 Siemens AG
4
+ # Copyright (c) 2022 0llirocks
4
5
  #
5
6
  # Authors:
6
- # Oliver Hambörger <oliver.hamboerger@siemens.com>
7
+ # 0llirocks <http://0lli.rocks>
7
8
  #
8
9
  # This work is licensed under the terms of the MIT license.
9
10
  # See the LICENSE.md file in the top-level directory.
@@ -12,33 +13,32 @@ require 'cvss_suite/cvss2/cvss2'
12
13
  require 'cvss_suite/cvss3/cvss3'
13
14
  require 'cvss_suite/cvss31/cvss31'
14
15
  require 'cvss_suite/version'
15
- require 'cvss_suite/helpers/extensions'
16
16
  require 'cvss_suite/errors'
17
17
  require 'cvss_suite/invalid_cvss'
18
18
 
19
19
  ##
20
20
  # Module of this gem.
21
-
22
21
  module CvssSuite
23
22
  CVSS_VECTOR_BEGINNINGS = [
24
- {:string => 'AV:', :version => 2},
25
- {:string => 'CVSS:3.0/', :version => 3.0},
26
- {:string => 'CVSS:3.1/', :version => 3.1}
27
- ]
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
+ ].freeze
28
28
 
29
29
  ##
30
30
  # Returns a CVSS class by a +vector+.
31
-
32
31
  def self.new(vector)
33
32
  return InvalidCvss.new unless vector.is_a? String
33
+
34
34
  @vector_string = vector
35
35
  case version
36
36
  when 2
37
- Cvss2.new(@vector_string, version)
37
+ Cvss2.new(prepare_vector(@vector_string))
38
38
  when 3.0
39
- Cvss3.new(@vector_string, version)
39
+ Cvss3.new(prepare_vector(@vector_string))
40
40
  when 3.1
41
- Cvss31.new(@vector_string, version)
41
+ Cvss31.new(prepare_vector(@vector_string))
42
42
  else
43
43
  InvalidCvss.new
44
44
  end
@@ -48,10 +48,41 @@ module CvssSuite
48
48
 
49
49
  def self.version
50
50
  CVSS_VECTOR_BEGINNINGS.each do |beginning|
51
- if @vector_string.start_with? beginning[:string]
52
- return beginning[:version]
53
- end
51
+ return beginning[:version] if @vector_string.start_with? beginning[:string]
52
+ end
53
+ end
54
+
55
+ def self.prepare_vector(vector)
56
+ vector = vector.clone
57
+
58
+ return prepare_cvss2_vector(vector) if version == 2
59
+
60
+ version_string = CVSS_VECTOR_BEGINNINGS.detect { |v| v[:version] == version } [:string]
61
+ start_of_vector = vector.index(version_string)
62
+
63
+ if start_of_vector.nil?
64
+ ''
65
+ else
66
+ vector[version_string.length..]
54
67
  end
55
68
  end
56
69
 
70
+ def self.prepare_cvss2_vector(vector)
71
+ start_of_vector = vector.index('AV')
72
+
73
+ if start_of_vector.nil?
74
+ ''
75
+ elsif start_of_vector == 1
76
+ match_array = vector.scan(/\((?>[^)(]+|\g<0>)*\)/)
77
+ if match_array.length == 1 && match_array[0] == vector
78
+ vector.slice!(0)
79
+ vector.slice!(vector.length - 1)
80
+ vector
81
+ else
82
+ ''
83
+ end
84
+ else
85
+ vector[start_of_vector..]
86
+ end
87
+ end
57
88
  end