cvss-suite 1.2.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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