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
@@ -60,6 +60,15 @@ module Analyzable
60
60
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
61
61
  module Timeout
62
62
 
63
+ # Override user audit options that don't play nice with this technique.
64
+ TIMEOUT_OPTIONS = {
65
+ skip_original: true,
66
+ with_both_http_methods: false,
67
+ parameter_names: false,
68
+ with_extra_parameter: false,
69
+ extensively: false
70
+ }
71
+
63
72
  class <<self
64
73
  def reset
65
74
 
@@ -322,34 +331,24 @@ module Timeout
322
331
  def timing_attack_probe( payloads, options, &block )
323
332
  fail ArgumentError, 'Missing block' if !block_given?
324
333
 
325
- options = options.dup
334
+ options = options.merge( TIMEOUT_OPTIONS )
326
335
  options[:delay] = options.delete(:timeout)
327
336
  options[:timeout_divider] ||= 1
328
337
  options[:add] ||= 0
329
338
 
330
- options.merge!(
331
- # Don't submit the form with its original values, we don't want
332
- # any interference during timing attacks.
333
- skip_original: true,
334
-
335
- # Disable {Arachni::OptionGroups::Audit#cookies_extensively},
336
- # there's little to be gained in this case and just causes interference.
337
- extensively: false,
339
+ # Intercept each element mutation prior to it being submitted and
340
+ # replace the '__TIME__' stub with the actual delay value.
341
+ options[:each_mutation] = proc do |mutation|
342
+ injected = mutation.affected_input_value
338
343
 
339
- # Intercept each element mutation prior to it being submitted and
340
- # replace the '__TIME__' stub with the actual delay value.
341
- each_mutation: proc do |mutation|
342
- injected = mutation.affected_input_value
344
+ # Preserve the placeholder (__TIME__) payload because it's going to
345
+ # be needed for the verification phases...
346
+ mutation.audit_options[:timing_string] = injected
343
347
 
344
- # Preserve the placeholder (__TIME__) payload because it's going to
345
- # be needed for the verification phases...
346
- mutation.audit_options[:timing_string] = injected
347
-
348
- # ...but update it to use a real payload for this audit.
349
- mutation.affected_input_value = injected.
350
- gsub( '__TIME__', payload_delay_from_options( options ) )
351
- end
352
- )
348
+ # ...but update it to use a real payload for this audit.
349
+ mutation.affected_input_value = injected.
350
+ gsub( '__TIME__', payload_delay_from_options( options ) )
351
+ end
353
352
 
354
353
  # Ignore response bodies to preserve bandwidth since we don't care
355
354
  # about them anyways.
@@ -441,7 +440,12 @@ module Timeout
441
440
  e = super
442
441
  return e if !@timing_attack_remark_data
443
442
 
444
- e.timing_attack_remark_data = @timing_attack_remark_data.deep_clone
443
+ dupped_remark_data = {}
444
+ @timing_attack_remark_data.each do |k, v|
445
+ dupped_remark_data[k] = v.dup
446
+ end
447
+
448
+ e.timing_attack_remark_data = dupped_remark_data
445
449
  e
446
450
  end
447
451
 
@@ -35,6 +35,20 @@ module DOM
35
35
  # @!method with_browser( &block )
36
36
  def_delegator :auditor, :with_browser
37
37
 
38
+ def self.included( parent )
39
+ parent.extend ClassMethods
40
+ end
41
+
42
+ module ClassMethods
43
+ def encode( string )
44
+ string
45
+ end
46
+
47
+ def decode( string )
48
+ string
49
+ end
50
+ end
51
+
38
52
  def initialize( options )
39
53
  options = options.dup
40
54
  @parent = options.delete(:parent)
@@ -139,6 +153,14 @@ module DOM
139
153
  options
140
154
  end
141
155
 
156
+ def encode( string )
157
+ self.class.encode( string )
158
+ end
159
+
160
+ def decode( string )
161
+ self.class.decode( string )
162
+ end
163
+
142
164
  private
143
165
 
144
166
  def prepare_browser( browser, options )
@@ -37,6 +37,10 @@ module Capabilities::Inputtable
37
37
  end
38
38
  end
39
39
 
40
+ INPUTTABLE_CACHE = {
41
+ inputtable_id: Support::Cache::LeastRecentlyPushed.new( 1_000 )
42
+ }
43
+
40
44
  # Frozen version of {#inputs}, has all the original names and values.
41
45
  #
42
46
  # @return [Hash]
@@ -223,7 +227,8 @@ module Capabilities::Inputtable
223
227
  # @return [String]
224
228
  # Uniquely identifies the {#inputs}.
225
229
  def inputtable_id
