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
@@ -6,8 +6,6 @@
6
6
  web site for more information on licensing and terms of use.
7
7
  =end
8
8
 
9
- require 'monitor'
10
-
11
9
  module Arachni
12
10
 
13
11
  # Real browser driver providing DOM/JS/AJAX support.
@@ -63,10 +61,6 @@ class BrowserCluster
63
61
  # List of crawled URLs with their HTTP codes.
64
62
  attr_reader :sitemap
65
63
 
66
- # @return [String]
67
- # Javascript token used to namespace the custom JS environment.
68
- attr_reader :javascript_token
69
-
70
64
  # @return [Array<Worker>]
71
65
  # Worker pool.
72
66
  attr_reader :workers
@@ -87,6 +81,8 @@ class BrowserCluster
87
81
  #
88
82
  # @raise ArgumentError On missing `:handler` option.
89
83
  def initialize( options = {} )
84
+ super()
85
+
90
86
  {
91
87
  pool_size: Options.browser_cluster.pool_size
92
88
  }.merge( options ).each do |k, v|
@@ -122,15 +118,16 @@ class BrowserCluster
122
118
  @mutex = Monitor.new
123
119
  @done_signal = Queue.new
124
120
 
125
- # Javascript token to share across all workers, this needs to be static
126
- # because of the browsers' disk-cache which leads to cached responses
127
- # being used between runs.
128
- @javascript_token = 'arachni_js_namespace'
129
-
130
121
  @consumed_pids = []
131
122
  initialize_workers
132
123
  end
133
124
 
125
+ # @return [String]
126
+ # Javascript token used to namespace the custom JS environment.
127
+ def javascript_token
128
+ Browser::Javascript::TOKEN
129
+ end
130
+
134
131
  # @note Operates in non-blocking mode.
135
132
  #
136
133
  # @param [Block] block
@@ -154,6 +151,8 @@ class BrowserCluster
154
151
  synchronize do
155
152
  print_debug "Queueing: #{job}"
156
153
 
154
+ notify_on_queue job
155
+
157
156
  @pending_job_counter += 1
158
157
  @pending_jobs[job.id] += 1
159
158
  @job_callbacks[job.id] = block if block
@@ -168,6 +167,14 @@ class BrowserCluster
168
167
  nil
169
168
  end
170
169
 
170
+ # def on_queue( &block )
171
+ # synchronize { @on_queue << block }
172
+ # end
173
+
174
+ # def on_job_done( &block )
175
+ # synchronize { @on_job_done << block }
176
+ # end
177
+
171
178
  # @param [Page, String, HTTP::Response] resource
172
179
  # Resource to explore, if given a `String` it will be treated it as a URL
173
180
  # and will be loaded.
@@ -206,6 +213,8 @@ class BrowserCluster
206
213
  synchronize do
207
214
  print_debug "Job done: #{job}"
208
215
 
216
+ notify_on_job_done job
217
+
209
218
  if !job.never_ending?
210
219
  @skip_states_per_job.delete job.id
211
220
  @job_callbacks.delete job.id
@@ -299,6 +308,7 @@ class BrowserCluster
299
308
  # @private
300
309
  def pop
301
310
  {} while job_done?( job = @jobs.pop )
311
+ notify_on_pop job
302
312
  job
303
313
  end
304
314
 
@@ -365,6 +375,23 @@ class BrowserCluster
365
375
 
366
376
  private
367
377
 
378
+ def notify_on_queue( job )
379
+ return if !@on_queue
380
+ @on_queue.call job
381
+ end
382
+
383
+ def notify_on_job_done( job )
384
+ return if !@on_job_done
385
+
386
+ @on_job_done.call job
387
+ end
388
+
389
+ def notify_on_pop( job )
390
+ return if !@on_pop
391
+
392
+ @on_pop.call job
393
+ end
394
+
368
395
  def fail_if_shutdown
369
396
  fail Error::AlreadyShutdown, 'Cluster has been shut down.' if @shutdown
