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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/README.md +9 -2
  4. data/components/checks/active/code_injection.rb +5 -5
  5. data/components/checks/active/code_injection_timing.rb +3 -3
  6. data/components/checks/active/no_sql_injection_differential.rb +3 -2
  7. data/components/checks/active/os_cmd_injection.rb +11 -5
  8. data/components/checks/active/os_cmd_injection_timing.rb +11 -4
  9. data/components/checks/active/path_traversal.rb +2 -2
  10. data/components/checks/active/sql_injection.rb +1 -1
  11. data/components/checks/active/sql_injection/patterns/mssql +1 -0
  12. data/components/checks/active/sql_injection_differential.rb +3 -2
  13. data/components/checks/active/unvalidated_redirect.rb +3 -3
  14. data/components/checks/passive/common_directories/directories.txt +2 -0
  15. data/components/checks/passive/common_files/filenames.txt +1 -0
  16. data/lib/arachni/browser.rb +17 -1
  17. data/lib/arachni/check/auditor.rb +5 -2
  18. data/lib/arachni/check/base.rb +30 -5
  19. data/lib/arachni/element/capabilities/analyzable/differential.rb +2 -5
  20. data/lib/arachni/element/capabilities/auditable.rb +3 -1
  21. data/lib/arachni/element/capabilities/with_dom.rb +1 -0
  22. data/lib/arachni/element/capabilities/with_node.rb +1 -1
  23. data/lib/arachni/element/cookie.rb +2 -2
  24. data/lib/arachni/element/form.rb +1 -1
  25. data/lib/arachni/element/header.rb +2 -2
  26. data/lib/arachni/element/link_template.rb +1 -1
  27. data/lib/arachni/framework.rb +21 -1144
  28. data/lib/arachni/framework/parts/audit.rb +282 -0
  29. data/lib/arachni/framework/parts/browser.rb +132 -0
  30. data/lib/arachni/framework/parts/check.rb +86 -0
  31. data/lib/arachni/framework/parts/data.rb +158 -0
  32. data/lib/arachni/framework/parts/platform.rb +34 -0
  33. data/lib/arachni/framework/parts/plugin.rb +61 -0
  34. data/lib/arachni/framework/parts/report.rb +128 -0
  35. data/lib/arachni/framework/parts/scope.rb +40 -0
  36. data/lib/arachni/framework/parts/state.rb +457 -0
  37. data/lib/arachni/http/client.rb +33 -30
  38. data/lib/arachni/http/request.rb +6 -2
  39. data/lib/arachni/issue.rb +55 -1
  40. data/lib/arachni/platform/manager.rb +25 -21
  41. data/lib/arachni/state/framework.rb +7 -1
  42. data/lib/arachni/utilities.rb +10 -0
  43. data/lib/version +1 -1
  44. data/spec/arachni/browser_spec.rb +13 -0
  45. data/spec/arachni/check/auditor_spec.rb +1 -0
  46. data/spec/arachni/check/base_spec.rb +80 -0
  47. data/spec/arachni/element/cookie_spec.rb +2 -2
  48. data/spec/arachni/framework/parts/audit_spec.rb +391 -0
  49. data/spec/arachni/framework/parts/browser_spec.rb +26 -0
  50. data/spec/arachni/framework/parts/check_spec.rb +24 -0
  51. data/spec/arachni/framework/parts/data_spec.rb +187 -0
  52. data/spec/arachni/framework/parts/platform_spec.rb +62 -0
  53. data/spec/arachni/framework/parts/plugin_spec.rb +41 -0
  54. data/spec/arachni/framework/parts/report_spec.rb +66 -0
  55. data/spec/arachni/framework/parts/scope_spec.rb +86 -0
  56. data/spec/arachni/framework/parts/state_spec.rb +528 -0
  57. data/spec/arachni/framework_spec.rb +17 -1344
  58. data/spec/arachni/http/client_spec.rb +12 -7
  59. data/spec/arachni/issue_spec.rb +35 -0
  60. data/spec/arachni/platform/manager_spec.rb +2 -3
  61. data/spec/arachni/state/framework_spec.rb +15 -0
  62. data/spec/components/checks/active/code_injection_timing_spec.rb +5 -5
  63. data/spec/components/checks/active/no_sql_injection_differential_spec.rb +4 -0
  64. data/spec/components/checks/active/os_cmd_injection_spec.rb +20 -7
  65. data/spec/components/checks/active/os_cmd_injection_timing_spec.rb +5 -5
  66. data/spec/components/checks/active/sql_injection_differential_spec.rb +4 -0
  67. data/spec/components/checks/active/sql_injection_spec.rb +2 -3
  68. data/spec/support/servers/arachni/browser.rb +31 -0
  69. data/spec/support/servers/checks/active/code_injection.rb +1 -1
  70. data/spec/support/servers/checks/active/no_sql_injection_differential.rb +36 -34
  71. data/spec/support/servers/checks/active/os_cmd_injection.rb +6 -12
  72. data/spec/support/servers/checks/active/os_cmd_injection_timing.rb +9 -4
  73. data/spec/support/servers/checks/active/sql_injection.rb +1 -1
  74. data/spec/support/servers/checks/active/sql_injection_differential.rb +37 -34
  75. data/spec/support/shared/element/capabilities/with_node.rb +25 -0
  76. data/spec/support/shared/framework.rb +26 -0
  77. data/ui/cli/output.rb +2 -0
  78. data/ui/cli/rpc/server/dispatcher/option_parser.rb +1 -1
  79. metadata +32 -4
  80. data/components/checks/active/sql_injection/patterns/coldfusion +0 -1
