nvd-json_feeds 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +3 -0
- data/.github/workflows/ruby.yml +29 -0
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +25 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +20 -0
- data/README.md +136 -0
- data/Rakefile +31 -0
- data/gemspec.yml +22 -0
- data/lib/nvd/json_feeds.rb +25 -0
- data/lib/nvd/json_feeds/exceptions.rb +15 -0
- data/lib/nvd/json_feeds/feed.rb +50 -0
- data/lib/nvd/json_feeds/feed_file.rb +95 -0
- data/lib/nvd/json_feeds/feed_uri.rb +131 -0
- data/lib/nvd/json_feeds/gz_feed_file.rb +60 -0
- data/lib/nvd/json_feeds/gz_feed_uri.rb +25 -0
- data/lib/nvd/json_feeds/json_feed_file.rb +21 -0
- data/lib/nvd/json_feeds/meta.rb +122 -0
- data/lib/nvd/json_feeds/meta_feed_uri.rb +22 -0
- data/lib/nvd/json_feeds/schema/configurations.rb +61 -0
- data/lib/nvd/json_feeds/schema/configurations/node.rb +98 -0
- data/lib/nvd/json_feeds/schema/cpe/has_uri.rb +66 -0
- data/lib/nvd/json_feeds/schema/cpe/match.rb +117 -0
- data/lib/nvd/json_feeds/schema/cpe/name.rb +67 -0
- data/lib/nvd/json_feeds/schema/cve_feed.rb +142 -0
- data/lib/nvd/json_feeds/schema/cve_item.rb +94 -0
- data/lib/nvd/json_feeds/schema/cvss_v2.rb +298 -0
- data/lib/nvd/json_feeds/schema/cvss_v3.rb +332 -0
- data/lib/nvd/json_feeds/schema/has_data_version.rb +54 -0
- data/lib/nvd/json_feeds/schema/impact.rb +73 -0
- data/lib/nvd/json_feeds/schema/impact/base_metric_v2.rb +132 -0
- data/lib/nvd/json_feeds/schema/impact/base_metric_v3.rb +79 -0
- data/lib/nvd/json_feeds/schema/timestamp.rb +9 -0
- data/lib/nvd/json_feeds/version.rb +6 -0
- data/lib/nvd/json_feeds/zip_feed_file.rb +64 -0
- data/lib/nvd/json_feeds/zip_feed_uri.rb +25 -0
- data/nvd-json_feeds.gemspec +61 -0
- data/spec/feed_file_examples.rb +27 -0
- data/spec/feed_file_spec.rb +42 -0
- data/spec/feed_spec.rb +56 -0
- data/spec/feed_uri_spec.rb +81 -0
- data/spec/fixtures/gz_feed_file/nvdcve-1.1-recent.json.gz +0 -0
- data/spec/fixtures/nvdcve-1.1-recent.json +180 -0
- data/spec/fixtures/zip_feed_file/nvdcve-1.1-recent.json.zip +0 -0
- data/spec/gz_feed_file_spec.rb +66 -0
- data/spec/gz_feed_uri_spec.rb +35 -0
- data/spec/json_feed_file_spec.rb +18 -0
- data/spec/json_feeds_spec.rb +8 -0
- data/spec/meta_spec.rb +141 -0
- data/spec/schema/configurations/node_spec.rb +87 -0
- data/spec/schema/configurations_spec.rb +57 -0
- data/spec/schema/cpe/match_spec.rb +188 -0
- data/spec/schema/cpe/name_spec.rb +54 -0
- data/spec/schema/cve_feed_spec.rb +162 -0
- data/spec/schema/cve_item_spec.rb +116 -0
- data/spec/schema/impact/base_metric_v2_spec.rb +183 -0
- data/spec/schema/impact/base_metric_v3_spec.rb +80 -0
- data/spec/schema/impact_spec.rb +53 -0
- data/spec/schema/shared_examples.rb +136 -0
- data/spec/schema/timestamp_spec.rb +8 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/zip_feed_file_spec.rb +66 -0
- data/spec/zip_feed_uri_spec.rb +35 -0
- metadata +156 -0
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'nvd/json_feeds/schema/cpe/match'
|
2
|
+
|
3
|
+
module NVD
|
4
|
+
module JSONFeeds
|
5
|
+
module Schema
|
6
|
+
class Configurations
|
7
|
+
#
|
8
|
+
# Represents a value within `"nodes"`.
|
9
|
+
#
|
10
|
+
class Node
|
11
|
+
|
12
|
+
OPERATORS = {
|
13
|
+
'OR' => :OR,
|
14
|
+
'AND' => :AND
|
15
|
+
}
|
16
|
+
|
17
|
+
# @return [:OR, :AND, String]
|
18
|
+
attr_reader :operator
|
19
|
+
|
20
|
+
# @return [Boolean, nil]
|
21
|
+
attr_reader :negate
|
22
|
+
|
23
|
+
# @return [Array<Node>]
|
24
|
+
attr_reader :children
|
25
|
+
|
26
|
+
# @return [Array<CPE::Match>]
|
27
|
+
attr_reader :cpe_match
|
28
|
+
|
29
|
+
#
|
30
|
+
# Initializes the node.
|
31
|
+
#
|
32
|
+
# @param [:OR, :AND, String] operator
|
33
|
+
#
|
34
|
+
# @param [Boolean, nil] negate
|
35
|
+
#
|
36
|
+
# @param [Array<Node>] children
|
37
|
+
#
|
38
|
+
# @param [Array<CPE::Match>] cpe_match
|
39
|
+
#
|
40
|
+
def initialize(operator: nil, negate: nil, children: [], cpe_match: [])
|
41
|
+
@operator = operator
|
42
|
+
@negate = negate
|
43
|
+
|
44
|
+
@children = children
|
45
|
+
@cpe_match = cpe_match
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Determines if {#negate} is `true`.
|
50
|
+
#
|
51
|
+
# @return [Boolean]
|
52
|
+
#
|
53
|
+
def negate?
|
54
|
+
@negate == true
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Maps the parsed JSON to a Symbol Hash for {#initialize}.
|
59
|
+
#
|
60
|
+
# @param [Hash{String => Object}] json
|
61
|
+
# The parsed JSON.
|
62
|
+
#
|
63
|
+
# @return [Hash{Symbol => Object}]
|
64
|
+
# The Symbol Hash.
|
65
|
+
#
|
66
|
+
# @see https://csrc.nist.gov/schema/nvd/feed/1.1-Beta/cvss-v2.0_beta.json
|
67
|
+
#
|
68
|
+
def self.from_json(json)
|
69
|
+
{
|
70
|
+
operator: if (operator = json['operator'])
|
71
|
+
OPERATORS.fetch(operator,operator)
|
72
|
+
end,
|
73
|
+
negate: json['negate'],
|
74
|
+
|
75
|
+
children: Array(json['children']).map(&method(:load)),
|
76
|
+
cpe_match: Array(json['cpe_match']).map(&CPE::Match.method(:load))
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Loads the node object from the parsed JSON.
|
82
|
+
#
|
83
|
+
# @param [Hash{String => Object}] json
|
84
|
+
# The parsed JSON.
|
85
|
+
#
|
86
|
+
# @return [Node]
|
87
|
+
# The node object.
|
88
|
+
#
|
89
|
+
def self.load(json)
|
90
|
+
new(**from_json(json))
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module NVD
|
2
|
+
module JSONFeeds
|
3
|
+
module Schema
|
4
|
+
module CPE
|
5
|
+
#
|
6
|
+
# Adds the {#cpe23uri} and {#cpe22uri} attributes.
|
7
|
+
#
|
8
|
+
module HasURI
|
9
|
+
#
|
10
|
+
# Extends {ClassMethods}.
|
11
|
+
#
|
12
|
+
def self.included(base)
|
13
|
+
base.extend ClassMethods
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# Class methods.
|
18
|
+
#
|
19
|
+
module ClassMethods
|
20
|
+
#
|
21
|
+
# Maps the parsed JSON to a Symbol Hash for {#initialize}.
|
22
|
+
#
|
23
|
+
# @param [Hash{String => Object}] json
|
24
|
+
# The parsed JSON.
|
25
|
+
#
|
26
|
+
# @return [Hash{Symbol => Object}]
|
27
|
+
# The Symbol Hash.
|
28
|
+
#
|
29
|
+
def from_json(json)
|
30
|
+
{
|
31
|
+
cpe23uri: json.fetch('cpe23Uri'),
|
32
|
+
cpe22uri: json['cpe22Uri']
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Initializes the CPE URIs.
|
39
|
+
#
|
40
|
+
# @param [String] cpe23uri
|
41
|
+
# The CPE 2.3 URI.
|
42
|
+
#
|
43
|
+
# @param [String] cpe22uri
|
44
|
+
# The CPE 2.2 URI.
|
45
|
+
#
|
46
|
+
def initialize(cpe23uri: , cpe22uri: nil)
|
47
|
+
@cpe23uri = cpe23uri
|
48
|
+
@cpe22uri = cpe22uri
|
49
|
+
end
|
50
|
+
|
51
|
+
# The CPE 2.3 URI.
|
52
|
+
#
|
53
|
+
# @return [String]
|
54
|
+
attr_reader :cpe23uri
|
55
|
+
|
56
|
+
alias cpe_uri cpe23uri
|
57
|
+
|
58
|
+
# The CPE 2.2 URI.
|
59
|
+
#
|
60
|
+
# @return [String, nil]
|
61
|
+
attr_reader :cpe22uri
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'nvd/json_feeds/schema/cpe/name'
|
2
|
+
|
3
|
+
module NVD
|
4
|
+
module JSONFeeds
|
5
|
+
module Schema
|
6
|
+
module CPE
|
7
|
+
#
|
8
|
+
# Represents the `"cpe_match"` value.
|
9
|
+
#
|
10
|
+
class Match
|
11
|
+
|
12
|
+
include HasURI
|
13
|
+
|
14
|
+
# @return [Boolean]
|
15
|
+
attr_reader :vulnerable
|
16
|
+
|
17
|
+
# @return [String, nil]
|
18
|
+
attr_reader :version_start_excluding
|
19
|
+
|
20
|
+
# @return [String, nil]
|
21
|
+
attr_reader :version_start_including
|
22
|
+
|
23
|
+
# @return [String, nil]
|
24
|
+
attr_reader :version_end_excluding
|
25
|
+
|
26
|
+
# @return [String, nil]
|
27
|
+
attr_reader :version_end_including
|
28
|
+
|
29
|
+
# @return [Array<Name>]
|
30
|
+
attr_reader :cpe_name
|
31
|
+
|
32
|
+
#
|
33
|
+
# Initializes the CPE match object.
|
34
|
+
#
|
35
|
+
# @param [Boolean] vulnerable
|
36
|
+
#
|
37
|
+
# @param [String, nil] version_start_excluding
|
38
|
+
#
|
39
|
+
# @param [String, nil] version_start_including
|
40
|
+
#
|
41
|
+
# @param [String, nil] version_end_excluding
|
42
|
+
#
|
43
|
+
# @param [String, nil] version_end_including
|
44
|
+
#
|
45
|
+
# @param [Array<Name>] cpe_name
|
46
|
+
#
|
47
|
+
def initialize(vulnerable: , version_start_excluding: nil,
|
48
|
+
version_start_including: nil,
|
49
|
+
version_end_excluding: nil,
|
50
|
+
version_end_including: nil,
|
51
|
+
cpe_name: [],
|
52
|
+
**kwargs)
|
53
|
+
super(**kwargs)
|
54
|
+
|
55
|
+
@vulnerable = vulnerable
|
56
|
+
|
57
|
+
@version_start_excluding = version_start_excluding
|
58
|
+
@version_start_including = version_start_including
|
59
|
+
|
60
|
+
@version_end_excluding = version_end_excluding
|
61
|
+
@version_end_including = version_end_including
|
62
|
+
|
63
|
+
@cpe_name = cpe_name
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Determines if the CPE match indicates whether it's vulnerable.
|
68
|
+
#
|
69
|
+
# @return [Boolean]
|
70
|
+
#
|
71
|
+
def vulnerable?
|
72
|
+
@vulnerable == true
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# Maps the CPE match JSON to a Symbol Hash for {#initialize}.
|
77
|
+
#
|
78
|
+
# @param [Hash{String => Object}] json
|
79
|
+
# The parsed JSON.
|
80
|
+
#
|
81
|
+
# @return [Hash{Symbol => Object}]
|
82
|
+
# The mapped Symbol Hash.
|
83
|
+
#
|
84
|
+
def self.from_json(json)
|
85
|
+
{
|
86
|
+
vulnerable: json.fetch('vulnerable'),
|
87
|
+
|
88
|
+
**super(json),
|
89
|
+
|
90
|
+
version_start_excluding: json['versionStartExcluding'],
|
91
|
+
version_start_including: json['versionStartIncluding'],
|
92
|
+
|
93
|
+
version_end_excluding: json['versionEndExcluding'],
|
94
|
+
version_end_including: json['versionEndIncluding'],
|
95
|
+
|
96
|
+
cpe_name: Array(json['cpe_name']).map(&Name.method(:load))
|
97
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
# Loads the CPE match object from the parsed JSON.
|
102
|
+
#
|
103
|
+
# @param [Hash{String => Object}] json
|
104
|
+
# The parsed JSON.
|
105
|
+
#
|
106
|
+
# @return [Match]
|
107
|
+
# The loaded CPE match object.
|
108
|
+
#
|
109
|
+
def self.load(json)
|
110
|
+
new(**from_json(json))
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'nvd/json_feeds/schema/cpe/has_uri'
|
2
|
+
require 'nvd/json_feeds/schema/timestamp'
|
3
|
+
|
4
|
+
module NVD
|
5
|
+
module JSONFeeds
|
6
|
+
module Schema
|
7
|
+
module CPE
|
8
|
+
#
|
9
|
+
# Represents the `"cpe_name"` value.
|
10
|
+
#
|
11
|
+
class Name
|
12
|
+
|
13
|
+
include HasURI
|
14
|
+
|
15
|
+
# Last modified date.
|
16
|
+
#
|
17
|
+
# @return [DateTime, nil]
|
18
|
+
attr_reader :last_modified_date
|
19
|
+
|
20
|
+
#
|
21
|
+
# Initializes the CPE name.
|
22
|
+
#
|
23
|
+
# @param [DateTime, nil] last_modified_date
|
24
|
+
#
|
25
|
+
def initialize(last_modified_date: nil, **kwargs)
|
26
|
+
super(**kwargs)
|
27
|
+
|
28
|
+
@last_modified_date = last_modified_date
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Maps the parsed JSON to a Symbol Hash for {#initialize}.
|
33
|
+
#
|
34
|
+
# @param [Hash{String => Object}] json
|
35
|
+
# The parsed JSON Hash.
|
36
|
+
#
|
37
|
+
# @return [Hash{Symbol => Object}]
|
38
|
+
# The mapped Symbol Hash.
|
39
|
+
#
|
40
|
+
def self.from_json(json)
|
41
|
+
{
|
42
|
+
**super(json),
|
43
|
+
|
44
|
+
last_modified_date: if (last_modified_date = json['lastModifiedDate'])
|
45
|
+
Timestamp.parse(last_modified_date)
|
46
|
+
end
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Loads the CPE name object from the parsed JSON.
|
52
|
+
#
|
53
|
+
# @param [Hash{String => Object}] json
|
54
|
+
# The parsed JSON Hash.
|
55
|
+
#
|
56
|
+
# @return [Name]
|
57
|
+
# The loaded CPE name object.
|
58
|
+
#
|
59
|
+
def self.load(json)
|
60
|
+
new(**from_json(json))
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'nvd/json_feeds/schema/has_data_version'
|
2
|
+
require 'nvd/json_feeds/schema/timestamp'
|
3
|
+
require 'nvd/json_feeds/schema/cve_item'
|
4
|
+
|
5
|
+
module NVD
|
6
|
+
module JSONFeeds
|
7
|
+
module Schema
|
8
|
+
#
|
9
|
+
# Represents the parsed contents of a JSON CVE feed.
|
10
|
+
#
|
11
|
+
class CVEFeed
|
12
|
+
|
13
|
+
include HasDataVersion
|
14
|
+
include Enumerable
|
15
|
+
|
16
|
+
DATA_TYPES = {
|
17
|
+
'CVE' => :CVE
|
18
|
+
}
|
19
|
+
|
20
|
+
DATA_FORMATS = {
|
21
|
+
'MITRE' => :MITRE
|
22
|
+
}
|
23
|
+
|
24
|
+
# The feed format.
|
25
|
+
#
|
26
|
+
# @return [:MITRE]
|
27
|
+
attr_reader :data_format
|
28
|
+
|
29
|
+
alias format data_format
|
30
|
+
|
31
|
+
# The feed type.
|
32
|
+
#
|
33
|
+
# @return [:CVE]
|
34
|
+
attr_reader :data_type
|
35
|
+
|
36
|
+
alias type data_type
|
37
|
+
|
38
|
+
# The number of CVEs.
|
39
|
+
#
|
40
|
+
# @return [Integer, nil]
|
41
|
+
attr_reader :data_number_of_cves
|
42
|
+
|
43
|
+
alias number_of_cves data_number_of_cves
|
44
|
+
|
45
|
+
# The feed timestamp.
|
46
|
+
#
|
47
|
+
# @return [DateTime, nil]
|
48
|
+
attr_reader :data_timestamp
|
49
|
+
|
50
|
+
alias timestamp data_timestamp
|
51
|
+
|
52
|
+
# The CVE items.
|
53
|
+
#
|
54
|
+
# @return [Array<CVEItem>]
|
55
|
+
attr_reader :cve_items
|
56
|
+
|
57
|
+
#
|
58
|
+
# Initializes the JSON feed.
|
59
|
+
#
|
60
|
+
# @param [:MITRE] data_format
|
61
|
+
#
|
62
|
+
# @param [:CVE] data_type
|
63
|
+
#
|
64
|
+
# @param [Integer, nil] data_number_of_cves
|
65
|
+
#
|
66
|
+
# @param [DateTime, nil] data_timestamp
|
67
|
+
#
|
68
|
+
# @param [Array<CVEItem>] cve_items
|
69
|
+
#
|
70
|
+
def initialize(data_format: , data_type: , data_number_of_cves: nil,
|
71
|
+
data_timestamp: nil,
|
72
|
+
cve_items: ,
|
73
|
+
**kwargs)
|
74
|
+
super(**kwargs)
|
75
|
+
|
76
|
+
@data_format = data_format
|
77
|
+
@data_type = data_type
|
78
|
+
|
79
|
+
@data_number_of_cves = data_number_of_cves
|
80
|
+
@data_timestamp = data_timestamp
|
81
|
+
|
82
|
+
@cve_items = cve_items
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# Maps the parsed JSON to a Symbol Hash for {#initialize}.
|
87
|
+
#
|
88
|
+
# @param [Hash{String => Object}] json
|
89
|
+
# The parsed JSON.
|
90
|
+
#
|
91
|
+
# @return [Hash{Symbol => Object}]
|
92
|
+
# The mapped Symbol Hash.
|
93
|
+
#
|
94
|
+
def self.from_json(json)
|
95
|
+
{
|
96
|
+
data_type: DATA_TYPES.fetch(json.fetch('CVE_data_type')),
|
97
|
+
data_format: DATA_FORMATS.fetch(json.fetch('CVE_data_format')),
|
98
|
+
**super(json),
|
99
|
+
|
100
|
+
data_number_of_cves: json.fetch('CVE_data_numberOfCVEs').to_i,
|
101
|
+
|
102
|
+
data_timestamp: if (timestamp = json['CVE_data_timestamp'])
|
103
|
+
Timestamp.parse(timestamp)
|
104
|
+
end,
|
105
|
+
|
106
|
+
cve_items: json.fetch('CVE_Items').map(&CVEItem.method(:load))
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Loads the JSON feed data from the parsed JSON.
|
112
|
+
#
|
113
|
+
# @param [Hash{String => Object}] json
|
114
|
+
# The parsed JSON.
|
115
|
+
#
|
116
|
+
# @return [JSONFeed]
|
117
|
+
# The loaded JSON data.
|
118
|
+
#
|
119
|
+
def self.load(json)
|
120
|
+
new(**from_json(json))
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
# Enumerates over each CVE item in the feed.
|
125
|
+
#
|
126
|
+
# @yield [cve_itme]
|
127
|
+
# The given block will be passed each CVE Item.
|
128
|
+
#
|
129
|
+
# @yieldparam [CVEItem] cve_item
|
130
|
+
# A CVE Item in the feed.
|
131
|
+
#
|
132
|
+
# @return [Enumerator]
|
133
|
+
# If no block is given, an enumerator will be returned.
|
134
|
+
#
|
135
|
+
def each(&block)
|
136
|
+
@cve_items.each(&block)
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|