226
- inputs.sort_by { |k,_| k }.to_s
230
+ INPUTTABLE_CACHE[:inputtable_id][@inputs] ||=
231
+ @inputs ? @inputs.sort_by { |k, _| k }.hash.to_s : ''
227
232
  end
228
233
 
229
234
  def to_h
@@ -255,7 +260,6 @@ module Capabilities::Inputtable
255
260
  fail Error::InvalidData::Value,
256
261
  "Invalid value for #{self.class}: #{value.inspect}"
257
262
  end
258
-
259
263
  end
260
264
 
261
265
  def copy_inputtable( other )
@@ -74,7 +74,7 @@ module Capabilities::Submittable
74
74
 
75
75
  @auditor ||= options.delete( :auditor )
76
76
 
77
- options[:performer] = self
77
+ options[:performer] ||= self
78
78
  http_request( options, &block )
79
79
  end
80
80
 
@@ -26,6 +26,7 @@ class Cookie < Base
26
26
 
27
27
  # Generic element capabilities.
28
28
  include Arachni::Element::Capabilities::Analyzable
29
+ include Arachni::Element::Capabilities::WithSource
29
30
 
30
31
  # Cookie-specific overrides.
31
32
  include Capabilities::WithDOM
@@ -49,9 +50,6 @@ class Cookie < Base
49
50
  httponly: false
50
51
  }
51
52
 
52
- ENCODE_CHARACTERS = ['+', ';', '%', "\0", "'", '&', '=', ' ', '"']
53
- ENCODE_CHARACTERS_LIST = ENCODE_CHARACTERS.join
54
-
55
53
  attr_reader :data
56
54
 
57
55
  # @param [Hash] options
@@ -78,7 +76,7 @@ class Cookie < Base
78
76
  end
79
77
 
80
78
  @data.merge!( DEFAULT.merge( @data ) )
81
- @data[:value] = decode( @data[:value].to_s )
79
+ @data[:value] = decode( @data[:value].to_s ) rescue @data[:value].to_s
82
80
 
83
81
  parsed_uri = uri_parse( action )
84
82
  if !@data[:path]
@@ -287,8 +285,8 @@ class Cookie < Base
287
285
  # @see .from_document
288
286
  # @see .from_headers
289
287
  def from_response( response )
290
- ( from_document( response.url, response.body ) |
291
- from_headers( response.url, response.headers ) )
288
+ from_document( response.url, response.body ) |
289
+ from_headers( response.url, response.headers )
292
290
  end
293
291
 
294
292
  # Extracts cookies from a document based on `Set-Cookie` `http-equiv`
@@ -302,21 +300,18 @@ class Cookie < Base
302
300
  #
303
301
  # @see .parse_set_cookie
304
302
  def from_document( url, document )
305
- # optimizations in case there are no cookies in the doc,
306
- # avoid parsing unless absolutely necessary!
307
303
  if !document.is_a?( Nokogiri::HTML::Document )
308
- # get get the head in order to check if it has an http-equiv for set-cookie
309
- head = document.to_s.match( /<head(.*)<\/head>/imx )
304
+ document = document.to_s
310
305
 
311
- # if it does feed the head to the parser in order to extract the cookies
312
- return [] if !head || !head.to_s.downcase.include?( 'set-cookie' )
306
+ return [] if !(document =~ /set-cookie/i )
313
307
 
314
- document = Nokogiri::HTML( head.to_s )
308
+ document = Nokogiri::HTML( document )
315
309
  end
316
310
 
317
311
  Arachni::Utilities.exception_jail {
318
- document.search( "//meta[@http-equiv]" ).map do |elem|
312
+ document.search( '//meta[@http-equiv]' ).map do |elem|
319
313
  next if elem['http-equiv'].downcase != 'set-cookie'
314
+
320
315
  from_set_cookie( url, elem['content'] )
321
316
  end.flatten.compact
322
317
  } rescue []
@@ -358,9 +353,14 @@ class Cookie < Base
358
353
 
359
354
  cookie_hash['path'] ||= '/'
360
355
  cookie_hash['name'] = decode( cookie.name )
361
- cookie_hash['value'] = decode( cookie.value )
362
356
 
363
- new( { url: url }.merge( cookie_hash.my_symbolize_keys ) )
357
+ if too_big?( cookie.value )
358
+ cookie_hash['value'] = ''
359
+ else
360
+ cookie_hash['value'] = decode( cookie.value )
361
+ end
362
+
363
+ new( { url: url, source: str }.merge( cookie_hash.my_symbolize_keys ) )
364
364
  end.flatten.compact
365
365
  end
366
366
  alias :parse_set_cookie :from_set_cookie
@@ -376,27 +376,41 @@ class Cookie < Base
376
376
  # @return [Array<Cookie>]
