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
@@ -28,7 +28,7 @@ class Preference < Base
28
28
  # `v`
29
29
  def store( k, v )
30
30
  prune if capped? && (size > max_size - 1)
31
- cache[k.hash] = v
31
+ cache[make_key( k )] = v
32
32
  end
33
33
 
34
34
  def prefer( &block )
@@ -16,34 +16,10 @@ module Support::Cache
16
16
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
17
17
  class RandomReplacement < Base
18
18
 
19
- # @see Arachni::Cache::Base#initialize
20
- def initialize( * )
21
- super
22
- @keys = []
23
- end
24
-
25
- # @see Arachni::Cache::Base#store
26
- def store( k, v )
27
- already_in = include?( k )
28
-
29
- super( k, v )
30
- ensure
31
- @keys << k if !already_in
32
- end
33
-
34
- def clear
35
- super
36
- @keys.clear
37
- end
38
-
39
19
  private
40
20
 
41
- def prune_candidate
42
- @keys.delete_at( rand( size ) )
43
- end
44
-
45
21
  def prune
46
- delete( prune_candidate )
22
+ @cache.delete( @cache.keys.sample )
47
23
  end
48
24
 
49
25
  end
@@ -80,12 +80,13 @@ class Queue < Base
80
80
  def pop( non_block = false )
81
81
  synchronize do
82
82
  loop do
83
- if empty?
83
+ if internal_empty?
84
84
  raise ThreadError, 'queue empty' if non_block
85
85
  @waiting.push Thread.current
86
86
  @mutex.sleep
87
87
  else
88
- return @buffer.shift || load_and_delete_file( @disk.shift )
88
+ return @buffer.shift if !@buffer.empty?
89
+ return load_and_delete_file( @disk.shift )
89
90
  end
90
91
  end
91
92
  end
@@ -100,6 +101,10 @@ class Queue < Base
100
101
  end
101
102
  alias :length :size
102
103
 
104
+ def free_buffer_size
105
+ max_buffer_size - buffer_size
106
+ end
107
+
103
108
  def buffer_size
104
109
  @buffer.size
105
110
  end
@@ -111,17 +116,21 @@ class Queue < Base
111
116
  # @return [Bool]
112
117
  # `true` if the queue if empty, `false` otherwise.
113
118
  def empty?
114
- @buffer.empty? && @disk.empty?
119
+ synchronize do
120
+ internal_empty?
121
+ end
115
122
  end
116
123
 
117
124
  # Removes all objects from the queue.
118
125
  def clear
119
- @buffer.clear
126
+ synchronize do
127
+ @buffer.clear
120
128
 
121
- while !@disk.empty?
122
- path = @disk.pop
123
- next if !path
124
- delete_file path
129
+ while !@disk.empty?
130
+ path = @disk.pop
131
+ next if !path
132
+ delete_file path
133
+ end
125
134
  end
126
135
  end
127
136
 
@@ -131,6 +140,10 @@ class Queue < Base
131
140
 
132
141
  private
133
142
 
143
+ def internal_empty?
144
+ @buffer.empty? && @disk.empty?
145
+ end
146
+
134
147
  def synchronize( &block )
135
148
  @mutex.synchronize( &block )
136
149
  end
@@ -83,7 +83,13 @@ class Base
83
83
  end
84
84
 
85
85
  def dup
86
- deep_clone
86
+ self.class.new( @options.dup ).tap { |c| c.collection = @collection.dup }
87
+ end
88
+
89
+ protected
90
+
91
+ def collection=( c )
92
+ @collection = c
87
93
  end
88
94
 
89
95
  private
@@ -24,7 +24,9 @@ module Mixins
24
24
  #
25
25
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
26
26
  module Observable
27
+ include UI::Output
27
28
  include Utilities
29
+
28
30
  include MonitorMixin
29
31
 
30
32
  def self.included( base )
@@ -73,7 +75,7 @@ module Observable
73
75
  def notify_observers( event, *args )
74
76
  synchronize do
75
77
  observers_for( event ).each do |block|
76
- exception_jail { block.call( *args ) }
78
+ exception_jail( false ) { block.call( *args ) }
77
79
  end
