cve_schema 0.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.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.github/workflows/ruby.yml +28 -0
  4. data/.gitignore +6 -0
  5. data/.rspec +1 -0
  6. data/.yardopts +1 -0
  7. data/ChangeLog.md +26 -0
  8. data/Gemfile +14 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +50 -0
  11. data/Rakefile +23 -0
  12. data/benchmark.rb +47 -0
  13. data/cve_schema.gemspec +61 -0
  14. data/gemspec.yml +19 -0
  15. data/lib/cve_schema.rb +2 -0
  16. data/lib/cve_schema/cve.rb +257 -0
  17. data/lib/cve_schema/cve/affects.rb +55 -0
  18. data/lib/cve_schema/cve/configuration.rb +14 -0
  19. data/lib/cve_schema/cve/credit.rb +14 -0
  20. data/lib/cve_schema/cve/data_meta.rb +185 -0
  21. data/lib/cve_schema/cve/description.rb +24 -0
  22. data/lib/cve_schema/cve/exploit.rb +14 -0
  23. data/lib/cve_schema/cve/has_lang_value.rb +93 -0
  24. data/lib/cve_schema/cve/id.rb +79 -0
  25. data/lib/cve_schema/cve/impact.rb +75 -0
  26. data/lib/cve_schema/cve/impact/cvss_v2.rb +318 -0
  27. data/lib/cve_schema/cve/impact/cvss_v3.rb +388 -0
  28. data/lib/cve_schema/cve/na.rb +8 -0
  29. data/lib/cve_schema/cve/problem_type.rb +56 -0
  30. data/lib/cve_schema/cve/product.rb +79 -0
  31. data/lib/cve_schema/cve/reference.rb +82 -0
  32. data/lib/cve_schema/cve/solution.rb +14 -0
  33. data/lib/cve_schema/cve/source.rb +75 -0
  34. data/lib/cve_schema/cve/timeline.rb +65 -0
  35. data/lib/cve_schema/cve/timestamp.rb +25 -0
  36. data/lib/cve_schema/cve/vendor.rb +83 -0
  37. data/lib/cve_schema/cve/version.rb +126 -0
  38. data/lib/cve_schema/cve/work_around.rb +14 -0
  39. data/lib/cve_schema/exceptions.rb +20 -0
  40. data/lib/cve_schema/version.rb +6 -0
  41. data/spec/affects_spec.rb +28 -0
  42. data/spec/configuration_spec.rb +6 -0
  43. data/spec/credit_spec.rb +6 -0
  44. data/spec/cve_schema_spec.rb +8 -0
  45. data/spec/cve_spec.rb +414 -0
  46. data/spec/data_meta_spec.rb +167 -0
  47. data/spec/description.rb +24 -0
  48. data/spec/exploit_spec.rb +6 -0
  49. data/spec/fixtures/CVE-2020-1994.json +140 -0
  50. data/spec/fixtures/CVE-2020-2005.json +152 -0
  51. data/spec/fixtures/CVE-2020-2050.json +233 -0
  52. data/spec/fixtures/CVE-2020-4700.json +99 -0
  53. data/spec/has_lang_value_spec.rb +56 -0
  54. data/spec/id_spec.rb +91 -0
  55. data/spec/impact/cvss_v3_spec.rb +118 -0
  56. data/spec/impact_spec.rb +45 -0
  57. data/spec/na_spec.rb +14 -0
  58. data/spec/problem_type_spec.rb +26 -0
  59. data/spec/product_spec.rb +73 -0
  60. data/spec/reference_spec.rb +70 -0
  61. data/spec/shared_examples.rb +19 -0
  62. data/spec/solution_spec.rb +6 -0
  63. data/spec/source_spec.rb +84 -0
  64. data/spec/spec_helper.rb +4 -0
  65. data/spec/timeline_spec.rb +86 -0
  66. data/spec/timestamp_spec.rb +24 -0
  67. data/spec/vendor_spec.rb +73 -0
  68. data/spec/version_spec.rb +104 -0
  69. data/spec/work_around_spec.rb +6 -0
  70. metadata +133 -0
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CVESchema
4
+ class CVE
5
+ # The `n/a` constant.
6
+ NA = 'n/a'
7
+ end
8
+ end
@@ -0,0 +1,56 @@
1
+ require 'cve_schema/cve/description'
2
+
3
+ module CVESchema
4
+ class CVE
5
+ #
6
+ # Represents an element within the `"problemtype_data"` JSON Array.
7
+ #
8
+ class ProblemType
9
+
10
+ # @return [Array<Description>]
11
+ attr_reader :description
12
+
13
+ alias descriptions description
14
+
15
+ #
16
+ # Initializes the problem-type object.
17
+ #
18
+ # @param [Array<Description>] description
19
+ #
20
+ def initialize(description)
21
+ @description = description
22
+ end
23
+
24
+ #
25
+ # Maps the parsed JSON to an Array of {Description} objects for
26
+ # {#initialize}.
27
+ #
28
+ # @param [Hash{String => Object}] json
29
+ # The parsed JSON.
30
+ #
31
+ # @return [Array<Description>]
32
+ #
33
+ # @api semipublic
34
+ #
35
+ def self.from_json(json)
36
+ json['description'].map(&Description.method(:load))
37
+ end
38
+
39
+ #
40
+ # Loads the problem-type object from parsed JSON.
41
+ #
42
+ # @param [Hash{String => Object}] json
43
+ # The parsed JSON.
44
+ #
45
+ # @return [ProblemType]
46
+ # The loaded problem-type object.
47
+ #
48
+ # @api semipublic
49
+ #
50
+ def self.load(json)
51
+ new(from_json(json))
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,79 @@
1
+ require 'cve_schema/cve/version'
2
+ require 'cve_schema/cve/na'
3
+
4
+ module CVESchema
5
+ class CVE
6
+ #
7
+ # Represents a product element within the `"product_data"` JSON Array.
8
+ #
9
+ class Product
10
+
11
+ # The product name.
12
+ #
13
+ # @return [String]
14
+ attr_reader :product_name
15
+
16
+ # The list of affected versions.
17
+ #
18
+ # @return [Array<Version>]
19
+ attr_reader :version
20
+
21
+ alias versions version
22
+
23
+ #
24
+ # Initializes the product.
25
+ #
26
+ # @param [String] product_name
27
+ #
28
+ # @param [Array<Version>] version
29
+ #
30
+ def initialize(product_name: , version: [])
31
+ @product_name = product_name
32
+ @version = version
33
+ end
34
+
35
+ #
36
+ # Determines if the {#product_name} is `n/a`.
37
+ #
38
+ # @return [Boolean]
39
+ #
40
+ def na?
41
+ @product_name == NA
42
+ end
43
+
44
+ #
45
+ # Maps the parsed JSON to a Symbol Hash for {#initialize}.
46
+ #
47
+ # @param [Hash{String => Object}] json
48
+ # The parsed JSON.
49
+ #
50
+ # @return [Hash{Symbol => Object}]
51
+ # The mapped Symbol Hash.
52
+ #
53
+ # @api semipublic
54
+ #
55
+ def self.from_json(json)
56
+ {
57
+ product_name: json['product_name'],
58
+ version: Array(json['version']['version_data']).map(&Version.method(:load))
59
+ }
60
+ end
61
+
62
+ #
63
+ # Loads the product object from parsed JSON.
64
+ #
65
+ # @param [Hash{String => Object}] json
66
+ # The parsed JSON.
67
+ #
68
+ # @return [Product]
69
+ # The loaded product.
70
+ #
71
+ # @api semipublic
72
+ #
73
+ def self.load(json)
74
+ new(**from_json(json))
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CVESchema
4
+ class CVE
5
+ #
6
+ # Represents a reference object within the `"reference_data"` JSON Array.
7
+ #
8
+ class Reference
9
+
10
+ # Reference URL.
11
+ #
12
+ # @return [String]
13
+ attr_reader :url
14
+
15
+ # Optional reference name.
16
+ #
17
+ # @return [String, nil]
18
+ attr_reader :name
19
+
20
+ REFSOURCES = {
21
+ 'MISC' => :MISC
22
+ }
23
+ REFSOURCES.default_proc = proc { |hash,key| key }
24
+
25
+ # Optional reference source identifier.
26
+ #
27
+ # @return [:MISC, String, nil]
28
+ attr_reader :refsource
29
+
30
+ alias ref_source refsource
31
+
32
+ #
33
+ # Initializes the reference.
34
+ #
35
+ # @param [String] url
36
+ #
37
+ # @param [nil, String] name
38
+ #
39
+ # @param [nil, :MISC, String] refsource
40
+ #
41
+ def initialize(url: , name: nil, refsource: nil)
42
+ @url = url
43
+ @name = name
44
+ @refsource = refsource
45
+ end
46
+
47
+ #
48
+ # Maps the parsed JSON to a Symbol Hash for {#initialize}.
49
+ #
50
+ # @param [Hash{String => Object}] json
51
+ # The parsed JSON.
52
+ #
53
+ # @return [Hash{Symbol => Object}]
54
+ # The mapped Symbol Hash.
55
+ #
56
+ # @api semipublic
57
+ #
58
+ def self.from_json(json)
59
+ {
60
+ url: json['url'],
61
+ name: json['name'],
62
+ refsource: REFSOURCES[json['refsource']]
63
+ }
64
+ end
65
+
66
+ #
67
+ # Loads the reference from the parsed JSON.
68
+ #
69
+ # @param [Hash{String => Object}] json
70
+ # The parsed JSON.
71
+ #
72
+ # @return [Reference]
73
+ #
74
+ # @api semipublic
75
+ #
76
+ def self.load(json)
77
+ new(**from_json(json))
78
+ end
79
+
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,14 @@
1
+ require 'cve_schema/cve/has_lang_value'
2
+
3
+ module CVESchema
4
+ class CVE
5
+ #
6
+ # Represents a solution object within the `"solutions"` JSON Array.
7
+ #
8
+ class Solution
9
+
10
+ include HasLangValue
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CVESchema
4
+ class CVE
5
+ #
6
+ # Represents the `"source"` JSON object.
7
+ #
8
+ class Source
9
+
10
+ # @return [Array<String>, nil]
11
+ attr_reader :defect
12
+
13
+ DISCOVERY = {
14
+ 'INTERNAL' => :INTERNAL,
15
+ 'EXTERNAL' => :EXTERNAL,
16
+ 'USER' => :USER,
17
+ 'UNKNOWN' => :UNKNOWN
18
+ }
19
+
20
+ # @return [:INTERNAL, :EXTERNAL, :USER, :UNKNOWN]
21
+ attr_reader :discovery
22
+
23
+ # @return [String, nil]
24
+ attr_reader :advisory
25
+
26
+ #
27
+ # Initializes the source object.
28
+ #
29
+ # @param [Array<String>, nil] defect
30
+ #
31
+ # @param [:INTERNAL, :EXTERNAL, :USER, :UNKNOWN] discovery
32
+ #
33
+ # @param [String, nil] advisory
34
+ #
35
+ def initialize(discovery: , defect: nil, advisory: nil)
36
+ @defect = defect
37
+ @discovery = discovery
38
+ @advisory = advisory
39
+ end
40
+
41
+ #
42
+ # Maps the parsed JSON to a Symbol Hash for {#initialize}.
43
+ #
44
+ # @param [Hash{String => Object}] json
45
+ # The parsed JSON.
46
+ #
47
+ # @return [Hash{Symbol => Object}]
48
+ # The mapped Symbol Hash.
49
+ #
50
+ def self.from_json(json)
51
+ {
52
+ defect: json['defect'],
53
+ discovery: DISCOVERY[json['discovery']],
54
+ advisory: json['advisory']
55
+ }
56
+ end
57
+
58
+ #
59
+ # Loads the source from the parsed JSON.
60
+ #
61
+ # @param [Hash{String => Object}] json
62
+ # The parsed JSON.
63
+ #
64
+ # @return [Source]
65
+ # The loaded source object.
66
+ #
67
+ # @api semipublic
68
+ #
69
+ def self.load(json)
70
+ new(**from_json(json))
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,65 @@
1
+ require 'cve_schema/cve/timestamp'
2
+ require 'cve_schema/cve/has_lang_value'
3
+
4
+ module CVESchema
5
+ class CVE
6
+ #
7
+ # Represents a timeline entry in the `"timeline"` JSON Array.
8
+ #
9
+ class Timeline
10
+
11
+ include HasLangValue
12
+
13
+ # The time of the timeline event.
14
+ #
15
+ # @return [DateTime]
16
+ attr_reader :time
17
+
18
+ #
19
+ # Initializes the timeline object.
20
+ #
21
+ # @param [DateTime] time
22
+ #
23
+ def initialize(time: , **kargs)
24
+ super(**kargs)
25
+
26
+ @time = time
27
+ end
28
+
29
+ #
30
+ # Maps the parsed JSON to a Symbol Hash for {#initialize}.
31
+ #
32
+ # @param [Hash{String => Object}] json
33
+ # The parsed JSON.
34
+ #
35
+ # @return [Hash{Symbol => Object}]
36
+ # The mapped Symbol Hash.
37
+ #
38
+ # @api semipublic
39
+ #
40
+ def self.from_json(json)
41
+ {
42
+ lang: json['lang'],
43
+ time: Timestamp.parse(json['time']),
44
+ value: json['value']
45
+ }
46
+ end
47
+
48
+ #
49
+ # Loads the timeline object from the parsed JSON.
50
+ #
51
+ # @param [Hash{String => Object}] json
52
+ # The parsed JSON.
53
+ #
54
+ # @return [Timeline]
55
+ # The loaded timeline object.
56
+ #
57
+ # @api semipublic
58
+ #
59
+ def self.load(json)
60
+ new(**from_json(json))
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,25 @@
1
+ require 'cve_schema/exceptions'
2
+ require 'time'
3
+
4
+ module CVESchema
5
+ class CVE
6
+ module Timestamp
7
+ #
8
+ # Parses a CVE timestamp (ISO 8601).
9
+ #
10
+ # @param [String] timestamp
11
+ # The raw timestamp String.
12
+ #
13
+ # @return [DateTime]
14
+ # The parsed ISO 8601 timestamp.
15
+ #
16
+ # @see https://github.com/CVEProject/cve-schema/blob/master/schema/v4.0/DRAFT-JSON-file-format-v4.md#timestamps
17
+ #
18
+ def self.parse(timestamp)
19
+ DateTime.iso8601(timestamp)
20
+ rescue Date::Error
21
+ raise InvalidJSON.new("invalid ISO-8601 timestamp: #{timestamp.inspect}")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,83 @@
1
+ require 'cve_schema/cve/product'
2
+ require 'cve_schema/cve/na'
3
+
4
+ module CVESchema
5
+ class CVE
6
+ #
7
+ # Represents an element within the `"vendor_data"` JSON Array.
8
+ #
9
+ class Vendor
10
+
11
+ # @return [String]
12
+ attr_reader :vendor_name
13
+
14
+ # @return [Array<Product>]
15
+ attr_reader :product
16
+
17
+ #
18
+ # Initializes the vendor object.
19
+ #
20
+ # @param [String] vendor_name
21
+ #
22
+ # @param [Array<Product>] product
23
+ #
24
+ def initialize(vendor_name: , product: )
25
+ @vendor_name = vendor_name
26
+ @product = product
27
+ end
28
+
29
+ #
30
+ # Determines if the {#vendor_name} is `n/a`.
31
+ #
32
+ # @return [Boolean]
33
+ #
34
+ def na?
35
+ @vendor_name == NA
36
+ end
37
+
38
+ #
39
+ # Converts the vendor object to a String.
40
+ #
41
+ # @return [String]
42
+ # The vendor name
43
+ #
44
+ def to_s
45
+ @vendor_name
46
+ end
47
+
48
+ #
49
+ # Maps the parsed JSON to a Symbol Hash for {#initialize}.
50
+ #
51
+ # @param [Hash{String => Object}] json
52
+ # The parsed JSON.
53
+ #
54
+ # @return [Hash{Symbol => Object}]
55
+ # The mapped Symbol Hash.
56
+ #
57
+ # @api semipublic
58
+ #
59
+ def self.from_json(json)
60
+ {
61
+ vendor_name: json['vendor_name'],
62
+ product: json['product']['product_data'].map(&Product.method(:load))
63
+ }
64
+ end
65
+
66
+ #
67
+ # Loads the vendor object from the parsed JSON.
68
+ #
69
+ # @param [Hash{String => Object}] json
70
+ # The parsed JSON.
71
+ #
72
+ # @return [Vendor]
73
+ # The loaded vendor object.
74
+ #
75
+ # @api semipublic
76
+ #
77
+ def self.load(json)
78
+ new(**from_json(json))
79
+ end
80
+
81
+ end
82
+ end
83
+ end