370
397
  end
@@ -389,10 +416,9 @@ class BrowserCluster
389
416
  @workers = []
390
417
  pool_size.times do |i|
391
418
  worker = Worker.new(
392
- javascript_token: @javascript_token,
393
- master: self,
394
- width: Options.browser_cluster.screen_width,
395
- height: Options.browser_cluster.screen_height
419
+ master: self,
420
+ width: Options.browser_cluster.screen_width,
421
+ height: Options.browser_cluster.screen_height
396
422
  )
397
423
  @workers << worker
398
424
  @consumed_pids << worker.pid
@@ -101,6 +101,10 @@ class Job
101
101
  # @param [Hash] data
102
102
  # Used to initialize the {Result}.
103
103
  def save_result( data )
104
+ # Results coming in after the job has already finished won't have a
105
+ # browser.
106
+ return if !browser
107
+
104
108
  browser.master.handle_job_result(
105
109
  self.class::Result.new( data.merge( job: self.clean_copy ) )
106
110
  )
@@ -36,15 +36,6 @@ class ResourceExploration < Job
36
36
  browser.trigger_events
37
37
  end
38
38
 
39
- def resource=( resource )
40
- # Get a copy of the page with the caches cleared, this way when the
41
- # modules (or anything else) lazy-load elements and populate the caches
42
- # there won't be any lingering references to them from the more time
43
- # consuming browser analysis.
44
- resource = resource.dup if resource.is_a? Page
45
- @resource = resource
46
- end
47
-
48
39
  def dup
49
40
  super.tap { |j| j.resource = resource }
50
41
  end
@@ -45,7 +45,6 @@ class Worker < Arachni::Browser
45
45
  attr_reader :time_to_live
46
46
 
47
47
  def initialize( options = {} )
48
- javascript_token = options.delete( :javascript_token )
49
48
  @master = options.delete( :master )
50
49
 
51
50
  @max_time_to_live = options.delete( :max_time_to_live ) ||
@@ -59,8 +58,6 @@ class Worker < Arachni::Browser
59
58
  # as soon as they're logged.
60
59
  super options.merge( store_pages: false )
61
60
 
62
- @javascript.token = javascript_token
63
-
64
61
  @done_signal = Queue.new
65
62
 
66
63
  start_capture
@@ -90,13 +87,19 @@ class Worker < Arachni::Browser
90
87
  exception_jail false do
91
88
  begin
92
89
  @job.configure_and_run( self )
93
- rescue Selenium::WebDriver::Error::WebDriverError
90
+ rescue Selenium::WebDriver::Error::WebDriverError,
91
+ Watir::Exception::Error => e
92
+
93
+ print_debug "Error while processing job: #{@job}"
94
+ print_debug
95
+ print_debug_exception e
96
+
94
97
  browser_respawn
95
98
  end
96
99
  end
97
100
  end
98
101
  rescue TimeoutError => e
99
- print_debug "Job timed-out after #{@job_timeout} seconds: #{@job}"
102
+ print_bad "Job timed-out after #{@job_timeout} seconds: #{@job}"
100
103
 
101
104
  # Could have left us with a broken browser.
102
105
  browser_respawn
@@ -80,16 +80,21 @@ module Auditor
80
80
  # Element types to check for.
81
81
  #
82
82
  # @return [Bool]
83
- def self.check?( page, restrict_to_elements = nil )
83
+ def self.check?( page, restrict_to_elements = nil, ignore_dom_depth = false )
84
84
  return false if issue_limit_reached?
85
85
  return true if elements.empty?
86
86
 
87
87
  audit = Arachni::Options.audit
88
88
  restrict_to_elements = [restrict_to_elements].flatten.compact
89
89
 
