arachni 1.1 → 1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (287) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +159 -0
  3. data/LICENSE.md +126 -196
  4. data/README.md +32 -24
  5. data/arachni.gemspec +7 -7
  6. data/components/checks/active/code_injection_timing.rb +3 -3
  7. data/components/checks/active/csrf.rb +2 -2
  8. data/components/checks/active/file_inclusion.rb +6 -7
  9. data/components/checks/active/os_cmd_injection.rb +3 -3
  10. data/components/checks/active/path_traversal.rb +7 -7
  11. data/components/checks/active/response_splitting.rb +9 -4
  12. data/components/checks/active/session_fixation.rb +7 -3
  13. data/components/checks/active/source_code_disclosure.rb +5 -5
  14. data/components/checks/active/unvalidated_redirect.rb +12 -3
  15. data/components/checks/active/unvalidated_redirect_dom.rb +3 -3
  16. data/components/checks/active/xss.rb +23 -10
  17. data/components/checks/active/xss_dom_inputs.rb +113 -11
  18. data/components/checks/active/xxe.rb +3 -3
  19. data/components/checks/passive/backdoors.rb +6 -5
  20. data/components/checks/passive/backup_directories.rb +6 -6
  21. data/components/checks/passive/backup_files.rb +6 -6
  22. data/components/checks/passive/common_admin_interfaces.rb +58 -0
  23. data/components/checks/passive/common_admin_interfaces/admin-panels.txt +49 -0
  24. data/components/checks/passive/common_directories/directories.txt +0 -16
  25. data/components/checks/passive/common_files.rb +6 -5
  26. data/components/checks/passive/common_files/filenames.txt +0 -2
  27. data/components/checks/passive/directory_listing.rb +6 -6
  28. data/components/checks/passive/grep/cookie_set_for_parent_domain.rb +3 -3
  29. data/components/checks/passive/grep/hsts.rb +6 -3
  30. data/components/checks/passive/grep/http_only_cookies.rb +3 -3
  31. data/components/checks/passive/grep/insecure_cookies.rb +2 -2
  32. data/components/checks/passive/grep/insecure_cors_policy.rb +6 -4
  33. data/components/checks/passive/grep/x_frame_options.rb +6 -4
  34. data/components/checks/passive/htaccess_limit.rb +6 -2
  35. data/components/checks/passive/http_put.rb +8 -4
  36. data/components/checks/passive/interesting_responses.rb +3 -2
  37. data/components/checks/passive/localstart_asp.rb +6 -2
  38. data/components/checks/passive/origin_spoof_access_restriction_bypass.rb +5 -1
  39. data/components/checks/passive/xst.rb +6 -2
  40. data/components/fingerprinters/frameworks/aspx_mvc.rb +43 -0
  41. data/components/fingerprinters/frameworks/cakephp.rb +28 -0
  42. data/components/fingerprinters/frameworks/cherrypy.rb +31 -0
  43. data/components/fingerprinters/frameworks/django.rb +33 -0
  44. data/components/fingerprinters/frameworks/jsf.rb +30 -0
  45. data/components/fingerprinters/frameworks/rack.rb +5 -7
  46. data/components/fingerprinters/frameworks/rails.rb +43 -0
  47. data/components/fingerprinters/languages/aspx.rb +11 -11
  48. data/components/fingerprinters/languages/{jsp.rb → java.rb} +11 -7
  49. data/components/fingerprinters/languages/php.rb +6 -6
  50. data/components/fingerprinters/languages/python.rb +14 -6
  51. data/components/fingerprinters/languages/ruby.rb +3 -5
  52. data/components/fingerprinters/servers/apache.rb +5 -4
  53. data/components/fingerprinters/servers/gunicorn.rb +33 -0
  54. data/components/fingerprinters/servers/jetty.rb +1 -1
  55. data/components/fingerprinters/servers/tomcat.rb +11 -4
  56. data/components/path_extractors/anchors.rb +5 -12
  57. data/components/path_extractors/areas.rb +5 -13
  58. data/components/path_extractors/comments.rb +5 -3
  59. data/components/path_extractors/data_url.rb +21 -0
  60. data/components/path_extractors/forms.rb +5 -13
  61. data/components/path_extractors/frames.rb +6 -13
  62. data/components/path_extractors/generic.rb +3 -12
  63. data/components/path_extractors/links.rb +5 -13
  64. data/components/path_extractors/meta_refresh.rb +5 -13
  65. data/components/path_extractors/scripts.rb +8 -14
  66. data/components/plugins/autologin.rb +17 -5
  67. data/components/plugins/defaults/meta/remedies/discovery.rb +11 -29
  68. data/components/plugins/login_script.rb +40 -10
  69. data/components/plugins/metrics.rb +235 -0
  70. data/components/plugins/proxy.rb +21 -4
  71. data/components/plugins/proxy/panel/page_accordion.html.erb +34 -2
  72. data/components/plugins/restrict_to_dom_state.rb +70 -0
  73. data/components/plugins/vector_feed.rb +38 -9
  74. data/components/reporters/plugin_formatters/html/metrics.rb +290 -0
  75. data/components/reporters/plugin_formatters/stdout/metrics.rb +80 -0
  76. data/components/reporters/plugin_formatters/xml/metrics.rb +29 -0
  77. data/components/reporters/stdout.rb +4 -2
  78. data/components/reporters/xml.rb +4 -4
  79. data/components/reporters/xml/schema.xsd +95 -0
  80. data/lib/arachni.rb +2 -0
  81. data/lib/arachni/browser.rb +132 -77
  82. data/lib/arachni/browser/javascript.rb +173 -45
  83. data/lib/arachni/browser/javascript/scripts/dom_monitor.js +81 -6
  84. data/lib/arachni/browser/javascript/scripts/taint_tracer.js +31 -3
  85. data/lib/arachni/browser_cluster.rb +41 -15
  86. data/lib/arachni/browser_cluster/job.rb +4 -0
  87. data/lib/arachni/browser_cluster/jobs/resource_exploration.rb +0 -9
  88. data/lib/arachni/browser_cluster/worker.rb +8 -5
  89. data/lib/arachni/check/auditor.rb +20 -8
  90. data/lib/arachni/check/base.rb +38 -6
  91. data/lib/arachni/element/base.rb +18 -1
  92. data/lib/arachni/element/capabilities/analyzable/differential.rb +0 -1
  93. data/lib/arachni/element/capabilities/analyzable/taint.rb +40 -10
  94. data/lib/arachni/element/capabilities/analyzable/timeout.rb +27 -23
  95. data/lib/arachni/element/capabilities/auditable/dom.rb +22 -0
  96. data/lib/arachni/element/capabilities/inputtable.rb +6 -2
  97. data/lib/arachni/element/capabilities/submittable.rb +1 -1
  98. data/lib/arachni/element/cookie.rb +37 -23
  99. data/lib/arachni/element/cookie/capabilities/mutable.rb +6 -6
  100. data/lib/arachni/element/cookie/dom.rb +0 -8
  101. data/lib/arachni/element/form.rb +28 -14
  102. data/lib/arachni/element/form/capabilities/auditable.rb +2 -2
  103. data/lib/arachni/element/form/capabilities/mutable.rb +5 -5
  104. data/lib/arachni/element/form/dom.rb +0 -8
  105. data/lib/arachni/element/generic_dom.rb +1 -1
  106. data/lib/arachni/element/json.rb +2 -1
  107. data/lib/arachni/element/json/capabilities/inputtable.rb +6 -6
  108. data/lib/arachni/element/json/capabilities/mutable.rb +1 -1
  109. data/lib/arachni/element/link.rb +13 -16
  110. data/lib/arachni/element/link/dom.rb +1 -14
  111. data/lib/arachni/element/link_template.rb +3 -2
  112. data/lib/arachni/element/link_template/dom.rb +0 -16
  113. data/lib/arachni/element/server.rb +51 -9
  114. data/lib/arachni/element/xml.rb +1 -0
  115. data/lib/arachni/ethon/easy.rb +4 -1
  116. data/lib/arachni/framework/parts/audit.rb +26 -77
  117. data/lib/arachni/framework/parts/browser.rb +50 -55
  118. data/lib/arachni/framework/parts/check.rb +4 -3
  119. data/lib/arachni/framework/parts/data.rb +41 -6
  120. data/lib/arachni/framework/parts/state.rb +16 -7
  121. data/lib/arachni/http/client.rb +66 -38
  122. data/lib/arachni/http/client/dynamic_404_handler.rb +46 -14
  123. data/lib/arachni/http/headers.rb +22 -10
  124. data/lib/arachni/http/proxy_server.rb +67 -22
  125. data/lib/arachni/http/proxy_server/ssl-interceptor-cacert.pem +34 -0
  126. data/lib/arachni/http/proxy_server/ssl-interceptor-cakey.pem +51 -0
  127. data/lib/arachni/http/request.rb +71 -18
  128. data/lib/arachni/issue.rb +17 -3
  129. data/lib/arachni/option_groups/browser_cluster.rb +34 -1
  130. data/lib/arachni/option_groups/http.rb +1 -1
  131. data/lib/arachni/page.rb +26 -13
  132. data/lib/arachni/page/dom/transition.rb +2 -2
  133. data/lib/arachni/parser.rb +28 -11
  134. data/lib/arachni/platform/fingerprinter.rb +5 -0
  135. data/lib/arachni/platform/manager.rb +65 -32
  136. data/lib/arachni/plugin/base.rb +8 -0
  137. data/lib/arachni/processes/instances.rb +25 -11
  138. data/lib/arachni/reporter/manager.rb +2 -2
  139. data/lib/arachni/rpc/client/instance.rb +4 -0
  140. data/lib/arachni/rpc/server/framework/master.rb +3 -3
  141. data/lib/arachni/rpc/server/framework/multi_instance.rb +0 -8
  142. data/lib/arachni/rpc/server/instance.rb +2 -1
  143. data/lib/arachni/ruby/array.rb +5 -0
  144. data/lib/arachni/ruby/hash.rb +5 -0
  145. data/lib/arachni/ruby/string.rb +2 -3
  146. data/lib/arachni/session.rb +32 -6
  147. data/lib/arachni/state/framework.rb +6 -2
  148. data/lib/arachni/support/cache.rb +1 -0
  149. data/lib/arachni/support/cache/base.rb +12 -8
  150. data/lib/arachni/support/cache/least_recently_pushed.rb +29 -0
  151. data/lib/arachni/support/cache/least_recently_used.rb +5 -8
  152. data/lib/arachni/support/cache/preference.rb +1 -1
  153. data/lib/arachni/support/cache/random_replacement.rb +1 -25
  154. data/lib/arachni/support/database/queue.rb +21 -8
  155. data/lib/arachni/support/lookup/base.rb +7 -1
  156. data/lib/arachni/support/mixins/observable.rb +3 -1
  157. data/lib/arachni/support/profiler.rb +51 -10
  158. data/lib/arachni/support/signature.rb +11 -2
  159. data/lib/arachni/trainer.rb +8 -2
  160. data/lib/arachni/uri.rb +28 -25
  161. data/lib/arachni/uri/scope.rb +1 -1
  162. data/lib/arachni/utilities.rb +8 -0
  163. data/lib/arachni/watir/element.rb +1 -1
  164. data/lib/version +1 -1
  165. data/spec/arachni/browser/javascript/dom_monitor_spec.rb +388 -53
  166. data/spec/arachni/browser/javascript/taint_tracer_spec.rb +41 -0
  167. data/spec/arachni/browser/javascript_spec.rb +235 -61
  168. data/spec/arachni/browser_cluster/jobs/resource_exploration_spec.rb +0 -9
  169. data/spec/arachni/browser_cluster_spec.rb +58 -10
  170. data/spec/arachni/browser_spec.rb +170 -26
  171. data/spec/arachni/check/auditor_spec.rb +22 -3
  172. data/spec/arachni/check/base_spec.rb +84 -0
  173. data/spec/arachni/element/body_spec.rb +1 -1
  174. data/spec/arachni/element/capabilities/analyzable/taint_spec.rb +3 -3
  175. data/spec/arachni/element/capabilities/analyzable/timeout_spec.rb +1 -1
  176. data/spec/arachni/element/cookie/dom_spec.rb +0 -9
  177. data/spec/arachni/element/cookie_spec.rb +85 -0
  178. data/spec/arachni/element/form/dom_spec.rb +0 -9
  179. data/spec/arachni/element/form_spec.rb +46 -3
  180. data/spec/arachni/element/json_spec.rb +20 -0
  181. data/spec/arachni/element/link/dom_spec.rb +0 -9
  182. data/spec/arachni/element/link_spec.rb +40 -15
  183. data/spec/arachni/element/link_template/dom_spec.rb +0 -8
  184. data/spec/arachni/element/link_template_spec.rb +2 -6
  185. data/spec/arachni/element/server_spec.rb +94 -8
  186. data/spec/arachni/element/xml_spec.rb +20 -0
  187. data/spec/arachni/framework/parts/audit_spec.rb +12 -14
  188. data/spec/arachni/framework/parts/browser_spec.rb +0 -171
  189. data/spec/arachni/framework/parts/platform_spec.rb +14 -8
  190. data/spec/arachni/framework/parts/report_spec.rb +1 -1
  191. data/spec/arachni/framework/parts/state_spec.rb +0 -9
  192. data/spec/arachni/http/client/dynamic_404_handlers_spec.rb +19 -0
  193. data/spec/arachni/http/client_spec.rb +169 -42
  194. data/spec/arachni/http/headers_spec.rb +18 -0
  195. data/spec/arachni/http/request_spec.rb +23 -0
  196. data/spec/arachni/issue_spec.rb +17 -6
  197. data/spec/arachni/page_spec.rb +22 -2
  198. data/spec/arachni/parser_spec.rb +5 -0
  199. data/spec/arachni/platform/manager_spec.rb +57 -25
  200. data/spec/arachni/reporter/manager_spec.rb +26 -0
  201. data/spec/arachni/rpc/server/active_options_spec.rb +9 -4
  202. data/spec/arachni/state/framework_spec.rb +2 -8
  203. data/spec/arachni/support/cache/least_recently_pushed_spec.rb +90 -0
  204. data/spec/arachni/support/cache/least_recently_used_spec.rb +5 -13
  205. data/spec/arachni/support/database/queue_spec.rb +7 -0
  206. data/spec/arachni/support/mixins/observable_spec.rb +15 -1
  207. data/spec/arachni/trainer_spec.rb +2 -2
  208. data/spec/components/checks/active/code_injection_timing_spec.rb +1 -1
  209. data/spec/components/checks/active/file_inclusion_spec.rb +6 -6
  210. data/spec/components/checks/active/path_traversal_spec.rb +2 -2
  211. data/spec/components/checks/active/source_code_disclosure_spec.rb +2 -2
  212. data/spec/components/checks/active/unvalidated_redirect_spec.rb +6 -6
  213. data/spec/components/checks/active/xss_dom_inputs_spec.rb +3 -5
  214. data/spec/components/checks/active/xss_dom_script_context_spec.rb +1 -1
  215. data/spec/components/checks/active/xss_spec.rb +5 -5
  216. data/spec/components/checks/passive/common_admin_interfaces_spec.rb +15 -0
  217. data/spec/components/checks/passive/interesting_responses_spec.rb +14 -1
  218. data/spec/components/fingerprinters/frameworks/aspx_mvc_spec.rb +31 -0
  219. data/spec/components/fingerprinters/frameworks/cakephp_spec.rb +22 -0
  220. data/spec/components/fingerprinters/frameworks/cherrypy_spec.rb +28 -0
  221. data/spec/components/fingerprinters/frameworks/django_spec.rb +37 -0
  222. data/spec/components/fingerprinters/frameworks/jsf_spec.rb +27 -0
  223. data/spec/components/fingerprinters/frameworks/rack_spec.rb +11 -14
  224. data/spec/components/fingerprinters/frameworks/rails_spec.rb +53 -0
  225. data/spec/components/fingerprinters/languages/asp_spec.rb +7 -9
  226. data/spec/components/fingerprinters/languages/aspx_spec.rb +10 -24
  227. data/spec/components/fingerprinters/languages/java_spec.rb +88 -0
  228. data/spec/components/fingerprinters/languages/php_spec.rb +19 -12
  229. data/spec/components/fingerprinters/languages/python_spec.rb +22 -9
  230. data/spec/components/fingerprinters/languages/ruby.rb +6 -4
  231. data/spec/components/fingerprinters/os/bsd_spec.rb +6 -4
  232. data/spec/components/fingerprinters/os/linux_spec.rb +6 -4
  233. data/spec/components/fingerprinters/os/solaris_spec.rb +6 -4
  234. data/spec/components/fingerprinters/os/unix_spec.rb +6 -4
  235. data/spec/components/fingerprinters/os/windows_spec.rb +6 -4
  236. data/spec/components/fingerprinters/servers/apache_spec.rb +15 -4
  237. data/spec/components/fingerprinters/servers/gunicorn_spec.rb +28 -0
  238. data/spec/components/fingerprinters/servers/iis_spec.rb +6 -6
  239. data/spec/components/fingerprinters/servers/jetty_spec.rb +6 -6
  240. data/spec/components/fingerprinters/servers/nginx_spec.rb +6 -4
  241. data/spec/components/fingerprinters/servers/tomcat_spec.rb +15 -6
  242. data/spec/components/path_extractors/data_url_spec.rb +19 -0
  243. data/spec/components/plugins/autologin_spec.rb +23 -0
  244. data/spec/components/plugins/login_script_spec.rb +112 -24
  245. data/spec/components/plugins/restrict_to_dom_state_spec.rb +16 -0
  246. data/spec/components/plugins/vector_feed_spec.rb +39 -1
  247. data/spec/support/factories/page/dom.rb +9 -4
  248. data/spec/support/factories/page/dom/transition.rb +31 -9
  249. data/spec/support/factories/scan_report.rb +8 -6
  250. data/spec/support/fixtures/empty/placeholder +0 -0
  251. data/spec/support/fixtures/report.afr +0 -0
  252. data/spec/support/fixtures/reporters/manager_spec/error.rb +18 -0
  253. data/spec/support/servers/arachni/browser.rb +117 -11
  254. data/spec/support/servers/arachni/browser/javascript/dom_monitor.rb +148 -4
  255. data/spec/support/servers/arachni/check/auditor.rb +4 -0
  256. data/spec/support/servers/arachni/element/cookie/cookie_dom.rb +1 -1
  257. data/spec/support/servers/arachni/http/client.rb +5 -0
  258. data/spec/support/servers/arachni/http/client/dynamic_404_handler.rb +13 -0
  259. data/spec/support/servers/checks/active/code_injection_timing.rb +1 -1
  260. data/spec/support/servers/checks/active/file_inclusion.rb +2 -2
  261. data/spec/support/servers/checks/active/path_traversal.rb +2 -2
  262. data/spec/support/servers/checks/active/source_code_disclosure.rb +40 -33
  263. data/spec/support/servers/checks/active/trainer_check.rb +9 -10
  264. data/spec/support/servers/checks/active/unvalidated_redirect_dom.rb +7 -4
  265. data/spec/support/servers/checks/active/xss.rb +35 -0
  266. data/spec/support/servers/checks/active/xss_dom.rb +1 -1
  267. data/spec/support/servers/checks/active/xss_dom_inputs.rb +24 -0
  268. data/spec/support/servers/checks/active/xss_dom_script_context.rb +1 -1
  269. data/spec/support/servers/checks/passive/common_admin_interfaces.rb +6 -0
  270. data/spec/support/servers/plugins/autologin.rb +9 -0
  271. data/spec/support/servers/plugins/restrict_to_dom_state.rb +4 -0
  272. data/spec/support/shared/element/base.rb +42 -0
  273. data/spec/support/shared/element/capabilities/auditable.rb +4 -4
  274. data/spec/support/shared/element/capabilities/auditable/dom.rb +26 -0
  275. data/spec/support/shared/element/capabilities/inputtable.rb +16 -11
  276. data/spec/support/shared/element/capabilities/submitable.rb +7 -2
  277. data/spec/support/shared/fingerprinter.rb +8 -0
  278. data/spec/support/shared/path_extractor.rb +1 -1
  279. data/ui/cli/framework.rb +3 -3
  280. data/ui/cli/framework/option_parser.rb +9 -0
  281. data/ui/cli/output.rb +9 -0
  282. data/ui/cli/reporter.rb +5 -2
  283. data/ui/cli/utilities.rb +4 -2
  284. metadata +76 -17
  285. data/lib/arachni/http/proxy_server/ssl-interceptor-cert.pem +0 -34
  286. data/lib/arachni/http/proxy_server/ssl-interceptor-pkey.pem +0 -51
  287. data/spec/components/fingerprinters/languages/jsp_spec.rb +0 -56
