cve_schema 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 +28 -0
- data/.gitignore +6 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +26 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +20 -0
- data/README.md +50 -0
- data/Rakefile +23 -0
- data/benchmark.rb +47 -0
- data/cve_schema.gemspec +61 -0
- data/gemspec.yml +19 -0
- data/lib/cve_schema.rb +2 -0
- data/lib/cve_schema/cve.rb +257 -0
- data/lib/cve_schema/cve/affects.rb +55 -0
- data/lib/cve_schema/cve/configuration.rb +14 -0
- data/lib/cve_schema/cve/credit.rb +14 -0
- data/lib/cve_schema/cve/data_meta.rb +185 -0
- data/lib/cve_schema/cve/description.rb +24 -0
- data/lib/cve_schema/cve/exploit.rb +14 -0
- data/lib/cve_schema/cve/has_lang_value.rb +93 -0
- data/lib/cve_schema/cve/id.rb +79 -0
- data/lib/cve_schema/cve/impact.rb +75 -0
- data/lib/cve_schema/cve/impact/cvss_v2.rb +318 -0
- data/lib/cve_schema/cve/impact/cvss_v3.rb +388 -0
- data/lib/cve_schema/cve/na.rb +8 -0
- data/lib/cve_schema/cve/problem_type.rb +56 -0
- data/lib/cve_schema/cve/product.rb +79 -0
- data/lib/cve_schema/cve/reference.rb +82 -0
- data/lib/cve_schema/cve/solution.rb +14 -0
- data/lib/cve_schema/cve/source.rb +75 -0
- data/lib/cve_schema/cve/timeline.rb +65 -0
- data/lib/cve_schema/cve/timestamp.rb +25 -0
- data/lib/cve_schema/cve/vendor.rb +83 -0
- data/lib/cve_schema/cve/version.rb +126 -0
- data/lib/cve_schema/cve/work_around.rb +14 -0
- data/lib/cve_schema/exceptions.rb +20 -0
- data/lib/cve_schema/version.rb +6 -0
- data/spec/affects_spec.rb +28 -0
- data/spec/configuration_spec.rb +6 -0
- data/spec/credit_spec.rb +6 -0
- data/spec/cve_schema_spec.rb +8 -0
- data/spec/cve_spec.rb +414 -0
- data/spec/data_meta_spec.rb +167 -0
- data/spec/description.rb +24 -0
- data/spec/exploit_spec.rb +6 -0
- data/spec/fixtures/CVE-2020-1994.json +140 -0
- data/spec/fixtures/CVE-2020-2005.json +152 -0
- data/spec/fixtures/CVE-2020-2050.json +233 -0
- data/spec/fixtures/CVE-2020-4700.json +99 -0
- data/spec/has_lang_value_spec.rb +56 -0
- data/spec/id_spec.rb +91 -0
- data/spec/impact/cvss_v3_spec.rb +118 -0
- data/spec/impact_spec.rb +45 -0
- data/spec/na_spec.rb +14 -0
- data/spec/problem_type_spec.rb +26 -0
- data/spec/product_spec.rb +73 -0
- data/spec/reference_spec.rb +70 -0
- data/spec/shared_examples.rb +19 -0
- data/spec/solution_spec.rb +6 -0
- data/spec/source_spec.rb +84 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/timeline_spec.rb +86 -0
- data/spec/timestamp_spec.rb +24 -0
- data/spec/vendor_spec.rb +73 -0
- data/spec/version_spec.rb +104 -0
- data/spec/work_around_spec.rb +6 -0
- metadata +133 -0
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cve_schema/exceptions'
|
4
|
+
require 'cve_schema/cve/na'
|
5
|
+
|
6
|
+
module CVESchema
|
7
|
+
class CVE
|
8
|
+
#
|
9
|
+
# Represents an element within the `"version_data"` JSON Array.
|
10
|
+
#
|
11
|
+
class Version
|
12
|
+
|
13
|
+
# @return [String]
|
14
|
+
attr_reader :version_value
|
15
|
+
|
16
|
+
VERSION_AFFECTED = {
|
17
|
+
'=' => :"=", # affects version_value
|
18
|
+
'<' => :"<", # affects versions prior to version_value
|
19
|
+
'>' => :">", # affects versions later than version_value
|
20
|
+
'<=' => :"<=", # affects version_value and prior versions
|
21
|
+
'>=' => :">=", # affects version_value and later versions
|
22
|
+
'!' => :"!", # doesn't affect version_value
|
23
|
+
'!<' => :"!<", # doesn't affect versions prior to version_value
|
24
|
+
'!>' => :"!>", # doesn't affect versions later than version_value
|
25
|
+
'!<=' => :"!<=",# doesn't affect version_value and prior versions
|
26
|
+
'!>=' => :"!>=",# doesn't affect version_value and later versions
|
27
|
+
'?' => :"?", # status of version_value is unknown
|
28
|
+
'?<' => :"?<", # status of versions prior to version_value is unknown
|
29
|
+
'?>' => :"?>", # status of versions later than version_value is unknown
|
30
|
+
'?<=' => :"?<=",# status of version_value and prior versions is unknown
|
31
|
+
'?>=' => :"?>=",# status of version_value and later versions is unknown
|
32
|
+
}
|
33
|
+
|
34
|
+
# @return [nil, :'=', :'<', :'>', :'<=', , :'>=', :'!', :'!<', :'!>', :'!<=', :'!>=', :'?', :'?<', :'?>', :'?<=', :'?>=']
|
35
|
+
attr_reader :version_affected
|
36
|
+
|
37
|
+
# @return [nil, String]
|
38
|
+
attr_reader :version_name
|
39
|
+
|
40
|
+
#
|
41
|
+
# Initializes the version.
|
42
|
+
#
|
43
|
+
# @param [String] version_value
|
44
|
+
#
|
45
|
+
# @param [String, nil] version_name
|
46
|
+
#
|
47
|
+
# @param [nil, :'=', :'<', :'>', :'<=', , :'>=', :'!', :'!<', :'!>', :'!<=', :'!>=', :'?', :'?<', :'?>', :'?<=', :'?>='] version_affected
|
48
|
+
# The version comparison operator. See {VERSION_AFFECTED}.
|
49
|
+
#
|
50
|
+
def initialize(version_value: , version_name: nil, version_affected: nil)
|
51
|
+
@version_value = version_value
|
52
|
+
@version_name = version_name
|
53
|
+
@version_affected = version_affected
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Maps the parsed JSON to a Symbol Hash for {#initialize}.
|
58
|
+
#
|
59
|
+
# @param [Hash{String => String}] json
|
60
|
+
# The parsed JSON.
|
61
|
+
#
|
62
|
+
# @return [Hash{Symbol => Object}]
|
63
|
+
# The mapped Symbol Hash.
|
64
|
+
#
|
65
|
+
# @raise [UnknownJSONValue]
|
66
|
+
# The `"version_affected"` JSON value was unknown.
|
67
|
+
#
|
68
|
+
# @api semipublic
|
69
|
+
#
|
70
|
+
def self.from_json(json)
|
71
|
+
{
|
72
|
+
version_affected: if (version_affected = json['version_affected'])
|
73
|
+
VERSION_AFFECTED.fetch(version_affected) do
|
74
|
+
raise UnknownJSONValue.new('version_affected',version_affected)
|
75
|
+
end
|
76
|
+
end,
|
77
|
+
|
78
|
+
version_name: json['version_name'],
|
79
|
+
version_value: json['version_value']
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# Loads the version object from parsed JSON.
|
85
|
+
#
|
86
|
+
# @param [Hash{String => String}] json
|
87
|
+
# The parsed JSON.
|
88
|
+
#
|
89
|
+
# @return [Version]
|
90
|
+
# The loaded version object.
|
91
|
+
#
|
92
|
+
# @raise [UnknownJSONValue]
|
93
|
+
# The `"version_affected"` JSON value was unknown.
|
94
|
+
#
|
95
|
+
# @api semipublic
|
96
|
+
#
|
97
|
+
def self.load(json)
|
98
|
+
new(**from_json(json))
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# Determines if the {#version_value} is `n/a`.
|
103
|
+
#
|
104
|
+
# @return [Boolean]
|
105
|
+
#
|
106
|
+
def na?
|
107
|
+
@version_value == NA
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Converts the version into a String.
|
112
|
+
#
|
113
|
+
# @return [String]
|
114
|
+
# The {#version_value} and additionally the {#version_affected}.
|
115
|
+
#
|
116
|
+
def to_s
|
117
|
+
if @version_affected
|
118
|
+
"#{@version_affected} #{@version_value}"
|
119
|
+
else
|
120
|
+
@version_value
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module CVESchema
|
2
|
+
class InvalidJSON < StandardError
|
3
|
+
end
|
4
|
+
|
5
|
+
class MissingJSONKey < InvalidJSON
|
6
|
+
|
7
|
+
def initialize(key)
|
8
|
+
super("missing #{key.inspect} key")
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
class UnknownJSONValue < InvalidJSON
|
14
|
+
|
15
|
+
def initialize(key,value)
|
16
|
+
super("unknown #{key.inspect} value: #{value.inspect}")
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'shared_examples'
|
3
|
+
require 'cve_schema/cve/affects'
|
4
|
+
|
5
|
+
describe CVESchema::CVE::Affects do
|
6
|
+
describe "#initialize" do
|
7
|
+
let(:vendor) { double(:vendor) }
|
8
|
+
|
9
|
+
subject { described_class.new(vendor) }
|
10
|
+
|
11
|
+
it "must set #vendor" do
|
12
|
+
expect(subject.vendor).to be(vendor)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe ".load" do
|
17
|
+
include_examples ".load"
|
18
|
+
|
19
|
+
let(:json_node) { json_tree['affects'] }
|
20
|
+
|
21
|
+
context '"vendor":' do
|
22
|
+
context '"vendor_data":' do
|
23
|
+
it { expect(subject.vendor).to_not be_empty }
|
24
|
+
it { expect(subject.vendor).to all(be_kind_of(CVESchema::CVE::Vendor)) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/spec/credit_spec.rb
ADDED
data/spec/cve_spec.rb
ADDED
@@ -0,0 +1,414 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'shared_examples'
|
3
|
+
require 'cve_schema/cve'
|
4
|
+
|
5
|
+
describe CVESchema::CVE do
|
6
|
+
describe "#initialize" do
|
7
|
+
let(:data_type) { :CVE }
|
8
|
+
let(:data_format) { :MITRE }
|
9
|
+
let(:data_version) { :"4.0" }
|
10
|
+
let(:data_meta) { double(:DataMeta) }
|
11
|
+
|
12
|
+
context "required keywords" do
|
13
|
+
context "when the data_type: keyword is not given" do
|
14
|
+
it do
|
15
|
+
expect {
|
16
|
+
described_class.new(
|
17
|
+
data_format: data_format,
|
18
|
+
data_version: data_version,
|
19
|
+
data_meta: data_meta
|
20
|
+
)
|
21
|
+
}.to raise_error(ArgumentError)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when the data_format: keyword is not given" do
|
26
|
+
it do
|
27
|
+
expect {
|
28
|
+
described_class.new(
|
29
|
+
data_type: data_type,
|
30
|
+
data_version: data_version,
|
31
|
+
data_meta: data_meta
|
32
|
+
)
|
33
|
+
}.to raise_error(ArgumentError)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when the data_version: keyword is not given" do
|
38
|
+
it do
|
39
|
+
expect {
|
40
|
+
described_class.new(
|
41
|
+
data_type: data_type,
|
42
|
+
data_format: data_format,
|
43
|
+
data_meta: data_meta
|
44
|
+
)
|
45
|
+
}.to raise_error(ArgumentError)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when the data_meta: keyword is not given" do
|
50
|
+
it do
|
51
|
+
expect {
|
52
|
+
described_class.new(
|
53
|
+
data_type: data_type,
|
54
|
+
data_format: data_format,
|
55
|
+
data_version: data_version,
|
56
|
+
)
|
57
|
+
}.to raise_error(ArgumentError)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "default values" do
|
63
|
+
subject do
|
64
|
+
described_class.new(
|
65
|
+
data_type: data_type,
|
66
|
+
data_format: data_format,
|
67
|
+
data_version: data_version,
|
68
|
+
data_meta: data_meta
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
it { expect(subject.affects).to eq(nil) }
|
73
|
+
it { expect(subject.configurations).to eq([]) }
|
74
|
+
it { expect(subject.problemtype).to eq([]) }
|
75
|
+
it { expect(subject.references).to eq([]) }
|
76
|
+
it { expect(subject.description).to eq([]) }
|
77
|
+
it { expect(subject.exploit).to eq([]) }
|
78
|
+
it { expect(subject.credit).to eq([]) }
|
79
|
+
it { expect(subject.impact).to eq(nil) }
|
80
|
+
it { expect(subject.solution).to eq([]) }
|
81
|
+
it { expect(subject.source).to eq(nil) }
|
82
|
+
it { expect(subject.work_around).to eq([]) }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe ".load" do
|
87
|
+
include_examples ".load"
|
88
|
+
|
89
|
+
it "must return a new CVE object" do
|
90
|
+
expect(subject).to be_kind_of(described_class)
|
91
|
+
end
|
92
|
+
|
93
|
+
context '"data_type":' do
|
94
|
+
let(:json_value) { json_node['data_type'] }
|
95
|
+
let(:expected) { described_class::DATA_TYPES[json_value] }
|
96
|
+
|
97
|
+
it "must convert and set #data_type" do
|
98
|
+
expect(subject.data_type).to eq(expected)
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'whne "data_type" key is missing' do
|
102
|
+
before { json_node.delete('data_type') }
|
103
|
+
|
104
|
+
it do
|
105
|
+
expect {
|
106
|
+
described_class.load(json_node)
|
107
|
+
}.to raise_error(described_class::MissingJSONKey)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context '"data_format":' do
|
113
|
+
let(:json_value) { json_node['data_format'] }
|
114
|
+
let(:expected) { described_class::DATA_FORMAT[json_value] }
|
115
|
+
|
116
|
+
it "must convert and set #data_format" do
|
117
|
+
expect(subject.data_format).to eq(expected)
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'whne "data_format" key is missing' do
|
121
|
+
before { json_node.delete('data_format') }
|
122
|
+
|
123
|
+
it do
|
124
|
+
expect {
|
125
|
+
described_class.load(json_node)
|
126
|
+
}.to raise_error(described_class::MissingJSONKey)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context '"data_version":' do
|
132
|
+
let(:json_value) { json_node['data_version'] }
|
133
|
+
let(:expected) { described_class::DATA_VERSIONS[json_value] }
|
134
|
+
|
135
|
+
it "must convert and set #data_version" do
|
136
|
+
expect(subject.data_version).to eq(expected)
|
137
|
+
end
|
138
|
+
|
139
|
+
context 'whne "data_version" key is missing' do
|
140
|
+
before { json_node.delete('data_version') }
|
141
|
+
|
142
|
+
it do
|
143
|
+
expect {
|
144
|
+
described_class.load(json_node)
|
145
|
+
}.to raise_error(described_class::MissingJSONKey)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context '"data_meta":' do
|
151
|
+
let(:json_value) { json_node['CVE_data_meta'] }
|
152
|
+
|
153
|
+
it "must convert the JSON Hash into a DataMeta objects and set #data_meta" do
|
154
|
+
expect(subject.data_meta).to be_kind_of(described_class::DataMeta)
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'when "CVE_data_meta" key is missing' do
|
158
|
+
before { json_node.delete('CVE_data_meta') }
|
159
|
+
|
160
|
+
it do
|
161
|
+
expect {
|
162
|
+
described_class.load(json_node)
|
163
|
+
}.to raise_error(described_class::MissingJSONKey)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context '"affects":' do
|
169
|
+
context "when present" do
|
170
|
+
describe "#affects" do
|
171
|
+
it do
|
172
|
+
expect(subject.affects).to be_kind_of(described_class::Affects)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context "when missing" do
|
178
|
+
before { json_node.delete('affects') }
|
179
|
+
|
180
|
+
describe "#affects" do
|
181
|
+
it { expect(subject.affects).to be_nil }
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
context '"configuration":' do
|
187
|
+
let(:cve_id) { 'CVE-2020-2005' }
|
188
|
+
|
189
|
+
context "when present" do
|
190
|
+
describe "#configuration" do
|
191
|
+
it { expect(subject.configuration).to be_kind_of(Array) }
|
192
|
+
it { expect(subject.configuration).to_not be_empty }
|
193
|
+
it do
|
194
|
+
expect(subject.configuration).to all(be_kind_of(described_class::Configuration))
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
context "when missing" do
|
200
|
+
before { json_node.delete('configuration') }
|
201
|
+
|
202
|
+
describe "#configuration" do
|
203
|
+
it { expect(subject.configuration).to eq([]) }
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context '"problemtype":' do
|
209
|
+
context "when present" do
|
210
|
+
describe "#problemtype" do
|
211
|
+
it { expect(subject.problemtype).to be_kind_of(Array) }
|
212
|
+
it { expect(subject.problemtype).to_not be_empty }
|
213
|
+
|
214
|
+
it do
|
215
|
+
expect(subject.problemtype).to all(be_kind_of(described_class::ProblemType))
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
context "when missing" do
|
221
|
+
before { json_node.delete('problemtype') }
|
222
|
+
|
223
|
+
describe "#problemtype" do
|
224
|
+
it { expect(subject.problemtype).to eq([]) }
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
context '"references":' do
|
230
|
+
context "when present" do
|
231
|
+
describe "#references" do
|
232
|
+
it { expect(subject.references).to be_kind_of(Array) }
|
233
|
+
it { expect(subject.references).to_not be_empty }
|
234
|
+
|
235
|
+
it do
|
236
|
+
expect(subject.references).to all(be_kind_of(described_class::Reference))
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
context "when missing" do
|
242
|
+
before { json_node.delete('references') }
|
243
|
+
|
244
|
+
describe "#references" do
|
245
|
+
it { expect(subject.references).to eq([]) }
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
context '"description":' do
|
251
|
+
context "when present" do
|
252
|
+
describe "#description" do
|
253
|
+
it { expect(subject.description).to be_kind_of(Array) }
|
254
|
+
it { expect(subject.description).to_not be_empty }
|
255
|
+
|
256
|
+
it do
|
257
|
+
expect(subject.description).to all(be_kind_of(described_class::Description))
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
context "when missing" do
|
263
|
+
before { json_node.delete('description') }
|
264
|
+
|
265
|
+
describe "#description" do
|
266
|
+
it { expect(subject.description).to eq([]) }
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
context '"exploit":' do
|
272
|
+
let(:cve_id) { 'CVE-2020-2050' }
|
273
|
+
|
274
|
+
context "when present" do
|
275
|
+
describe "#exploit" do
|
276
|
+
it { expect(subject.exploit).to be_kind_of(Array) }
|
277
|
+
it { expect(subject.exploit).to_not be_empty }
|
278
|
+
|
279
|
+
it do
|
280
|
+
expect(subject.exploit).to all(be_kind_of(described_class::Exploit))
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
context "when missing" do
|
286
|
+
before { json_node.delete('exploit') }
|
287
|
+
|
288
|
+
describe "#exploit" do
|
289
|
+
it { expect(subject.exploit).to eq([]) }
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
context '"credit":' do
|
295
|
+
context "when present" do
|
296
|
+
describe "#credit" do
|
297
|
+
it { expect(subject.credit).to be_kind_of(Array) }
|
298
|
+
it { expect(subject.credit).to_not be_empty }
|
299
|
+
|
300
|
+
it do
|
301
|
+
expect(subject.credit).to all(be_kind_of(described_class::Credit))
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
context "when missing" do
|
307
|
+
before { json_node.delete('credit') }
|
308
|
+
|
309
|
+
describe "#credit" do
|
310
|
+
it { expect(subject.credit).to eq([]) }
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
context '"impact":' do
|
316
|
+
context "when present" do
|
317
|
+
describe "#impact" do
|
318
|
+
it { expect(subject.impact).to be_kind_of(described_class::Impact) }
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
context "when missing" do
|
323
|
+
before { json_node.delete('impact') }
|
324
|
+
|
325
|
+
describe "#impact" do
|
326
|
+
it { expect(subject.impact).to be_nil }
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
context '"solution":' do
|
332
|
+
context "when present" do
|
333
|
+
describe "#solution" do
|
334
|
+
it { expect(subject.solution).to be_kind_of(Array) }
|
335
|
+
it { expect(subject.solution).to_not be_empty }
|
336
|
+
|
337
|
+
it do
|
338
|
+
expect(subject.solution).to all(be_kind_of(described_class::Solution))
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
context "when missing" do
|
344
|
+
before { json_node.delete('solution') }
|
345
|
+
|
346
|
+
describe "#solution" do
|
347
|
+
it { expect(subject.solution).to eq([]) }
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
context '"source":' do
|
353
|
+
context "when present" do
|
354
|
+
describe "#source" do
|
355
|
+
it { expect(subject.source).to be_kind_of(described_class::Source) }
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
context "when missing" do
|
360
|
+
before { json_node.delete('source') }
|
361
|
+
|
362
|
+
describe "#source" do
|
363
|
+
it { expect(subject.source).to be_nil }
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
context '"work_around":' do
|
369
|
+
let(:cve_id) { 'CVE-2020-2005' }
|
370
|
+
|
371
|
+
context "when present" do
|
372
|
+
describe "#work_around" do
|
373
|
+
it { expect(subject.work_around).to be_kind_of(Array) }
|
374
|
+
it { expect(subject.work_around).to_not be_empty }
|
375
|
+
|
376
|
+
it do
|
377
|
+
expect(subject.work_around).to all(be_kind_of(described_class::WorkAround))
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
context "when missing" do
|
383
|
+
before { json_node.delete('work_around') }
|
384
|
+
|
385
|
+
describe "#work_around" do
|
386
|
+
it { expect(subject.work_around).to eq([]) }
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
context '"timeline":' do
|
392
|
+
let(:cve_id) { 'CVE-2020-2005' }
|
393
|
+
|
394
|
+
context "when present" do
|
395
|
+
describe "#timeline" do
|
396
|
+
it { expect(subject.timeline).to be_kind_of(Array) }
|
397
|
+
it { expect(subject.timeline).to_not be_empty }
|
398
|
+
|
399
|
+
it do
|
400
|
+
expect(subject.timeline).to all(be_kind_of(described_class::Timeline))
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
context "when missing" do
|
406
|
+
before { json_node.delete('timeline') }
|
407
|
+
|
408
|
+
describe "#timeline" do
|
409
|
+
it { expect(subject.timeline).to eq([]) }
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|