90
+ # We use procs to make the decisions to avoid loading the page
91
+ # element caches unless it's absolutely necessary.
92
+ #
93
+ # Also, it's better to audit Form & Cookie DOM elements only
94
+ # after the page has gone through the browser, because then
95
+ # we'll have some context in the metadata which can help us
96
+ # optimize DOM audits.
90
97
  {
91
- # We use procs to make the decision, to avoid loading the page
92
- # element caches unless it's absolutely necessary.
93
98
  Element::Link =>
94
99
  proc { audit.links? && !!page.links.find { |e| e.inputs.any? } },
95
100
  Element::Link::DOM =>
@@ -97,20 +102,22 @@ module Auditor
97
102
  Element::Form =>
98
103
  proc { audit.forms? && !!page.forms.find { |e| e.inputs.any? } },
99
104
  Element::Form::DOM =>
100
- proc { audit.form_doms? && page.has_script? && !!page.forms.find(&:dom) },
105
+ proc { (ignore_dom_depth || page.dom.depth > 0) &&
106
+ audit.form_doms? && page.has_script? && !!page.forms.find(&:dom) },
101
107
  Element::Cookie =>
102
108
  proc { audit.cookies? && page.cookies.any? },
103
109
  Element::Cookie::DOM =>
104
- proc { audit.cookie_doms? && page.has_script? && page.cookies.any? },
110
+ proc { (ignore_dom_depth || page.dom.depth > 0) &&
111
+ audit.cookie_doms? && page.has_script? && page.cookies.any? },
105
112
  Element::Header =>
106
113
  proc { audit.headers? && page.headers.any? },
107
114
  Element::LinkTemplate =>
108
115
  proc { audit.link_templates? && page.link_templates.find { |e| e.inputs.any? } },
109
116
  Element::LinkTemplate::DOM =>
110
117
  proc { audit.link_template_doms? && !!page.link_templates.find(&:dom) },
111
- Element::JSON =>
118
+ Element::JSON =>
112
119
  proc { audit.jsons? && page.jsons.find { |e| e.inputs.any? } },
113
- Element::XML =>
120
+ Element::XML =>
114
121
  proc { audit.xmls? && page.xmls.find { |e| e.inputs.any? } },
115
122
  Element::Body => !page.body.empty?,
116
123
  Element::GenericDOM => page.has_script?,
@@ -348,17 +355,22 @@ module Auditor
348
355
  # If `false`, a message will be printed to stdout containing the status of
349
356
  # the operation.
350
357
  #
358
+ # @return [Issue]
359
+ #
351
360
  # @see #log_issue
352
361
  def log_remote_file( page_or_response, silent = false )
353
362
  page = page_or_response.is_a?( Page ) ?
354
363
  page_or_response : page_or_response.to_page
355
364
 
356
- log_issue(
365
+ issue = log_issue(
357
366
  vector: Element::Server.new( page.url ),
367
+ proof: page.response.status_line,
358
368
  page: page
359
369
  )
360
370
 
361
371
  print_ok( "Found #{page.url}" ) if !silent
372
+
373
+ issue
362
374
  end
363
375
  alias :log_remote_directory :log_remote_file
364
376
 
@@ -152,18 +152,50 @@ class Base < Component::Base
152
152
  @platforms ||= [info[:platforms]].flatten.compact
153
153
  end
154
154
 
155
- # @param [Array<Symbol, String>] platforms
155
+ # @return [Bool]
156
+ # `true` if the check has specified platforms for which it does not apply.
157
+ #
158
+ # @see .platforms
159
+ def has_exempt_platforms?
160
+ exempt_platforms.any?
161
+ end
162
+
163
+ # @return [Array<Symbol>]
164
+ # Platforms not applicable to this check.
165
+ #
166
+ # @see .info
167
+ def exempt_platforms
168
+ @exempt_platforms ||= [info[:exempt_platforms]].flatten.compact
169
+ end
170
+
171
+ # @param [Array<Symbol, String>] resource_platforms
156
172
  # List of platforms to check for support.
157
173
  #
158
174
  # @return [Boolean]
159
175
  # `true` if any of the given platforms are supported, `false` otherwise.
160
- def supports_platforms?( platforms )
161
- return true if platforms.empty? || !has_platforms?
176
+ def supports_platforms?( resource_platforms )
177
+ if resource_platforms.any? && has_exempt_platforms?
178
+ manager = Platform::Manager.new( exempt_platforms )
179
+
180
+ resource_platforms.each do |p|
181
+
182
+ # When we check for exempt platforms we're looking for info
183
+ # from the same type.
184
+ ptype = Platform::Manager.find_type( p )
185
+ type_manager = manager.send( ptype )
186
+
187
+ return false if type_manager.pick( p => true ).any?
188
+ end
189
+ end
190
+
191
+ return true if resource_platforms.empty? || !has_platforms?
162
192
 
163
193
  # Determine if we've got anything for the given platforms, the same
164
194
  # way payloads are picked.
165
- foo_data = self.platforms.inject({}) { |h, platform| h.merge!( platform => true ) }
166
- Platform::Manager.new( platforms ).pick( foo_data ).any?
195
+ foo_data = self.platforms.
196
+ inject({}) { |h, platform| h.merge!( platform => true ) }
197
+
198
+ Platform::Manager.new( resource_platforms ).pick( foo_data ).any?
167
199
  end
168
200
 
169
201
  # @return [Array<Symbol>]
@@ -193,7 +225,7 @@ class Base < Component::Base
193
225
 
194
226
  # @private
195
227
  def clear_info_cache
196
- @elements = @platforms = nil
228
+ @elements = @exempt_platforms = @platforms = nil
197
229
  end
198
230
  end
199
231
 
@@ -38,6 +38,19 @@ class Base
38
38
 
39
39
  include Capabilities::WithScope
40
40
 
41
+ # Maximum element size in bytes.
42
+ # Anything larger than this should be exempt from parse and storage or have
43
+ # its value ignored.
44
+ #
45
+ # During the audit, thousands of copies will be generated and the same
46
+ # amount of HTP requests will be stored in the HTTP::Client queue.
47
+ # Thus, elements with inputs of excessive size will lead to excessive RAM
48
+ # consumption.
49
+ #
50
+ # This will almost never be necessary, but there have been cases of
51
+ # buggy `_VIEWSTATE` inputs that grow infinitely.
52
+ MAX_SIZE = 10_000
53
+
41
54
  # @return [Page]
42
55
  # Page this element belongs to.
43
56
  attr_accessor :page
@@ -124,7 +137,7 @@ class Base
124
137
  # @return [Symbol]
125
138
  # Element type.
126
139
  def self.type
127
- name.split( ':' ).last.downcase.to_sym
140
+ @type ||= name.split( ':' ).last.downcase.to_sym
128
141
  end
129
142
 
130
143
  def dup
@@ -189,6 +202,10 @@ class Base
189
202
  instance
190
203
  end
191
204
 
205
+ def self.too_big?( element )
206
+ (element.is_a?( Numeric ) ? element : element.to_s.size) >= MAX_SIZE
207
+ end
208
+
192
209
  end
193
210
  end
194
211
  end
@@ -28,7 +28,6 @@ module Differential
28
28
  end
29
29
 
30
30
  DIFFERENTIAL_OPTIONS = {
31
- # Append our seeds to the default values.
32
31
  format: [Mutable::Format::STRAIGHT],
33
32
 
34
33
  # Amount of refinement operations to remove context-irrelevant dynamic
@@ -15,6 +15,10 @@ module Analyzable
15
15
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
16
16
  module Taint
17
17
 
18
+ TAINT_CACHE = {
19
+ match: Support::Cache::LeastRecentlyPushed.new( 10_000 )
20
+ }
21
+
18
22
  TAINT_OPTIONS = {
19
23
  # The regular expression to match against the response body.
20
24
  #
@@ -99,28 +103,38 @@ module Taint
99
103
  end
100
104
 
101
105
  def match_patterns( patterns, matcher, response, opts )
106
+ k = [patterns, response.body]
107
+ return if TAINT_CACHE[:match][k] == false
108
+
102
109
  if opts[:longest_word_optimization]
103
110
  opts[:downcased_body] = response.body.downcase
104
111
  end
105
112
 
106
113
  case patterns
107
114
  when Regexp, String, Array
108
- [patterns].flatten.compact.
109
- each { |pattern| matcher.call( pattern, response, opts ) }
115
+ [patterns].flatten.compact.each do |pattern|
116
+ res = matcher.call( pattern, response, opts )
117
+ TAINT_CACHE[:match][k] ||= !!res
118
+ end
110
119
 
111
120
  when Hash
112
121
  if opts[:platform] && patterns[opts[:platform]]
113
122
  [patterns[opts[:platform]]].flatten.compact.each do |p|
114
- [p].flatten.compact.
115
- each { |pattern| matcher.call( pattern, response, opts ) }
123
+ [p].flatten.compact.each do |pattern|
124
+ res = matcher.call( pattern, response, opts )
125
+ TAINT_CACHE[:match][k] ||= !!res
126
+ end
116
127
  end
128
+
117
129
  else
118
130
  patterns.each do |platform, p|
119
131
  dopts = opts.dup
120
132
  dopts[:platform] = platform
121
133
 
122
- [p].flatten.compact.
123
- each { |pattern| matcher.call( pattern, response, dopts ) }
134
+ [p].flatten.compact.each do |pattern|
135
+ res = matcher.call( pattern, response, dopts )
136
+ TAINT_CACHE[:match][k] ||= !!res
137
+ end
124
138
  end
125
139
  end
126
140
 
@@ -133,15 +147,22 @@ module Taint
133
147
  dopts = opts.dup
134
148
  dopts[:platform] = platform
135
149
 
136
- [p].flatten.compact.
137
- each { |pattern| matcher.call( pattern, response, dopts ) }
150
+ [p].flatten.compact.each do |pattern|
151
+ res = matcher.call( pattern, response, dopts )
152
+ TAINT_CACHE[:match][k] ||= !!res
153
+ end
138
154
  end
139
155
  end
140
156
  end
141
157
 
142
158
  def match_substring_and_log( substring, response, opts )
143
159
  return if substring.to_s.empty?
144
- return if !response.body.include?( substring ) || ignore?( response, opts )
160
+
161
+ k = [substring, response.body]
162
+ return if TAINT_CACHE[:match][k] == false
163
+
164
+ TAINT_CACHE[:match][k] = includes = response.body.include?( substring )
165
+ return if !includes || ignore?( response, opts )
145
166
 
146
167
  @candidate_issues << {
147
168
  response: response,
@@ -151,9 +172,14 @@ module Taint
151
172
  vector: response.request.performer
152
173
  }
153
174
  setup_verification_callbacks
175
+
176
+ true
154
177
  end
155
178
 
156
179
  def match_regexp_and_log( regexp, response, opts )
180
+ k = [regexp, response.body]
181
+ return if TAINT_CACHE[:match][k] == false
182
+
157
183
  regexp = regexp.is_a?( Regexp ) ? regexp :
158
184
  Regexp.new( regexp.to_s, Regexp::IGNORECASE )
159
185
 
@@ -166,7 +192,9 @@ module Taint
166
192
 
167
193
  match_data = match_data[0].to_s
168
194
 
169
- return if match_data.to_s.empty? || ignore?( response, opts )
195
+ TAINT_CACHE[:match][k] = !match_data.empty?
196
+
197
+ return if match_data.empty? || ignore?( response, opts )
170
198
 
171
199
  @candidate_issues << {
172
200
  response: response,
@@ -176,6 +204,8 @@ module Taint
176
204
  vector: response.request.performer
177
205
  }
178
206
  setup_verification_callbacks
207
+
208
+ true
179
209
  end
180
210
 
181
211
  def ignore?( res, opts )