Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wpscanteam
GitHub Repository: wpscanteam/wpscan
Path: blob/master/spec/lib/db/vuln_api_spec.rb
486 views
1
# frozen_string_literal: true
2
3
describe WPScan::DB::VulnApi do
4
subject(:api) { described_class }
5
6
let(:request_headers) do
7
{
8
'User-Agent' => WPScan::Browser.instance.default_user_agent,
9
'Authorization' => "Token token=#{api.token}"
10
}
11
end
12
13
before do
14
# Reset the default_request_params
15
api.instance_variable_set(:@default_request_params, nil)
16
end
17
18
describe '#uri' do
19
its(:uri) { should be_a Addressable::URI }
20
end
21
22
describe '#token, @token=' do
23
context 'when no token set' do
24
before { api.token = nil } # In case it was set by a previous spec
25
26
its(:token) { should be nil }
27
end
28
29
context 'when token set' do
30
it 'returns it' do
31
api.token = 's3cRet'
32
33
expect(api.token).to eql 's3cRet'
34
end
35
end
36
end
37
38
describe '#get' do
39
context 'when no token' do
40
before { api.token = nil }
41
42
it 'returns an empty hash' do
43
expect(api.get('test')).to eql({})
44
end
45
end
46
47
context 'when a token' do
48
before { api.token = 's3cRet' }
49
50
let(:path) { 'path' }
51
52
context 'when params used' do
53
it 'ensures they override the defaults' do
54
expect(Typhoeus).to receive(:get)
55
.with(api.uri.join(path), hash_including(cache_ttl: 0))
56
.and_return(Typhoeus::Response.new(code: 404))
57
58
api.get(path, cache_ttl: 0)
59
end
60
end
61
62
context 'when no timeouts' do
63
before do
64
stub_request(:get, api.uri.join(path))
65
.with(headers: request_headers)
66
.to_return(status: code, body: body)
67
end
68
69
context 'when 200' do
70
let(:code) { 200 }
71
let(:body) { { data: 'something' }.to_json }
72
73
it 'returns the expected hash' do
74
expect(api.get(path)).to eql('data' => 'something')
75
end
76
end
77
78
context 'when 403' do
79
let(:code) { 403 }
80
let(:body) { { status: 'forbidden' }.to_json }
81
82
it 'returns the expected hash' do
83
expect(api.get(path)).to eql('status' => 'forbidden')
84
end
85
end
86
87
context 'when 404' do
88
let(:code) { 404 }
89
let(:body) { { error: 'Not found' }.to_json }
90
91
it 'returns an empty hash' do
92
expect(api.get(path)).to eql({})
93
end
94
end
95
96
context 'when 429 (API Limit Reached)' do
97
let(:code) { 429 }
98
let(:body) { { status: 'rate limit hit' }.to_json }
99
100
it 'returns an empty hash' do
101
expect(api.get(path)).to eql({})
102
end
103
end
104
end
105
106
context 'when timeouts' do
107
context 'when all requests timeout' do
108
before do
109
stub_request(:get, api.uri.join('path'))
110
.with(headers: request_headers)
111
.to_return(status: 0)
112
end
113
114
it 'tries 3 times and returns the hash with the error' do
115
expect(api).to receive(:sleep).with(1).exactly(3).times
116
117
result = api.get('path')
118
119
expect(result['http_error']).to be_a WPScan::Error::HTTP
120
expect(api.default_request_params[:headers]['X-Retry']).to eql 3
121
end
122
end
123
124
context 'when only the first request timeout' do
125
before do
126
stub_request(:get, api.uri.join('path'))
127
.with(headers: request_headers)
128
.to_return(status: 0).then
129
.to_return(status: 200, body: { data: 'test' }.to_json)
130
end
131
132
it 'tries 1 time and returns expected data' do
133
expect(api).to receive(:sleep).with(1).exactly(1).times
134
135
expect(api.get('path')).to eql('data' => 'test')
136
expect(api.default_request_params[:headers]['X-Retry']).to eql 1
137
end
138
end
139
end
140
end
141
end
142
143
describe '#plugin_data' do
144
before { api.token = api_token }
145
146
context 'when no --api-token' do
147
let(:api_token) { nil }
148
149
it 'returns an empty hash' do
150
expect(api.plugin_data('slug')).to eql({})
151
end
152
end
153
154
context 'when valid --api-token' do
155
let(:api_token) { 's3cRet' }
156
157
context 'when the slug exist' do
158
it 'calls the correct URL' do
159
stub_request(:get, api.uri.join('plugins/slug'))
160
.to_return(status: 200, body: { slug: { p: 'aa' } }.to_json)
161
162
expect(api.plugin_data('slug')).to eql('p' => 'aa')
163
end
164
end
165
166
context 'when the slug does not exist' do
167
it 'returns an empty hash' do
168
stub_request(:get, api.uri.join('plugins/slug-404')).to_return(status: 404, body: '{}')
169
170
expect(api.plugin_data('slug-404')).to eql({})
171
end
172
end
173
174
context 'when API limit reached' do
175
it 'returns an empty hash' do
176
stub_request(:get, api.uri.join('plugins/slug-429'))
177
.to_return(status: 429, body: { status: 'rate limit hit' }.to_json)
178
179
expect(api.plugin_data('slug-429')).to eql({})
180
end
181
end
182
end
183
end
184
185
describe '#theme_data' do
186
before { api.token = api_token }
187
188
context 'when no --api-token' do
189
let(:api_token) { nil }
190
191
it 'returns an empty hash' do
192
expect(api.theme_data('slug')).to eql({})
193
end
194
end
195
196
context 'when valid --api-token' do
197
let(:api_token) { 's3cRet' }
198
199
context 'when the slug exist' do
200
it 'calls the correct URL' do
201
stub_request(:get, api.uri.join('themes/slug'))
202
.to_return(status: 200, body: { slug: { t: 'aa' } }.to_json)
203
204
expect(api.theme_data('slug')).to eql('t' => 'aa')
205
end
206
end
207
208
context 'when the slug does not exist' do
209
it 'returns an empty hash' do
210
stub_request(:get, api.uri.join('themes/slug-404')).to_return(status: 404, body: '{}')
211
212
expect(api.theme_data('slug-404')).to eql({})
213
end
214
end
215
216
context 'when API limit reached' do
217
it 'returns an empty hash' do
218
stub_request(:get, api.uri.join('themes/slug-429'))
219
.to_return(status: 429, body: { status: 'rate limit hit' }.to_json)
220
221
expect(api.theme_data('slug-429')).to eql({})
222
end
223
end
224
end
225
end
226
227
describe '#wordpress_data' do
228
before { api.token = api_token }
229
230
context 'when no --api-token' do
231
let(:api_token) { nil }
232
233
it 'returns an empty hash' do
234
expect(api.wordpress_data('1.2')).to eql({})
235
end
236
end
237
238
context 'when valid --api-token' do
239
let(:api_token) { 's3cRet' }
240
241
context 'when the version exist' do
242
it 'calls the correct URL' do
243
stub_request(:get, api.uri.join('wordpresses/522'))
244
.to_return(status: 200, body: { '5.2.2' => { w: 'aa' } }.to_json)
245
246
expect(api.wordpress_data('5.2.2')).to eql('w' => 'aa')
247
end
248
end
249
250
context 'when the version does not exist' do
251
it 'returns an empty hash' do
252
stub_request(:get, api.uri.join('wordpresses/11')).to_return(status: 404, body: '{}')
253
254
expect(api.wordpress_data('1.1')).to eql({})
255
end
256
end
257
258
context 'when API limit reached' do
259
it 'returns an empty hash' do
260
stub_request(:get, api.uri.join('wordpresses/429'))
261
.to_return(status: 429, body: { status: 'rate limit hit' }.to_json)
262
263
expect(api.wordpress_data('4.2.9')).to eql({})
264
end
265
end
266
end
267
end
268
269
describe '#status' do
270
before do
271
api.token = 's3cRet'
272
273
stub_request(:get, api.uri.join('status'))
274
.with(query: { version: WPScan::VERSION },
275
headers: request_headers)
276
.to_return(status: code, body: return_body.to_json)
277
end
278
279
let(:code) { 200 }
280
let(:return_body) { {} }
281
282
context 'when 200' do
283
let(:return_body) { { success: true, plan: 'free', requests_remaining: 100 } }
284
285
it 'returns the expected hash' do
286
status = api.status
287
288
expect(status['success']).to be true
289
expect(status['plan']).to eql 'free'
290
expect(status['requests_remaining']).to eql 100
291
end
292
293
context 'when unlimited requests' do
294
let(:return_body) { super().merge(plan: 'enterprise', requests_remaining: -1) }
295
296
it 'returns the expected hash, witht he correct requests_remaining' do
297
status = api.status
298
299
expect(status['success']).to be true
300
expect(status['plan']).to eql 'enterprise'
301
expect(status['requests_remaining']).to eql 'Unlimited'
302
end
303
end
304
305
context 'when CF-Connecting-IP provided in CLI' do
306
let(:return_body) { { success: true, plan: 'free', requests_remaining: 100 } }
307
308
before do
309
WPScan::Browser.instance.headers = { 'CF-Connecting-IP' => '123.123.123.123' }
310
end
311
312
it 'does not contain the CF-Connecting-IP header in the request' do
313
status = api.status
314
315
expect(status['success']).to be true
316
expect(status['plan']).to eql 'free'
317
expect(status['requests_remaining']).to eql 100
318
end
319
end
320
end
321
322
# When invalid/empty API token
323
context 'when 403' do
324
let(:code) { 403 }
325
let(:return_body) { { status: 'forbidden' } }
326
327
it 'returns the expected hash' do
328
expect(api.status['status']).to eql 'forbidden'
329
end
330
end
331
332
context 'otherwise' do
333
let(:code) { 0 }
334
335
it 'returns the expected hash with the response as an exception' do
336
status = api.status
337
338
expect(status['http_error']).to be_a WPScan::Error::HTTP
339
end
340
end
341
end
342
end
343
344