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
@@ -0,0 +1,29 @@
1
+ =begin
2
+ Copyright 2010-2015 Tasos Laskos <tasos.laskos@arachni-scanner.com>
3
+
4
+ This file is part of the Arachni Framework project and is subject to
5
+ redistribution and commercial restrictions. Please see the Arachni Framework
6
+ web site for more information on licensing and terms of use.
7
+ =end
8
+
9
+ class Arachni::Reporters::XML
10
+
11
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
12
+ class PluginFormatters::Metrics < Arachni::Plugin::Formatter
13
+
14
+ def run( xml )
15
+ results.each do |category, data|
16
+ xml.send( category ) {
17
+ data.each do |k, v|
18
+ if category == 'platforms'
19
+ v = v.join( ',' )
20
+ end
21
+
22
+ xml.send k, v
23
+ end
24
+ }
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -10,7 +10,7 @@
10
10
  # All UIs must have a default report.
11
11
  #
12
12
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
13
- # @version 0.3
13
+ # @version 0.3.1
14
14
  class Arachni::Reporters::Stdout < Arachni::Reporter::Base
15
15
 
16
16
  def run
@@ -46,6 +46,8 @@ class Arachni::Reporters::Stdout < Arachni::Reporter::Base
46
46
  print_info '* Forms' if report.options[:audit][:forms]
47
47
  print_info '* Cookies' if report.options[:audit][:cookies]
48
48
  print_info '* Headers' if report.options[:audit][:headers]
49
+ print_info '* XMLs' if report.options[:audit][:xmls]
50
+ print_info '* JSONs' if report.options[:audit][:jsons]
49
51
  print_line
50
52
  print_status "Checks: #{report.options[:checks].join( ', ' )}"
51
53
 
@@ -223,7 +225,7 @@ class Arachni::Reporters::Stdout < Arachni::Reporter::Base
223
225
  name: 'Stdout',
224
226
  description: %q{Prints the results to standard output.},
225
227
  author: 'Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>',
226
- version: '0.3'
228
+ version: '0.3.1'
227
229
  }
228
230
  end
229
231
 
@@ -11,7 +11,7 @@ require 'nokogiri'
11
11
  # Creates an XML report of the audit.
12
12
  #
13
13
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
14
- # @version 0.3.3
14
+ # @version 0.3.4
15
15
  class Arachni::Reporters::XML < Arachni::Reporter::Base
16
16
 
17
17
  LOCAL_SCHEMA = File.dirname( __FILE__ ) + '/xml/schema.xsd'
@@ -77,8 +77,8 @@ class Arachni::Reporters::XML < Arachni::Reporter::Base
77
77
  xml.method_ vector.method
78
78
  end
79
79
 
80
- if vector.respond_to? :affected_input_name
81
- xml.affected_input_name vector.affected_input_name
80
+ if issue.variations.first.vector.respond_to? :affected_input_name
81
+ xml.affected_input_name issue.variations.first.vector.affected_input_name
82
82
  end
83
83
 
84
84
  if vector.respond_to? :inputs
@@ -166,7 +166,7 @@ class Arachni::Reporters::XML < Arachni::Reporter::Base
166
166
  description: %q{Exports the audit results as an XML (.xml) file.},
167
167
  content_type: 'text/xml',
168
168
  author: 'Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>',
169
- version: '0.3.3',
169
+ version: '0.3.4',
170
170
  options: [ Options.outfile( '.xml' ), Options.skip_responses ]
171
171
  }
172
172
  end
@@ -35,9 +35,104 @@
35
35
  <xs:element name="waf_detector" type="plugin_waf_detector" maxOccurs="1"/>
36
36
  <xs:element name="vector_collector" type="plugin_vector_collector" maxOccurs="1"/>
37
37
  <xs:element name="exec" type="plugin_exec" maxOccurs="1"/>
38
+ <xs:element name="metrics" type="plugin_metrics" maxOccurs="1"/>
38
39
  </xs:choice>
39
40
  </xs:complexType>
40
41
 
