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.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.github/workflows/ruby.yml +29 -0
  4. data/.gitignore +6 -0
  5. data/.rspec +1 -0
  6. data/.yardopts +1 -0
  7. data/ChangeLog.md +10 -0
  8. data/Gemfile +13 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +82 -0
  11. data/Rakefile +13 -0
  12. data/benchmark.rb +38 -0
  13. data/cvelist.gemspec +61 -0
  14. data/gemspec.yml +21 -0
  15. data/lib/cvelist.rb +2 -0
  16. data/lib/cvelist/cve.rb +83 -0
  17. data/lib/cvelist/directory.rb +80 -0
  18. data/lib/cvelist/exceptions.rb +31 -0
  19. data/lib/cvelist/malformed_cve.rb +42 -0
  20. data/lib/cvelist/range_dir.rb +121 -0
  21. data/lib/cvelist/repository.rb +240 -0
  22. data/lib/cvelist/version.rb +4 -0
  23. data/lib/cvelist/year_dir.rb +178 -0
  24. data/spec/cve_methods_examples.rb +130 -0
  25. data/spec/cve_spec.rb +51 -0
  26. data/spec/cvelist_spec.rb +8 -0
  27. data/spec/directory_spec.rb +83 -0
  28. data/spec/fixtures/CVE-2020-1994.json +140 -0
  29. data/spec/fixtures/cvelist/.gitkeep +0 -0
  30. data/spec/fixtures/cvelist/1999/.gitkeep +0 -0
  31. data/spec/fixtures/cvelist/2000/.gitkeep +0 -0
  32. data/spec/fixtures/cvelist/2001/.gitkeep +0 -0
  33. data/spec/fixtures/cvelist/2002/.gitkeep +0 -0
  34. data/spec/fixtures/cvelist/2003/.gitkeep +0 -0
  35. data/spec/fixtures/cvelist/2004/.gitkeep +0 -0
  36. data/spec/fixtures/cvelist/2005/.gitkeep +0 -0
  37. data/spec/fixtures/cvelist/2006/.gitkeep +0 -0
  38. data/spec/fixtures/cvelist/2007/.gitkeep +0 -0
  39. data/spec/fixtures/cvelist/2008/.gitkeep +0 -0
  40. data/spec/fixtures/cvelist/2009/.gitkeep +0 -0
  41. data/spec/fixtures/cvelist/2010/.gitkeep +0 -0
  42. data/spec/fixtures/cvelist/2011/.gitkeep +0 -0
  43. data/spec/fixtures/cvelist/2012/.gitkeep +0 -0
  44. data/spec/fixtures/cvelist/2013/.gitkeep +0 -0
  45. data/spec/fixtures/cvelist/2014/.gitkeep +0 -0
  46. data/spec/fixtures/cvelist/2015/.gitkeep +0 -0
  47. data/spec/fixtures/cvelist/2016/.gitkeep +0 -0
  48. data/spec/fixtures/cvelist/2017/.gitkeep +0 -0
  49. data/spec/fixtures/cvelist/2018/.gitkeep +0 -0
  50. data/spec/fixtures/cvelist/2019/.gitkeep +0 -0
  51. data/spec/fixtures/cvelist/2020/.gitkeep +0 -0
  52. data/spec/fixtures/cvelist/2021/.gitkeep +0 -0
  53. data/spec/fixtures/cvelist/2021/0xxx/.gitkeep +0 -0
  54. data/spec/fixtures/cvelist/2021/1xxx/.gitkeep +0 -0
  55. data/spec/fixtures/cvelist/2021/20xxx/.gitkeep +0 -0
  56. data/spec/fixtures/cvelist/2021/21xxx/.gitkeep +0 -0
  57. data/spec/fixtures/cvelist/2021/2xxx/.gitkeep +0 -0
  58. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2000.json +18 -0
  59. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2001.json +18 -0
  60. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2002.json +18 -0
  61. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2003.json +18 -0
  62. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2004.json +18 -0
  63. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2005.json +18 -0
  64. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2006.json +18 -0
  65. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2007.json +18 -0
  66. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2008.json +18 -0
  67. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2009.json +18 -0
  68. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2998.json +3 -0
  69. data/spec/fixtures/cvelist/2021/2xxx/CVE-2021-2999.json +2 -0
  70. data/spec/range_dir_spec.rb +55 -0
  71. data/spec/repository_spec.rb +248 -0
  72. data/spec/spec_helper.rb +4 -0
  73. data/spec/year_dir_spec.rb +96 -0
  74. metadata +165 -0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,18 @@
