arachni 1.0.5 → 1.0.6
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 +4 -4
- data/CHANGELOG.md +50 -0
- data/README.md +9 -2
- data/components/checks/active/code_injection.rb +5 -5
- data/components/checks/active/code_injection_timing.rb +3 -3
- data/components/checks/active/no_sql_injection_differential.rb +3 -2
- data/components/checks/active/os_cmd_injection.rb +11 -5
- data/components/checks/active/os_cmd_injection_timing.rb +11 -4
- data/components/checks/active/path_traversal.rb +2 -2
- data/components/checks/active/sql_injection.rb +1 -1
- data/components/checks/active/sql_injection/patterns/mssql +1 -0
- data/components/checks/active/sql_injection_differential.rb +3 -2
- data/components/checks/active/unvalidated_redirect.rb +3 -3
- data/components/checks/passive/common_directories/directories.txt +2 -0
- data/components/checks/passive/common_files/filenames.txt +1 -0
- data/lib/arachni/browser.rb +17 -1
- data/lib/arachni/check/auditor.rb +5 -2
- data/lib/arachni/check/base.rb +30 -5
- data/lib/arachni/element/capabilities/analyzable/differential.rb +2 -5
- data/lib/arachni/element/capabilities/auditable.rb +3 -1
- data/lib/arachni/element/capabilities/with_dom.rb +1 -0
- data/lib/arachni/element/capabilities/with_node.rb +1 -1
- data/lib/arachni/element/cookie.rb +2 -2
- data/lib/arachni/element/form.rb +1 -1
- data/lib/arachni/element/header.rb +2 -2
- data/lib/arachni/element/link_template.rb +1 -1
- data/lib/arachni/framework.rb +21 -1144
- data/lib/arachni/framework/parts/audit.rb +282 -0
- data/lib/arachni/framework/parts/browser.rb +132 -0
- data/lib/arachni/framework/parts/check.rb +86 -0
- data/lib/arachni/framework/parts/data.rb +158 -0
- data/lib/arachni/framework/parts/platform.rb +34 -0
- data/lib/arachni/framework/parts/plugin.rb +61 -0
- data/lib/arachni/framework/parts/report.rb +128 -0
- data/lib/arachni/framework/parts/scope.rb +40 -0
- data/lib/arachni/framework/parts/state.rb +457 -0
- data/lib/arachni/http/client.rb +33 -30
- data/lib/arachni/http/request.rb +6 -2
- data/lib/arachni/issue.rb +55 -1
- data/lib/arachni/platform/manager.rb +25 -21
- data/lib/arachni/state/framework.rb +7 -1
- data/lib/arachni/utilities.rb +10 -0
- data/lib/version +1 -1
- data/spec/arachni/browser_spec.rb +13 -0
- data/spec/arachni/check/auditor_spec.rb +1 -0
- data/spec/arachni/check/base_spec.rb +80 -0
- data/spec/arachni/element/cookie_spec.rb +2 -2
- data/spec/arachni/framework/parts/audit_spec.rb +391 -0
- data/spec/arachni/framework/parts/browser_spec.rb +26 -0
- data/spec/arachni/framework/parts/check_spec.rb +24 -0
- data/spec/arachni/framework/parts/data_spec.rb +187 -0
- data/spec/arachni/framework/parts/platform_spec.rb +62 -0
- data/spec/arachni/framework/parts/plugin_spec.rb +41 -0
- data/spec/arachni/framework/parts/report_spec.rb +66 -0
- data/spec/arachni/framework/parts/scope_spec.rb +86 -0
- data/spec/arachni/framework/parts/state_spec.rb +528 -0
- data/spec/arachni/framework_spec.rb +17 -1344
- data/spec/arachni/http/client_spec.rb +12 -7
- data/spec/arachni/issue_spec.rb +35 -0
- data/spec/arachni/platform/manager_spec.rb +2 -3
- data/spec/arachni/state/framework_spec.rb +15 -0
- data/spec/components/checks/active/code_injection_timing_spec.rb +5 -5
- data/spec/components/checks/active/no_sql_injection_differential_spec.rb +4 -0
- data/spec/components/checks/active/os_cmd_injection_spec.rb +20 -7
- data/spec/components/checks/active/os_cmd_injection_timing_spec.rb +5 -5
- data/spec/components/checks/active/sql_injection_differential_spec.rb +4 -0
- data/spec/components/checks/active/sql_injection_spec.rb +2 -3
- data/spec/support/servers/arachni/browser.rb +31 -0
- data/spec/support/servers/checks/active/code_injection.rb +1 -1
- data/spec/support/servers/checks/active/no_sql_injection_differential.rb +36 -34
- data/spec/support/servers/checks/active/os_cmd_injection.rb +6 -12
- data/spec/support/servers/checks/active/os_cmd_injection_timing.rb +9 -4
- data/spec/support/servers/checks/active/sql_injection.rb +1 -1
- data/spec/support/servers/checks/active/sql_injection_differential.rb +37 -34
- data/spec/support/shared/element/capabilities/with_node.rb +25 -0
- data/spec/support/shared/framework.rb +26 -0
- data/ui/cli/output.rb +2 -0
- data/ui/cli/rpc/server/dispatcher/option_parser.rb +1 -1
- metadata +32 -4
- data/components/checks/active/sql_injection/patterns/coldfusion +0 -1
@@ -1,30 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Arachni::Framework do
|
4
|
-
|
5
|
-
before( :all ) do
|
6
|
-
@url = web_server_url_for( :auditor )
|
7
|
-
@f_url = web_server_url_for( :framework )
|
8
|
-
|
9
|
-
@options = Arachni::Options.instance
|
10
|
-
end
|
11
|
-
|
12
|
-
before( :each ) do
|
13
|
-
reset_options
|
14
|
-
@options.paths.reporters = fixtures_path + '/reporters/manager_spec/'
|
15
|
-
@options.paths.checks = fixtures_path + '/taint_check/'
|
16
|
-
|
17
|
-
@f = Arachni::Framework.new
|
18
|
-
@f.options.url = @url
|
19
|
-
end
|
20
|
-
after( :each ) do
|
21
|
-
File.delete( @snapshot ) rescue nil
|
22
|
-
|
23
|
-
@f.clean_up
|
24
|
-
@f.reset
|
25
|
-
end
|
26
|
-
|
27
|
-
subject { @f }
|
4
|
+
include_examples 'framework'
|
28
5
|
|
29
6
|
describe '#initialize' do
|
30
7
|
context 'when passed a block' do
|
@@ -62,241 +39,10 @@ describe Arachni::Framework do
|
|
62
39
|
end
|
63
40
|
end
|
64
41
|
|
65
|
-
describe '#browser_cluster' do
|
66
|
-
it "returns #{Arachni::BrowserCluster}" do
|
67
|
-
subject.browser_cluster.should be_kind_of Arachni::BrowserCluster
|
68
|
-
end
|
69
|
-
|
70
|
-
context "when #{Arachni::OptionGroups::BrowserCluster}#pool_size" do
|
71
|
-
it 'returns nil' do
|
72
|
-
subject.options.browser_cluster.pool_size = 0
|
73
|
-
subject.browser_cluster.should be_nil
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
context "when #{Arachni::OptionGroups::Scope}#dom_depth_limit" do
|
78
|
-
it 'returns nil' do
|
79
|
-
subject.options.scope.dom_depth_limit = 0
|
80
|
-
subject.browser_cluster.should be_nil
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
describe '#state' do
|
86
|
-
it "returns #{Arachni::State::Framework}" do
|
87
|
-
subject.state.should be_kind_of Arachni::State::Framework
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
describe '#data' do
|
92
|
-
it "returns #{Arachni::Data::Framework}" do
|
93
|
-
subject.data.should be_kind_of Arachni::Data::Framework
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
describe '#on_page_audit' do
|
98
|
-
it 'calls the given block before each page is audited' do
|
99
|
-
ok = false
|
100
|
-
Arachni::Framework.new do |f|
|
101
|
-
f.options.url = @url
|
102
|
-
f.on_page_audit { ok = true }
|
103
|
-
|
104
|
-
f.audit_page Arachni::Page.from_url( @url + '/link' )
|
105
|
-
end
|
106
|
-
ok.should be_true
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
describe '#after_page_audit' do
|
111
|
-
it 'calls the given block before each page is audited' do
|
112
|
-
ok = false
|
113
|
-
Arachni::Framework.new do |f|
|
114
|
-
f.options.url = @url
|
115
|
-
f.after_page_audit { ok = true }
|
116
|
-
|
117
|
-
f.audit_page Arachni::Page.from_url( @url + '/link' )
|
118
|
-
end
|
119
|
-
ok.should be_true
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
context 'when unable to get a response for the given URL' do
|
124
|
-
context 'due to a network error' do
|
125
|
-
it 'returns an empty sitemap and have failures' do
|
126
|
-
@options.url = 'http://blahaha'
|
127
|
-
@options.scope.restrict_paths = [@options.url]
|
128
|
-
|
129
|
-
subject.checks.load :taint
|
130
|
-
subject.run
|
131
|
-
subject.failures.should be_any
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
context 'due to a server error' do
|
136
|
-
it 'returns an empty sitemap and have failures' do
|
137
|
-
@options.url = @f_url + '/fail'
|
138
|
-
@options.scope.restrict_paths = [@options.url]
|
139
|
-
|
140
|
-
subject.checks.load :taint
|
141
|
-
subject.run
|
142
|
-
subject.failures.should be_any
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
it "retries #{Arachni::Framework::AUDIT_PAGE_MAX_TRIES} times" do
|
147
|
-
@options.url = @f_url + '/fail_4_times'
|
148
|
-
@options.scope.restrict_paths = [@options.url]
|
149
|
-
|
150
|
-
subject.checks.load :taint
|
151
|
-
subject.run
|
152
|
-
subject.failures.should be_empty
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
describe '#failures' do
|
157
|
-
context 'when there are no failed requests' do
|
158
|
-
it 'returns an empty array' do
|
159
|
-
@options.url = @f_url
|
160
|
-
@options.scope.restrict_paths = [@options.url]
|
161
|
-
|
162
|
-
subject.checks.load :taint
|
163
|
-
subject.run
|
164
|
-
subject.failures.should be_empty
|
165
|
-
end
|
166
|
-
end
|
167
|
-
context 'when there are failed requests' do
|
168
|
-
it 'returns an array containing the failed URLs' do
|
169
|
-
@options.url = @f_url + '/fail'
|
170
|
-
@options.scope.restrict_paths = [@options.url]
|
171
|
-
|
172
|
-
subject.checks.load :taint
|
173
|
-
subject.run
|
174
|
-
subject.failures.should be_any
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
42
|
describe '#options' do
|
180
43
|
it "provides access to #{Arachni::Options}" do
|
181
44
|
subject.options.should be_kind_of Arachni::Options
|
182
45
|
end
|
183
|
-
|
184
|
-
describe "#{Arachni::OptionGroups::Scope}#exclude_binaries" do
|
185
|
-
it 'excludes binary pages from the scan' do
|
186
|
-
audited = []
|
187
|
-
Arachni::Framework.new do |f|
|
188
|
-
f.options.url = @url
|
189
|
-
f.options.scope.restrict_paths << @url + '/binary'
|
190
|
-
f.options.audit.elements :links, :forms, :cookies
|
191
|
-
f.checks.load :taint
|
192
|
-
|
193
|
-
f.on_page_audit { |p| audited << p.url }
|
194
|
-
f.run
|
195
|
-
end
|
196
|
-
audited.sort.should == [@url + '/binary'].sort
|
197
|
-
|
198
|
-
audited = []
|
199
|
-
Arachni::Framework.new do |f|
|
200
|
-
f.options.url = @url
|
201
|
-
f.options.scope.restrict_paths << @url + '/binary'
|
202
|
-
f.options.scope.exclude_binaries = true
|
203
|
-
f.checks.load :taint
|
204
|
-
|
205
|
-
f.on_page_audit { |p| audited << p.url }
|
206
|
-
f.run
|
207
|
-
end
|
208
|
-
audited.should be_empty
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
describe "#{Arachni::OptionGroups::Scope}#extend_paths" do
|
213
|
-
it 'extends the crawl scope' do
|
214
|
-
Arachni::Framework.new do |f|
|
215
|
-
f.options.url = "#{@url}/elem_combo"
|
216
|
-
f.options.scope.extend_paths = %w(/some/stuff /more/stuff)
|
217
|
-
f.options.audit.elements :links, :forms, :cookies
|
218
|
-
f.checks.load :taint
|
219
|
-
|
220
|
-
f.run
|
221
|
-
|
222
|
-
f.report.sitemap.should include "#{@url}/some/stuff"
|
223
|
-
f.report.sitemap.should include "#{@url}/more/stuff"
|
224
|
-
f.report.sitemap.size.should > 3
|
225
|
-
end
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
describe "#{Arachni::OptionGroups::Scope}#restrict_paths" do
|
230
|
-
it 'serves as a replacement to crawling' do
|
231
|
-
Arachni::Framework.new do |f|
|
232
|
-
f.options.url = "#{@url}/elem_combo"
|
233
|
-
f.options.scope.restrict_paths = %w(/log_remote_file_if_exists/true)
|
234
|
-
f.options.audit.elements :links, :forms, :cookies
|
235
|
-
f.checks.load :taint
|
236
|
-
|
237
|
-
f.run
|
238
|
-
|
239
|
-
sitemap = f.report.sitemap.map { |u, _| u.split( '?' ).first }
|
240
|
-
sitemap.sort.uniq.should == f.options.scope.restrict_paths.
|
241
|
-
map { |p| f.to_absolute( p ) }.sort
|
242
|
-
end
|
243
|
-
end
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
|
-
describe '#sitemap' do
|
248
|
-
it 'returns a hash with covered URLs and HTTP status codes' do
|
249
|
-
Arachni::Framework.new do |f|
|
250
|
-
f.options.url = "#{@url}/"
|
251
|
-
f.options.audit.elements :links, :forms, :cookies
|
252
|
-
f.checks.load :taint
|
253
|
-
|
254
|
-
f.run
|
255
|
-
f.sitemap.should == { "#{@url}/" => 200 }
|
256
|
-
end
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
describe '#reporters' do
|
261
|
-
it 'provides access to the reporter manager' do
|
262
|
-
subject.reporters.is_a?( Arachni::Reporter::Manager ).should be_true
|
263
|
-
subject.reporters.available.sort.should == %w(afr foo).sort
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
describe '#checks' do
|
268
|
-
it 'provides access to the check manager' do
|
269
|
-
subject.checks.is_a?( Arachni::Check::Manager ).should be_true
|
270
|
-
subject.checks.available.should == %w(taint)
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
describe '#plugins' do
|
275
|
-
it 'provides access to the plugin manager' do
|
276
|
-
subject.plugins.is_a?( Arachni::Plugin::Manager ).should be_true
|
277
|
-
subject.plugins.available.sort.should ==
|
278
|
-
%w(wait bad with_options distributable loop default suspendable).sort
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
describe '#http' do
|
283
|
-
it 'provides access to the HTTP interface' do
|
284
|
-
subject.http.is_a?( Arachni::HTTP::Client ).should be_true
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
describe '#scanning?' do
|
289
|
-
it "delegates to #{Arachni::State::Framework}#scanning?" do
|
290
|
-
subject.state.stub(:scanning?) { :stuff }
|
291
|
-
subject.scanning?.should == :stuff
|
292
|
-
end
|
293
|
-
end
|
294
|
-
|
295
|
-
describe '#paused?' do
|
296
|
-
it "delegates to #{Arachni::State::Framework}#paused?" do
|
297
|
-
subject.state.stub(:paused?) { :stuff }
|
298
|
-
subject.paused?.should == :stuff
|
299
|
-
end
|
300
46
|
end
|
301
47
|
|
302
48
|
describe '#run' do
|
@@ -336,7 +82,7 @@ describe Arachni::Framework do
|
|
336
82
|
end
|
337
83
|
|
338
84
|
it 'handles heavy load' do
|
339
|
-
@options.paths.checks
|
85
|
+
@options.paths.checks = fixtures_path + '/taint_check/'
|
340
86
|
|
341
87
|
Arachni::Framework.new do |f|
|
342
88
|
f.options.url = web_server_url_for :framework_multi
|
@@ -417,1104 +163,31 @@ describe Arachni::Framework do
|
|
417
163
|
end
|
418
164
|
end
|
419
165
|
|
420
|
-
describe '#
|
421
|
-
|
422
|
-
@options.paths.checks = fixtures_path + '/taint_check/'
|
423
|
-
|
424
|
-
described_class.new do |f|
|
425
|
-
f.options.url = web_server_url_for :framework_multi
|
426
|
-
f.options.audit.elements :links
|
427
|
-
|
428
|
-
f.plugins.load :wait
|
429
|
-
f.checks.load :taint
|
430
|
-
|
431
|
-
t = Thread.new do
|
432
|
-
f.run
|
433
|
-
end
|
434
|
-
|
435
|
-
sleep 0.1 while Arachni::Data.issues.size < 2
|
436
|
-
|
437
|
-
f.abort
|
438
|
-
t.join
|
439
|
-
|
440
|
-
Arachni::Data.issues.size.should < 500
|
441
|
-
end
|
442
|
-
end
|
443
|
-
|
444
|
-
it 'sets #status to :aborted' do
|
445
|
-
described_class.new do |f|
|
446
|
-
f.options.url = web_server_url_for :framework_multi
|
447
|
-
f.options.audit.elements :links
|
448
|
-
f.checks.load :taint
|
449
|
-
|
450
|
-
t = Thread.new do
|
451
|
-
f.run
|
452
|
-
end
|
453
|
-
sleep 0.1 while f.status != :scanning
|
454
|
-
|
455
|
-
f.abort
|
456
|
-
f.status.should == :aborted
|
457
|
-
|
458
|
-
t.join
|
459
|
-
f.status.should == :aborted
|
460
|
-
end
|
461
|
-
end
|
462
|
-
end
|
463
|
-
|
464
|
-
describe '#suspend' do
|
465
|
-
it 'suspends the system' do
|
466
|
-
@options.paths.checks = fixtures_path + '/taint_check/'
|
467
|
-
|
468
|
-
described_class.new do |f|
|
469
|
-
f.options.url = web_server_url_for :framework_multi
|
470
|
-
f.options.audit.elements :links
|
471
|
-
|
472
|
-
f.plugins.load :wait
|
473
|
-
f.checks.load :taint
|
474
|
-
|
475
|
-
t = Thread.new do
|
476
|
-
f.run
|
477
|
-
end
|
478
|
-
|
479
|
-
sleep 0.1 while Arachni::Data.issues.size < 2
|
480
|
-
|
481
|
-
@snapshot = f.suspend
|
482
|
-
t.join
|
483
|
-
|
484
|
-
Arachni::Data.issues.size.should < 500
|
485
|
-
end
|
486
|
-
|
487
|
-
Arachni::Snapshot.load( @snapshot ).should be_true
|
488
|
-
end
|
489
|
-
|
490
|
-
it 'sets #status to :suspended' do
|
491
|
-
described_class.new do |f|
|
492
|
-
f.options.url = web_server_url_for :framework_multi
|
493
|
-
f.options.audit.elements :links
|
494
|
-
f.checks.load :taint
|
495
|
-
|
496
|
-
t = Thread.new do
|
497
|
-
f.run
|
498
|
-
end
|
499
|
-
sleep 0.1 while f.status != :scanning
|
500
|
-
|
501
|
-
@snapshot = f.suspend
|
502
|
-
f.status.should == :suspended
|
503
|
-
|
504
|
-
t.join
|
505
|
-
f.status.should == :suspended
|
506
|
-
end
|
507
|
-
end
|
508
|
-
|
509
|
-
it 'suspends plugins' do
|
510
|
-
Arachni::Options.plugins['suspendable'] = {
|
511
|
-
'my_option' => 'my value'
|
512
|
-
}
|
513
|
-
|
514
|
-
described_class.new do |f|
|
515
|
-
f.options.url = web_server_url_for :framework_multi
|
516
|
-
f.options.audit.elements :links
|
517
|
-
|
518
|
-
f.checks.load :taint
|
519
|
-
f.plugins.load :suspendable
|
520
|
-
|
521
|
-
t = Thread.new do
|
522
|
-
f.run
|
523
|
-
end
|
524
|
-
|
525
|
-
sleep 0.1 while f.status != :scanning
|
526
|
-
|
527
|
-
f.suspend
|
528
|
-
t.join
|
166
|
+
describe '#statistics' do
|
167
|
+
let(:statistics) { subject.statistics }
|
529
168
|
|
530
|
-
|
531
|
-
|
169
|
+
it 'includes http statistics' do
|
170
|
+
statistics[:http].should == subject.http.statistics
|
532
171
|
end
|
533
172
|
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
context 'is a directory' do
|
538
|
-
it 'stores the snapshot under it' do
|
539
|
-
@options.paths.checks = fixtures_path + '/taint_check/'
|
540
|
-
@options.snapshot.save_path = Dir.tmpdir
|
541
|
-
|
542
|
-
described_class.new do |f|
|
543
|
-
f.options.url = web_server_url_for :framework_multi
|
544
|
-
f.options.audit.elements :links
|
545
|
-
|
546
|
-
f.plugins.load :wait
|
547
|
-
f.checks.load :taint
|
548
|
-
|
549
|
-
t = Thread.new do
|
550
|
-
f.run
|
551
|
-
end
|
552
|
-
|
553
|
-
sleep 0.1 while Arachni::Data.issues.size < 2
|
554
|
-
|
555
|
-
@snapshot = f.suspend
|
556
|
-
t.join
|
557
|
-
|
558
|
-
Arachni::Data.issues.size.should < 500
|
559
|
-
end
|
560
|
-
|
561
|
-
File.dirname( @snapshot ).should == Dir.tmpdir
|
562
|
-
Arachni::Snapshot.load( @snapshot ).should be_true
|
563
|
-
end
|
564
|
-
end
|
565
|
-
|
566
|
-
context 'is a file path' do
|
567
|
-
it 'stores the snapshot there' do
|
568
|
-
@options.paths.checks = fixtures_path + '/taint_check/'
|
569
|
-
@options.snapshot.save_path = "#{Dir.tmpdir}/snapshot"
|
570
|
-
|
571
|
-
described_class.new do |f|
|
572
|
-
f.options.url = web_server_url_for :framework_multi
|
573
|
-
f.options.audit.elements :links
|
574
|
-
|
575
|
-
f.plugins.load :wait
|
576
|
-
f.checks.load :taint
|
577
|
-
|
578
|
-
t = Thread.new do
|
579
|
-
f.run
|
580
|
-
end
|
581
|
-
|
582
|
-
sleep 0.1 while Arachni::Data.issues.size < 2
|
583
|
-
|
584
|
-
@snapshot = f.suspend
|
585
|
-
t.join
|
586
|
-
|
587
|
-
Arachni::Data.issues.size.should < 500
|
588
|
-
end
|
589
|
-
|
590
|
-
@snapshot.should == "#{Dir.tmpdir}/snapshot"
|
591
|
-
Arachni::Snapshot.load( @snapshot ).should be_true
|
592
|
-
end
|
173
|
+
[:found_pages, :audited_pages, :current_page].each do |k|
|
174
|
+
it "includes #{k}" do
|
175
|
+
statistics.should include k
|
593
176
|
end
|
594
177
|
end
|
595
|
-
end
|
596
|
-
|
597
|
-
describe '#restore' do
|
598
|
-
it 'restores a suspended scan' do
|
599
|
-
@options.paths.checks = fixtures_path + '/taint_check/'
|
600
|
-
|
601
|
-
logged_issues = 0
|
602
|
-
described_class.new do |f|
|
603
|
-
f.options.url = web_server_url_for :framework_multi
|
604
|
-
f.options.audit.elements :links
|
605
|
-
|
606
|
-
f.plugins.load :wait
|
607
|
-
f.checks.load :taint
|
608
|
-
|
609
|
-
Arachni::Data.issues.on_new do
|
610
|
-
logged_issues += 1
|
611
|
-
end
|
612
178
|
|
613
|
-
|
614
|
-
|
179
|
+
describe :runtime do
|
180
|
+
context 'when the scan has been running' do
|
181
|
+
it 'returns the runtime in seconds' do
|
182
|
+
subject.run
|
183
|
+
statistics[:runtime].should > 0
|
615
184
|
end
|
616
|
-
|
617
|
-
sleep 0.1 while logged_issues < 200
|
618
|
-
|
619
|
-
@snapshot = f.suspend
|
620
|
-
t.join
|
621
|
-
|
622
|
-
logged_issues.should < 500
|
623
185
|
end
|
624
186
|
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
described_class.new do |f|
|
629
|
-
f.restore @snapshot
|
630
|
-
|
631
|
-
Arachni::Data.issues.on_new do
|
632
|
-
logged_issues += 1
|
187
|
+
context 'when no scan has been running' do
|
188
|
+
it 'returns 0' do
|
189
|
+
statistics[:runtime].should == 0
|
633
190
|
end
|
634
|
-
f.run
|
635
|
-
|
636
|
-
# logged_issues.should == 500
|
637
|
-
Arachni::Data.issues.size.should == 500
|
638
|
-
|
639
|
-
f.report.plugins[:wait][:results].should == { 'stuff' => true }
|
640
|
-
end
|
641
|
-
end
|
642
|
-
|
643
|
-
it 'restores options' do
|
644
|
-
options_hash = nil
|
645
|
-
|
646
|
-
described_class.new do |f|
|
647
|
-
f.options.url = @url + '/with_ajax'
|
648
|
-
f.options.audit.elements :links, :forms, :cookies
|
649
|
-
f.options.datastore.my_custom_option = 'my custom value'
|
650
|
-
options_hash = f.options.update( f.options.to_rpc_data ).to_h.deep_clone
|
651
|
-
|
652
|
-
f.checks.load :taint
|
653
|
-
|
654
|
-
t = Thread.new { f.run }
|
655
|
-
|
656
|
-
sleep 0.1 while f.browser_cluster.done?
|
657
|
-
@snapshot = f.suspend
|
658
|
-
|
659
|
-
t.join
|
660
|
-
end
|
661
|
-
|
662
|
-
described_class.restore( @snapshot ) do |f|
|
663
|
-
f.options.to_h.should == options_hash.merge( checks: ['taint'] )
|
664
|
-
f.browser_job_skip_states.should be_any
|
665
|
-
end
|
666
|
-
end
|
667
|
-
|
668
|
-
it 'restores BrowserCluster skip states' do
|
669
|
-
described_class.new do |f|
|
670
|
-
f.options.url = @url + '/with_ajax'
|
671
|
-
f.options.audit.elements :links, :forms, :cookies
|
672
|
-
|
673
|
-
f.checks.load :taint
|
674
|
-
|
675
|
-
t = Thread.new { f.run }
|
676
|
-
|
677
|
-
sleep 0.1 while f.browser_cluster.done?
|
678
|
-
@snapshot = f.suspend
|
679
|
-
|
680
|
-
t.join
|
681
|
-
end
|
682
|
-
|
683
|
-
described_class.restore( @snapshot ) do |f|
|
684
|
-
f.browser_job_skip_states.should be_any
|
685
|
-
end
|
686
|
-
end
|
687
|
-
|
688
|
-
it 'restores loaded checks' do
|
689
|
-
described_class.new do |f|
|
690
|
-
f.options.url = @url
|
691
|
-
f.checks.load :taint
|
692
|
-
|
693
|
-
t = Thread.new { f.run }
|
694
|
-
sleep 0.1 while f.status != :scanning
|
695
|
-
|
696
|
-
@snapshot = f.suspend
|
697
|
-
|
698
|
-
t.join
|
699
|
-
end
|
700
|
-
|
701
|
-
described_class.restore( @snapshot ) do |f|
|
702
|
-
f.checks.loaded.should == ['taint']
|
703
|
-
end
|
704
|
-
end
|
705
|
-
|
706
|
-
it 'restores loaded plugins' do
|
707
|
-
described_class.new do |f|
|
708
|
-
f.options.url = @url
|
709
|
-
f.plugins.load :wait
|
710
|
-
|
711
|
-
t = Thread.new { f.run }
|
712
|
-
sleep 0.1 while f.status != :scanning
|
713
|
-
|
714
|
-
@snapshot = f.suspend
|
715
|
-
t.join
|
716
|
-
end
|
717
|
-
|
718
|
-
described_class.restore( @snapshot ) do |f|
|
719
|
-
f.plugins.loaded.should == ['wait']
|
720
|
-
end
|
721
|
-
end
|
722
|
-
|
723
|
-
it 'restores plugin states' do
|
724
|
-
Arachni::Options.plugins['suspendable'] = {
|
725
|
-
'my_option' => 'my value'
|
726
|
-
}
|
727
|
-
|
728
|
-
described_class.new do |f|
|
729
|
-
f.options.url = web_server_url_for :framework_multi
|
730
|
-
f.options.audit.elements :links
|
731
|
-
|
732
|
-
f.checks.load :taint
|
733
|
-
f.plugins.load :suspendable
|
734
|
-
|
735
|
-
t = Thread.new do
|
736
|
-
f.run
|
737
|
-
end
|
738
|
-
|
739
|
-
sleep 0.1 while f.status != :scanning
|
740
|
-
|
741
|
-
@snapshot = f.suspend
|
742
|
-
t.join
|
743
|
-
|
744
|
-
Arachni::State.plugins.runtime[:suspendable][:data].should == 1
|
745
|
-
end
|
746
|
-
|
747
|
-
described_class.restore( @snapshot ) do |f|
|
748
|
-
t = Thread.new do
|
749
|
-
f.run
|
750
|
-
end
|
751
|
-
|
752
|
-
sleep 0.1 while f.status != :scanning
|
753
|
-
|
754
|
-
f.plugins.jobs[:suspendable][:instance].counter.should == 2
|
755
|
-
|
756
|
-
f.abort
|
757
|
-
t.join
|
758
|
-
end
|
759
|
-
end
|
760
|
-
end
|
761
|
-
|
762
|
-
describe '#pause' do
|
763
|
-
it 'pauses the system' do
|
764
|
-
described_class.new do |f|
|
765
|
-
f.options.url = @url + '/elem_combo'
|
766
|
-
f.options.audit.elements :links, :forms, :cookies
|
767
|
-
f.checks.load :taint
|
768
|
-
|
769
|
-
t = Thread.new do
|
770
|
-
f.run
|
771
|
-
end
|
772
|
-
|
773
|
-
f.pause
|
774
|
-
|
775
|
-
sleep 10
|
776
|
-
|
777
|
-
f.running?.should be_true
|
778
|
-
t.kill
|
779
|
-
end
|
780
|
-
end
|
781
|
-
|
782
|
-
it 'returns an Integer request ID' do
|
783
|
-
described_class.new do |f|
|
784
|
-
f.options.url = @url + '/elem_combo'
|
785
|
-
f.options.audit.elements :links, :forms, :cookies
|
786
|
-
f.checks.load :taint
|
787
|
-
|
788
|
-
t = Thread.new do
|
789
|
-
f.run
|
790
|
-
end
|
791
|
-
|
792
|
-
f.pause.should be_kind_of Integer
|
793
|
-
|
794
|
-
sleep 10
|
795
|
-
|
796
|
-
f.running?.should be_true
|
797
|
-
t.kill
|
798
|
-
end
|
799
|
-
end
|
800
|
-
|
801
|
-
it 'sets #status to :paused' do
|
802
|
-
described_class.new do |f|
|
803
|
-
f.options.url = @url + '/elem_combo'
|
804
|
-
f.options.audit.elements :links, :forms, :cookies
|
805
|
-
f.checks.load :taint
|
806
|
-
|
807
|
-
t = Thread.new do
|
808
|
-
f.run
|
809
|
-
end
|
810
|
-
sleep 0.1 while f.status != :scanning
|
811
|
-
|
812
|
-
f.pause
|
813
|
-
f.status.should == :paused
|
814
|
-
|
815
|
-
t.kill
|
816
|
-
end
|
817
|
-
end
|
818
|
-
end
|
819
|
-
|
820
|
-
describe '#resume' do
|
821
|
-
it 'resumes the system' do
|
822
|
-
described_class.new do |f|
|
823
|
-
f.options.url = @url + '/elem_combo'
|
824
|
-
f.options.audit.elements :links, :forms, :cookies
|
825
|
-
f.checks.load :taint
|
826
|
-
|
827
|
-
t = Thread.new do
|
828
|
-
f.run
|
829
|
-
end
|
830
|
-
|
831
|
-
id = f.pause
|
832
|
-
|
833
|
-
sleep 10
|
834
|
-
|
835
|
-
f.running?.should be_true
|
836
|
-
f.resume id
|
837
|
-
t.join
|
838
|
-
end
|
839
|
-
end
|
840
|
-
|
841
|
-
it 'sets #status to scanning' do
|
842
|
-
described_class.new do |f|
|
843
|
-
f.options.url = @url + '/elem_combo'
|
844
|
-
f.options.audit.elements :links, :forms, :cookies
|
845
|
-
f.checks.load :taint
|
846
|
-
|
847
|
-
t = Thread.new do
|
848
|
-
f.run
|
849
|
-
end
|
850
|
-
|
851
|
-
id = f.pause
|
852
|
-
f.status.should == :paused
|
853
|
-
|
854
|
-
f.resume id
|
855
|
-
Timeout.timeout( 5 ) do
|
856
|
-
sleep 0.1 while f.status != :scanning
|
857
|
-
end
|
858
|
-
t.join
|
859
|
-
end
|
860
|
-
end
|
861
|
-
end
|
862
|
-
|
863
|
-
describe '#clean_up' do
|
864
|
-
it 'stops the #plugins' do
|
865
|
-
described_class.new do |f|
|
866
|
-
f.options.url = @url + '/elem_combo'
|
867
|
-
f.plugins.load :wait
|
868
|
-
|
869
|
-
f.plugins.run
|
870
|
-
f.clean_up
|
871
|
-
f.plugins.jobs.should be_empty
|
872
|
-
end
|
873
|
-
end
|
874
|
-
|
875
|
-
it 'sets the status to cleanup' do
|
876
|
-
described_class.new do |f|
|
877
|
-
f.options.url = @url + '/elem_combo'
|
878
|
-
|
879
|
-
f.clean_up
|
880
|
-
f.status.should == :cleanup
|
881
|
-
end
|
882
|
-
end
|
883
|
-
|
884
|
-
it 'clears the page queue' do
|
885
|
-
described_class.new do |f|
|
886
|
-
f.options.url = @url + '/elem_combo'
|
887
|
-
f.push_to_page_queue Arachni::Page.from_url( f.options.url )
|
888
|
-
|
889
|
-
f.data.page_queue.should_not be_empty
|
890
|
-
f.clean_up
|
891
|
-
f.data.page_queue.should be_empty
|
892
|
-
end
|
893
|
-
end
|
894
|
-
|
895
|
-
it 'clears the URL queue' do
|
896
|
-
described_class.new do |f|
|
897
|
-
f.options.url = @url + '/elem_combo'
|
898
|
-
f.push_to_url_queue f.options.url
|
899
|
-
|
900
|
-
f.data.url_queue.should_not be_empty
|
901
|
-
f.clean_up
|
902
|
-
f.data.url_queue.should be_empty
|
903
|
-
end
|
904
|
-
end
|
905
|
-
|
906
|
-
it 'sets #running? to false' do
|
907
|
-
described_class.new do |f|
|
908
|
-
f.options.url = @url + '/elem_combo'
|
909
|
-
f.clean_up
|
910
|
-
f.should_not be_running
|
911
|
-
end
|
912
|
-
end
|
913
|
-
end
|
914
|
-
|
915
|
-
describe '#report_as' do
|
916
|
-
before( :each ) do
|
917
|
-
reset_options
|
918
|
-
@new_framework = Arachni::Framework.new
|
919
|
-
end
|
920
|
-
|
921
|
-
context 'when passed a valid reporter name' do
|
922
|
-
it 'returns the reporter as a string' do
|
923
|
-
json = @new_framework.report_as( :json )
|
924
|
-
JSON.load( json )['issues'].size.should == @new_framework.report.issues.size
|
925
|
-
end
|
926
|
-
|
927
|
-
context 'which does not support the \'outfile\' option' do
|
928
|
-
it 'raises Arachni::Component::Options::Error::Invalid' do
|
929
|
-
expect { @new_framework.report_as( :stdout ) }.to raise_error Arachni::Component::Options::Error::Invalid
|
930
|
-
end
|
931
|
-
end
|
932
|
-
end
|
933
|
-
|
934
|
-
context 'when passed an invalid reporter name' do
|
935
|
-
it 'raises Arachni::Component::Error::NotFound' do
|
936
|
-
expect { @new_framework.report_as( :blah ) }.to raise_error Arachni::Component::Error::NotFound
|
937
|
-
end
|
938
|
-
end
|
939
|
-
end
|
940
|
-
|
941
|
-
describe '#audit_page' do
|
942
|
-
it 'updates the #sitemap with the DOM URL' do
|
943
|
-
subject.options.audit.elements :links, :forms, :cookies
|
944
|
-
subject.checks.load :taint
|
945
|
-
|
946
|
-
subject.sitemap.should be_empty
|
947
|
-
|
948
|
-
page = Arachni::Page.from_url( @url + '/link' )
|
949
|
-
page.dom.url = @url + '/link/#/stuff'
|
950
|
-
|
951
|
-
subject.audit_page page
|
952
|
-
subject.sitemap.should include @url + '/link/#/stuff'
|
953
|
-
end
|
954
|
-
|
955
|
-
it "runs #{Arachni::Check::Manager}#without_platforms before #{Arachni::Check::Manager}#with_platforms" do
|
956
|
-
@options.paths.checks = fixtures_path + '/checks/'
|
957
|
-
|
958
|
-
described_class.new do |f|
|
959
|
-
f.checks.load_all
|
960
|
-
|
961
|
-
page = Arachni::Page.from_url( @url + '/link' )
|
962
|
-
|
963
|
-
responses = []
|
964
|
-
f.http.on_complete do |response|
|
965
|
-
responses << response.url
|
966
|
-
end
|
967
|
-
|
968
|
-
f.audit_page page
|
969
|
-
|
970
|
-
responses.sort.should ==
|
971
|
-
%w(http://localhost/test3 http://localhost/test
|
972
|
-
http://localhost/test2).sort
|
973
|
-
end
|
974
|
-
end
|
975
|
-
|
976
|
-
context 'when checks were' do
|
977
|
-
context 'ran against the page' do
|
978
|
-
it 'returns true' do
|
979
|
-
subject.checks.load :taint
|
980
|
-
subject.audit_page( Arachni::Page.from_url( @url + '/link' ) ).should be_true
|
981
|
-
end
|
982
|
-
end
|
983
|
-
|
984
|
-
context 'not ran against the page' do
|
985
|
-
it 'returns false' do
|
986
|
-
subject.audit_page( Arachni::Page.from_url( @url + '/link' ) ).should be_false
|
987
|
-
end
|
988
|
-
end
|
989
|
-
end
|
990
|
-
|
991
|
-
context 'when the page contains JavaScript code' do
|
992
|
-
it 'analyzes the DOM and pushes new pages to the page queue' do
|
993
|
-
Arachni::Framework.new do |f|
|
994
|
-
f.options.audit.elements :links, :forms, :cookies
|
995
|
-
f.checks.load :taint
|
996
|
-
|
997
|
-
f.page_queue_total_size.should == 0
|
998
|
-
|
999
|
-
f.audit_page( Arachni::Page.from_url( @url + '/with_javascript' ) )
|
1000
|
-
|
1001
|
-
sleep 0.1 while f.wait_for_browser?
|
1002
|
-
|
1003
|
-
f.page_queue_total_size.should > 0
|
1004
|
-
end
|
1005
|
-
end
|
1006
|
-
|
1007
|
-
it 'analyzes the DOM and pushes new paths to the url queue' do
|
1008
|
-
Arachni::Framework.new do |f|
|
1009
|
-
f.options.url = @url
|
1010
|
-
f.options.audit.elements :links, :forms, :cookies
|
1011
|
-
f.checks.load :taint
|
1012
|
-
|
1013
|
-
f.url_queue_total_size.should == 0
|
1014
|
-
|
1015
|
-
f.audit_page( Arachni::Page.from_url( @url + '/with_javascript' ) )
|
1016
|
-
|
1017
|
-
sleep 0.1 while f.wait_for_browser?
|
1018
|
-
|
1019
|
-
f.url_queue_total_size.should == 2
|
1020
|
-
end
|
1021
|
-
end
|
1022
|
-
|
1023
|
-
context 'when the DOM depth limit has been reached' do
|
1024
|
-
it 'does not analyze the DOM' do
|
1025
|
-
Arachni::Framework.new do |f|
|
1026
|
-
f.options.url = @url
|
1027
|
-
|
1028
|
-
f.options.audit.elements :links, :forms, :cookies
|
1029
|
-
f.checks.load :taint
|
1030
|
-
f.options.scope.dom_depth_limit = 1
|
1031
|
-
f.url_queue_total_size.should == 0
|
1032
|
-
f.audit_page( Arachni::Page.from_url( @url + '/with_javascript' ) ).should be_true
|
1033
|
-
sleep 0.1 while f.wait_for_browser?
|
1034
|
-
f.url_queue_total_size.should == 2
|
1035
|
-
|
1036
|
-
f.reset
|
1037
|
-
|
1038
|
-
f.options.audit.elements :links, :forms, :cookies
|
1039
|
-
f.checks.load :taint
|
1040
|
-
f.options.scope.dom_depth_limit = 1
|
1041
|
-
f.url_queue_total_size.should == 0
|
1042
|
-
|
1043
|
-
page = Arachni::Page.from_url( @url + '/with_javascript' )
|
1044
|
-
page.dom.push_transition Arachni::Page::DOM::Transition.new( :page, :load )
|
1045
|
-
|
1046
|
-
f.audit_page( page ).should be_true
|
1047
|
-
sleep 0.1 while f.wait_for_browser?
|
1048
|
-
f.url_queue_total_size.should == 0
|
1049
|
-
end
|
1050
|
-
end
|
1051
|
-
|
1052
|
-
it 'returns false' do
|
1053
|
-
page = Arachni::Page.from_data(
|
1054
|
-
url: @url,
|
1055
|
-
dom: {
|
1056
|
-
transitions: [
|
1057
|
-
{ page: :load },
|
1058
|
-
{ "<a href='javascript:click();'>" => :click },
|
1059
|
-
{ "<button dblclick='javascript:doubleClick();'>" => :ondblclick }
|
1060
|
-
].map { |t| Arachni::Page::DOM::Transition.new *t.first }
|
1061
|
-
}
|
1062
|
-
)
|
1063
|
-
|
1064
|
-
Arachni::Framework.new do |f|
|
1065
|
-
f.checks.load :taint
|
1066
|
-
|
1067
|
-
f.options.scope.dom_depth_limit = 10
|
1068
|
-
f.audit_page( page ).should be_true
|
1069
|
-
|
1070
|
-
f.options.scope.dom_depth_limit = 2
|
1071
|
-
f.audit_page( page ).should be_false
|
1072
|
-
end
|
1073
|
-
end
|
1074
|
-
end
|
1075
|
-
end
|
1076
|
-
|
1077
|
-
context 'when the page matches exclusion criteria' do
|
1078
|
-
it 'does not audit it' do
|
1079
|
-
subject.options.scope.exclude_path_patterns << /link/
|
1080
|
-
subject.options.audit.elements :links, :forms, :cookies
|
1081
|
-
|
1082
|
-
subject.checks.load :taint
|
1083
|
-
|
1084
|
-
subject.audit_page( Arachni::Page.from_url( @url + '/link' ) )
|
1085
|
-
subject.report.issues.size.should == 0
|
1086
|
-
end
|
1087
|
-
|
1088
|
-
it 'returns false' do
|
1089
|
-
subject.options.scope.exclude_path_patterns << /link/
|
1090
|
-
subject.audit_page( Arachni::Page.from_url( @url + '/link' ) ).should be_false
|
1091
|
-
end
|
1092
|
-
end
|
1093
|
-
|
1094
|
-
context "when #{Arachni::Check::Auditor}.has_timeout_candidates?" do
|
1095
|
-
it "calls #{Arachni::Check::Auditor}.timeout_audit_run" do
|
1096
|
-
Arachni::Check::Auditor.stub(:has_timeout_candidates?){ true }
|
1097
|
-
|
1098
|
-
Arachni::Check::Auditor.should receive(:timeout_audit_run)
|
1099
|
-
subject.audit_page( Arachni::Page.from_url( @url + '/link' ) )
|
1100
|
-
end
|
1101
|
-
end
|
1102
|
-
|
1103
|
-
context 'when the page contains elements seen in previous pages' do
|
1104
|
-
it 'removes them from the page'
|
1105
|
-
end
|
1106
|
-
|
1107
|
-
context 'when a check fails with an exception' do
|
1108
|
-
it 'moves to the next one' do
|
1109
|
-
@options.paths.checks = fixtures_path + '/checks/'
|
1110
|
-
|
1111
|
-
described_class.new do |f|
|
1112
|
-
f.checks.load_all
|
1113
|
-
|
1114
|
-
f.checks[:test].any_instance.stub(:run) { raise }
|
1115
|
-
|
1116
|
-
page = Arachni::Page.from_url( @url + '/link' )
|
1117
|
-
|
1118
|
-
responses = []
|
1119
|
-
f.http.on_complete do |response|
|
1120
|
-
responses << response.url
|
1121
|
-
end
|
1122
|
-
|
1123
|
-
f.audit_page page
|
1124
|
-
|
1125
|
-
responses.should == %w(http://localhost/test3 http://localhost/test2)
|
1126
|
-
end
|
1127
|
-
end
|
1128
|
-
end
|
1129
|
-
end
|
1130
|
-
|
1131
|
-
describe '#page_limit_reached?' do
|
1132
|
-
context "when the #{Arachni::OptionGroups::Scope}#page_limit has" do
|
1133
|
-
context 'been reached' do
|
1134
|
-
it 'returns true' do
|
1135
|
-
Arachni::Framework.new do |f|
|
1136
|
-
f.options.url = web_server_url_for :framework_multi
|
1137
|
-
f.options.audit.elements :links
|
1138
|
-
f.options.scope.page_limit = 10
|
1139
|
-
|
1140
|
-
f.page_limit_reached?.should be_false
|
1141
|
-
f.run
|
1142
|
-
f.page_limit_reached?.should be_true
|
1143
|
-
|
1144
|
-
f.sitemap.size.should == 10
|
1145
|
-
end
|
1146
|
-
end
|
1147
|
-
end
|
1148
|
-
|
1149
|
-
context 'not been reached' do
|
1150
|
-
it 'returns false' do
|
1151
|
-
Arachni::Framework.new do |f|
|
1152
|
-
f.options.url = web_server_url_for :framework
|
1153
|
-
f.options.audit.elements :links
|
1154
|
-
f.options.scope.page_limit = 100
|
1155
|
-
|
1156
|
-
f.checks.load :taint
|
1157
|
-
|
1158
|
-
f.page_limit_reached?.should be_false
|
1159
|
-
f.run
|
1160
|
-
f.page_limit_reached?.should be_false
|
1161
|
-
end
|
1162
|
-
end
|
1163
|
-
end
|
1164
|
-
|
1165
|
-
context 'not been set' do
|
1166
|
-
it 'returns false' do
|
1167
|
-
Arachni::Framework.new do |f|
|
1168
|
-
f.options.url = web_server_url_for :framework
|
1169
|
-
f.options.audit.elements :links
|
1170
|
-
|
1171
|
-
f.checks.load :taint
|
1172
|
-
|
1173
|
-
f.page_limit_reached?.should be_false
|
1174
|
-
f.run
|
1175
|
-
f.page_limit_reached?.should be_false
|
1176
|
-
end
|
1177
|
-
end
|
1178
|
-
end
|
1179
|
-
end
|
1180
|
-
end
|
1181
|
-
|
1182
|
-
describe '#accepts_more_pages?' do
|
1183
|
-
context 'when #page_limit_reached? and #crawl?' do
|
1184
|
-
it 'return true' do
|
1185
|
-
subject.stub(:page_limit_reached?) { false }
|
1186
|
-
subject.stub(:crawl?) { true }
|
1187
|
-
|
1188
|
-
subject.accepts_more_pages?.should be_true
|
1189
|
-
end
|
1190
|
-
end
|
1191
|
-
|
1192
|
-
context 'when #page_limit_reached?' do
|
1193
|
-
context true do
|
1194
|
-
it 'returns false' do
|
1195
|
-
subject.stub(:page_limit_reached?) { true }
|
1196
|
-
subject.accepts_more_pages?.should be_false
|
1197
|
-
end
|
1198
|
-
end
|
1199
|
-
end
|
1200
|
-
|
1201
|
-
context 'when #crawl?' do
|
1202
|
-
context false do
|
1203
|
-
it 'returns false' do
|
1204
|
-
subject.stub(:crawl?) { false }
|
1205
|
-
subject.accepts_more_pages?.should be_false
|
1206
|
-
end
|
1207
|
-
end
|
1208
|
-
end
|
1209
|
-
end
|
1210
|
-
|
1211
|
-
describe '#push_to_page_queue' do
|
1212
|
-
let(:page) { Arachni::Page.from_url( @url + '/train/true' ) }
|
1213
|
-
|
1214
|
-
it 'pushes it to the page audit queue and returns true' do
|
1215
|
-
subject.options.audit.elements :links, :forms, :cookies
|
1216
|
-
subject.checks.load :taint
|
1217
|
-
|
1218
|
-
subject.page_queue_total_size.should == 0
|
1219
|
-
subject.push_to_page_queue( page ).should be_true
|
1220
|
-
subject.run
|
1221
|
-
|
1222
|
-
subject.report.issues.size.should == 1
|
1223
|
-
subject.page_queue_total_size.should > 0
|
1224
|
-
end
|
1225
|
-
|
1226
|
-
it 'updates the #sitemap with the DOM URL' do
|
1227
|
-
subject.options.audit.elements :links, :forms, :cookies
|
1228
|
-
subject.checks.load :taint
|
1229
|
-
|
1230
|
-
subject.sitemap.should be_empty
|
1231
|
-
|
1232
|
-
page = Arachni::Page.from_url( @url + '/link' )
|
1233
|
-
page.dom.url = @url + '/link/#/stuff'
|
1234
|
-
|
1235
|
-
subject.push_to_page_queue page
|
1236
|
-
subject.sitemap.should include @url + '/link/#/stuff'
|
1237
|
-
end
|
1238
|
-
|
1239
|
-
it "passes it to #{Arachni::ElementFilter}#update_from_page_cache" do
|
1240
|
-
page = Arachni::Page.from_url( @url + '/link' )
|
1241
|
-
|
1242
|
-
Arachni::ElementFilter.should receive(:update_from_page_cache).with(page)
|
1243
|
-
|
1244
|
-
subject.push_to_page_queue page
|
1245
|
-
end
|
1246
|
-
|
1247
|
-
context 'when the page has already been seen' do
|
1248
|
-
it 'ignores it' do
|
1249
|
-
page = Arachni::Page.from_url( @url + '/stuff' )
|
1250
|
-
|
1251
|
-
subject.page_queue_total_size.should == 0
|
1252
|
-
subject.push_to_page_queue( page )
|
1253
|
-
subject.push_to_page_queue( page )
|
1254
|
-
subject.push_to_page_queue( page )
|
1255
|
-
subject.page_queue_total_size.should == 1
|
1256
|
-
end
|
1257
|
-
|
1258
|
-
it 'returns false' do
|
1259
|
-
page = Arachni::Page.from_url( @url + '/stuff' )
|
1260
|
-
|
1261
|
-
subject.page_queue_total_size.should == 0
|
1262
|
-
subject.push_to_page_queue( page ).should be_true
|
1263
|
-
subject.push_to_page_queue( page ).should be_false
|
1264
|
-
subject.push_to_page_queue( page ).should be_false
|
1265
|
-
subject.page_queue_total_size.should == 1
|
1266
|
-
end
|
1267
|
-
end
|
1268
|
-
|
1269
|
-
context 'when #accepts_more_pages?' do
|
1270
|
-
context false do
|
1271
|
-
it 'returns false' do
|
1272
|
-
subject.stub(:accepts_more_pages?) { false }
|
1273
|
-
subject.push_to_page_queue( page ).should be_false
|
1274
|
-
end
|
1275
|
-
end
|
1276
|
-
|
1277
|
-
context true do
|
1278
|
-
it 'returns true' do
|
1279
|
-
subject.stub(:accepts_more_pages?) { true }
|
1280
|
-
subject.push_to_page_queue( page ).should be_true
|
1281
|
-
end
|
1282
|
-
end
|
1283
|
-
end
|
1284
|
-
|
1285
|
-
context "when #{Arachni::Page::Scope}#out? is true" do
|
1286
|
-
it 'returns false' do
|
1287
|
-
Arachni::Page::Scope.any_instance.stub(:out?) { true }
|
1288
|
-
subject.push_to_page_queue( page ).should be_false
|
1289
|
-
end
|
1290
|
-
end
|
1291
|
-
|
1292
|
-
context "when #{Arachni::URI::Scope}#redundant? is true" do
|
1293
|
-
it 'returns false' do
|
1294
|
-
Arachni::Page::Scope.any_instance.stub(:redundant?) { true }
|
1295
|
-
subject.push_to_page_queue( page ).should be_false
|
1296
|
-
end
|
1297
|
-
end
|
1298
|
-
|
1299
|
-
context "when #{Arachni::Page::Scope}#auto_redundant? is true" do
|
1300
|
-
it 'returns false' do
|
1301
|
-
Arachni::Page::Scope.any_instance.stub(:auto_redundant?) { true }
|
1302
|
-
subject.push_to_page_queue( page ).should be_false
|
1303
|
-
end
|
1304
|
-
end
|
1305
|
-
end
|
1306
|
-
|
1307
|
-
describe '#push_to_url_queue' do
|
1308
|
-
it 'pushes a URL to the URL audit queue' do
|
1309
|
-
subject.options.audit.elements :links, :forms, :cookies
|
1310
|
-
subject.checks.load :taint
|
1311
|
-
|
1312
|
-
subject.url_queue_total_size.should == 0
|
1313
|
-
subject.push_to_url_queue( @url + '/link' ).should be_true
|
1314
|
-
subject.run
|
1315
|
-
|
1316
|
-
subject.report.issues.size.should == 1
|
1317
|
-
subject.url_queue_total_size.should == 3
|
1318
|
-
end
|
1319
|
-
|
1320
|
-
context 'when the URL has already been seen' do
|
1321
|
-
it 'returns false' do
|
1322
|
-
subject.push_to_url_queue( @url + '/link' ).should be_true
|
1323
|
-
subject.push_to_url_queue( @url + '/link' ).should be_false
|
1324
|
-
end
|
1325
|
-
|
1326
|
-
it 'ignores it' do
|
1327
|
-
subject.url_queue_total_size.should == 0
|
1328
|
-
subject.push_to_url_queue( @url + '/link' )
|
1329
|
-
subject.push_to_url_queue( @url + '/link' )
|
1330
|
-
subject.push_to_url_queue( @url + '/link' )
|
1331
|
-
subject.url_queue_total_size.should == 1
|
1332
|
-
end
|
1333
|
-
end
|
1334
|
-
|
1335
|
-
context 'when #accepts_more_pages?' do
|
1336
|
-
context false do
|
1337
|
-
it 'returns false' do
|
1338
|
-
subject.stub(:accepts_more_pages?) { false }
|
1339
|
-
subject.push_to_url_queue( @url ).should be_false
|
1340
|
-
end
|
1341
|
-
end
|
1342
|
-
|
1343
|
-
context true do
|
1344
|
-
it 'returns true' do
|
1345
|
-
subject.stub(:accepts_more_pages?) { true }
|
1346
|
-
subject.push_to_url_queue( @url ).should be_true
|
1347
|
-
end
|
1348
|
-
end
|
1349
|
-
end
|
1350
|
-
|
1351
|
-
context "when #{Arachni::URI::Scope}#out? is true" do
|
1352
|
-
it 'returns false' do
|
1353
|
-
Arachni::URI::Scope.any_instance.stub(:out?) { true }
|
1354
|
-
subject.push_to_url_queue( @url ).should be_false
|
1355
|
-
end
|
1356
|
-
end
|
1357
|
-
|
1358
|
-
context "when #{Arachni::URI::Scope}#redundant? is true" do
|
1359
|
-
it 'returns false' do
|
1360
|
-
Arachni::URI::Scope.any_instance.stub(:redundant?) { true }
|
1361
|
-
subject.push_to_url_queue( @url ).should be_false
|
1362
|
-
end
|
1363
|
-
end
|
1364
|
-
|
1365
|
-
context "when #{Arachni::URI::Scope}#auto_redundant? is true" do
|
1366
|
-
it 'returns false' do
|
1367
|
-
Arachni::URI::Scope.any_instance.stub(:auto_redundant?) { true }
|
1368
|
-
subject.push_to_url_queue( @url ).should be_false
|
1369
|
-
end
|
1370
|
-
end
|
1371
|
-
end
|
1372
|
-
|
1373
|
-
describe '#statistics' do
|
1374
|
-
let(:statistics) { subject.statistics }
|
1375
|
-
|
1376
|
-
it 'includes http statistics' do
|
1377
|
-
statistics[:http].should == subject.http.statistics
|
1378
|
-
end
|
1379
|
-
|
1380
|
-
[:found_pages, :audited_pages, :current_page].each do |k|
|
1381
|
-
it "includes #{k}" do
|
1382
|
-
statistics.should include k
|
1383
|
-
end
|
1384
|
-
end
|
1385
|
-
|
1386
|
-
describe :runtime do
|
1387
|
-
context 'when the scan has been running' do
|
1388
|
-
it 'returns the runtime in seconds' do
|
1389
|
-
subject.run
|
1390
|
-
statistics[:runtime].should > 0
|
1391
|
-
end
|
1392
|
-
end
|
1393
|
-
|
1394
|
-
context 'when no scan has been running' do
|
1395
|
-
it 'returns 0' do
|
1396
|
-
statistics[:runtime].should == 0
|
1397
|
-
end
|
1398
|
-
end
|
1399
|
-
end
|
1400
|
-
end
|
1401
|
-
|
1402
|
-
describe '#list_platforms' do
|
1403
|
-
it 'returns information about all valid platforms' do
|
1404
|
-
subject.list_platforms.should == {
|
1405
|
-
'Operating systems' => {
|
1406
|
-
unix: 'Generic Unix family',
|
1407
|
-
linux: 'Linux',
|
1408
|
-
bsd: 'Generic BSD family',
|
1409
|
-
aix: 'IBM AIX',
|
1410
|
-
solaris: 'Solaris',
|
1411
|
-
windows: 'MS Windows'
|
1412
|
-
},
|
1413
|
-
'Databases' => {
|
1414
|
-
access: 'MS Access',
|
1415
|
-
coldfusion: 'ColdFusion',
|
1416
|
-
db2: 'DB2',
|
1417
|
-
emc: 'EMC',
|
1418
|
-
firebird: 'Firebird',
|
1419
|
-
frontbase: 'Frontbase',
|
1420
|
-
hsqldb: 'HSQLDB',
|
1421
|
-
informix: 'Informix',
|
1422
|
-
ingres: 'IngresDB',
|
1423
|
-
interbase: 'InterBase',
|
1424
|
-
maxdb: 'SaP Max DB',
|
1425
|
-
mssql: 'MSSQL',
|
1426
|
-
mysql: 'MySQL',
|
1427
|
-
oracle: 'Oracle',
|
1428
|
-
pgsql: 'Postgresql',
|
1429
|
-
sqlite: 'SQLite',
|
1430
|
-
sybase: 'Sybase',
|
1431
|
-
mongodb: 'MongoDB'
|
1432
|
-
},
|
1433
|
-
'Web servers' => {
|
1434
|
-
apache: 'Apache',
|
1435
|
-
iis: 'IIS',
|
1436
|
-
jetty: 'Jetty',
|
1437
|
-
nginx: 'Nginx',
|
1438
|
-
tomcat: 'TomCat'
|
1439
|
-
},
|
1440
|
-
'Programming languages' => {
|
1441
|
-
asp: 'ASP',
|
1442
|
-
aspx: 'ASP.NET',
|
1443
|
-
jsp: 'JSP',
|
1444
|
-
perl: 'Perl',
|
1445
|
-
php: 'PHP',
|
1446
|
-
python: 'Python',
|
1447
|
-
ruby: 'Ruby'
|
1448
|
-
},
|
1449
|
-
'Frameworks' => {
|
1450
|
-
rack: 'Rack'
|
1451
|
-
}
|
1452
|
-
}
|
1453
|
-
|
1454
|
-
end
|
1455
|
-
end
|
1456
|
-
|
1457
|
-
describe '#list_checks' do
|
1458
|
-
context 'when a pattern is given' do
|
1459
|
-
it 'uses it to filter out checks that do not match it' do
|
1460
|
-
subject.list_checks( 'boo' ).size == 0
|
1461
|
-
|
1462
|
-
subject.list_checks( 'taint' ).should == subject.list_checks
|
1463
|
-
subject.list_checks.size == 1
|
1464
|
-
end
|
1465
|
-
end
|
1466
|
-
end
|
1467
|
-
|
1468
|
-
describe '#list_plugins' do
|
1469
|
-
it 'returns info on all plugins' do
|
1470
|
-
subject.list_plugins.size.should == subject.plugins.available.size
|
1471
|
-
|
1472
|
-
info = subject.list_plugins.find { |p| p[:options].any? }
|
1473
|
-
plugin = subject.plugins[info[:shortname]]
|
1474
|
-
|
1475
|
-
plugin.info.each do |k, v|
|
1476
|
-
if k == :author
|
1477
|
-
info[k].should == [v].flatten
|
1478
|
-
next
|
1479
|
-
end
|
1480
|
-
|
1481
|
-
info[k].should == v
|
1482
|
-
end
|
1483
|
-
|
1484
|
-
info[:shortname].should == plugin.shortname
|
1485
|
-
end
|
1486
|
-
|
1487
|
-
context 'when a pattern is given' do
|
1488
|
-
it 'uses it to filter out plugins that do not match it' do
|
1489
|
-
subject.list_plugins( 'bad|foo' ).size == 2
|
1490
|
-
subject.list_plugins( 'boo' ).size == 0
|
1491
|
-
end
|
1492
|
-
end
|
1493
|
-
end
|
1494
|
-
|
1495
|
-
describe '#list_reporters' do
|
1496
|
-
it 'returns info on all reporters' do
|
1497
|
-
subject.list_reporters.size.should == subject.reporters.available.size
|
1498
|
-
|
1499
|
-
info = subject.list_reporters.find { |p| p[:options].any? }
|
1500
|
-
report = subject.reporters[info[:shortname]]
|
1501
|
-
|
1502
|
-
report.info.each do |k, v|
|
1503
|
-
if k == :author
|
1504
|
-
info[k].should == [v].flatten
|
1505
|
-
next
|
1506
|
-
end
|
1507
|
-
|
1508
|
-
info[k].should == v
|
1509
|
-
end
|
1510
|
-
|
1511
|
-
info[:shortname].should == report.shortname
|
1512
|
-
end
|
1513
|
-
|
1514
|
-
context 'when a pattern is given' do
|
1515
|
-
it 'uses it to filter out reporters that do not match it' do
|
1516
|
-
subject.list_reporters( 'foo' ).size == 1
|
1517
|
-
subject.list_reporters( 'boo' ).size == 0
|
1518
191
|
end
|
1519
192
|
end
|
1520
193
|
end
|