Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wpscanteam
GitHub Repository: wpscanteam/wpscan
Path: blob/master/spec/app/models/theme_spec.rb
486 views
1
# frozen_string_literal: true
2
3
describe WPScan::Model::Theme do
4
subject(:theme) { described_class.new(slug, blog, opts) }
5
let(:slug) { 'spec' }
6
let(:blog) { WPScan::Target.new('http://wp.lab/') }
7
let(:opts) { {} }
8
let(:fixtures) { FIXTURES.join('models', 'theme') }
9
10
before { expect(blog).to receive(:content_dir).at_least(1).and_return('wp-content') }
11
12
describe '#new' do
13
before { stub_request(:get, /.*\.css\z/).to_return(body: File.read(fixture)) }
14
15
let(:fixture) { fixtures.join('style.css') }
16
17
its(:url) { should eql 'http://wp.lab/wp-content/themes/spec/' }
18
its(:style_url) { should eql 'http://wp.lab/wp-content/themes/spec/style.css' }
19
20
its(:style_name) { should eql 'Twenty Fifteen' }
21
its(:style_uri) { should eql 'https://wordpress.org/themes/twentyfifteen' }
22
its(:author) { should eql 'the WordPress team' }
23
its(:author_uri) { should eql nil }
24
its(:template) { should eql nil }
25
its(:description) { should eql 'Our 2015 default theme is clean, blog-focused.' }
26
its(:license) { should eql 'GNU General Public License v2 or later' }
27
its(:license_uri) { should eql 'http://www.gnu.org/licenses/gpl-2.0.html' }
28
its(:tags) { should eql 'black, blue, gray, pink, purple, white, yellow.' }
29
its(:text_domain) { should eql 'twentyfifteen' }
30
31
context 'when opts[:style_url]' do
32
let(:opts) { super().merge(style_url: 'http://wp.lab/wp-content/themes/spec/custom.css') }
33
34
its(:style_url) { should eql opts[:style_url] }
35
end
36
37
context 'when some new lines are stripped' do
38
let(:fixture) { fixtures.join('stripped_new_lines.css') }
39
40
its(:style_name) { should eql 'Divi' }
41
its(:style_uri) { should eql 'http://www.elegantthemes.com/gallery/divi/' }
42
its(:license_uri) { should eql 'http://www.gnu.org/licenses/gpl-2.0.html' }
43
end
44
45
context 'when no tags' do
46
let(:fixture) { fixtures.join('no_tags.css') }
47
48
its(:author) { should eql nil }
49
end
50
end
51
52
describe '#version' do
53
after do
54
stub_request(:get, /.*\.css\z/)
55
.to_return(body: File.read(fixtures.join('style.css')))
56
57
expect(WPScan::Finders::ThemeVersion::Base).to receive(:find).with(theme, @expected_opts)
58
theme.version(version_opts)
59
end
60
61
let(:default_opts) { {} }
62
63
context 'when no :detection_mode' do
64
context 'when no :mode opt supplied' do
65
let(:version_opts) { { something: 'k' } }
66
67
it 'calls the finder with the correct parameters' do
68
@expected_opts = version_opts
69
end
70
end
71
72
context 'when :mode supplied' do
73
let(:version_opts) { { mode: :passive } }
74
75
it 'calls the finder with the correct parameters' do
76
@expected_opts = default_opts.merge(mode: :passive)
77
end
78
end
79
end
80
81
context 'when :detection_mode' do
82
let(:opts) { super().merge(mode: :passive) }
83
84
context 'when no :mode' do
85
let(:version_opts) { {} }
86
87
it 'calls the finder without mode' do
88
@expected_opts = version_opts
89
end
90
end
91
92
context 'when :mode' do
93
let(:version_opts) { { mode: :mixed } }
94
95
it 'calls the finder with the :mixed mode' do
96
@expected_opts = default_opts.merge(mode: :mixed)
97
end
98
end
99
end
100
end
101
102
describe '#latest_version, #last_updated, #popular' do
103
before do
104
stub_request(:get, /.*\.css\z/)
105
allow(theme).to receive(:db_data).and_return(db_data)
106
end
107
108
context 'when no db_data and no metadata' do
109
let(:slug) { 'not-known' }
110
let(:db_data) { {} }
111
112
its(:latest_version) { should be_nil }
113
its(:last_updated) { should be_nil }
114
its(:popular?) { should be false }
115
end
116
117
context 'when no db_data but metadata' do
118
let(:slug) { 'no-vulns-popular' }
119
let(:db_data) { {} }
120
121
its(:latest_version) { should eql WPScan::Model::Version.new('2.0') }
122
its(:last_updated) { should eql '2015-05-16T00:00:00.000Z' }
123
its(:popular?) { should be true }
124
end
125
126
context 'when db_data' do
127
let(:slug) { 'no-vulns-popular' }
128
let(:db_data) { vuln_api_data_for('themes/no-vulns-popular') }
129
130
its(:latest_version) { should eql WPScan::Model::Version.new('2.2') }
131
its(:last_updated) { should eql '2015-05-16T00:00:00.000Z-via-api' }
132
its(:popular?) { should be true }
133
end
134
end
135
136
describe '#outdated?' do
137
before do
138
stub_request(:get, /.*\.css\z/)
139
allow(theme).to receive(:db_data).and_return({})
140
end
141
142
context 'when last_version' do
143
let(:slug) { 'no-vulns-popular' }
144
145
context 'when no version' do
146
before { expect(theme).to receive(:version).at_least(1).and_return(nil) }
147
148
its(:outdated?) { should eql false }
149
end
150
151
context 'when version' do
152
before do
153
expect(theme)
154
.to receive(:version)
155
.at_least(1)
156
.and_return(WPScan::Model::Version.new(version_number))
157
end
158
159
context 'when version < latest_version' do
160
let(:version_number) { '1.2' }
161
162
its(:outdated?) { should eql true }
163
end
164
165
context 'when version >= latest_version' do
166
let(:version_number) { '3.0' }
167
168
its(:outdated?) { should eql false }
169
end
170
end
171
end
172
173
context 'when no latest_version' do
174
let(:slug) { 'vulnerable-not-popular' }
175
176
context 'when no version' do
177
before { expect(theme).to receive(:version).at_least(1).and_return(nil) }
178
179
its(:outdated?) { should eql false }
180
end
181
182
context 'when version' do
183
before do
184
expect(theme)
185
.to receive(:version)
186
.at_least(1)
187
.and_return(WPScan::Model::Version.new('1.0'))
188
end
189
190
its(:outdated?) { should eql false }
191
end
192
end
193
end
194
195
describe '#vulnerabilities' do
196
before do
197
stub_request(:get, /.*\.css\z/)
198
allow(theme).to receive(:db_data).and_return(db_data)
199
end
200
201
after do
202
expect(theme.vulnerabilities).to eq @expected
203
expect(theme.vulnerable?).to eql !@expected.empty?
204
end
205
206
context 'when theme not in the DB' do
207
let(:slug) { 'not-in-db' }
208
let(:db_data) { {} }
209
210
it 'returns an empty array' do
211
@expected = []
212
end
213
end
214
215
context 'when in the DB' do
216
context 'when no vulnerabilities' do
217
let(:slug) { 'no-vulns-popular' }
218
let(:db_data) { vuln_api_data_for('themes/no-vulns-popular') }
219
220
it 'returns an empty array' do
221
@expected = []
222
end
223
end
224
225
context 'when vulnerabilities' do
226
let(:slug) { 'vulnerable-not-popular' }
227
let(:db_data) { vuln_api_data_for('themes/vulnerable-not-popular') }
228
229
let(:all_vulns) do
230
[
231
WPScan::Vulnerability.new(
232
'First Vuln',
233
references: { wpvulndb: '1' },
234
type: 'LFI',
235
fixed_in: '6.3.10'
236
),
237
WPScan::Vulnerability.new('No Fixed In', references: { wpvulndb: '2' })
238
]
239
end
240
241
context 'when no theme version' do
242
before { expect(theme).to receive(:version).at_least(1).and_return(false) }
243
244
it 'returns all the vulnerabilities' do
245
@expected = all_vulns
246
end
247
end
248
249
context 'when theme version' do
250
before do
251
expect(theme)
252
.to receive(:version)
253
.at_least(1)
254
.and_return(WPScan::Model::Version.new(number))
255
end
256
257
context 'when < to a fixed_in' do
258
let(:number) { '5.0' }
259
260
it 'returns it' do
261
@expected = all_vulns
262
end
263
end
264
265
context 'when >= to a fixed_in' do
266
let(:number) { '6.3.10' }
267
268
it 'does not return it ' do
269
@expected = [all_vulns.last]
270
end
271
end
272
end
273
end
274
end
275
end
276
277
describe '#parent_theme' do
278
before do
279
stub_request(:get, blog.url('wp-content/themes/spec/style.css'))
280
.to_return(body: File.read(fixtures.join(main_theme)))
281
end
282
283
context 'when no template' do
284
let(:main_theme) { 'style.css' }
285
286
it 'returns nil' do
287
expect(theme.parent_theme).to eql nil
288
end
289
end
290
291
context 'when a template' do
292
let(:main_theme) { 'child_style.css' }
293
let(:parent_url) { blog.url('wp-content/themes/twentyfourteen/custom.css') }
294
295
before do
296
stub_request(:get, parent_url)
297
.to_return(body: File.read(fixtures.join('style.css')))
298
end
299
300
%w[child_style windows_line_endings].each do |fixture|
301
context "when #{fixture}" do
302
let(:main_theme) { "#{fixture}.css" }
303
304
it 'returns the expected theme' do
305
parent = theme.parent_theme
306
307
expect(parent).to eql described_class.new(
308
'twentyfourteen', blog,
309
style_url: parent_url,
310
confidence: 100,
311
found_by: 'Parent Themes (Passive Detection)'
312
)
313
expect(parent.style_url).to eql parent_url
314
end
315
end
316
end
317
end
318
end
319
320
describe '#parent_themes' do
321
xit
322
end
323
324
describe '#==' do
325
before { stub_request(:get, /.*\.css\z/) }
326
327
context 'when default style' do
328
it 'returns true when equal' do
329
expect(theme == described_class.new(slug, blog, opts)).to be true
330
end
331
332
it 'returns false when not equal' do
333
expect(theme == described_class.new(slug, blog, opts.merge(style_url: 'spec.css'))).to be false
334
end
335
end
336
337
context 'when custom style' do
338
let(:opts) { super().merge(style_url: 'spec.css') }
339
340
it 'returns true when equal' do
341
expect(theme == described_class.new(slug, blog, opts.merge(style_url: 'spec.css'))).to be true
342
end
343
344
it 'returns false when not equal' do
345
expect(theme == described_class.new(slug, blog, opts.merge(style_url: 'spec2.css'))).to be false
346
end
347
end
348
end
349
end
350
351