78
80
  end
79
81
 
@@ -6,6 +6,7 @@
6
6
  web site for more information on licensing and terms of use.
7
7
  =end
8
8
 
9
+ require 'objspace'
9
10
  require 'sys/proctable'
10
11
  require 'ruby-mass'
11
12
  require 'stackprof'
@@ -79,30 +80,70 @@ class Profiler
79
80
 
80
81
  def object_space( options = {} )
81
82
  klass = options[:class]
82
- namespaces = options[:namespaces] || [Arachni]
83
+ namespaces = options[:namespaces] || [
84
+ Arachni,
85
+ Ethon,
86
+ Typhoeus,
87
+ Watir,
88
+ Selenium,
89
+ Addressable,
90
+ Nokogiri,
91
+ String,
92
+ Hash,
93
+ Array,
94
+ Set,
95
+ Thread
96
+ ]
97
+
83
98
  max_entries = options[:max_entries] || 50
84
99
 
85
- object_space = Hash.new(0)
86
- @object_space ||= Hash.new(0)
100
+ object_space = {}
101
+ @object_space ||= {}
87
102
 
88
103
  ObjectSpace.each_object do |o|
89
104
  next if o.class != klass && !object_within_namespace?( o, namespaces )
90
- object_space[o.class] += 1
105
+
106
+ # if o.class == Thread
107
+ # ap ObjectSpace.allocation_class_path( o ).to_s
108
+ # ap "#{ObjectSpace.allocation_sourcefile( o )}:#{ObjectSpace.allocation_sourceline( o )}"
109
+ # ap Utilities.bytes_to_megabytes( ObjectSpace.memsize_of( o ) )
110
+ # ap '-' * 120
111
+ # end
112
+
113
+ object_space[o.class] ||= {
114
+ memsize: 0,
115
+ count: 0
116
+ }
117
+
118
+ object_space[o.class][:memsize] += ObjectSpace.memsize_of(o)
119
+ object_space[o.class][:count] += 1
91
120
  end
92
121
 
93
- object_space = Hash[object_space.sort_by { |_, v| v }.reverse[0..max_entries]]
122
+ object_space = Hash[object_space.sort_by { |_, v| v[:memsize] }.reverse[0..max_entries]]
123
+
124
+ with_deltas = {}
125
+ object_space.each do |k, v|
126
+ @object_space[k] ||= {
127
+ memsize: 0,
128
+ count: 0
129
+ }
94
130
 
95
- with_deltas = object_space.dup
96
- with_deltas.each do |k, v|
97
- if v.is_a? Numeric
98
- with_deltas[k] = "#{v} (#{v - @object_space[k].to_i})"
131
+ if v[:count].is_a? Numeric
132
+ with_deltas[k] = "#{v[:count]} (#{v[:count] - @object_space[k][:count]})"
133
+ with_deltas[k] << " -- #{Utilities.bytes_to_megabytes v[:memsize]}"
134
+ with_deltas[k] << " (#{Utilities.bytes_to_megabytes v[:memsize] - @object_space[k][:memsize]})"
99
135
  else
100
- with_deltas[k] = v
136
+ with_deltas[k] = "#{v[:count]} -- #{Utilities.bytes_to_megabytes v[:memsize]}"
101
137
  end
102
138
  end
103
139
 
104
140
  @object_space = object_space.dup
105
141
  with_deltas
142
+
143
+ rescue => e
144
+ ap e
145
+ ap e.backtrace
146
+ {}
106
147
  end
107
148
 
108
149
  def write_object_space( file, options = {} )
@@ -14,6 +14,10 @@ module Arachni::Support
14
14
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
15
15
  class Signature
16
16
 
17
+ CACHE = {
18
+ tokens: Cache::LeastRecentlyPushed.new( 100 )
19
+ }
20
+
17
21
  attr_reader :tokens
18
22
 
19
23
  # @note The string will be tokenized based on whitespace.
@@ -109,14 +113,19 @@ class Signature
109
113
  # hashes, depending on which is smaller in size.