1
+ {
2
+ "data_type": "CVE",
3
+ "data_format": "MITRE",
4
+ "data_version": "4.0",
5
+ "CVE_data_meta": {
6
+ "ID": "CVE-2021-2000",
7
+ "ASSIGNER": "cve@mitre.org",
8
+ "STATE": "RESERVED"
9
+ },
10
+ "description": {
11
+ "description_data": [
12
+ {
13
+ "lang": "eng",
14
+ "value": "** RESERVED ** This candidate has been reserved by an organization or individual that will use it when announcing a new security problem. When the candidate has been publicized, the details for this candidate will be provided."
15
+ }
16
+ ]
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "data_type": "CVE",
3
+ "data_format": "MITRE",
4
+ "data_version": "4.0",
5
+ "CVE_data_meta": {
6
+ "ID": "CVE-2021-2001",
7
+ "ASSIGNER": "cve@mitre.org",
8
+ "STATE": "RESERVED"
9
+ },
10
+ "description": {
11
+ "description_data": [
12
+ {
13
+ "lang": "eng",
14
+ "value": "** RESERVED ** This candidate has been reserved by an organization or individual that will use it when announcing a new security problem. When the candidate has been publicized, the details for this candidate will be provided."
15
+ }
16
+ ]
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "data_type": "CVE",
3
+ "data_format": "MITRE",
4
+ "data_version": "4.0",
5
+ "CVE_data_meta": {
6
+ "ID": "CVE-2021-2002",
7
+ "ASSIGNER": "cve@mitre.org",
8
+ "STATE": "RESERVED"
9
+ },
10
+ "description": {
11
+ "description_data": [
12
+ {
13
+ "lang": "eng",
14
+ "value": "** RESERVED ** This candidate has been reserved by an organization or individual that will use it when announcing a new security problem. When the candidate has been publicized, the details for this candidate will be provided."
15
+ }
16
+ ]
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "data_type": "CVE",
3
+ "data_format": "MITRE",
4
+ "data_version": "4.0",
5
+ "CVE_data_meta": {
6
+ "ID": "CVE-2021-2003",
7
+ "ASSIGNER": "cve@mitre.org",
8
+ "STATE": "RESERVED"
9
+ },
10
+ "description": {
11
+ "description_data": [
12
+ {
13
+ "lang": "eng",
14
+ "value": "** RESERVED ** This candidate has been reserved by an organization or individual that will use it when announcing a new security problem. When the candidate has been publicized, the details for this candidate will be provided."
15
+ }
16
+ ]
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "data_type": "CVE",
3
+ "data_format": "MITRE",
4
+ "data_version": "4.0",
5
+ "CVE_data_meta": {
6
+ "ID": "CVE-2021-2004",
7
+ "ASSIGNER": "cve@mitre.org",
8
+ "STATE": "RESERVED"
9
+ },
10
+ "description": {
11
+ "description_data": [
12
+ {
13
+ "lang": "eng",
14
+ "value": "** RESERVED ** This candidate has been reserved by an organization or individual that will use it when announcing a new security problem. When the candidate has been publicized, the details for this candidate will be provided."
15
+ }
16
+ ]
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "data_type": "CVE",
3
+ "data_format": "MITRE",
4
+ "data_version": "4.0",
5
+ "CVE_data_meta": {
6
+ "ID": "CVE-2021-2005",
7
+ "ASSIGNER": "cve@mitre.org",
8
+ "STATE": "RESERVED"
9
+ },
10
+ "description": {
11
+ "description_data": [
12
+ {
13
+ "lang": "eng",
14
+ "value": "** RESERVED ** This candidate has been reserved by an organization or individual that will use it when announcing a new security problem. When the candidate has been publicized, the details for this candidate will be provided."
15
+ }
16
+ ]
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "data_type": "CVE",
3
+ "data_format": "MITRE",
4
+ "data_version": "4.0",
5
+ "CVE_data_meta": {
6
+ "ID": "CVE-2021-2006",
7
+ "ASSIGNER": "cve@mitre.org",
8
+ "STATE": "RESERVED"
9
+ },
10
+ "description": {
11
+ "description_data": [
12
+ {
13
+ "lang": "eng",
14
+ "value": "** RESERVED ** This candidate has been reserved by an organization or individual that will use it when announcing a new security problem. When the candidate has been publicized, the details for this candidate will be provided."
15
+ }
16
+ ]
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "data_type": "CVE",
3
+ "data_format": "MITRE",
4
+ "data_version": "4.0",
5
+ "CVE_data_meta": {
6
+ "ID": "CVE-2021-2007",
7
+ "ASSIGNER": "cve@mitre.org",
8
+ "STATE": "RESERVED"
9
+ },
10
+ "description": {
11
+ "description_data": [
12
+ {
13
+ "lang": "eng",
14
+ "value": "** RESERVED ** This candidate has been reserved by an organization or individual that will use it when announcing a new security problem. When the candidate has been publicized, the details for this candidate will be provided."
15
+ }
16
+ ]
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "data_type": "CVE",
3
+ "data_format": "MITRE",
4
+ "data_version": "4.0",
5
+ "CVE_data_meta": {
6
+ "ID": "CVE-2021-2008",
7
+ "ASSIGNER": "cve@mitre.org",
8
+ "STATE": "RESERVED"
9
+ },
10
+ "description": {
11
+ "description_data": [
12
+ {
13
+ "lang": "eng",
14
+ "value": "** RESERVED ** This candidate has been reserved by an organization or individual that will use it when announcing a new security problem. When the candidate has been publicized, the details for this candidate will be provided."
15
+ }
16
+ ]
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "data_type": "CVE",
3
+ "data_format": "MITRE",
4
+ "data_version": "4.0",
5
+ "CVE_data_meta": {
6
+ "ID": "CVE-2021-2009",
7
+ "ASSIGNER": "cve@mitre.org",
8
+ "STATE": "RESERVED"
9
+ },
10
+ "description": {
11
+ "description_data": [
12
+ {
13
+ "lang": "eng",
14
+ "value": "** RESERVED ** This candidate has been reserved by an organization or individual that will use it when announcing a new security problem. When the candidate has been publicized, the details for this candidate will be provided."
15
+ }
16
+ ]
17
+ }
18
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "description": "valid JSON, but invalid CVE schema"
3
+ }
@@ -0,0 +1,2 @@
1
+ {
2
+ "description": "invalid JSON"
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+ require 'cve_methods_examples'
3
+ require 'cvelist/range_dir'
4
+
5
+ describe RangeDir do
6
+ let(:year_number) { 2021 }
7
+ let(:xxx_range) { '2xxx' }
8
+
9
+ let(:fixture_dir) { File.expand_path('../fixtures',__FILE__) }
10
+ let(:cvelist_dir) { File.join(fixture_dir,'cvelist') }
11
+ let(:path) { File.join(cvelist_dir,year_number.to_s,xxx_range) }
12
+
13
+ subject { described_class.new(path) }
14
+
15
+ describe "#initialize" do
16
+ it "must set #range" do
17
+ expect(subject.range).to eq(xxx_range)
18
+ end
19
+ end
20
+
21
+ let(:valid_cve_files) do
22
+ %w[
23
+ CVE-2021-2000.json CVE-2021-2003.json CVE-2021-2006.json CVE-2021-2009.json
24
+ CVE-2021-2001.json CVE-2021-2004.json CVE-2021-2007.json
25
+ CVE-2021-2002.json CVE-2021-2005.json CVE-2021-2008.json
26
+ ]
27
+ end
28
+ let(:malformed_cve_files) { %w[CVE-2021-2998.json CVE-2021-2999.json] }
29
+
30
+ let(:cve_files) { valid_cve_files + malformed_cve_files }
31
+ let(:cve_paths) do
32
+ cve_files.map { |file| File.join(path,file) }
33
+ end
34
+
35
+ let(:sorted_cve_files) { cve_files.sort }
36
+ let(:sorted_cve_paths) do
37
+ sorted_cve_files.map { |file| File.join(path,file) }
38
+ end
39
+
40
+ describe "#files" do
41
+ it "must find all CVE-*.json files" do
42
+ expect(subject.files).to all(be =~ /\/CVE-\d{4}-\d{4}\.json$/)
43
+ end
44
+
45
+ it "must return the paths to 'CVE-*.json' files within the range directory" do
46
+ expect(subject.files).to match_array(cve_paths)
47
+ end
48
+
49
+ it "must sort the 'CVE-*.json' files" do
50
+ expect(subject.files).to eq(sorted_cve_paths)
51
+ end
52
+ end
53
+
54
+ include_examples "CVE methods"
55
+ end
@@ -0,0 +1,248 @@
1
+ require 'spec_helper'
2
+ require 'cve_methods_examples'
3
+ require 'cvelist/repository'
4
+
5
+ describe Repository do
6
+ let(:fixture_dir) { File.expand_path('../fixtures',__FILE__) }
7
+ let(:cvelist_dir) { File.join(fixture_dir,'cvelist') }
8
+
9
+ describe ".clone" do
10
+ subject { described_class }
11
+
12
+ let(:depth) { 1 }
13
+ let(:url) { described_class::URL }
14
+ let(:path) { File.join(fixture_dir,'new_cvelist') }
15
+
16
+ context "when only given the path argument" do
17
+ it "must execute `git clone --depth 1 URL path` command" do
18
+ expect(subject).to receive(:system).with(
19
+ 'git', 'clone', '--depth', depth.to_s, url, path
20
+ ).and_return(true)
21
+
22
+ subject.clone(path)
23
+ end
24
+ end
25
+
26
+ context "when given a custom url: argument" do
27
+ let(:url) { 'https://github.com/some_other/cvelist.git' }
28
+
29
+ it "must pass the `URL` git argument to `git clone`" do
30
+ expect(subject).to receive(:system).with(
31
+ 'git', 'clone', '--depth', depth.to_s, url, path
32
+ ).and_return(true)
33
+
34
+ subject.clone(path, url: url)
35
+ end
36
+ end
37
+
38
+ context "when given a custom depth: argument" do
39
+ let(:depth) { 2 }
40
+
41
+ it "must pass the `--depth DEPTH` option to `git clone`" do
42
+ expect(subject).to receive(:system).with(
43
+ 'git', 'clone', '--depth', depth.to_s, url, path
44
+ ).and_return(true)
45
+
46
+ subject.clone(path, depth: depth)
47
+ end
48
+ end
49
+
50
+ context "when the `git clone` command succeeds" do
51
+ it do
52
+ expect(subject).to receive(:system).with(
53
+ 'git', 'clone', '--depth', depth.to_s, url, path
54
+ ).and_return(true)
55
+
56
+ expect(subject.clone(path)).to be_kind_of(described_class)
57
+ end
58
+ end
59
+
60
+ context "when the `git clone` command fails" do
61
+ it do
62
+ expect(subject).to receive(:system).with(
63
+ 'git', 'clone', '--depth', depth.to_s, url, path
64
+ ).and_return(false)
65
+
66
+ expect {
67
+ subject.clone(path)
68
+ }.to raise_error(GitCloneFailed)
69
+ end
70
+ end
71
+ end
72
+
73
+ let(:path) { cvelist_dir }
74
+
75
+ subject { described_class.new(path) }
76
+
77
+ describe "#git?" do
78
+ it "must test for the presence of a '.git' directory" do
79
+ expect(subject).to receive(:directory?).with('.git')
80
+
81
+ subject.git?
82
+ end
83
+ end
84
+
85
+ describe "#pull!" do
86
+ context "when the repository is a git repository" do
87
+ before { expect(subject).to receive(:git?).and_return(true) }
88
+
89
+ let(:remote) { 'origin' }
90
+ let(:branch) { 'master' }
91
+
92
+ context "when no arguments are given" do
93
+ it "must run a `git pull REMOTE BRANCH` command" do
94
+ expect(subject).to receive(:system).with(
95
+ 'git', 'pull', remote, branch
96
+ ).and_return(true)
97
+
98
+ subject.pull!
99
+ end
100
+ end
101
+
102
+ context "when a remote: argument is given" do
103
+ let(:remote) { 'other' }
104
+
105
+ it "must pass the `REMOTE` argument to the `git pull` command" do
106
+ expect(subject).to receive(:system).with(
107
+ 'git', 'pull', remote, branch
108
+ ).and_return(true)
109
+
110
+ subject.pull!(remote: remote)
111
+ end
112
+ end
113
+
114
+ context "when a branch: argument is given" do
115
+ let(:branch) { 'other_branch' }
116
+
117
+ it "must pass the `BRANCH` argument to the `git pull` command" do
118
+ expect(subject).to receive(:system).with(
119
+ 'git', 'pull', remote, branch
120
+ ).and_return(true)
121
+
122
+ subject.pull!(branch: branch)
123
+ end
124
+ end
125
+
126
+ context "when the `git pull` command succeeds" do
127
+ it "must return true" do
128
+ expect(subject).to receive(:system).with(
129
+ 'git', 'pull', remote, branch
130
+ ).and_return(true)
131
+
132
+ expect(subject.pull!).to eq(true)
133
+ end
134
+ end
135
+
136
+ context "when the `git pull` command fails" do
137
+ it do
138
+ expect(subject).to receive(:system).with(
139
+ 'git', 'pull', remote, branch
140
+ ).and_return(false)
141
+
142
+ expect {
143
+ subject.pull!
144
+ }.to raise_error(GitPullFailed)
145
+ end
146
+ end
147
+ end
148
+
149
+ context "when the repository is not a git repository" do
150
+ before { expect(subject).to receive(:git?).and_return(false) }
151
+
152
+ it "must return false" do
153
+ expect(subject.pull!).to eq(false)
154
+ end
155
+ end
156
+ end
157
+
158
+ describe "#has_year?" do
159
+ it "should test if a repository has a directory for the given year" do
160
+ expect(subject.has_year?('1999')).to eq(true)
161
+ end
162
+
163
+ context "when the given year does not exist in the repository" do
164
+ it "must return false" do
165
+ expect(subject.has_year?('3000')).to eq(false)
166
+ end
167
+ end
168
+ end
169
+
170
+ let(:dirs) do
171
+ %w[
172
+ 1999 2001 2003 2005 2007 2009 2011 2013 2015 2017 2019 2021
173
+ 2000 2002 2004 2006 2008 2010 2012 2014 2016 2018 2020
174
+ ]
175
+ end
176
+ let(:dir_paths) do
177
+ dirs.map { |dir| File.join(path,dir) }
178
+ end
179
+
180
+ let(:sorted_dirs) { dirs.sort }
181
+ let(:sorted_dir_paths) do
182
+ sorted_dirs.map { |dir| File.join(path,dir) }
183
+ end
184
+
185
+ describe "#directories" do
186
+ subject { super().directories }
187
+
188
+ it "must find all year directories" do
189
+ expect(subject).to all(be =~ /\/[1-2][0-9]{3}$/)
190
+ end
191
+
192
+ it "must return paths to all year directories within the repository" do
193
+ expect(subject).to match_array(dir_paths)
194
+ end
195
+
196
+ it "must sort the year directories" do
197
+ expect(subject).to eq(sorted_dir_paths)
198
+ end
199
+ end
200
+
201
+ describe "#years" do
202
+ subject { super().years }
203
+
204
+ it do
205
+ expect(subject).to_not be_empty
206
+ expect(subject).to all(be_kind_of(YearDir))
207
+ end
208
+
209
+ it "must map the #directories to YearDir objects" do
210
+ expect(subject.map(&:path)).to eq(sorted_dir_paths)
211
+ end
212
+ end
213
+
214
+ describe "#year" do
215
+ let(:year_number) { '2001' }
216
+
217
+ subject { super().year(year_number) }
218
+
219
+ it "must return a YearDir object for the given year" do
220
+ expect(subject).to be_kind_of(YearDir)
221
+ expect(subject.path).to eq(File.join(path,year_number))
222
+ end
223
+
224
+ context "when the given year does not exist in the repository" do
225
+ let(:year_number) { '3000' }
226
+
227
+ it do
228
+ expect { subject }.to raise_error(YearDirNotFound)
229
+ end
230
+ end
231
+ end
232
+
233
+ it { expect(described_class).to include(Enumerable) }
234
+
235
+ include_examples "CVE methods"
236
+
237
+ describe "#size" do
238
+ it "must match the total number of CVE .json files in the repository" do
239
+ expect(subject.size).to eq(
240
+ subject.years.reduce(0) { |i,year|
241
+ i + year.ranges.reduce(0) { |j,range|
242
+ j + range.files.length
243
+ }
244
+ }
245
+ )
246
+ end
247
+ end
248
+ end