377
377
  def from_string( url, string )
378
378
  return [] if string.empty?
379
+
379
380
  string.split( ';' ).map do |cookie_pair|
381
+ cookie_pair.strip!
382
+
380
383
  k, v = *cookie_pair.split( '=', 2 )
381
- new( url: url, inputs: { decode( k.strip ) => decode( v.strip ) } )
384
+
385
+ v = '' if too_big?( v )
386
+
387
+ new(
388
+ url: url,
389
+ source: cookie_pair,
390
+ inputs: { decode( k ) => value_to_v0( v ) }
391
+ )
382
392
  end.flatten.compact
383
393
  end
384
394
 
395
+ def value_to_v0( value )
396
+ return '' if !value
397
+
398
+ value.start_with?( '"' ) && value.end_with?( '"' ) ?
399
+ value[1...-1] : decode( value )
400
+ end
401
+
385
402
  # Encodes a {String}'s reserved characters in order to prepare it for
386
403
  # the `Cookie` header field.
387
404
  #
388
405
  # @example
389
406
  # p Cookie.encode "+;%=\0 "
390
- # #=> "%2B%3B%25%3D%00+"
407
+ # #=> "%2B%3B%25%3D%00%20"
391
408
  #
392
409
  # @param [String] str
393
410
  #
394
411
  # @return [String]
395
412
  def encode( str )
396
- str = str.to_s
397
- return str if !ENCODE_CHARACTERS.find { |c| str.include? c }
398
-
399
- ::URI.encode( str, ENCODE_CHARACTERS_LIST )
413
+ Arachni::HTTP::Request.encode( str )
400
414
  end
401
415
 
402
416
  # Decodes a {String} encoded for the `Cookie` header field.
@@ -409,7 +423,7 @@ class Cookie < Base
409
423
  #
410
424
  # @return [String]
411
425
  def decode( str )
412
- ::URI.decode( str.to_s.gsub('+', ' ' ) )
426
+ ::URI.decode_www_form_component str.to_s
413
427
  end
414
428
 
415
429
  def keep_for_set_cookie
@@ -17,15 +17,15 @@ module Capabilities
17
17
  module Mutable
18
18
  include Arachni::Element::Capabilities::Mutable
19
19
 
20
- # Overrides {Capabilities::Mutable#each_mutation} to handle cookie-specific
20
+ # Overrides {Arachni::Element::Capabilities::Mutable#each_mutation} to handle cookie-specific
21
21
  # limitations and the {Arachni::OptionGroups::Audit#cookies_extensively} option.
22
22
  #
23
- # @param (see Capabilities::Mutable#each_mutation)
24
- # @return (see Capabilities::Mutable#each_mutation)
25
- # @yield (see Capabilities::Mutable#each_mutation)
26
- # @yieldparam (see Capabilities::Mutable#each_mutation)
23
+ # @param (see Arachni::Element::Capabilities::Mutable#each_mutation)
24
+ # @return (see Arachni::Element::Capabilities::Mutable#each_mutation)
25
+ # @yield (see Arachni::Element::Capabilities::Mutable#each_mutation)
26
+ # @yieldparam (see Arachni::Element::Capabilities::Mutable#each_mutation)
27
27
  #
28
- # @see Capabilities::Mutable#each_mutation
28
+ # @see Arachni::Element::Capabilities::Mutable#each_mutation
29
29
  def each_mutation( payload, options = {}, &block )
30
30
  options = prepare_mutation_options( options )
31
31
  parameter_names = options.delete( :parameter_names )
@@ -42,14 +42,6 @@ class DOM < Base
42
42
  p.to_set_cookie
43
43
  end
44
44
 
45
- def encode( *args )
46
- Cookie.encode( *args )
47
- end
48
-
49
- def decode( *args )
50
- Cookie.decode( *args )
51
- end
52
-
53
45
  def type
54
46
  self.class.type
55
47
  end
@@ -262,7 +262,14 @@ class Form < Base
262
262
  #
263
263
  # @return [Array<Form>]
264
264
  def from_document( url, document, ignore_scope = false )
265
- document = Nokogiri::HTML( document.to_s ) if !document.is_a?( Nokogiri::HTML::Document )
265
+ if !document.is_a?( Nokogiri::HTML::Document )
266
+ document = document.to_s
267
+
268
+ return [] if !(document =~ /<\s*form/i)
269
+
270
+ document = Nokogiri::HTML( document )
271
+ end
272
+
266
273
  base_url = (document.search( '//base[@href]' )[0]['href'] rescue url)
267
274
  base_url = to_absolute( base_url, url )
268
275
 
@@ -305,16 +312,20 @@ class Form < Base
305
312
 
