cvelist 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.
- checksums.yaml +7 -0
- data/.document +3 -0
- data/.github/workflows/ruby.yml +29 -0
- data/.gitignore +6 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +10 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +20 -0
- data/README.md +82 -0
- data/Rakefile +13 -0
- data/benchmark.rb +38 -0
- data/cvelist.gemspec +61 -0
- data/gemspec.yml +21 -0
- data/lib/cvelist.rb +2 -0
- data/lib/cvelist/cve.rb +83 -0
- data/lib/cvelist/directory.rb +80 -0
- data/lib/cvelist/exceptions.rb +31 -0
- data/lib/cvelist/malformed_cve.rb +42 -0
- data/lib/cvelist/range_dir.rb +121 -0
- data/lib/cvelist/repository.rb +240 -0
- data/lib/cvelist/version.rb +4 -0
- data/lib/cvelist/year_dir.rb +178 -0
- data/spec/cve_methods_examples.rb +130 -0
- data/spec/cve_spec.rb +51 -0
- data/spec/cvelist_spec.rb +8 -0
- data/spec/directory_spec.rb +83 -0
- data/spec/fixtures/CVE-2020-1994.json +140 -0
- data/spec/fixtures/cvelist/.gitkeep +0 -0
- data/spec/fixtures/cvelist/1999/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2000/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2001/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2002/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2003/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2004/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2005/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2006/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2007/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2008/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2009/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2010/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2011/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2012/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2013/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2014/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2015/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2016/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2017/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2018/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2019/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2020/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2021/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2021/0xxx/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2021/1xxx/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2021/20xxx/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2021/21xxx/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2021/2xxx/.gitkeep +0 -0
- data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2000.json +18 -0
- data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2001.json +18 -0
- data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2002.json +18 -0
- data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2003.json +18 -0
- data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2004.json +18 -0
- data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2005.json +18 -0
- data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2006.json +18 -0
- data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2007.json +18 -0
- data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2008.json +18 -0
- data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2009.json +18 -0
- data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2998.json +3 -0
- data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2999.json +2 -0
- data/spec/range_dir_spec.rb +55 -0
- data/spec/repository_spec.rb +248 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/year_dir_spec.rb +96 -0
- metadata +165 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'cve_schema/exceptions'
|
2
|
+
|
3
|
+
module CVEList
|
4
|
+
# Base class for all git related exceptions.
|
5
|
+
class GitError < RuntimeError
|
6
|
+
end
|
7
|
+
|
8
|
+
# Raised when a `git clone` failed.
|
9
|
+
class GitCloneFailed < GitError
|
10
|
+
end
|
11
|
+
|
12
|
+
# Raised when a `git pull` failed.
|
13
|
+
class GitPullFailed < GitError
|
14
|
+
end
|
15
|
+
|
16
|
+
class NotFoundError < RuntimeError
|
17
|
+
end
|
18
|
+
|
19
|
+
class YearDirNotFound < NotFoundError
|
20
|
+
end
|
21
|
+
|
22
|
+
class RangeDirNotFound < NotFoundError
|
23
|
+
end
|
24
|
+
|
25
|
+
class CVENotFound < NotFoundError
|
26
|
+
end
|
27
|
+
|
28
|
+
InvalidJSON = CVESchema::InvalidJSON
|
29
|
+
MissingJSONKey = CVESchema::MissingJSONKey
|
30
|
+
UnkownJSONValue = CVESchema::UnknownJSONValue
|
31
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module CVEList
|
2
|
+
#
|
3
|
+
# Represents malformed/invalid CVE JSON that could not be loaded.
|
4
|
+
#
|
5
|
+
class MalformedCVE
|
6
|
+
|
7
|
+
# Path to the JSON file.
|
8
|
+
#
|
9
|
+
# @return [String]
|
10
|
+
attr_reader :path
|
11
|
+
|
12
|
+
# The exception encountered when parsing the JSON file.
|
13
|
+
#
|
14
|
+
# @return [StandardError]
|
15
|
+
attr_reader :exception
|
16
|
+
|
17
|
+
#
|
18
|
+
# Initializes the malformed json.
|
19
|
+
#
|
20
|
+
# @param [String] path
|
21
|
+
# Path to the JSON file.
|
22
|
+
#
|
23
|
+
# @param [StandardError] exception
|
24
|
+
# The exception encountered when parsing the JSON file.
|
25
|
+
#
|
26
|
+
def initialize(path,exception)
|
27
|
+
@path = path
|
28
|
+
@exception = exception
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Converts the malformed JSON back into a String.
|
33
|
+
#
|
34
|
+
# @return [String]
|
35
|
+
# The String containing the {#path}, the {#exception} class and message.
|
36
|
+
#
|
37
|
+
def to_s
|
38
|
+
"#{@path}: #{@exception.class}: #{@exception.message}"
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cvelist/directory'
|
4
|
+
require 'cvelist/exceptions'
|
5
|
+
require 'cvelist/cve'
|
6
|
+
require 'cvelist/malformed_cve'
|
7
|
+
|
8
|
+
module CVEList
|
9
|
+
class RangeDir < Directory
|
10
|
+
|
11
|
+
include Enumerable
|
12
|
+
|
13
|
+
# @return [String]
|
14
|
+
attr_reader :path
|
15
|
+
|
16
|
+
# @return [String]
|
17
|
+
attr_reader :range
|
18
|
+
|
19
|
+
#
|
20
|
+
# Initializes the range directory.
|
21
|
+
#
|
22
|
+
# @param [String] path
|
23
|
+
# The path to the range directory.
|
24
|
+
#
|
25
|
+
def initialize(path)
|
26
|
+
super(path)
|
27
|
+
|
28
|
+
@range = File.basename(@path)
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Determines whether the range directory contains the given CVE ID.
|
33
|
+
#
|
34
|
+
# @param [String] cve_id
|
35
|
+
# The given CVE ID.
|
36
|
+
#
|
37
|
+
# @return [Boolean]
|
38
|
+
#
|
39
|
+
def has_cve?(cve_id)
|
40
|
+
file?("#{cve_id}.json")
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Loads a CVE.
|
45
|
+
#
|
46
|
+
# @param [String] cve_id
|
47
|
+
# The CVE ID.
|
48
|
+
#
|
49
|
+
# @return [CVE, nil]
|
50
|
+
# The loaded CVE of `nil` if the CVE could not be found within the range
|
51
|
+
# directory.
|
52
|
+
#
|
53
|
+
def [](cve_id)
|
54
|
+
cve_file = "#{cve_id}.json"
|
55
|
+
cve_path = join(cve_file)
|
56
|
+
|
57
|
+
if File.file?(cve_path)
|
58
|
+
CVE.load(cve_path)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# `Dir.glob` pattern for all CVE `.json` files.
|
63
|
+
GLOB = 'CVE-[0-9][0-9][0-9][0-9]-*.json'
|
64
|
+
|
65
|
+
#
|
66
|
+
# The JSON files within the range directory.
|
67
|
+
#
|
68
|
+
# @return [Array<String>]
|
69
|
+
#
|
70
|
+
def files
|
71
|
+
glob(GLOB).sort
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Enumerates over the CVEs in the range directory.
|
76
|
+
#
|
77
|
+
# @yield [cve]
|
78
|
+
# The given block will be passed each CVE in the range directory.
|
79
|
+
#
|
80
|
+
# @yieldparam [CVE] cve
|
81
|
+
# A CVE within the range directory.
|
82
|
+
#
|
83
|
+
# @return [Enumerator]
|
84
|
+
# If no block is given, an Enumerator object will be returned.
|
85
|
+
#
|
86
|
+
def each
|
87
|
+
return enum_for(__method__) unless block_given?
|
88
|
+
|
89
|
+
files.each do |cve_path|
|
90
|
+
begin
|
91
|
+
yield CVE.load(cve_path)
|
92
|
+
rescue InvalidJSON
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
#
|
98
|
+
# Enumerates over the malformed CVEs within the range directory.
|
99
|
+
#
|
100
|
+
# @yield [malformed]
|
101
|
+
# The given block will be passed each malformed
|
102
|
+
#
|
103
|
+
# @yieldparam [MalformedCVE] malformed
|
104
|
+
#
|
105
|
+
# @return [Enumerator]
|
106
|
+
# If no block is given, an Enumerator object will be returned.
|
107
|
+
#
|
108
|
+
def each_malformed
|
109
|
+
return enum_for(__method__) unless block_given?
|
110
|
+
|
111
|
+
files.each do |cve_path|
|
112
|
+
begin
|
113
|
+
CVE.load(cve_path)
|
114
|
+
rescue InvalidJSON => error
|
115
|
+
yield MalformedCVE.new(cve_path,error)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,240 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cvelist/directory'
|
4
|
+
require 'cvelist/exceptions'
|
5
|
+
require 'cvelist/year_dir'
|
6
|
+
|
7
|
+
module CVEList
|
8
|
+
class Repository < Directory
|
9
|
+
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
# The default git URI for the cvelist repository
|
13
|
+
URL = 'https://github.com/CVEProject/cvelist.git'
|
14
|
+
|
15
|
+
#
|
16
|
+
# Clones a new repository.
|
17
|
+
#
|
18
|
+
# @param [#to_s] path
|
19
|
+
# The path to where the cvelist repository will be cloned to.
|
20
|
+
#
|
21
|
+
# @param [#to_s] url
|
22
|
+
# The URL for the cvelist repository.
|
23
|
+
#
|
24
|
+
# @param [#to_s] depth
|
25
|
+
# The depth of the git clone.
|
26
|
+
#
|
27
|
+
# @raise [CloneFailedError]
|
28
|
+
# The `git clone` command failed.
|
29
|
+
#
|
30
|
+
def self.clone(path, url: URL, depth: 1)
|
31
|
+
unless system 'git', 'clone', '--depth', depth.to_s, url.to_s, path.to_s
|
32
|
+
raise(GitCloneFailed,"failed to clone #{url.inspect} into #{path.inspect}")
|
33
|
+
end
|
34
|
+
|
35
|
+
return new(path)
|
36
|
+
end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
alias download clone
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Determines whether the repository is a git repository.
|
44
|
+
#
|
45
|
+
# @return [Boolean]
|
46
|
+
# Specifies whether the repository is a git repository or not.
|
47
|
+
#
|
48
|
+
def git?
|
49
|
+
directory?('.git')
|
50
|
+
end
|
51
|
+
|
52
|
+
# The default git remote.
|
53
|
+
REMOTE = 'origin'
|
54
|
+
|
55
|
+
# The default git branch.
|
56
|
+
BRANCH = 'master'
|
57
|
+
|
58
|
+
#
|
59
|
+
# Pulls down new commits from the given remote/branch.
|
60
|
+
#
|
61
|
+
# @param [#to_s] remote
|
62
|
+
# The git remote.
|
63
|
+
#
|
64
|
+
# @param [#to_s] branch
|
65
|
+
# The git branch.
|
66
|
+
#
|
67
|
+
# @return [true, false]
|
68
|
+
# Returns `true` if the `git pull` succeeds.
|
69
|
+
# Returns `false` if the repository is not a git repository.
|
70
|
+
#
|
71
|
+
# @raise [PullFailedError]
|
72
|
+
# The `git pull` command faild.
|
73
|
+
#
|
74
|
+
def pull!(remote: REMOTE, branch: BRANCH)
|
75
|
+
return false unless git?
|
76
|
+
|
77
|
+
Dir.chdir(@path) do
|
78
|
+
unless system('git', 'pull', remote.to_s, branch.to_s)
|
79
|
+
raise(GitPullFailed,"failed to pull from remote #{remote.inspect} branch #{branch.inspect}")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
return true
|
84
|
+
end
|
85
|
+
|
86
|
+
alias update! pull!
|
87
|
+
|
88
|
+
#
|
89
|
+
# Determines if the repository contains a directory for the given year.
|
90
|
+
#
|
91
|
+
# @param [#to_s] year
|
92
|
+
# The given year.
|
93
|
+
#
|
94
|
+
# @return [Boolean]
|
95
|
+
# Specifies whether the repository contains the directory for the year.
|
96
|
+
#
|
97
|
+
def has_year?(year)
|
98
|
+
directory?(year.to_s)
|
99
|
+
end
|
100
|
+
|
101
|
+
# `Dir.glob` for year directories.
|
102
|
+
GLOB = '[1-2][0-9][0-9][0-9]'
|
103
|
+
|
104
|
+
#
|
105
|
+
# The year directories within the repository.
|
106
|
+
#
|
107
|
+
# @return [Array<String>]
|
108
|
+
# The paths to the year directories.
|
109
|
+
#
|
110
|
+
def directories
|
111
|
+
glob(GLOB).sort
|
112
|
+
end
|
113
|
+
|
114
|
+
#
|
115
|
+
# The year directories contained within the repository.
|
116
|
+
#
|
117
|
+
# @return [Array<YearDir>]
|
118
|
+
# The year directories within the repository.
|
119
|
+
#
|
120
|
+
def years(&block)
|
121
|
+
directories.map { |dir| YearDir.new(dir) }
|
122
|
+
end
|
123
|
+
|
124
|
+
#
|
125
|
+
# Requests a year directory from the repository.
|
126
|
+
#
|
127
|
+
# @param [#to_s] year_number
|
128
|
+
# The given year number.
|
129
|
+
#
|
130
|
+
# @return [YearDir]
|
131
|
+
# The year directory.
|
132
|
+
#
|
133
|
+
# @raise [YearNotFound]
|
134
|
+
# There is no year directory within the repository for the given year.
|
135
|
+
#
|
136
|
+
def year(year_number)
|
137
|
+
year_dir = join(year_number.to_s)
|
138
|
+
|
139
|
+
unless File.directory?(year_dir)
|
140
|
+
raise(YearDirNotFound,"year #{year_number.inspect} not found within #{@path.inspect}")
|
141
|
+
end
|
142
|
+
|
143
|
+
return YearDir.new(year_dir)
|
144
|
+
end
|
145
|
+
|
146
|
+
alias / year
|
147
|
+
|
148
|
+
#
|
149
|
+
# Enumerates over every CVE withing the year directories.
|
150
|
+
#
|
151
|
+
# @yield [cve]
|
152
|
+
# The given block will be passed each CVE from within the repository.
|
153
|
+
#
|
154
|
+
# @yieldparam [CVE] cve
|
155
|
+
# A CVE from the repository.
|
156
|
+
#
|
157
|
+
# @return [Enumerator]
|
158
|
+
# If no block is given, an Enumerator will be returned.
|
159
|
+
#
|
160
|
+
def each(&block)
|
161
|
+
return enum_for(__method__) unless block_given?
|
162
|
+
|
163
|
+
years.each do |year_dir|
|
164
|
+
year_dir.each(&block)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
#
|
169
|
+
# Enumerates over every malformed CVE within the repository.
|
170
|
+
#
|
171
|
+
# @yield [malformed_cve]
|
172
|
+
# The given block will be passed each malformed CVE from within the
|
173
|
+
# repository.
|
174
|
+
#
|
175
|
+
# @yieldparam [MalformedCVE] malformed_cve
|
176
|
+
# A malformed CVE from within the repository.
|
177
|
+
#
|
178
|
+
# @return [Enumerator]
|
179
|
+
# If no block is given, an Enumerator will be returned.
|
180
|
+
#
|
181
|
+
def each_malformed(&block)
|
182
|
+
return enum_for(__method__) unless block_given?
|
183
|
+
|
184
|
+
years.each do |year_dir|
|
185
|
+
year_dir.each_malformed(&block)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
#
|
190
|
+
# Determines whether the repository contains the given CVE ID.
|
191
|
+
#
|
192
|
+
# @param [String] cve_id
|
193
|
+
# The given CVE ID.
|
194
|
+
#
|
195
|
+
# @return [Boolean]
|
196
|
+
#
|
197
|
+
def has_cve?(cve_id)
|
198
|
+
year_number = cve_to_year(cve_id)
|
199
|
+
|
200
|
+
return has_year?(year_number) && year(year_number).has_cve?(cve_id)
|
201
|
+
end
|
202
|
+
|
203
|
+
#
|
204
|
+
# Accesses a CVE from the repository with the given CVE ID.
|
205
|
+
#
|
206
|
+
# @param [String] cve_id
|
207
|
+
# The given CVE ID.
|
208
|
+
#
|
209
|
+
# @return [CVE, nil]
|
210
|
+
# The CVE with the given ID. If no CVE with the given ID could be found,
|
211
|
+
# `nil` will be returned.
|
212
|
+
#
|
213
|
+
# @raise [InvalidJSON, MissingJSONKey, UnknownJSONValue]
|
214
|
+
# The CVE's JSON is invalid or malformed.
|
215
|
+
#
|
216
|
+
def [](cve_id)
|
217
|
+
year_number = cve_to_year(cve_id)
|
218
|
+
|
219
|
+
if has_year?(year_number)
|
220
|
+
year(year_number)[cve_id]
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
#
|
225
|
+
# Calculates the total number of CVEs in the repository.
|
226
|
+
#
|
227
|
+
# @return [Integer]
|
228
|
+
#
|
229
|
+
def size
|
230
|
+
Dir[join(GLOB,YearDir::GLOB,RangeDir::GLOB)].length
|
231
|
+
end
|
232
|
+
|
233
|
+
private
|
234
|
+
|
235
|
+
def cve_to_year(cve_id)
|
236
|
+
cve_id[cve_id.index('-')+1 .. cve_id.rindex('-')-1]
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cvelist/directory'
|
4
|
+
require 'cvelist/exceptions'
|
5
|
+
require 'cvelist/range_dir'
|
6
|
+
|
7
|
+
module CVEList
|
8
|
+
class YearDir < Directory
|
9
|
+
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
# Path to the year directory.
|
13
|
+
#
|
14
|
+
# @return [String]
|
15
|
+
attr_reader :path
|
16
|
+
|
17
|
+
# The year of the directory.
|
18
|
+
#
|
19
|
+
# @return [Integer]
|
20
|
+
attr_reader :year
|
21
|
+
|
22
|
+
#
|
23
|
+
# Initializes the year dir.
|
24
|
+
#
|
25
|
+
# @param [String] path
|
26
|
+
# The path to the year directory.
|
27
|
+
#
|
28
|
+
def initialize(path)
|
29
|
+
super(path)
|
30
|
+
|
31
|
+
@year = File.basename(@path).to_i
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Determines if the year directory contains the given range directory.
|
36
|
+
#
|
37
|
+
# @param [String] xxx_range
|
38
|
+
# The given range directory ending in `xxx`.
|
39
|
+
#
|
40
|
+
# @return [Boolean]
|
41
|
+
#
|
42
|
+
def has_range?(xxx_range)
|
43
|
+
directory?(xxx_range)
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Access a range directory within the year directory.
|
48
|
+
#
|
49
|
+
# @param [String] xxx_range
|
50
|
+
# The "xxx" range.
|
51
|
+
#
|
52
|
+
# @return [RangeDir]
|
53
|
+
# The range directory.
|
54
|
+
#
|
55
|
+
# @raise [RangeDirNotFound]
|
56
|
+
# Could not find the given range directory within the year directory.
|
57
|
+
#
|
58
|
+
def range(xxx_range)
|
59
|
+
range_dir_path = join(xxx_range)
|
60
|
+
|
61
|
+
unless File.directory?(range_dir_path)
|
62
|
+
raise(RangeDirNotFound,"#{xxx_range.inspect} not found within #{@path.inspect}")
|
63
|
+
end
|
64
|
+
|
65
|
+
return RangeDir.new(range_dir_path)
|
66
|
+
end
|
67
|
+
|
68
|
+
alias / range
|
69
|
+
|
70
|
+
# `Dir.glob` pattern for `Nxxx` range directories.
|
71
|
+
GLOB = '*xxx'
|
72
|
+
|
73
|
+
#
|
74
|
+
# The `xxx` number ranges within the directory.
|
75
|
+
#
|
76
|
+
# @return [Array<String>]
|
77
|
+
#
|
78
|
+
def directories
|
79
|
+
glob(GLOB).sort
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# The range directories within the year directory.
|
84
|
+
#
|
85
|
+
# @return [Enumerator]
|
86
|
+
# If no block is given, an Enumerator will be returned.
|
87
|
+
#
|
88
|
+
def ranges(&block)
|
89
|
+
directories.map { |dir| RangeDir.new(dir) }
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# Enumerates over each CVE, in each range directory, within the year
|
94
|
+
# directory.
|
95
|
+
#
|
96
|
+
# @yield [cve]
|
97
|
+
# The given block will be passed each CVE in the year dir.
|
98
|
+
#
|
99
|
+
# @yieldparam [CVE] cve
|
100
|
+
# A CVE within one of the range directories.
|
101
|
+
#
|
102
|
+
# @return [Enumerator]
|
103
|
+
# If no block is given, an Enumerator will be returned.
|
104
|
+
#
|
105
|
+
def each(&block)
|
106
|
+
return enum_for(__method__) unless block_given?
|
107
|
+
|
108
|
+
ranges.each do |range_dir|
|
109
|
+
range_dir.each(&block)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# Enumerates over every malformed CVE within the year directories.
|
115
|
+
#
|
116
|
+
# @yield [malformed_cve]
|
117
|
+
# The given block will be passed each malformed CVE from within the
|
118
|
+
# year directory.
|
119
|
+
#
|
120
|
+
# @yieldparam [MalformedCVE] malformed_cve
|
121
|
+
# A malformed CVE from within the year directory.
|
122
|
+
#
|
123
|
+
# @return [Enumerator]
|
124
|
+
# If no block is given, an Enumerator will be returned.
|
125
|
+
#
|
126
|
+
def each_malformed(&block)
|
127
|
+
return enum_for(__method__) unless block_given?
|
128
|
+
|
129
|
+
ranges.each do |range_dir|
|
130
|
+
range_dir.each_malformed(&block)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
#
|
135
|
+
# Determines whether a CVE exists with the given ID, within any of the range
|
136
|
+
# directories, within the year directory.
|
137
|
+
#
|
138
|
+
# @param [String] cve_id
|
139
|
+
# The given CVE ID.
|
140
|
+
#
|
141
|
+
# @return [Boolean]
|
142
|
+
# Specifies whether the CVE exists.
|
143
|
+
#
|
144
|
+
def has_cve?(cve_id)
|
145
|
+
xxx_range = cve_to_xxx_range(cve_id)
|
146
|
+
|
147
|
+
has_range?(xxx_range) && range(xxx_range).has_cve?(cve_id)
|
148
|
+
end
|
149
|
+
|
150
|
+
#
|
151
|
+
# Loads a CVE.
|
152
|
+
#
|
153
|
+
# @param [String] cve_id
|
154
|
+
# The CVE ID.
|
155
|
+
#
|
156
|
+
# @return [CVE, nil]
|
157
|
+
# The loaded CVE or `nil` if the accompaning range directory for the CVE
|
158
|
+
# could not be found.
|
159
|
+
#
|
160
|
+
def [](cve_id)
|
161
|
+
xxx_range = cve_to_xxx_range(cve_id)
|
162
|
+
|
163
|
+
if has_range?(xxx_range)
|
164
|
+
range(xxx_range)[cve_id]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
|
170
|
+
def cve_to_xxx_range(cve_id)
|
171
|
+
cve_number = cve_id[cve_id.rindex('-')+1 ..]
|
172
|
+
cve_number[-3,3] = 'xxx'
|
173
|
+
|
174
|
+
return cve_number
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|