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