42
+ <xs:complexType name="plugin_metrics">
43
+ <xs:all>
44
+ <xs:element name="name" type="xs:string"/>
45
+ <xs:element name="description" type="xs:string"/>
46
+ <xs:element name="results" type="plugin_metrics_results"/>
47
+ </xs:all>
48
+ </xs:complexType>
49
+
50
+ <xs:complexType name="plugin_metrics_results">
51
+ <xs:all>
52
+ <xs:element name="general" type="plugin_metrics_results_general"/>
53
+ <xs:element name="scan" type="plugin_metrics_results_scan"/>
54
+ <xs:element name="http" type="plugin_metrics_results_http"/>
55
+ <xs:element name="resource" type="plugin_metrics_results_resource"/>
56
+ <xs:element name="element" type="plugin_metrics_results_element"/>
57
+ <xs:element name="dom" type="plugin_metrics_results_dom"/>
58
+ <xs:element name="platforms" type="plugin_metrics_results_platforms"/>
59
+ </xs:all>
60
+ </xs:complexType>
61
+
62
+ <xs:complexType name="plugin_metrics_results_general">
63
+ <xs:all>
64
+ <xs:element name="egress_traffic" type="xs:integer"/>
65
+ <xs:element name="ingress_traffic" type="xs:integer"/>
66
+ <xs:element name="uses_http" type="xs:boolean"/>
67
+ <xs:element name="uses_https" type="xs:boolean"/>
68
+ </xs:all>
69
+ </xs:complexType>
70
+
71
+ <xs:complexType name="plugin_metrics_results_scan">
72
+ <xs:all>
73
+ <xs:element name="duration" type="xs:float"/>
74
+ <xs:element name="authenticated" type="xs:boolean"/>
75
+ </xs:all>
76
+ </xs:complexType>
77
+
78
+ <xs:complexType name="plugin_metrics_results_http">
79
+ <xs:all>
80
+ <xs:element name="requests" type="xs:integer"/>
81
+ <xs:element name="response_time_min" type="xs:float"/>
82
+ <xs:element name="response_time_max" type="xs:float"/>
83
+ <xs:element name="response_time_average" type="xs:float"/>
84
+
85
+ <xs:element name="response_size_min" type="xs:float"/>
86
+ <xs:element name="response_size_max" type="xs:float"/>
87
+ <xs:element name="response_size_average" type="xs:float"/>
88
+
89
+ <xs:element name="request_size_min" type="xs:float"/>
90
+ <xs:element name="request_size_max" type="xs:float"/>
91
+ <xs:element name="request_size_average" type="xs:float"/>
92
+ </xs:all>
93
+ </xs:complexType>
94
+
95
+ <xs:complexType name="plugin_metrics_results_resource">
96
+ <xs:all>
97
+ <xs:element name="binary" type="xs:integer"/>
98
+ <xs:element name="without_parameters" type="xs:integer"/>
99
+ <xs:element name="with_parameters" type="xs:integer"/>
100
+ </xs:all>
101
+ </xs:complexType>
102
+
103
+ <xs:complexType name="plugin_metrics_results_element">
104
+ <xs:all>
105
+ <xs:element name="links" type="xs:integer"/>
106
+ <xs:element name="forms" type="xs:integer"/>
107
+ <xs:element name="has_forms_with_nonces" type="xs:boolean"/>
108
+ <xs:element name="has_forms_with_passwords" type="xs:boolean"/>
109
+ <xs:element name="cookies" type="xs:integer"/>
110
+ <xs:element name="headers" type="xs:integer"/>
111
+ <xs:element name="xmls" type="xs:integer"/>
112
+ <xs:element name="jsons" type="xs:integer"/>
113
+ <xs:element name="input_names_total" type="xs:integer"/>
114
+ <xs:element name="input_names_unique" type="xs:integer"/>
115
+ </xs:all>
116
+ </xs:complexType>
117
+
118
+ <xs:complexType name="plugin_metrics_results_dom">
119
+ <xs:all>
120
+ <xs:element name="event_listeners" type="xs:integer"/>
121
+ <xs:element name="swf_objects" type="xs:integer"/>
122
+ </xs:all>
123
+ </xs:complexType>
124
+
125
+ <xs:complexType name="plugin_metrics_results_platforms">
126
+ <xs:all>
127
+ <!-- CSV platform names for values in these fields. -->
128
+ <xs:element name="os" type="xs:string"/>
129
+ <xs:element name="db" type="xs:string"/>
130
+ <xs:element name="servers" type="xs:string"/>
131
+ <xs:element name="languages" type="xs:string"/>
132
+ <xs:element name="frameworks" type="xs:string"/>
133
+ </xs:all>
134
+ </xs:complexType>
135
+
41
136
  <xs:complexType name="plugin_exec">
42
137
  <xs:all>
43
138
  <xs:element name="name" type="xs:string"/>
@@ -9,6 +9,8 @@
9
9
  require 'rubygems'
10
10
  require 'bundler/setup'
11
11
 
12
+ require 'oj_mimic_json'
13
+
12
14
  def ap( obj )
13
15
  super obj, raw: true
14
16
  end
