cve_schema 0.1.0

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