arachni 1.1 → 1.2

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 (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