nvd-json_feeds 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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.github/workflows/ruby.yml +29 -0
  4. data/.gitignore +9 -0
  5. data/.rspec +1 -0
  6. data/.yardopts +1 -0
  7. data/ChangeLog.md +25 -0
  8. data/Gemfile +13 -0
  9. data/LICENSE.txt +20 -0
  10. data/README.md +136 -0
  11. data/Rakefile +31 -0
  12. data/gemspec.yml +22 -0
  13. data/lib/nvd/json_feeds.rb +25 -0
  14. data/lib/nvd/json_feeds/exceptions.rb +15 -0
  15. data/lib/nvd/json_feeds/feed.rb +50 -0
  16. data/lib/nvd/json_feeds/feed_file.rb +95 -0
  17. data/lib/nvd/json_feeds/feed_uri.rb +131 -0
  18. data/lib/nvd/json_feeds/gz_feed_file.rb +60 -0
  19. data/lib/nvd/json_feeds/gz_feed_uri.rb +25 -0
  20. data/lib/nvd/json_feeds/json_feed_file.rb +21 -0
  21. data/lib/nvd/json_feeds/meta.rb +122 -0
  22. data/lib/nvd/json_feeds/meta_feed_uri.rb +22 -0
  23. data/lib/nvd/json_feeds/schema/configurations.rb +61 -0
  24. data/lib/nvd/json_feeds/schema/configurations/node.rb +98 -0
  25. data/lib/nvd/json_feeds/schema/cpe/has_uri.rb +66 -0
  26. data/lib/nvd/json_feeds/schema/cpe/match.rb +117 -0
  27. data/lib/nvd/json_feeds/schema/cpe/name.rb +67 -0
  28. data/lib/nvd/json_feeds/schema/cve_feed.rb +142 -0
  29. data/lib/nvd/json_feeds/schema/cve_item.rb +94 -0
  30. data/lib/nvd/json_feeds/schema/cvss_v2.rb +298 -0
  31. data/lib/nvd/json_feeds/schema/cvss_v3.rb +332 -0
  32. data/lib/nvd/json_feeds/schema/has_data_version.rb +54 -0
  33. data/lib/nvd/json_feeds/schema/impact.rb +73 -0
  34. data/lib/nvd/json_feeds/schema/impact/base_metric_v2.rb +132 -0
  35. data/lib/nvd/json_feeds/schema/impact/base_metric_v3.rb +79 -0
  36. data/lib/nvd/json_feeds/schema/timestamp.rb +9 -0
  37. data/lib/nvd/json_feeds/version.rb +6 -0
  38. data/lib/nvd/json_feeds/zip_feed_file.rb +64 -0
  39. data/lib/nvd/json_feeds/zip_feed_uri.rb +25 -0
  40. data/nvd-json_feeds.gemspec +61 -0
  41. data/spec/feed_file_examples.rb +27 -0
  42. data/spec/feed_file_spec.rb +42 -0
  43. data/spec/feed_spec.rb +56 -0
  44. data/spec/feed_uri_spec.rb +81 -0
  45. data/spec/fixtures/gz_feed_file/nvdcve-1.1-recent.json.gz +0 -0
  46. data/spec/fixtures/nvdcve-1.1-recent.json +180 -0
  47. data/spec/fixtures/zip_feed_file/nvdcve-1.1-recent.json.zip +0 -0
  48. data/spec/gz_feed_file_spec.rb +66 -0
  49. data/spec/gz_feed_uri_spec.rb +35 -0
  50. data/spec/json_feed_file_spec.rb +18 -0
  51. data/spec/json_feeds_spec.rb +8 -0
  52. data/spec/meta_spec.rb +141 -0
  53. data/spec/schema/configurations/node_spec.rb +87 -0
  54. data/spec/schema/configurations_spec.rb +57 -0
  55. data/spec/schema/cpe/match_spec.rb +188 -0
  56. data/spec/schema/cpe/name_spec.rb +54 -0
  57. data/spec/schema/cve_feed_spec.rb +162 -0
  58. data/spec/schema/cve_item_spec.rb +116 -0
  59. data/spec/schema/impact/base_metric_v2_spec.rb +183 -0
  60. data/spec/schema/impact/base_metric_v3_spec.rb +80 -0
  61. data/spec/schema/impact_spec.rb +53 -0
  62. data/spec/schema/shared_examples.rb +136 -0
  63. data/spec/schema/timestamp_spec.rb +8 -0
  64. data/spec/spec_helper.rb +8 -0
  65. data/spec/zip_feed_file_spec.rb +66 -0
  66. data/spec/zip_feed_uri_spec.rb +35 -0
  67. metadata +156 -0
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+ require 'feed_file_examples'
3
+ require 'nvd/json_feeds/gz_feed_file'
4
+
5
+ require 'fileutils'
6
+ require 'shellwords'
7
+
8
+ describe NVD::JSONFeeds::GzFeedFile do
9
+ let(:fixtures_dir) { File.expand_path('../fixtures',__FILE__) }
10
+ let(:json_filename) { 'nvdcve-1.1-recent.json' }
11
+ let(:json_file) { File.join(fixtures_dir,json_filename) }
12
+
13
+ let(:gz_filename) { "#{json_filename}.gz" }
14
+ let(:dir) { File.join(fixtures_dir,'gz_feed_file') }
15
+ let(:path) { File.join(dir,gz_filename) }
16
+
17
+ subject { described_class.new(path) }
18
+
19
+ include_examples "FeedFile"
20
+
21
+ describe "#json_filename" do
22
+ it "must return the '.json' filename without the '.gz' extension" do
23
+ expect(subject.json_filename).to eq(json_filename)
24
+ end
25
+ end
26
+
27
+ describe "#read" do
28
+ it "must read the ungziped contents" do
29
+ expect(subject.read).to be == File.read(json_file)
30
+ end
31
+
32
+ context "when gunzip is not installed" do
33
+ before do
34
+ expect(subject).to receive(:`).and_raise(Errno::ENOENT)
35
+ end
36
+
37
+ it do
38
+ expect { subject.read }.to raise_error(ReadFailed)
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "#extract" do
44
+ let(:extracted_json_file) { File.join(dir,json_filename) }
45
+
46
+ it "must gunzip the '.gz' file and return a JSONFeedfile" do
47
+ feed_file = subject.extract
48
+
49
+ expect(feed_file).to be_kind_of(JSONFeedFile)
50
+ expect(feed_file.path).to eq(extracted_json_file)
51
+ expect(File.file?(extracted_json_file)).to be(true)
52
+ end
53
+
54
+ context "when gunzip fails" do
55
+ before do
56
+ allow(subject).to receive(:system).and_return(false)
57
+ end
58
+
59
+ it do
60
+ expect { subject.extract }.to raise_error(ExtractFailed)
61
+ end
62
+ end
63
+
64
+ after { FileUtils.rm_f(extracted_json_file) }
65
+ end
66
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'nvd/json_feeds/gz_feed_uri'
3
+
4
+ require 'fileutils'
5
+
6
+ describe NVD::JSONFeeds::GzFeedURI do
7
+ let(:fixtures_dir) { File.expand_path('../fixtures',__FILE__) }
8
+
9
+ let(:name) { :recent }
10
+ let(:ext) { '.json.gz' }
11
+
12
+ subject { described_class.new(name,ext) }
13
+
14
+ describe "#download", :integration do
15
+ let(:download_dir) { File.join(fixtures_dir,'download') }
16
+ let(:dest) { File.join(download_dir,subject.filename) }
17
+
18
+ before do
19
+ FileUtils.mkdir_p(download_dir)
20
+ FileUtils.rm_f(dest)
21
+ end
22
+
23
+ it "must return a GzFeedFile object for the newly downloaded file" do
24
+ feed_file = subject.download(dest)
25
+
26
+ expect(feed_file).to be_kind_of(GzFeedFile)
27
+ expect(feed_file.path).to eq(dest)
28
+ expect(File.file?(dest)).to be(true)
29
+ end
30
+
31
+ after do
32
+ FileUtils.rm_f(dest)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+ require 'feed_file_examples'
3
+ require 'nvd/json_feeds/json_feed_file'
4
+
5
+ describe JSONFeedFile do
6
+ let(:fixtures_dir) { File.expand_path('../fixtures',__FILE__) }
7
+ let(:path) { File.join(fixtures_dir,'nvdcve-1.1-recent.json') }
8
+
9
+ subject { described_class.new(path) }
10
+
11
+ include_examples "FeedFile"
12
+
13
+ describe "#read" do
14
+ it "must read the contents of the json file" do
15
+ expect(subject.read).to eq(File.read(path))
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+ require 'nvd/json_feeds'
3
+
4
+ describe NVD::JSONFeeds do
5
+ it "should have a VERSION constant" do
6
+ expect(subject.const_get('VERSION')).to_not be_empty
7
+ end
8
+ end
@@ -0,0 +1,141 @@
1
+ require 'spec_helper'
2
+ require 'nvd/json_feeds/meta'
3
+
4
+ describe Meta do
5
+ let(:last_modified_date_string) { '2021-01-18T08:01:40-05:00' }
6
+ let(:last_modified_date) { DateTime.parse(last_modified_date_string) }
7
+ let(:size) { 3499526 }
8
+ let(:zip_size) {212317 }
9
+ let(:gz_size) { 212173 }
10
+ let(:sha256) { '9013088A0E882B4FBB590A25D8CC00431AE4CA465CB66EF9E2F7F231EE54BE6F' }
11
+
12
+ describe "#initialize" do
13
+ subject do
14
+ described_class.new(last_modified_date,size,zip_size,gz_size,sha256)
15
+ end
16
+
17
+ it "must set #last_modified_date" do
18
+ expect(subject.last_modified_date).to eq(last_modified_date)
19
+ end
20
+
21
+ it "must set #size" do
22
+ expect(subject.size).to eq(size)
23
+ end
24
+
25
+ it "must set #zip_size" do
26
+ expect(subject.zip_size).to eq(zip_size)
27
+ end
28
+
29
+ it "must set #gz_size" do
30
+ expect(subject.gz_size).to eq(gz_size)
31
+ end
32
+
33
+ it "must set #sha256" do
34
+ expect(subject.sha256).to eq(sha256)
35
+ end
36
+ end
37
+
38
+ describe ".parse" do
39
+ let(:string) do
40
+ [
41
+ "lastModifiedDate:#{last_modified_date_string}",
42
+ "size:#{size}",
43
+ "zipSize:#{zip_size}",
44
+ "gzSize:#{gz_size}",
45
+ "sha256:#{sha256}"
46
+ ].join("\r\n")
47
+ end
48
+
49
+ subject { described_class }
50
+
51
+ it "must return a #{described_class} object" do
52
+ expect(subject.parse(string)).to be_kind_of(described_class)
53
+ end
54
+
55
+ context "lastModifiedDate" do
56
+ subject { described_class.parse(string) }
57
+
58
+ it "must parse into a DatEtime" do
59
+ expect(subject.last_modified_date).to be_kind_of(DateTime)
60
+ end
61
+
62
+ it { expect(subject.last_modified_date).to eq(last_modified_date) }
63
+ end
64
+
65
+ context "size" do
66
+ subject { described_class.parse(string) }
67
+
68
+ it "must convert into an Integer" do
69
+ expect(subject.size).to be_kind_of(Integer)
70
+ end
71
+
72
+ it { expect(subject.size).to eq(size) }
73
+ end
74
+
75
+ context "zipSize" do
76
+ subject { described_class.parse(string) }
77
+
78
+ it "must convert into an Integer" do
79
+ expect(subject.zip_size).to be_kind_of(Integer)
80
+ end
81
+
82
+ it { expect(subject.zip_size).to eq(zip_size) }
83
+ end
84
+
85
+ context "gzSize" do
86
+ subject { described_class.parse(string) }
87
+
88
+ it "must convert into an Integer" do
89
+ expect(subject.gz_size).to be_kind_of(Integer)
90
+ end
91
+
92
+ it { expect(subject.gz_size).to eq(gz_size) }
93
+ end
94
+
95
+ context "sha256" do
96
+ subject { described_class.parse(string) }
97
+
98
+ it { expect(subject.sha256).to eq(sha256) }
99
+ end
100
+
101
+ context "when lastModifiedDate is missing" do
102
+ let(:string) { super().sub(/^lastModifiedDate:.*$/,'') }
103
+
104
+ it do
105
+ expect { subject.parse(string) }.to raise_error(MetaParseError)
106
+ end
107
+ end
108
+
109
+ context "when size is missing" do
110
+ let(:string) { super().sub(/^size:.*$/,'') }
111
+
112
+ it do
113
+ expect { subject.parse(string) }.to raise_error(MetaParseError)
114
+ end
115
+ end
116
+
117
+ context "when zipSize is missing" do
118
+ let(:string) { super().sub(/^zipSize:.*$/,'') }
119
+
120
+ it do
121
+ expect { subject.parse(string) }.to raise_error(MetaParseError)
122
+ end
123
+ end
124
+
125
+ context "when gzSize is missing" do
126
+ let(:string) { super().sub(/^gzSize:.*$/,'') }
127
+
128
+ it do
129
+ expect { subject.parse(string) }.to raise_error(MetaParseError)
130
+ end
131
+ end
132
+
133
+ context "when sha256 is missing" do
134
+ let(:string) { super().sub(/^sha256:.*$/,'') }
135
+
136
+ it do
137
+ expect { subject.parse(string) }.to raise_error(MetaParseError)
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+ require 'schema/shared_examples'
3
+ require 'nvd/json_feeds/schema/configurations/node'
4
+
5
+ describe Schema::Configurations::Node do
6
+ describe "#initialize" do
7
+ context "when operator: is given" do
8
+ let(:operator) { :AND }
9
+
10
+ subject { described_class.new(operator: operator) }
11
+
12
+ it "must set #operator" do
13
+ expect(subject.operator).to eq(operator)
14
+ end
15
+ end
16
+
17
+ context "when operator: is not given" do
18
+ it { expect(subject.operator).to be(nil) }
19
+ end
20
+
21
+ context "when negate: is given" do
22
+ let(:negate) { true }
23
+
24
+ subject { described_class.new(negate: negate) }
25
+
26
+ it "must set #negate" do
27
+ expect(subject.negate).to eq(negate)
28
+ end
29
+ end
30
+
31
+ context "when negate: is not given" do
32
+ it { expect(subject.negate).to be(nil) }
33
+ end
34
+
35
+ context "when children: is given" do
36
+ let(:children) { [double(:Node1), double(:Node2)] }
37
+
38
+ subject { described_class.new(children: children) }
39
+
40
+ it "must set #children" do
41
+ expect(subject.children).to eq(children)
42
+ end
43
+ end
44
+
45
+ context "when children: is not given" do
46
+ it { expect(subject.children).to eq([]) }
47
+ end
48
+
49
+ context "when cpe_match: is given" do
50
+ let(:cpe_match) { [double("CPE::Match1"), double("CPE::Match2")] }
51
+
52
+ subject { described_class.new(cpe_match: cpe_match) }
53
+
54
+ it "must set #cpe_match" do
55
+ expect(subject.cpe_match).to eq(cpe_match)
56
+ end
57
+ end
58
+
59
+ context "when cpe_match: is not given" do
60
+ it { expect(subject.cpe_match).to eq([]) }
61
+ end
62
+ end
63
+
64
+ describe ".load" do
65
+ include_examples ".load"
66
+
67
+ let(:json_node) { json_tree['CVE_Items'][0]['configurations']['nodes'][0] }
68
+
69
+ include_examples "JSON field", json_key: 'operator',
70
+ method: :operator,
71
+ map: described_class::OPERATORS
72
+
73
+ include_examples "JSON field", json_key: 'negate',
74
+ method: :negate
75
+
76
+ pending 'need an example containing \"children\"'do
77
+ include_examples "JSON Array field", json_key: 'children',
78
+ method: :children,
79
+ element_class: described_class
80
+ end
81
+
82
+ include_examples "JSON Array field", json_key: 'cpe_match',
83
+ method: :cpe_match,
84
+ element_class: Schema::CPE::Match
85
+
86
+ end
87
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+ require 'schema/shared_examples'
3
+ require 'nvd/json_feeds/schema/configurations'
4
+
5
+ describe Schema::Configurations do
6
+ describe "#initialize" do
7
+ let(:data_version) { :"4.0" }
8
+
9
+ context "when data_version: is given" do
10
+ subject { described_class.new(data_version: data_version) }
11
+
12
+ it "must set #data_version" do
13
+ expect(subject.data_version).to eq(data_version)
14
+ end
15
+
16
+ context "and when nodes: is given" do
17
+ let(:nodes) { [double(:Node1), double(:Node2)] }
18
+
19
+ subject do
20
+ described_class.new(data_version: data_version, nodes: nodes)
21
+ end
22
+
23
+ it "must set #nodes" do
24
+ expect(subject.nodes).to eq(nodes)
25
+ end
26
+ end
27
+
28
+ context "but when nodes: is not given" do
29
+ it { expect(subject.nodes).to eq([]) }
30
+ end
31
+ end
32
+
33
+ context "when data_version: is not given" do
34
+ it do
35
+ expect {
36
+ described_class.new
37
+ }.to raise_error(ArgumentError)
38
+ end
39
+ end
40
+ end
41
+
42
+ describe ".load" do
43
+ include_examples ".load"
44
+
45
+ let(:json_node) { json_tree['CVE_Items'][0]['configurations'] }
46
+
47
+ include_examples "JSON field", json_key: 'CVE_data_version',
48
+ required: true,
49
+ method: :data_version,
50
+ map: described_class::DATA_VERSIONS
51
+
52
+ include_examples "JSON Array field", json_key: 'nodes',
53
+ method: :nodes,
54
+ element_class: described_class::Node
55
+
56
+ end
57
+ end
@@ -0,0 +1,188 @@
1
+ require 'spec_helper'
2
+ require 'schema/shared_examples'
3
+ require 'nvd/json_feeds/schema/cpe/match'
4
+
5
+ describe Schema::CPE::Match do
6
+ describe "#initialize" do
7
+ context "when cpe23uri: is given" do
8
+ let(:cpe23uri) { 'cpe:2.3:a:bitcoinsv:bitcoin_sv:*:*:*:*:*:*:*:*' }
9
+
10
+ context "and when vulnerable: is given" do
11
+ let(:vulnerable) { true }
12
+
13
+ subject do
14
+ described_class.new(
15
+ cpe23uri: cpe23uri,
16
+ vulnerable: vulnerable
17
+ )
18
+ end
19
+
20
+ it "must set #vulnerable" do
21
+ expect(subject.vulnerable).to eq(vulnerable)
22
+ end
23
+
24
+ context "and when version_start_excluding: is given" do
25
+ let(:version) { '1.2.3' }
26
+
27
+ subject do
28
+ described_class.new(
29
+ cpe23uri: cpe23uri,
30
+ vulnerable: vulnerable,
31
+ version_start_excluding: version
32
+ )
33
+ end
34
+
35
+ it "must set #version_start_excluding" do
36
+ expect(subject.version_start_excluding).to eq(version)
37
+ end
38
+ end
39
+
40
+ context "but when version_start_excluding: is not given" do
41
+ it { expect(subject.version_start_excluding).to be(nil) }
42
+ end
43
+
44
+ context "and when version_start_including: is given" do
45
+ let(:version) { '1.2.3' }
46
+
47
+ subject do
48
+ described_class.new(
49
+ cpe23uri: cpe23uri,
50
+ vulnerable: vulnerable,
51
+ version_start_including: version
52
+ )
53
+ end
54
+
55
+ it "must set #version_start_including" do
56
+ expect(subject.version_start_including).to eq(version)
57
+ end
58
+ end
59
+
60
+ context "but when version_start_including: is not given" do
61
+ it { expect(subject.version_start_including).to be(nil) }
62
+ end
63
+
64
+ context "and when version_end_excluding: is given" do
65
+ let(:version) { '1.2.3' }
66
+
67
+ subject do
68
+ described_class.new(
69
+ cpe23uri: cpe23uri,
70
+ vulnerable: vulnerable,
71
+ version_end_excluding: version
72
+ )
73
+ end
74
+
75
+ it "must set #version_end_excluding" do
76
+ expect(subject.version_end_excluding).to eq(version)
77
+ end
78
+ end
79
+
80
+ context "but when version_end_excluding: is not given" do
81
+ it { expect(subject.version_end_excluding).to be(nil) }
82
+ end
83
+
84
+ context "and when version_end_including: is given" do
85
+ let(:version) { '1.2.3' }
86
+
87
+ subject do
88
+ described_class.new(
89
+ cpe23uri: cpe23uri,
90
+ vulnerable: vulnerable,
91
+ version_end_including: version
92
+ )
93
+ end
94
+
95
+ it "must set #version_end_including" do
96
+ expect(subject.version_end_including).to eq(version)
97
+ end
98
+ end
99
+
100
+ context "but when version_end_including: is not given" do
101
+ it { expect(subject.version_end_including).to be(nil) }
102
+ end
103
+
104
+ context "and when cpe_name: is given" do
105
+ let(:cpe_name) { [double("CPE::Name1"), double("CPE::Name2")] }
106
+
107
+ subject do
108
+ described_class.new(
109
+ cpe23uri: cpe23uri,
110
+ vulnerable: vulnerable,
111
+ cpe_name: cpe_name
112
+ )
113
+ end
114
+
115
+ it "must set #cpe_name" do
116
+ expect(subject.cpe_name).to eq(cpe_name)
117
+ end
118
+ end
119
+
120
+ context "but when cpe_name: is not given" do
121
+ it { expect(subject.cpe_name).to eq([]) }
122
+ end
123
+ end
124
+
125
+ context "but when vulnerable: is not given" do
126
+ it do
127
+ expect {
128
+ described_class.new
129
+ }.to raise_error(ArgumentError)
130
+ end
131
+ end
132
+ end
133
+
134
+ context "but when cpe23uri: is not given" do
135
+ it do
136
+ expect {
137
+ described_class.new
138
+ }.to raise_error(ArgumentError)
139
+ end
140
+ end
141
+ end
142
+
143
+ describe ".load" do
144
+ include_examples ".load"
145
+
146
+ let(:json_node) do
147
+ json_tree['CVE_Items'][0]['configurations']['nodes'][0]['cpe_match'][0]
148
+ end
149
+
150
+ include_examples "JSON field", json_key: 'vulnerable',
151
+ required: true,
152
+ method: :vulnerable
153
+
154
+ include_examples "JSON field", json_key: 'cpe23Uri',
155
+ required: true,
156
+ method: :cpe23uri
157
+
158
+ pending 'need to find an example containing the "cpe22Uri" key' do
159
+ include_examples "JSON field", json_key: 'cpe22Uri',
160
+ required: true,
161
+ method: :cpe22uri
162
+ end
163
+
164
+ pending 'need to find an example containing the "versionStartExcluding" key' do
165
+ include_examples "JSON field", json_key: 'versionStartExcluding',
166
+ method: :version_start_excluding
167
+ end
168
+
169
+ pending 'need to find an example containing the "versionStartIncluding" key' do
170
+ include_examples "JSON field", json_key: 'versionStartIncluding',
171
+ method: :version_start_including
172
+ end
173
+
174
+ include_examples "JSON field", json_key: 'versionEndExcluding',
175
+ method: :version_end_excluding
176
+
177
+ pending 'need to find an example containing the "versionEndIncluding" key' do
178
+ include_examples "JSON field", json_key: 'versionEndIncluding',
179
+ method: :version_end_including
180
+ end
181
+
182
+ pending 'need to find an example containing the "cpe_name" key' do
183
+ include_examples "JSON Array field", json_key: 'cpe_name',
184
+ method: :cpe_name,
185
+ element_class: Schema::CPE::Name
186
+ end
187
+ end
188
+ end