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
@@ -15,7 +15,7 @@ require Options.paths.lib + 'issue/severity'
15
15
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
16
16
  class Issue
17
17
 
18
- # Attributes removed from a parent issue (i.e. an issues with variations)
18
+ # Attributes removed from a parent issue (i.e. an issue with variations)
19
19
  # and solely populating variations.
20
20
  VARIATION_ATTRIBUTES = Set.new([
21
21
  :@page, :@referring_page, :@proof, :@signature, :@remarks, :@trusted
@@ -286,12 +286,23 @@ class Issue
286
286
  end
287
287
  end
288
288
 
289
- [:name, :description, :remedy_guidance, :remedy_code, :proof, :signature].each do |m|
289
+ [:name, :description, :remedy_guidance, :remedy_code, :proof].each do |m|
290
290
  define_method "#{m}=" do |s|
291
291
  instance_variable_set( "@#{m}".to_sym, s ? s.to_s.freeze : nil )
292
292
  end
293
293
  end
294
294
 
295
+ def signature=( sig )
296
+ @signature = case sig
297
+ when Regexp
298
+ sig.source
299
+ when String, nil
300
+ sig
301
+ else
302
+ sig.to_s
303
+ end
304
+ end
305
+
295
306
  # @return [Hash]
296
307
  def to_h
297
308
  h = {}
@@ -495,7 +506,10 @@ class Issue
495
506
  data['variations'] = data['variations'].map(&:to_rpc_data)
496
507
  end
497
508
 
498
- data['digest'] = digest
509
+ if !variation?
510
+ data['digest'] = digest
511
+ end
512
+
499
513
  data['severity'] = data['severity'].to_s
500
514
 
501
515
  data
@@ -13,6 +13,11 @@ module Arachni::OptionGroups
13
13
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
14
14
  class BrowserCluster < Arachni::OptionGroup
15
15
 
16
+ # @return [Hash<Regexp,String>]
17
+ # When the page URL matched the key `Regexp`, wait until the `String` CSS
18
+ # selector in the value matches an element.
19
+ attr_accessor :wait_for_elements
20
+
16
21
  # @return [Integer]
17
22
  # Amount of {BrowserCluster::Worker} to keep in the pool and put to work.
18
23
  attr_accessor :pool_size
@@ -38,13 +43,41 @@ class BrowserCluster < Arachni::OptionGroup
38
43
  attr_accessor :screen_height
39
44
 
40
45
  set_defaults(
46
+ wait_for_elements: {},
41
47
  pool_size: 6,
42
- job_timeout: 15,
48
+ job_timeout: 25,
43
49
  worker_time_to_live: 100,
44
50
  ignore_images: false,
45
51
  screen_width: 1600,
46
52
  screen_height: 1200
47
53
  )
48
54
 
55
+ def css_to_wait_for( url )
56
+ wait_for_elements.map do |pattern, css|
57
+ next if !(url =~ pattern)
58
+ css
59
+ end.compact
60
+ end
61
+
62
+ def wait_for_elements=( rules )
63
+ return @wait_for_elements = defaults[:wait_for_elements].dup if !rules
64
+
65
+ @wait_for_elements = rules.inject({}) do |h, (regexp, value)|
66
+ regexp = regexp.is_a?( Regexp ) ? regexp : Regexp.new( regexp.to_s )
67
+ h.merge!( regexp => value )
68
+ h
69
+ end
70
+ end
71
+
72
+ def to_rpc_data
73
+ d = super
74
+
75
+ %w(wait_for_elements).each do |k|
76
+ d[k] = d[k].my_stringify
77
+ end
78
+
79
+ d
80
+ end
81
+
49
82
  end
50
83
  end
@@ -242,7 +242,7 @@ class HTTP < Arachni::OptionGroup
242
242
  request_timeout: 10_000,
243
243
  request_redirect_limit: 5,
244
244
  request_concurrency: 20,
245
- request_queue_size: 500,
245
+ request_queue_size: 100,
246
246
  request_headers: {},
247
247
  response_max_size: 500_000,
248
248
  cookies: {}
@@ -379,31 +379,44 @@ class Page
379
379
  def has_script?
380
380
  return @has_javascript if !@has_javascript.nil?
381
381
 
382
- if !response.headers.content_type.to_s.start_with?( 'text/html' ) ||
383
- !text? || !document
382
+ if !response.headers.content_type.to_s.start_with?( 'text/html' ) || !text?
384
383
  return @has_javascript = false
385
384
  end
386
385
 
386
+ dbody = body.downcase
387
+
387
388
  # First check, quick and simple.
388
- return @has_javascript = true if document.css( 'script' ).any?
389
+ if dbody.include?( '<script' ) || dbody.include?( 'javascript:' )
390
+ return @has_javascript = true
391
+ end
389
392
 
390
393
  # Check for event attributes, if there are any then there's JS to be
391
394
  # executed.
392
395
  Browser::Javascript.events.flatten.each do |event|
393
- return @has_javascript = true if document.xpath( "//*[@#{event}]" ).any?
396
+ return @has_javascript = true if dbody.include?( "#{event}=" )
394
397
  end
395
398
 
396
- # If there's 'javascript:' in 'href' and 'action' attributes then
397
- # there's JS to be executed.
398
- [:action, :href].each do |candidate|
399
- document.xpath( "//*[@#{candidate}]" ).each do |attribute|
400
- if attribute.attributes[candidate.to_s].to_s.start_with?( 'javascript:' )
401
- return @has_javascript = true
402
- end
403
- end
399
+ @has_javascript = false
400
+ end
401
+
402
+ # @param [String, Symbol,Array<String, Symbol>] tags
403
+ # Element tag names.
404
+ #
405
+ # @return [Boolean]
406
+ # `true` if the page contains any of the given elements, `false` otherwise.
407
+ def has_elements?( *tags )
408
+ return if !text?
409
+
410
+ tags.flatten.each do |tag|
411
+ tag = tag.to_s
412
+
413
+ next if !(body =~ /<\s*#{tag}/i)
414
+
415
+ return false if !document
416
+ return true if document.css( tag ).any?
404
417
  end
405
418
 
406
- @has_javascript = false
419
+ false
407
420
  end
408
421
 
409
422
  # @return [Boolean]
@@ -270,13 +270,13 @@ class Transition
270
270
 
271
271
  when 'element'
272
272
  if value.is_a? String
273
- value.to_sym
273
+ data['event'].to_s == 'request' ? value : value.to_sym
274
274
  else
275
275
  Browser::ElementLocator.from_rpc_data( value )
276
276
  end
277
277
 
278
278
  when 'options'
279
- value.my_symbolize_keys
279
+ value.my_symbolize_keys(false)
280
280
 
281
281
  else
282
282
  value
@@ -31,14 +31,26 @@ class Parser
31
31
  # @abstract
32
32
  class Base
33
33
 
34
+ attr_reader :html
35
+ attr_reader :document
36
+ attr_reader :downcased_html
37
+
38
+ def initialize( options = {} )
39
+ @html = options[:html]
40
+ @downcased_html = @html.downcase
41
+ @document = options[:document]
42
+ end
43
+
34
44
  # This method must be implemented by all checks and must return an
35
45
  # array of paths as plain strings
36
46
  #
37
- # @param [Nokogiri] document Nokogiri document
38
- #
39
47
  # @return [Array<String>] paths
40
48
  # @abstract
41
- def run( document )
49
+ def run
50
+ end
51
+
52
+ def includes?( string_or_regexp )
53
+ !!@downcased_html[string_or_regexp]
42
54
  end
43
55
 
44
56
  end
@@ -125,9 +137,8 @@ class Parser
125
137
  @document = Nokogiri::HTML( body ) if text? rescue nil
126
138
  end
127
139
 
128
- # @note It's more of a placeholder method, it doesn't actually analyze anything.
129
- # It's a long shot that any of these will be vulnerable but better be safe
130
- # than sorry.
140
+ # @note It will include common request headers as well headers from the HTTP
141
+ # request.
131
142
  #
132
143
  # @return [Hash]
133
144
  # List of valid auditable HTTP header fields.
@@ -141,14 +152,15 @@ class Parser
141
152
  'User-Agent' => @options.http.user_agent || '',
142
153
  'Referer' => @url,
143
154
  'Pragma' => 'no-cache'
144
- }.map { |k, v| Header.new( url: @url, inputs: { k => v } ) }.freeze
155
+ }.merge( response.request.headers ).
156
+ map { |k, v| Header.new( url: @url, inputs: { k => v } ) }.freeze
145
157
  end
146
158
 
147
159
  # @return [Array<Element::Form>]
148
160
  # Forms from {#document}.
149
161
  def forms
150
162
  return @forms.freeze if @forms
151
- return [] if !text?
163
+ return [] if !text? || !(body =~ /<\s*form/i)
152
164
 
153
165
  f = Form.from_document( @url, document )
154
166
  return f if !@secondary_responses
@@ -199,7 +211,7 @@ class Parser
199
211
  # Links in {#document}.
200
212
  def links
201
213
  return @links.freeze if @links
202
- return @links = [link].compact if !text?
214
+ return @links = [link].compact if !text? || !(body =~ /\?.*=/)
203
215
 
204
216
  @links = [link].compact | Link.from_document( @url, document )
205
217
  end
@@ -227,7 +239,7 @@ class Parser
227
239
  # @return [Hash]
228
240
  # Parameters found in {#url}.
229
241
  def link_vars
230
- return {} if (!parsed = uri_parse( @url ))
242
+ return {} if !(parsed = uri_parse( @url ))
231
243
 
232
244
  @link_vars ||= parsed.rewrite.query_parameters.freeze
233
245
  end
@@ -309,7 +321,12 @@ class Parser
309
321
  def run_extractors
310
322
  begin
311
323
  self.class.extractors.available.map do |name|
312
- exception_jail( false ){ self.class.extractors[name].new.run( document ) }
324
+ exception_jail false do
325
+ self.class.extractors[name].new(
326
+ document: document,
327
+ html: body
328
+ ).run
329
+ end
313
330
  end.flatten.uniq.compact.
314
331
  map { |path| to_absolute( path ) }.compact.uniq.
315
332
  reject { |path| skip?( path ) }
@@ -36,6 +36,11 @@ class Fingerprinter
36
36
  def run
37
37
  end
38
38
 
39
+ def html?
40
+ @is_html ||= page.response.headers['content-type'].to_s.
41
+ downcase.include?( 'text/html' )
42
+ end
43
+
39
44
  # @param [String] string
40
45
  #
41
46
  # @return [Boolean]
@@ -89,12 +89,13 @@ class Manager
89
89
  :nginx,
90
90
  :tomcat,
91
91
  :iis,
92
- :jetty
92
+ :jetty,
93
+ :gunicorn
93
94
  ]