@@ -1342,6 +1342,21 @@ describe Arachni::Browser::Javascript::TaintTracer do
1342
1342
  event['srcElement'].should == form
1343
1343
  event['type'].should == 'submit'
1344
1344
  end
1345
+
1346
+ it 'is limited to 50' do
1347
+ load 'debug'
1348
+
1349
+ 100.times do |i|
1350
+ @browser.javascript.run( subject.stub.function( :log_execution_flow_sink, i ) )
1351
+ end
1352
+
1353
+ sinks = subject.execution_flow_sinks
1354
+ sinks.size.should == 50
1355
+
1356
+ 50.times do |i|
1357
+ sinks[i].data.should == [50 + i]
1358
+ end
1359
+ end
1345
1360
  end
1346
1361
 
1347
1362
  describe '#log_data_flow_sink' do
@@ -1373,6 +1388,32 @@ describe Arachni::Browser::Javascript::TaintTracer do
1373
1388
  event['srcElement'].should == form
1374
1389
  event['type'].should == 'submit'
1375
1390
  end
1391
+
1392
+ it 'is limited to 50 per taint' do
1393
+ load 'debug'
1394
+
1395
+ 100.times do |i|
1396
+ @browser.javascript.run(
1397
+ subject.stub.function(
1398
+ :log_data_flow_sink,
1399
+ 'taint',
1400
+ {
1401
+ function: {
1402
+ name: "f_#{i}"
1403
+ }
1404
+ }
1405
+ )
1406
+ )
1407
+ end
1408
+
1409
+ sinks = subject.data_flow_sinks['taint']
1410
+ sinks.size.should == 50
1411
+
1412
+ 50.times do |i|
1413
+ sinks[i].function.name.should == "f_#{i+50}"
1414
+ end
1415
+ end
1416
+
1376
1417
  end