110
114
  def tokenize( data )
111
115
  return data.tokens if data.is_a? self.class
112
- compress data.split( /(?![\w])/ )
116
+
117
+ if CACHE[:tokens][data]
118
+ CACHE[:tokens][data].dup
119
+ else
120
+ CACHE[:tokens][data] = compress( data.split( /(?![\w])/ ) )
121
+ end
113
122
  end
114
123
 
115
124
  # Compresses the tokens by only storing unique #hash values.
116
125
  # Seems kinda silly but this can actually save us GB of RAM when comparing
117
126
  # large signatures, not to mention CPU cycles.
118
127
  def compress( tokens )
119
- tokens.uniq.map(&:hash)
128
+ Set.new( tokens.map(&:hash) )
120
129
  end
121
130
 
122
131
  end
@@ -120,6 +120,7 @@ class Trainer
120
120
  # no new cookies have appeared there's no reason to analyze the page
121
121
  if incoming_page.body == @page.body && !has_new_elements &&
122
122
  @page.url == incoming_page.url
123
+ incoming_page.clear_cache
123
124
  print_debug 'Page hasn\'t changed.'
124
125
  return
125
126
  end
@@ -140,12 +141,17 @@ class Trainer
140
141
  end
141
142
  end
142
143
 
144
+ incoming_page.clear_cache
145
+
143
146
  print_debug 'Training complete.'
144
147
  end
145
148
 
146
149
  def has_new?( incoming_page, element_type )
147
- count = ElementFilter.send( "update_#{element_type}".to_sym, incoming_page.send( element_type ) )
148
- incoming_page.clear_cache
150
+ count = ElementFilter.send(
151
+ "update_#{element_type}".to_sym,
152
+ incoming_page.send( element_type )
153
+ )
154
+
149
155
  return if count == 0
150
156
 
151
157
  print_info "Found #{count} new #{element_type}."
@@ -50,21 +50,21 @@ class URI
50
50
  end
51
51
 
52
52
  CACHE_SIZES = {
53
- parse: 600,
54
- ruby_parse: 600,
55
- fast_parse: 600,
53
+ parse: 1000,
54
+ ruby_parse: 1000,
55
+ fast_parse: 1000,
56
+ encode: 1000,
57
+ decode: 1000,
56
58
  normalize: 1000,
57
59
  to_absolute: 1000
58
60
  }
59
61
 
60
62
  CACHE = {
61
- parser: ::URI::Parser.new,
62
- ruby_parse: Support::Cache::RandomReplacement.new( CACHE_SIZES[:ruby_parse] ),
63
- parse: Support::Cache::RandomReplacement.new( CACHE_SIZES[:parse] ),
64
- fast_parse: Support::Cache::RandomReplacement.new( CACHE_SIZES[:fast_parse] ),
65
- normalize: Support::Cache::RandomReplacement.new( CACHE_SIZES[:normalize] ),
66
- to_absolute: Support::Cache::RandomReplacement.new( CACHE_SIZES[:to_absolute] )
63
+ parser: ::URI::Parser.new
67
64
  }
65
+ CACHE_SIZES.each do |name, size|
66
+ CACHE[name] = Support::Cache::LeastRecentlyPushed.new( size )
67
+ end
68
68
 
69
69
  class <<self
70
70
 
@@ -76,14 +76,15 @@ class URI
76
76
  # URL encodes a string.
77
77
  #
78
78
  # @param [String] string
79
- # @param [String, Regexp] bad_characters
80
- # Class of characters to encode -- if {String} is passed, it should
79
+ # @param [String, Regexp] good_characters
80
+ # Class of characters to allow -- if {String} is passed, it should
81
81
  # formatted as a regexp (for `Regexp.new`).
82
82
  #
83
83
  # @return [String]
84
84
  # Encoded string.
85
- def encode( string, bad_characters = nil )
86
- Addressable::URI.encode_component( *[string, bad_characters].compact )
85
+ def encode( string, good_characters = nil )
86
+ CACHE[__method__][[string, good_characters]] ||=
87
+ Addressable::URI.encode_component( *[string, good_characters].compact )
87
88
  end