@@ -218,9 +218,9 @@ describe Arachni::Element::Cookie do
218
218
  end
219
219
  end
220
220
 
221
- describe '#encode' do
221
+ describe '.encode' do
222
222
  it 'encodes the string in a way that makes is suitable to be included in a cookie header' do
223
- described_class.encode( 'some stuff \'";%=' ).should == 'some+stuff+%27%22%3B%25='
223
+ described_class.encode( 'some stuff \'";%=&' ).should == 'some+stuff+%27%22%3B%25=%26'
224
224
  end
225
225
  end
226
226
 
@@ -0,0 +1,391 @@
1
+ require 'spec_helper'
2
+
3
+ describe Arachni::Framework::Parts::Audit do
4
+ include_examples 'framework'
5
+
6
+ describe Arachni::OptionGroups::Scope do
7
+ describe '#exclude_binaries' do
8
+ it 'excludes binary pages from the scan' do
9
+ audited = []
10
+ Arachni::Framework.new do |f|
11
+ f.options.url = @url
12
+ f.options.scope.restrict_paths << @url + '/binary'
13
+ f.options.audit.elements :links, :forms, :cookies
14
+ f.checks.load :taint
15
+
16
+ f.on_page_audit { |p| audited << p.url }
17
+ f.run
18
+ end
19
+ audited.sort.should == [@url + '/binary'].sort
20
+
21
+ audited = []
22
+ Arachni::Framework.new do |f|
23
+ f.options.url = @url
24
+ f.options.scope.restrict_paths << @url + '/binary'
25
+ f.options.scope.exclude_binaries = true
26
+ f.checks.load :taint
27
+
28
+ f.on_page_audit { |p| audited << p.url }
29
+ f.run
30
+ end
31
+ audited.should be_empty
32
+ end
33
+ end
34
+
35
+ describe '#extend_paths' do
36
+ it 'extends the crawl scope' do
37
+ Arachni::Framework.new do |f|
38
+ f.options.url = "#{@url}/elem_combo"
39
+ f.options.scope.extend_paths = %w(/some/stuff /more/stuff)
40
+ f.options.audit.elements :links, :forms, :cookies
41
+ f.checks.load :taint
42
+
43
+ f.run
44
+
45
+ f.report.sitemap.should include "#{@url}/some/stuff"
46
+ f.report.sitemap.should include "#{@url}/more/stuff"
47
+ f.report.sitemap.size.should > 3
48
+ end
49
+ end
50
+ end
51
+
52
+ describe '#restrict_paths' do
53
+ it 'serves as a replacement to crawling' do
54
+ Arachni::Framework.new do |f|
55
+ f.options.url = "#{@url}/elem_combo"
56
+ f.options.scope.restrict_paths = %w(/log_remote_file_if_exists/true)
57
+ f.options.audit.elements :links, :forms, :cookies
58
+ f.checks.load :taint
59
+
60
+ f.run
61
+
62
+ sitemap = f.report.sitemap.map { |u, _| u.split( '?' ).first }
63
+ sitemap.sort.uniq.should == f.options.scope.restrict_paths.
64
+ map { |p| f.to_absolute( p ) }.sort
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ context 'when unable to get a response for the given URL' do
71
+ context 'due to a network error' do
72
+ it 'returns an empty sitemap and have failures' do
73
+ @options.url = 'http://blahaha'
74
+ @options.scope.restrict_paths = [@options.url]
75
+
76
+ subject.checks.load :taint
77
+ subject.run
78
+ subject.failures.should be_any
79
+ end
80
+ end
81
+
82
+ context 'due to a server error' do
83
+ it 'returns an empty sitemap and have failures' do
84
+ @options.url = @f_url + '/fail'
85
+ @options.scope.restrict_paths = [@options.url]
86
+
87
+ subject.checks.load :taint
88
+ subject.run
89
+ subject.failures.should be_any
90
+ end
91
+ end
92
+
93
+ it "retries #{Arachni::Framework::AUDIT_PAGE_MAX_TRIES} times" do
94
+ @options.url = @f_url + '/fail_4_times'
95
+ @options.scope.restrict_paths = [@options.url]
96
+
97
+ subject.checks.load :taint
98
+ subject.run
99
+ subject.failures.should be_empty
100
+ end
101
+ end
102
+
103
+ describe '#http' do
104
+ it 'provides access to the HTTP interface' do
105
+ subject.http.is_a?( Arachni::HTTP::Client ).should be_true
106
+ end
107
+ end
108
+
109
+ describe '#failures' do
110
+ context 'when there are no failed requests' do
111
+ it 'returns an empty array' do
112
+ @options.url = @f_url
113
+ @options.scope.restrict_paths = [@options.url]
114
+
115
+ subject.checks.load :taint
116
+ subject.run
117
+ subject.failures.should be_empty
118
+ end
119
+ end
120
+ context 'when there are failed requests' do
121
+ it 'returns an array containing the failed URLs' do
122
+ @options.url = @f_url + '/fail'
123
+ @options.scope.restrict_paths = [@options.url]
124
+
125
+ subject.checks.load :taint
126
+ subject.run
127
+ subject.failures.should be_any
128
+ end
129
+ end
130
+ end
131
+
132
+ describe '#on_page_audit' do
133
+ it 'calls the given block before each page is audited' do
134
+ ok = false
135
+ Arachni::Framework.new do |f|
136
+ f.options.url = @url
137
+ f.on_page_audit { ok = true }
138
+
139
+ f.audit_page Arachni::Page.from_url( @url + '/link' )
140
+ end
141
+ ok.should be_true
142
+ end
143
+ end
144
+
145
+ describe '#after_page_audit' do
146
+ it 'calls the given block before each page is audited' do
147
+ ok = false
148
+ Arachni::Framework.new do |f|
149
+ f.options.url = @url
150
+ f.after_page_audit { ok = true }
151
+
152
+ f.audit_page Arachni::Page.from_url( @url + '/link' )
153
+ end
154
+ ok.should be_true
155
+ end
156
+ end
157
+
158
+ describe '#audit_page' do
159
+ it 'updates the #sitemap with the DOM URL' do
160
+ subject.options.audit.elements :links, :forms, :cookies
161
+ subject.checks.load :taint
162
+
163
+ subject.sitemap.should be_empty
164
+
165
+ page = Arachni::Page.from_url( @url + '/link' )
166
+ page.dom.url = @url + '/link/#/stuff'
167
+
168
+ subject.audit_page page
169
+ subject.sitemap.should include @url + '/link/#/stuff'
170
+ end
171
+
172
+ it "runs #{Arachni::Check::Manager}#without_platforms before #{Arachni::Check::Manager}#with_platforms" do
173
+ @options.paths.checks = fixtures_path + '/checks/'
174
+
175
+ Arachni::Framework.new do |f|
176
+ f.checks.load_all
177
+
178
+ page = Arachni::Page.from_url( @url + '/link' )
179
+
180
+ responses = []
181
+ f.http.on_complete do |response|
182
+ responses << response.url
183
+ end
184
+
185
+ f.audit_page page
186
+
187
+ responses.sort.should ==
188
+ %w(http://localhost/test3 http://localhost/test
189
+ http://localhost/test2).sort
190
+ end
191
+ end
192
+
193
+ context 'when checks were' do
194
+ context 'ran against the page' do
195
+ it 'returns true' do
196
+ subject.checks.load :taint
197
+ subject.audit_page( Arachni::Page.from_url( @url + '/link' ) ).should be_true
198
+ end
199
+ end
200
+
201
+ context 'not ran against the page' do
202
+ it 'returns false' do
203
+ subject.audit_page( Arachni::Page.from_url( @url + '/link' ) ).should be_false
204
+ end
205
+ end
206
+ end
207
+
208
+ context 'when the page contains JavaScript code' do
209
+ it 'analyzes the DOM and pushes new pages to the page queue' do
210
+ Arachni::Framework.new do |f|
211
+ f.options.audit.elements :links, :forms, :cookies
212
+ f.checks.load :taint
213
+
214
+ f.page_queue_total_size.should == 0
215
+
216
+ f.audit_page( Arachni::Page.from_url( @url + '/with_javascript' ) )
217
+
218
+ sleep 0.1 while f.wait_for_browser?
219
+
220
+ f.page_queue_total_size.should > 0
221
+ end
222
+ end
223
+
224
+ it 'analyzes the DOM and pushes new paths to the url queue' do
225
+ Arachni::Framework.new do |f|
226
+ f.options.url = @url
227
+ f.options.audit.elements :links, :forms, :cookies
228
+ f.checks.load :taint
229
+
230
+ f.url_queue_total_size.should == 0
231
+
232
+ f.audit_page( Arachni::Page.from_url( @url + '/with_javascript' ) )
233
+
234
+ sleep 0.1 while f.wait_for_browser?
235
+
236
+ f.url_queue_total_size.should == 2
237
+ end
238
+ end
239
+
240
+ context 'when the DOM depth limit has been reached' do
241
+ it 'does not analyze the DOM' do
242
+ Arachni::Framework.new do |f|
243
+ f.options.url = @url
244
+
245
+ f.options.audit.elements :links, :forms, :cookies
246
+ f.checks.load :taint
247
+ f.options.scope.dom_depth_limit = 1
248
+ f.url_queue_total_size.should == 0
249
+ f.audit_page( Arachni::Page.from_url( @url + '/with_javascript' ) ).should be_true
250
+ sleep 0.1 while f.wait_for_browser?
251
+ f.url_queue_total_size.should == 2
252
+
253
+ f.reset
254
+
255
+ f.options.audit.elements :links, :forms, :cookies
256
+ f.checks.load :taint
257
+ f.options.scope.dom_depth_limit = 1
258
+ f.url_queue_total_size.should == 0
259
+
260
+ page = Arachni::Page.from_url( @url + '/with_javascript' )
261
+ page.dom.push_transition Arachni::Page::DOM::Transition.new( :page, :load )
262
+
263
+ f.audit_page( page ).should be_true
264
+ sleep 0.1 while f.wait_for_browser?
265
+ f.url_queue_total_size.should == 0
266
+ end
267
+ end
268
+
269
+ it 'returns false' do
270
+ page = Arachni::Page.from_data(
271
+ url: @url,
272
+ dom: {
273
+ transitions: [
274
+ { page: :load },
275
+ { "<a href='javascript:click();'>" => :click },
276
+ { "<button dblclick='javascript:doubleClick();'>" => :ondblclick }
277
+ ].map { |t| Arachni::Page::DOM::Transition.new *t.first }
278
+ }
279
+ )
280
+
281
+ Arachni::Framework.new do |f|
282
+ f.checks.load :taint
283
+
284
+ f.options.scope.dom_depth_limit = 10
285
+ f.audit_page( page ).should be_true
286
+
287
+ f.options.scope.dom_depth_limit = 2
288
+ f.audit_page( page ).should be_false
289
+ end
290
+ end
291
+ end
292
+ end
293
+
294
+ context 'when the page matches exclusion criteria' do
295
+ it 'does not audit it' do
296
+ subject.options.scope.exclude_path_patterns << /link/
297
+ subject.options.audit.elements :links, :forms, :cookies
298
+
299
+ subject.checks.load :taint
300
+
301
+ subject.audit_page( Arachni::Page.from_url( @url + '/link' ) )
302
+ subject.report.issues.size.should == 0
303
+ end
304
+
305
+ it 'returns false' do
306
+ subject.options.scope.exclude_path_patterns << /link/
307
+ subject.audit_page( Arachni::Page.from_url( @url + '/link' ) ).should be_false
308
+ end
309
+ end
310
+
311
+ context "when #{Arachni::Options}#platforms" do
312
+ context 'have been provided' do
313
+ context 'and are supported by the check' do
314
+ it 'audits it' do
315
+ subject.options.platforms = [:unix]
316
+ subject.options.audit.elements :links, :forms, :cookies
317
+
318
+ subject.checks.load :taint
319
+ subject.checks[:taint].platforms << :unix
320
+
321
+ subject.audit_page( Arachni::Page.from_url( @url + '/link' ) )
322
+ subject.report.issues.should be_any
323
+ end
324
+ end
325
+
326
+ context 'and are not supported by the check' do
327
+ it 'does not audit it' do
328
+ subject.options.platforms = [:windows]
329
+ subject.options.audit.elements :links, :forms, :cookies
330
+
331
+ subject.checks.load :taint
332
+ subject.checks[:taint].platforms << :unix
333
+
334
+ subject.audit_page( Arachni::Page.from_url( @url + '/link' ) )
335
+ subject.report.issues.should be_empty
336
+ end
337
+ end
338
+ end
339
+
340
+ context 'have not been provided' do
341
+ it 'audits it' do
342
+ subject.options.platforms = []
343
+ subject.options.audit.elements :links, :forms, :cookies
344
+
345
+ subject.checks.load :taint
346
+ subject.checks[:taint].platforms << :unix
347
+
348
+ subject.audit_page( Arachni::Page.from_url( @url + '/link' ) )
349
+ subject.report.issues.should be_any
350
+ end
351
+ end
352
+ end
353
+
354
+ context "when #{Arachni::Check::Auditor}.has_timeout_candidates?" do
355
+ it "calls #{Arachni::Check::Auditor}.timeout_audit_run" do
356
+ Arachni::Check::Auditor.stub(:has_timeout_candidates?){ true }
357
+
358
+ Arachni::Check::Auditor.should receive(:timeout_audit_run)
359
+ subject.audit_page( Arachni::Page.from_url( @url + '/link' ) )
360
+ end
361
+ end
362
+
363
+ context 'when the page contains elements seen in previous pages' do
364
+ it 'removes them from the page'
365
+ end
366
+
367
+ context 'when a check fails with an exception' do
368
+ it 'moves to the next one' do
369
+ @options.paths.checks = fixtures_path + '/checks/'
370
+
371
+ Arachni::Framework.new do |f|
372
+ f.checks.load_all
373
+
374
+ f.checks[:test].any_instance.stub(:run) { raise }
375
+
376
+ page = Arachni::Page.from_url( @url + '/link' )
377
+
378
+ responses = []
379
+ f.http.on_complete do |response|
380
+ responses << response.url
381
+ end
382
+
383
+ f.audit_page page
384
+
385
+ responses.should == %w(http://localhost/test3 http://localhost/test2)
386
+ end
387
+ end
388
+ end
389
+ end
390
+
391
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Arachni::Framework::Parts::Browser do
4
+ include_examples 'framework'
5
+
6
+ describe '#browser_cluster' do
7
+ it "returns #{Arachni::BrowserCluster}" do
8
+ subject.browser_cluster.should be_kind_of Arachni::BrowserCluster
9
+ end
10
+
11
+ context "when #{Arachni::OptionGroups::BrowserCluster}#pool_size" do
12
+ it 'returns nil' do
13
+ subject.options.browser_cluster.pool_size = 0
14
+ subject.browser_cluster.should be_nil
15
+ end
16
+ end
17
+
18
+ context "when #{Arachni::OptionGroups::Scope}#dom_depth_limit" do
19
+ it 'returns nil' do
20
+ subject.options.scope.dom_depth_limit = 0
21
+ subject.browser_cluster.should be_nil
22
+ end
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Arachni::Framework::Parts::Check do
4
+ include_examples 'framework'
5
+
6
+ describe '#checks' do
7
+ it 'provides access to the check manager' do
8
+ subject.checks.is_a?( Arachni::Check::Manager ).should be_true
9
+ subject.checks.available.should == %w(taint)
10
+ end
11
+ end
12
+
13
+ describe '#list_checks' do
14
+ context 'when a pattern is given' do
15
+ it 'uses it to filter out checks that do not match it' do
16
+ subject.list_checks( 'boo' ).size == 0
17
+
18
+ subject.list_checks( 'taint' ).should == subject.list_checks
19
+ subject.list_checks.size == 1
20
+ end
21
+ end
22
+ end
23
+
24
+ end