@@ -70,7 +70,16 @@ class Browser
70
70
  # Let the browser take as long as it needs to complete an operation.
71
71
  WATIR_COM_TIMEOUT = 3600 # 1 hour.
72
72
 
73
- HTML_IDENTIFIERS = ['<!doctype html', '<html', '<head', '<body', '<title', '<script']
73
+ ASSET_EXTENSIONS = Set.new(
74
+ ['css', 'js', 'jpg', 'jpeg', 'png', 'gif', 'woff', 'json']
75
+ )
76
+
77
+ ASSET_EXTRACTORS = [
78
+ /<\s*link.*?href=['"](.*?)['"].*?>/im,
79
+ /<\s*script.*?src=['"](.*?)['"].*?>/im,
80
+ /<\s*img.*?src=['"](.*?)['"].*?>/im,
81
+ /<\s*input.*?src=['"](.*?)['"].*?>/im,
82
+ ]
74
83
 
75
84
  # @return [Array<Page::DOM::Transition>]
76
85
  attr_reader :transitions
@@ -107,6 +116,10 @@ class Browser
107
116
  # @return [Integer]
108
117
  attr_reader :pid
109
118
 
119
+ attr_reader :last_url
120
+
121
+ attr_reader :last_dom_url
122
+
110
123
  # @return [Bool]
111
124
  # `true` if a supported browser is in the OS PATH, `false` otherwise.
112
125
  def self.has_executable?
@@ -119,6 +132,19 @@ class Browser
119
132
  Selenium::WebDriver::PhantomJS.path
120
133
  end
121
134
 
135
+ def self.asset_domains
136
+ @asset_domains ||= Set.new
137
+ end
138
+ asset_domains
139
+
140
+ def self.add_asset_domain( url )
141
+ return if url.to_s.empty?
142
+ return if !(curl = Arachni::URI( url ))
143
+ return if !(domain = curl.domain)
144
+
145
+ asset_domains << domain
146
+ end
147
+
122
148
  # @param [Hash] options
123
149
  # @option options [Integer] :concurrency
124
150
  # Maximum number of concurrent connections.
@@ -313,7 +339,8 @@ class Browser
313
339
  @add_request_transitions = false
314
340
  end
315
341
 
316
- @last_url = url
342
+ @last_url = Arachni::URI( url ).to_s
343
+ self.class.add_asset_domain @last_url
317
344
 
318
345
  ensure_open_window
319
346
 
@@ -326,9 +353,23 @@ class Browser
326
353
  watir.goto url
327
354
 
328
355
  @javascript.wait_till_ready
356
+ wait_for_pending_requests
329
357
  wait_for_timers
330
358
 
331
- wait_for_pending_requests
359
+ url = watir.url
360
+ Options.browser_cluster.css_to_wait_for( url ).each do |css|
361
+ print_info "Waiting for #{css.inspect} to appear for: #{url}"
362
+
363
+ begin
364
+ watir.element( css: css ).
365
+ wait_until_present( Options.browser_cluster.job_timeout )
366
+
367
+ print_info "#{css.inspect} appeared for: #{url}"
368
+ rescue Watir::Wait::TimeoutError
369
+ print_bad "#{css.inspect} did not appeared for: #{url}"
370
+ end
371
+
372
+ end
332
373
 
333
374
  javascript.set_element_ids
334
375
  end
@@ -771,32 +812,35 @@ class Browser
771
812
  # them if no events are associated with it.
772
813
  #
773
814
  # This can save **A LOT** of time during the audit.
774
- if Options.audit.form_doms? && @javascript.supported?
775
- page.forms.each do |form|
776
- next if !form.node || !form.dom
815
+ if @javascript.supported?
816
+ if Options.audit.form_doms?
817
+ page.forms.each do |form|
818
+ next if !form.node || !form.dom
777
819
 
778
- action = form.node['action'].to_s
779
- form.dom.browser = self
820
+ action = form.node['action'].to_s
821
+ form.dom.browser = self
780
822
 
781
- next if action.downcase.start_with?( 'javascript:' ) ||
782
- form.dom.locate.events.any?
823
+ next if action.downcase.start_with?( 'javascript:' ) ||
824
+ form.dom.locate.events.any?
825
+
826
+ form.skip_dom = true
827
+ end
783
828
 
784
- form.skip_dom = true
829
+ page.update_metadata
830
+ page.clear_cache
785
831
  end
786
832
 
787
- page.update_metadata
788
- end
833
+ if Options.audit.cookie_doms?
834
+ sinks = @javascript.taint_tracer.data_flow_sinks
835
+ page.cookies.each do |cookie|
836
+ next if sinks.include?( cookie.name ) ||
837
+ sinks.include?( cookie.value )
789
838
 
790
- if Options.audit.cookie_doms? && @javascript.supported?
791
- sinks = @javascript.taint_tracer.data_flow_sinks
792
- page.cookies.each do |cookie|
793
- next if sinks.include?( cookie.name ) ||
794
- sinks.include?( cookie.value )
839
+ cookie.skip_dom = true
840
+ end
795
841
 
796
- cookie.skip_dom = true
842
+ page.update_metadata
797
843
  end
798
-
799
- page.update_metadata
800
844
  end
801
845
 
802
846
  page
@@ -890,7 +934,7 @@ class Browser
890
934
 
891
935
  c[:path] = '/' if c[:path] == '//'
892
936
  c[:name] = Cookie.decode( c[:name].to_s )
893
- c[:value] = Cookie.decode( c[:value].to_s )
937
+ c[:value] = Cookie.value_to_v0( c[:value].to_s )
894
938
  c[:httponly] = !js_cookies.include?( original_name )
895
939
 
896
940
  Cookie.new c.merge( url: @last_url || url )
@@ -1090,11 +1134,12 @@ class Browser
1090
1134
  buff = ''
1091
1135
  # Wait for PhantomJS to initialize.
1092
1136
  while !buff.include?( 'running on port' )
1093
- # It's silly to use #getc but it works consistently
1094
- # across MRI, Rubinius and JRuby.
1095
- buff << (out.getc rescue '').to_s
1137
+ # This can be problematic on something other than
1138
+ # MRI.
1139
+ buff << (out.readline rescue '').to_s
1096
1140
  end
1097
1141
 
1142
+ buff = nil
1098
1143
  print_debug 'Boot-up complete.'
1099
1144
  done = true
1100
1145
  end
@@ -1106,6 +1151,7 @@ class Browser
1106
1151
  if @process.io.stdout
1107
1152
  last_attempt_output = IO.read( @process.io.stdout )
1108
1153
  print_debug last_attempt_output
1154
+ @process.io.stdout.close!
1109
1155
  end
1110
1156
 
1111
1157
  if done
@@ -1286,7 +1332,17 @@ class Browser
1286
1332
  request.performer = self
1287
1333
 
1288
1334
  return if request.headers['X-Arachni-Browser-Auth'] != auth_token
1289
- request.headers.delete( 'X-Arachni-Browser-Auth' )
1335
+ request.headers.delete 'X-Arachni-Browser-Auth'
1336
+
1337
+ # We can't have 304 page responses in the framework, we need full request
1338
+ # and response data, the browser cache doesn't help us here.
1339
+ #
1340
+ # Still, it's a nice feature to have when requesting assets or anything
1341
+ # else.
1342
+ if request.url == @last_url
1343
+ request.headers.delete 'If-None-Match'
1344
+ request.headers.delete 'If-Modified-Since'
1345
+ end
1290
1346
 
1291
1347
  return if @javascript.serve( request, response )
1292
1348
 
@@ -1300,21 +1356,29 @@ class Browser
1300
1356
 
1301
1357
  # Signal the proxy to not actually perform the request if we have a
1302
1358
  # preloaded or cached response for it.
1303
- return if from_preloads( request, response ) || from_cache( request, response )
1304
-
1305
- begin
1306
- # Capture the request as elements of pages -- let's us grab AJAX and
1307
- # other browser requests and convert them into elements we can analyze
1308
- # and audit.
1309
- capture( request )
1310
- rescue => e
1311
- print_debug "Could not capture: #{request.url}"
1312
- print_debug request.body.to_s
1313
- print_debug_exception e
1359
+ if from_preloads( request, response ) || from_cache( request, response )
1360
+ # There may be taints or custom JS code that need to be updated.
1361
+ javascript.inject response
1362
+ return
1314
1363
  end
1315
1364
 
1365
+ # Capture the request as elements of pages -- let's us grab AJAX and
1366
+ # other browser requests and convert them into elements we can analyze
1367
+ # and audit.
1368
+ capture( request )
1369
+
1316
1370
  request.headers['user-agent'] = Options.http.user_agent
1317
1371
 
1372
+ # The proxy has an unlimited response_max_size so if we're not requesting
1373
+ # an asset remove the response_max_size option so that it'll end up using
1374
+ # the system settings.
1375
+ #
1376
+ # However, this is not foolproof, a lot of assets don't have the expected
1377
+ # extension.
1378
+ if !request_for_asset?( request )
1379
+ request.response_max_size = nil
1380
+ end
1381
+
1318
1382
  # Signal the proxy to continue with its request to the origin server.
1319
1383
  true
1320
1384
  end
@@ -1327,63 +1391,50 @@ class Browser
1327
1391
  transition.complete
1328
1392
  end
1329
1393
 
1394
+ javascript.inject response
1395
+
1396
+ # Don't store assets, the browsers will cache them accordingly.
1397
+ return if request_for_asset?( request ) || !response.text?
1398
+
1330
1399
  # No-matter the scope, don't store resources for external domains.
1331
1400
  return if !response.scope.in_domain?
1332
1401
 
1333
1402
  return if enforce_scope? && response.scope.out?
1334
1403
 
1335
- intercept response
1404
+ whitelist_asset_domains( response )
1336
1405
  save_response response
1337
1406
 
1338
1407
  nil
1339
1408
  end
1340
1409
 
1341
- def intercept( response )
1342
- return if !intercept?( response )
1343
- @javascript.inject( response )
1344
- end
1410
+ def ignore_request?( request )
1411
+ return if !enforce_scope?
1345
1412
 
1346
- def intercept?( response )
1347
- return false if response.body.empty?
1413
+ return false if request_for_asset?( request )
1348
1414
 
1349
- # We only care about HTML.
1350
- return false if !response.headers.content_type.to_s.downcase.start_with?( 'text/html' )
1415
+ return true if !request.scope.follow_protocol?
1351
1416
 
1352
- # Let's check that the response at least looks like it contains HTML
1353
- # code of interest.
1354
- body = response.body.downcase
1355
- return false if !HTML_IDENTIFIERS.find { |tag| body.include? tag.downcase }
1417
+ return true if !request.scope.in_domain? &&
1418
+ !self.class.asset_domains.include?( request.parsed_url.domain )
1356
1419
 
1357
- # The last check isn't fool-proof, so don't do it when loading the page
1358
- # for the first time, but only when the page loads stuff via AJAX and whatnot.
1359
- #
1360
- # Well, we can be pretty sure that the root page will be HTML anyways.
1361
- return true if @last_url == response.url
1420
+ return true if request.scope.too_deep?
1421
+ return true if !request.scope.include?
1422
+ return true if request.scope.exclude?
1423
+ return true if request.scope.redundant?
1362
1424
 
1363
- # Finally, verify that we're really working with markup (hopefully HTML)
1364
- # and that the previous checks weren't just flukes matching some other
1365
- # kind of document.
1366
- #
1367
- # For example, it may have been JSON with the wrong content-type that
1368
- # includes HTML -- it happens.
1369
- begin
1370
- return false if Nokogiri::XML( response.body ).children.empty?
1371
- rescue => e
1372
- print_debug "Javascript injection failed for: #{response.url}"
1373
- print_debug "\n#{response.body}"
1374
- print_debug_exception e
1375
- return false
1376
- end
1377
-
1378
- true
1425
+ false
1379
1426
  end
1380
1427
 
1381
- def ignore_request?( request )
1382
- return if !enforce_scope?
1428
+ def request_for_asset?( request )
1429
+ ASSET_EXTENSIONS.include?( request.parsed_url.resource_extension.to_s.downcase )
1430
+ end
1383
1431
 
1384
- # Only allow CSS and JS resources to be loaded from out-of-scope domains.
1385
- !['css', 'js'].include?( request.parsed_url.resource_extension ) &&
1386
- (request.scope.out? || request.scope.redundant?)
1432
+ def whitelist_asset_domains( response )
1433
+ ASSET_EXTRACTORS.each do |regexp|
1434
+ response.body.scan( regexp ).flatten.compact.each do |url|
1435
+ self.class.add_asset_domain url
1436
+ end
1437
+ end
1387
1438
  end
1388
1439
 
1389
1440
  def capture( request )
@@ -1452,6 +1503,10 @@ class Browser
1452
1503
 
1453
1504
  @captured_pages << page if store_pages?
1454
1505
  notify_on_new_page( page )
1506
+ rescue => e
1507
+ print_debug "Could not capture: #{request.url}"
1508
+ print_debug request.body.to_s
1509
+ print_debug_exception e
1455
1510
  end
1456
1511
 
1457
1512
  def from_preloads( request, response )
@@ -1478,7 +1533,7 @@ class Browser
1478
1533
  destination.send "#{m}=", source.send( m )
1479
1534
  end
1480
1535
 
1481
- intercept destination
1536
+ javascript.inject destination
1482
1537
  nil
1483
1538
  end
1484
1539