88
89
 
89
90
  # URL decodes a string.
@@ -92,7 +93,7 @@ class URI
92
93
  #
93
94
  # @return [String]
94
95
  def decode( string )
95
- Addressable::URI.unencode( string )
96
+ CACHE[__method__][string] ||= Addressable::URI.unencode( string )
96
97
  end
97
98
 
98
99
  # @note This method's results are cached for performance reasons.
@@ -176,8 +177,11 @@ class URI
176
177
  url = url.to_s.dup
177
178
 
178
179
  # Remove the fragment if there is one.
179
- url = url.split( '#', 2 )[0...-1].join if url.include?( '#' )
180
- c_url = url.to_s.dup
180
+ if url.include?( '#' )
181
+ url = url.split( '#', 2 )[0...-1].join
182
+ end
183
+
184
+ c_url = url.dup
181
185
 
182
186
  components = {
183
187
  scheme: nil,
@@ -204,7 +208,7 @@ class URI
204
208
  return cache[c_url] = addressable_parse( c_url ).freeze
205
209
  end
206
210
 
207
- url = url.recode
211
+ url = url.recode!
208
212
  url = html_decode( url )
209
213
 
210
214
  dupped_url = url.dup
@@ -282,9 +286,8 @@ class URI
282
286
  !(query = dupped_url.split( '?', 2 ).last).empty?
283
287
 
284
288
  components[:query] = (query.split( '&', -1 ).map do |pair|
285
- Addressable::URI.normalize_component( pair,
286
- Addressable::URI::CharacterClasses::QUERY.sub( '\\&', '' )
287
- )
289
+ encode( decode( pair ),
290
+ Addressable::URI::CharacterClasses::QUERY.sub( '\\&', '' ) )
288
291
  end).join( '&' )
289
292
  end
290
293
  end
@@ -300,7 +303,7 @@ class URI
300
303
  print_debug "Error: #{e}"
301
304
  print_debug_backtrace( e )
302
305
 
303
- cache[c_url] = addressable_parse( c_url.recode ).freeze
306
+ cache[c_url] = addressable_parse( c_url.recode! ).freeze
304
307
  rescue => ex
305
308
  print_debug "Failed to parse '#{c_url}'."
306
309
  print_debug "Error: #{ex}"
@@ -405,8 +408,8 @@ class URI
405
408
 
406
409
  cache = CACHE[__method__]
407
410
 
408
- url = url.to_s.strip.dup
409
- c_url = url.to_s.strip.dup
411
+ url = url.to_s.strip
412
+ c_url = url.dup
410
413
 
411
414
  begin
412
415
  if (v = cache[url]) && v == :err
@@ -494,13 +497,13 @@ class URI
494
497
  self.class.ruby_parse( url )
495
498
 
496
499
  when ::URI
497
- url.dup
500
+ url
498
501
 
499
502
  when Hash
500
503
  ::URI::Generic.build( url )
501
504
 
502
505
  when Arachni::URI
503
- self.parsed_url = url.parsed_url.dup
506
+ self.parsed_url = url.parsed_url
504
507
 
505
508
  else
506
509
  to_string = url.to_s rescue ''
@@ -80,7 +80,7 @@ class Scope < Arachni::Scope
80
80
 
81
81
  check_scheme = @url.scheme.to_s
82
82
 
83
- return false if !%(http https).include?( check_scheme )
83
+ return false if check_scheme != 'http' && check_scheme != 'https'
84
84
 
85
85
  parsed_ref = Arachni::URI( Options.url )
86
86
  return false if !parsed_ref
@@ -391,6 +391,14 @@ module Utilities
391
391
  0
392
392
  end
393
393
 
394
+ def bytes_to_megabytes( bytes )
395
+ (bytes / 1024.0 / 1024.0).round( 3 )
396
+ end
397
+
398
+ def bytes_to_kilobytes( bytes )
399
+ (bytes / 1024.0 ).round( 3 )
400
+ end
401
+
394
402
  # Wraps the `block` in exception handling code and runs it.
395
403
  #
396
404
  # @param [Bool] raise_exception