1377
1418
 
1378
1419
  describe '#debugging_data' do
@@ -18,6 +18,14 @@ describe Arachni::Browser::Javascript do
18
18
 
19
19
  subject { @browser.javascript }
20
20
 
21
+ describe '.events_for' do
22
+ it 'returns events for the given element' do
23
+ described_class::EVENTS_PER_ELEMENT.each do |element, events|
24
+ described_class.events_for( element ).should == described_class::GLOBAL_EVENTS | events
25
+ end
26
+ end
27
+ end
28
+
21
29
  describe '.select_event_attributes' do
22
30
  it 'selects only attributes that are events' do
23
31
  attributes = {
@@ -140,25 +148,83 @@ describe Arachni::Browser::Javascript do
140
148
  end
141
149
 
142
150
  describe '#dom_elements_with_events' do
143
- it 'returns information about all DOM elements along with their events' do
144
- @browser.load @dom_monitor_url + 'elements_with_events'
145
- subject.dom_elements_with_events.should == [
146
- { 'tag_name' => 'body', 'events' => [], 'attributes' => {} },
147
- { 'tag_name' => 'button',
148
- 'events' =>
149
- [[:click, 'function (my_button_click) {}'],
150
- [:click, 'function (my_button_click2) {}'],
151
- [:onmouseover, 'function (my_button_onmouseover) {}'],
152
- [:onclick, 'handler_1()']],
153
- 'attributes' => { 'onclick' => 'handler_1()', 'id' => 'my-button' } },
154
- { 'tag_name' => 'button',
155
- 'events' =>
156
- [[:click, 'function (my_button2_click) {}'], [:onclick, 'handler_2()']],
157
- 'attributes' => { 'onclick' => 'handler_2()', 'id' => 'my-button2' } },
158
- { 'tag_name' => 'button',
159
- 'events' => [[:onclick, 'handler_3()']],
160
- 'attributes' => { 'onclick' => 'handler_3()', 'id' => 'my-button3' } }
161
- ]
151
+ context 'when using event attributes' do
152
+ it 'returns information about all DOM elements along with their events' do
153
+ @browser.load @dom_monitor_url + 'elements_with_events/attributes'
154
+
155
+ subject.dom_elements_with_events.should == [
156
+ {
157
+ 'tag_name' => 'body', 'events' => [], 'attributes' => {}
158
+ },
159
+ {
160
+ 'tag_name' => 'button',
161
+ 'events' => [
162
+ [:onclick, 'handler_1()']
163
+ ],
164
+ 'attributes' => { 'onclick' => 'handler_1()', 'id' => 'my-button' }
165
+ },
166
+ {
167
+ 'tag_name' => 'button',
168
+ 'events' => [
169
+ [:onclick, 'handler_2()']
170
+ ],
171
+ 'attributes' => { 'onclick' => 'handler_2()', 'id' => 'my-button2' }
172
+ },
173
+ {
174
+ 'tag_name' => 'button',
175
+ 'events' => [
176
+ [:onclick, 'handler_3()']
177
+ ],
178
+ 'attributes' => { 'onclick' => 'handler_3()', 'id' => 'my-button3' }
179
+ }
180
+ ]
181
+ end
182
+ end
183
+
184
+ context 'when using event listeners' do
185
+ it 'returns information about all DOM elements along with their events' do
186
+ @browser.load @dom_monitor_url + 'elements_with_events/listeners'
187
+
188
+ subject.dom_elements_with_events.should == [
189
+ {
190
+ 'tag_name' => 'body', 'events' => [], 'attributes' => {}
191
+ },
192
+ {
193
+ 'tag_name' => 'button',
194
+ 'events' => [
195
+ [:click, 'function (my_button_click) {}'],
196
+ [:click, 'function (my_button_click2) {}'],
197
+ [:onmouseover, 'function (my_button_onmouseover) {}']
198
+ ],
199
+ 'attributes' => { 'id' => 'my-button' } },
200
+ {
201
+ 'tag_name' => 'button',
202
+ 'events' => [
203
+ [:click, 'function (my_button2_click) {}']
204
+ ],
205
+ 'attributes' => { 'id' => 'my-button2' } },
206
+ {
207
+ 'tag_name' => 'button',
208
+ 'events' => [],
209
+ 'attributes' => { 'id' => 'my-button3' }
210
+ }
211
+ ]
212
+ end
213
+
214
+ it 'does not include custom events' do
215
+ @browser.load @dom_monitor_url + 'elements_with_events/listeners/custom'
216
+
217
+ subject.dom_elements_with_events.should == [
218
+ {
219
+ 'tag_name' => 'body', 'events' => [], 'attributes' => {}
220
+ },
221
+ {
222
+ 'tag_name' => 'button',
223
+ 'events' => [],
224
+ 'attributes' => { 'id' => 'my-button' }
225
+ }
226
+ ]
227
+ end
162
228
  end
163
229
  end
164
230
 
@@ -316,71 +382,179 @@ describe Arachni::Browser::Javascript do
316
382
  end
317
383
 
318
384
  describe '#inject' do
319
- let(:response) { Arachni::HTTP::Response.new( url: @dom_monitor_url ) }
385
+ context 'when the response is' do
386
+ context 'JavaScript' do
387
+ let(:response) do
388
+ Arachni::HTTP::Response.new(
389
+ url: "#{@dom_monitor_url}/jquery.js",
390
+ headers: {
391
+ 'Content-Type' => 'text/javascript'
392
+ },
393
+ body: <<EOHTML
394
+ foo()
395
+ EOHTML
396
+ )
397
+ end
320
398
 
321
- context 'when the response does not already contain the JS code' do
322
- it 'injects the system\'s JS interfaces in the response body' do
323
- subject.inject( response )
324
- @browser.load response
399
+ let(:injected) do
400
+ r = response.deep_clone
401
+ subject.inject( r )
402
+ r
403
+ end
325
404
 
326
- subject.taint_tracer.initialized.should be_true
327
- subject.dom_monitor.initialized.should be_true
328
- end
405
+ let(:taint_tracer_update) do
406
+ "#{subject.taint_tracer.stub.function( :update_tracers )};"
407
+ end
408
+
409
+ let(:dom_monitor_update) do
410
+ "#{subject.dom_monitor.stub.function( :update_trackers )};"
411
+ end
329
412
 
330
- it 'updates the Content-Length' do
331
- old_content_length = response.headers['content-length'].to_i
413
+ it 'inject a TaintTracer.update_tracers() call before the code' do
414
+ injected.body.scan( /(.*)foo/m ).flatten.first.should include taint_tracer_update
415
+ end
332
416
 
333
- subject.inject( response )
417
+ it 'inject a DOMMonitor.update_trackers() call before the code' do
418
+ injected.body.scan( /(.*)foo/m ).flatten.first.should include dom_monitor_update
419
+ end
334
420
 
335
- new_content_length = response.headers['content-length'].to_i
421
+ it 'appends a semicolon and newline to the body' do
422
+ injected.body.should include "#{response.body};\n"
423
+ end
336
424
 
337
- new_content_length.should > old_content_length
338
- new_content_length.should == response.body.bytesize
339
- end
425
+ it 'updates the Content-Length' do
426
+ old_content_length = response.headers['content-length'].to_i
340
427
 
341
- it 'returns true' do
342
- subject.inject( response ).should be_true
428
+ subject.inject( response )
429
+
430
+ new_content_length = response.headers['content-length'].to_i
431
+
432
+ new_content_length.should > old_content_length
433
+ new_content_length.should == response.body.bytesize
434
+ end
343
435
  end
344
436
 
345
- context 'when the response body contains script elements' do
346
- before { response.body = '<script> // My code and stuff </script>' }
437
+ context 'HTML' do
438
+ let(:response) do
439
+ Arachni::HTTP::Response.new(
440
+ url: @dom_monitor_url,
441
+ headers: {
442
+ 'Content-Type' => 'text/html'
443
+ },
444
+ body: <<EOHTML
445
+ <body>
446
+ </body>
447
+ EOHTML
448
+ )
449
+ end
450
+
451
+ it 'updates the Content-Length' do
452
+ old_content_length = response.headers['content-length'].to_i
347
453
 
348
- context 'and a taint has been configured' do
349
- before { subject.taint = 'my_taint' }
454
+ subject.inject( response )
350
455
 
351
- it 'injects taint tracer update calls at the top of the script' do
456
+ new_content_length = response.headers['content-length'].to_i
457
+
458
+ new_content_length.should > old_content_length
459
+ new_content_length.should == response.body.bytesize
460
+ end
461
+
462
+ context 'when the response does not already contain the JS code' do
463
+ it 'injects the system\'s JS interfaces in the response body' do
464
+ subject.inject( response )
465
+
466
+ %w(taint_tracer dom_monitor).each do |name|
467
+ src = "#{described_class::SCRIPT_BASE_URL}#{name}.js"
468
+ Nokogiri::HTML( response.body ).xpath( "//script[@src='#{src}']" ).should be_any
469
+ end
470
+ end
471
+
472
+ context 'when the response body contains script elements' do
473
+ before { response.body = '<script>// My code and stuff</script>' }
474
+
475
+ it 'injects taint tracer update calls at the top of the script' do
476
+ subject.inject( response )
477
+ Nokogiri::HTML(response.body).css('script')[-2].to_s.should ==
478
+ "<script>
479
+
480
+ // Injected by #{described_class}
481
+ _#{subject.token}TaintTracer.update_tracers();
482
+ _#{subject.token}DOMMonitor.update_trackers();
483
+
484
+ // My code and stuff</script>"
485
+ end
486
+
487
+ it 'injects taint tracer update calls after the script' do
488
+ subject.inject( response )
489
+ Nokogiri::HTML(response.body).css('script')[-1].to_s.should ==
490
+ "<script type=\"text/javascript\">" +
491
+ "_#{subject.token}TaintTracer.update_tracers();" +
492
+ "_#{subject.token}DOMMonitor.update_trackers();" +
493
+ '</script>'
494
+ end
495
+ end
496
+ end
497
+
498
+ context 'when the response already contains the JS code' do
499
+ it 'updates the taints' do
500
+ subject.inject( response )
501
+
502
+ presponse = response.deep_clone
503
+ pintializer = subject.taint_tracer.stub.function( :initialize, [] )
504
+
505
+ subject.taint = [ 'taint1', 'taint2' ]
352
506
  subject.inject( response )
353
- Nokogiri::HTML(response.body).css('script')[-2].to_s.should ==
354
- "<script>
355
- _#{subject.token}TaintTracer.update_tracers(); // Injected by Arachni::Browser::Javascript
356
- // My code and stuff </script>"
507
+ intializer = subject.taint_tracer.stub.function( :initialize, subject.taint )
508
+
509
+ response.body.should == presponse.body.gsub( pintializer, intializer )
357
510
  end
358
511
 
359
- it 'injects taint tracer update calls after the script' do
512
+ it 'updates the custom code' do
513
+ subject.custom_code = 'alert(1);'
360
514
  subject.inject( response )
515
+
516
+ presponse = response.deep_clone
517
+ code = subject.custom_code
518
+
519
+ subject.custom_code = 'alert(2);'
361
520
  subject.inject( response )
362
- Nokogiri::HTML(response.body).css('script')[-1].to_s.should ==
363
- "<script type=\"text/javascript\">_#{subject.token}TaintTracer.update_tracers()</script>"
521
+
522
+ response.body.should == presponse.body.gsub( code, subject.custom_code )
364
523
  end
365
524
  end
366
525
  end
367
526
  end
527
+ end
368
528
 
369
- context 'when the response already contains the JS code' do
370
- it 'skips it' do
371
- original_response = response.deep_clone
372
- subject.inject( response )
373
- original_response.should_not == response
529
+ describe '#javascript?' do
530
+ context 'when the Content-Type includes javascript' do
531
+ it 'returns true'
532
+ end
374
533
 
375
- updated_response = response.deep_clone
376
- subject.inject( response )
377
- updated_response.should == response
378
- end
534
+ context 'when the Content-Type does not include javascript' do
535
+ it 'returns false'
536
+ end
537
+ end
379
538
 
380
- it 'returns false' do
381
- subject.inject( response ).should be_true
382
- subject.inject( response ).should be_false
383
- end
539
+ describe '#html?' do
540
+ context 'when the body is empty' do
541
+ it 'returns false'
542
+ end
543
+
544
+ context 'when the Content-Type does not include text/html' do
545
+ it 'returns false'
546
+ end
547
+
548
+ context 'when the body does not include HTML identifiers such as' do
549
+ it 'returns false'
550
+ end
551
+
552
+ context 'when it matches the last loaded URL' do
553
+ it 'returns true'
554
+ end
555
+
556
+ context 'when it contains markup' do
557
+ it 'returns true'
384
558
  end
385
559
  end
386
560
 
@@ -75,15 +75,6 @@ describe Arachni::BrowserCluster::Jobs::ResourceExploration do
75
75
  restored = q.pop
76
76
  restored.should == subject
77
77
  end
78
-
79
- it 'dups the page' do
80
- q = Arachni::Support::Database::Queue.new
81
- q.max_buffer_size = 0
82
-
83
- expect_any_instance_of(Arachni::Page).to receive(:dup)
84
-
85
- q << subject
86
- end
87
78
  end
88
79
  end
89
80
  end
@@ -18,6 +18,24 @@ describe Arachni::BrowserCluster do
18
18
  end
19
19
 
20
20
  describe '#initialize' do
21
+ it "sets window width to #{Arachni::OptionGroups::BrowserCluster}#screen_width" do
22
+ Arachni::Options.browser_cluster.screen_width = 100
23
+
24
+ @cluster = described_class.new
25
+ @cluster.workers.each do |browser|
26
+ browser.javascript.run('return window.innerWidth').should == 100
27
+ end
28
+ end
29
+
30
+ it "sets window height to #{Arachni::OptionGroups::BrowserCluster}#screen_height" do
31
+ Arachni::Options.browser_cluster.screen_height = 200
32
+
33
+ @cluster = described_class.new
34
+ @cluster.workers.each do |browser|
35
+ browser.javascript.run('return window.innerHeight').should == 200
36
+ end
37
+ end
38
+
21
39
  describe :pool_size do
22
40
  it 'sets the amount of browsers to instantiate' do
23
41
  @cluster = described_class.new( pool_size: 3 )
@@ -31,21 +49,51 @@ describe Arachni::BrowserCluster do
31
49
  end
32
50
  end
33
51
 
34
- it "sets window width to #{Arachni::OptionGroups::BrowserCluster}#screen_width" do
35
- Arachni::Options.browser_cluster.screen_width = 100
52
+ describe :on_pop do
53
+ it 'assigns blocks to be passed each poped job' do
54
+ cj = nil
55
+ @cluster = described_class.new(
56
+ on_pop: proc do |j|
57
+ cj = j
58
+ end
59
+ )
36
60
 
37
- @cluster = described_class.new
38
- @cluster.workers.each do |browser|
39
- browser.javascript.run('return window.innerWidth').should == 100
61
+ @cluster.queue( job ){}
62
+ @cluster.wait
63
+
64
+ cj.id.should == job.id
40
65
  end
41
66
  end
42
67
 
43
- it "sets window height to #{Arachni::OptionGroups::BrowserCluster}#screen_height" do
44
- Arachni::Options.browser_cluster.screen_height = 200
68
+ describe :on_queue do
69
+ it 'assigns blocks to be passed each queued job' do
70
+ cj = nil
71
+ @cluster = described_class.new(
72
+ on_queue: proc do |j|
73
+ cj = j
74
+ end
75
+ )
45
76
 
46
- @cluster = described_class.new
47
- @cluster.workers.each do |browser|
48
- browser.javascript.run('return window.innerHeight').should == 200
77
+ @cluster.queue( job ){}
78
+
79
+ cj.id.should == job.id
80
+ @cluster.wait
81
+ end
82
+ end
83
+
84
+ describe :on_job_done do
85
+ it 'assigns blocks to be passed each finished job' do
86
+ cj = nil
87
+ @cluster = described_class.new(
88
+ on_job_done: proc do |j|
89
+ cj = j
90
+ end
91
+ )
92
+
93
+ @cluster.queue( job ){}
94
+ @cluster.wait
95
+
96
+ cj.id.should == job.id
49
97
  end
50
98
  end
51
99
  end