306
313
  # Handle the easy stuff first...
307
314
  if elem.name != 'select'
308
- options[:inputs][name] = elem_attrs
315
+ options[:inputs][name] = elem_attrs
309
316
 
310
317
  if elem_attrs[:type] == :submit
311
318
  multiple_choice_submits[name] ||= Set.new
312
- multiple_choice_submits[name] <<
313
- elem_attrs[:value]
319
+ multiple_choice_submits[name] << elem_attrs[:value]
314
320
  end
315
321
 
316
322
  options[:inputs][name][:type] ||= :text
317
323
  options[:inputs][name][:value] ||= ''
324
+
325
+ if too_big?( options[:inputs][name][:value] )
326
+ options[:inputs][name][:value] = ''
327
+ end
328
+
318
329
  next
319
330
  end
320
331
 
@@ -322,19 +333,22 @@ class Form < Base
322
333
  if elem.children.css('option').any?
323
334
  elem.children.css('option').each do |child|
324
335
  h = attributes_to_hash( child.attributes )
325
- h[:type] = :select
336
+ h[:type] = :select
337
+ h[:value] ||= child.text
338
+
339
+ if too_big?( h[:value] )
340
+ h[:value] = ''
341
+ end
326
342
 
327
343
  # Prefer the selected or first option.
328
344
  if h[:selected]
329
- h[:value] ||= child.text
330
345
  options[:inputs][name] = h
331
346
  else
332
- h[:value] ||= child.text
333
347
  options[:inputs][name] ||= h
334
348
  end
335
349
  end
336
350
 
337
- # The select has no options, use an empty string.
351
+ # The select has no options, use an empty string.
338
352
  else
339
353
  options[:inputs][name] = {
340
354
  type: :select,
@@ -376,20 +390,20 @@ class Form < Base
376
390
  # Encodes a {String}'s reserved characters in order to prepare it
377
391
  # to be included in a request body.
378
392
  #
379
- # @param [String] str
393
+ # @param [String] string
380
394
  #
381
395
  # @return [String]
382
- def encode( str )
383
- ::URI.encode_www_form_component str.to_s
396
+ def encode( string )
397
+ Arachni::HTTP::Request.encode string
384
398
  end
385
399
 
386
400
  # Decodes a {String} encoded for an HTTP request's body.
387
401
  #
388
- # @param [String] str
402
+ # @param [String] string
389
403
  #
390
404
  # @return [String]
391
- def decode( str )
392
- ::URI.decode_www_form_component str.to_s
405
+ def decode( string )
406
+ ::URI.decode_www_form_component string.to_s
393
407
  end
394
408
 
395
409
  end
@@ -33,8 +33,8 @@ module Auditable
33
33
  end
34
34
  end
35
35
 
36
- # @param (see Capabilities::Auditable#audit_id)
37
- # @return (see Capabilities::Auditable#audit_id)
36
+ # @param (see Arachni::Element::Capabilities::Auditable#audit_id)
37
+ # @return (see Arachni::Element::Capabilities::Auditable#audit_id)
38
38
  def audit_id( payload = nil )
39
39
  force_train? ? id : super( payload )
40
40
  end
@@ -56,12 +56,12 @@ module Mutable
56
56
  # Whether or not to skip adding a mutation holding original values and
57
57
  # sample values.
58
58
  #
59
- # @param (see Capabilities::Mutable#each_mutation)
60
- # @return (see Capabilities::Mutable#each_mutation)
61
- # @yield (see Capabilities::Mutable#each_mutation)
62
- # @yieldparam (see Capabilities::Mutable#each_mutation)
59
+ # @param (see Arachni::Element::Capabilities::Mutable#each_mutation)
60
+ # @return (see Arachni::Element::Capabilities::Mutable#each_mutation)
61
+ # @yield (see Arachni::Element::Capabilities::Mutable#each_mutation)
62
+ # @yieldparam (see Arachni::Element::Capabilities::Mutable#each_mutation)
63
63
  #
64
- # @see Capabilities::Mutable#each_mutation
64
+ # @see Arachni::Element::Capabilities::Mutable#each_mutation
65
65
  # @see Arachni::OptionGroups::Input.fill
66
66
  def each_mutation( payload, opts = {} )
67
67
  opts = MUTATION_OPTIONS.merge( opts )
@@ -36,14 +36,6 @@ class DOM < Base
36
36
  @valid_input_names.include? name.to_s
37
37
  end
38
38
 
39
- def encode( *args )
40
- Form.encode( *args )
41
- end
42
-
43
- def decode( *args )
44
- Form.decode( *args )
45
- end
46
-
47
39
  def type
48
40
  self.class.type
49
41
  end