94
95
 
95
96
  LANGUAGES = [
96
97
  :php,
97
- :jsp,
98
+ :java,
98
99
  :python,
99
100
  :ruby,
100
101
  :asp,
@@ -104,7 +105,13 @@ class Manager
104
105
 
105
106
  # WebApp frameworks.
106
107
  FRAMEWORKS = [
107
- :rack
108
+ :rack,
109
+ :rails,
110
+ :cakephp,
111
+ :django,
112
+ :aspx_mvc,
113
+ :jsf,
114
+ :cherrypy
108
115
  ]
109
116
 
110
117
  PLATFORM_NAMES = {
@@ -143,10 +150,11 @@ class Manager
143
150
  tomcat: 'TomCat',
144
151
  iis: 'IIS',
145
152
  jetty: 'Jetty',
153
+ gunicorn: 'Gunicorn',
146
154
 
147
155
  # Programming languages
148
156
  php: 'PHP',
149
- jsp: 'JSP',
157
+ java: 'Java',
150
158
  python: 'Python',
151
159
  ruby: 'Ruby',
152
160
  asp: 'ASP',
@@ -154,7 +162,13 @@ class Manager
154
162
  perl: 'Perl',
155
163
 
156
164
  # Web frameworks
157
- rack: 'Rack'
165
+ rack: 'Rack',
166
+ django: 'Django',
167
+ cakephp: 'CakePHP',
168
+ rails: 'Ruby on Rails',
169
+ aspx_mvc: 'ASP.NET MVC',
170
+ jsf: 'JavaServer Faces',
171
+ cherrypy: 'CherryPy'
158
172
  }
159
173
 
160
174
  # Amount of
@@ -194,7 +208,7 @@ class Manager
194
208
  # Sets global platforms fingerprints
195
209
  # @private
196
210
  def self.set( platforms )
197
- @platforms = Support::Cache::RandomReplacement.new( PLATFORM_CACHE_SIZE )
211
+ @platforms = Support::Cache::LeastRecentlyPushed.new( PLATFORM_CACHE_SIZE )
198
212
  platforms.each { |k, v| @platforms[k] = v }
199
213
  @platforms
200
214
  end
@@ -228,8 +242,8 @@ class Manager
228
242
  # @return [Bool]
229
243
  # `true` if the resource should be fingerprinted, `false` otherwise.
230
244
  def self.fingerprint?( resource )
231
- !(!Options.fingerprint? || !resource.text? ||
232
- include?( resource.url ) || resource.scope.out?)
245
+ !(!Options.fingerprint? || resource.code != 200 || !resource.text? ||
246
+ include?( resource.url ) || resource.scope.out?)
233
247
  end
234
248
 
235
249
  # Runs all fingerprinters against the given `page`.
@@ -240,14 +254,40 @@ class Manager
240
254
  # @return [Manager]
241
255
  # Updated `self`.
242
256
  def self.fingerprint( page )
243
- return page if !fingerprint? page
257
+ synchronize do
258
+ return page if !fingerprint? page
244
259
 
245
- fingerprinters.available.each do |name|
246
- exception_jail( false ) do
247
- fingerprinters[name].new( page ).run
260
+ fingerprinters.available.each do |name|
261
+ exception_jail( false ) do
262
+ fingerprinters[name].new( page ).run
263
+ end
248
264
  end
265
+
266
+ # We do this to flag the resource as checked even if no platforms
267
+ # were identified. We don't want to keep checking a resource that
268
+ # yields nothing over and over.
269
+ update( page.url, [] )
249
270
  end
250
- page
271
+
272
+ # Fingerprinting will have resulted in element parsing, clear the element
273
+ # caches to keep RAM consumption down.
274
+ page.clear_cache
275
+ end
276
+
277
+ # @param [String, URI] uri
278
+ #
279
+ # @return [Manager]
280
+ # Platform for the given `uri`
281
+ def self.[]( uri )
282
+ # If fingerprinting is disabled there's no point in filling the cache
283
+ # with the same object over and over, create an identical one for all
284
+ # URLs and return that always.
285
+ if !Options.fingerprint?
286
+ return @default ||= new_from_options
287
+ end
288
+
289
+ return new_from_options if !(key = make_key( uri ))
290
+ synchronize { @platforms[key] ||= new_from_options }
251
291
  end
252
292
 
253
293
  # Sets platform manager for the given `uri`.
@@ -260,11 +300,16 @@ class Manager
260
300
  # @raise [Error::Invalid]
261
301
  # On {#invalid?} platforms.
262
302
  def self.[]=( uri, platforms )
263
- return new( platforms ) if !(key = make_key( uri ))
303
+ # For some reason we failed to make a key, try to salvage the situation.
304
+ if !(key = make_key( uri ))
305
+ return new_from_options( platforms )
306
+ end
264
307
 
265
308
  synchronize do
266
309
  @platforms[key] =
267
- platforms.is_a?( self ) ? platforms : new( platforms )
310
+ platforms.is_a?( self ) ?
311
+ platforms :
312
+ new_from_options( platforms )
268
313
  end
269
314
  end
270
315
 
@@ -293,22 +338,6 @@ class Manager
293
338
  end
294
339
  end
295
340
 
296
- # @param [String, URI] uri
297
- #
298
- # @return [Manager]
299
- # Platform for the given `uri`
300
- def self.[]( uri )
301
- # If fingerprinting is disabled there's no point in filling the cache
302
- # with the same object over and over, create an identical one for all
303
- # URLs and return that always.
304
- if !Options.fingerprint?
305
- return @default ||= new( Options.platforms )
306
- end
307
-
308
- return new if !(key = make_key( uri ))
309
- synchronize { @platforms[key] ||= new }
310
- end
311
-
312
341
  # @return [Boolean]
313
342
  # `true` if there are no platforms fingerprints, `false` otherwise.
314
343
  def self.empty?
@@ -326,6 +355,10 @@ class Manager
326
355
  parsed.without_query
327
356
  end
328
357
 
358
+ def self.new_from_options( platforms = [] )
359
+ new( platforms | Options.platforms )
360
+ end
361
+
329
362
  # @param [Array<String, Symbol>] platforms
330
363
  # Platforms with which to initialize the lists.
331
364
  def initialize( platforms = [] )
@@ -335,7 +368,7 @@ class Manager
335
368
  List.new( self.class.const_get( type.to_s.upcase.to_sym ) )
336
369
  end
337
370
 
338
- update [platforms | Options.platforms].flatten.compact
371
+ update platforms
339
372
  end
340
373
 
341
374
  # @!method os