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