arachni 1.4 → 1.6.0

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 (748) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +195 -0
  3. data/Gemfile +4 -4
  4. data/LICENSE.md +1 -1
  5. data/README.md +7 -3
  6. data/Rakefile +1 -43
  7. data/arachni.gemspec +35 -30
  8. data/bin/arachni +1 -1
  9. data/bin/arachni_console +1 -1
  10. data/bin/arachni_multi +6 -1
  11. data/bin/arachni_reporter +1 -1
  12. data/bin/arachni_reproduce +12 -0
  13. data/bin/arachni_rest_server +1 -1
  14. data/bin/arachni_restore +1 -1
  15. data/bin/arachni_rpc +6 -1
  16. data/bin/arachni_rpcd +1 -1
  17. data/bin/arachni_rpcd_monitor +6 -1
  18. data/bin/arachni_script +1 -1
  19. data/components/checks/active/code_injection.rb +1 -1
  20. data/components/checks/active/code_injection_php_input_wrapper.rb +1 -1
  21. data/components/checks/active/code_injection_timing.rb +1 -1
  22. data/components/checks/active/csrf.rb +20 -75
  23. data/components/checks/active/file_inclusion.rb +1 -1
  24. data/components/checks/active/ldap_injection.rb +1 -1
  25. data/components/checks/active/no_sql_injection.rb +1 -1
  26. data/components/checks/active/no_sql_injection_differential.rb +3 -3
  27. data/components/checks/active/os_cmd_injection.rb +1 -1
  28. data/components/checks/active/os_cmd_injection_timing.rb +1 -1
  29. data/components/checks/active/path_traversal.rb +3 -3
  30. data/components/checks/active/response_splitting.rb +1 -1
  31. data/components/checks/active/rfi.rb +1 -1
  32. data/components/checks/active/session_fixation.rb +1 -1
  33. data/components/checks/active/source_code_disclosure.rb +1 -1
  34. data/components/checks/active/sql_injection/regexps/hsqldb.yaml +1 -0
  35. data/components/checks/active/sql_injection/substrings/hsqldb +1 -0
  36. data/components/checks/active/sql_injection/substrings/java +4 -0
  37. data/components/checks/active/sql_injection/substrings/oracle +0 -1
  38. data/components/checks/active/sql_injection/substrings/sqlite +1 -0
  39. data/components/checks/active/sql_injection.rb +1 -1
  40. data/components/checks/active/sql_injection_differential.rb +3 -3
  41. data/components/checks/active/sql_injection_timing.rb +1 -1
  42. data/components/checks/active/trainer.rb +1 -1
  43. data/components/checks/active/unvalidated_redirect.rb +34 -11
  44. data/components/checks/active/unvalidated_redirect_dom.rb +4 -4
  45. data/components/checks/active/xpath_injection.rb +1 -1
  46. data/components/checks/active/xss.rb +54 -29
  47. data/components/checks/active/xss_dom.rb +15 -11
  48. data/components/checks/active/xss_dom_script_context.rb +4 -6
  49. data/components/checks/active/xss_event.rb +46 -34
  50. data/components/checks/active/xss_path.rb +9 -6
  51. data/components/checks/active/xss_script_context.rb +100 -47
  52. data/components/checks/active/xss_tag.rb +41 -15
  53. data/components/checks/active/xxe.rb +1 -1
  54. data/components/checks/passive/allowed_methods.rb +1 -1
  55. data/components/checks/passive/backdoors.rb +1 -1
  56. data/components/checks/passive/backup_directories.rb +15 -3
  57. data/components/checks/passive/backup_files.rb +39 -6
  58. data/components/checks/passive/common_admin_interfaces/admin-panels.txt +1 -0
  59. data/components/checks/passive/common_admin_interfaces.rb +1 -1
  60. data/components/checks/passive/common_directories/directories.txt +1 -0
  61. data/components/checks/passive/common_directories.rb +1 -1
  62. data/components/checks/passive/common_files.rb +1 -1
  63. data/components/checks/passive/directory_listing.rb +1 -1
  64. data/components/checks/passive/grep/captcha.rb +8 -9
  65. data/components/checks/passive/grep/cookie_set_for_parent_domain.rb +1 -1
  66. data/components/checks/passive/grep/credit_card.rb +1 -1
  67. data/components/checks/passive/grep/cvs_svn_users.rb +1 -1
  68. data/components/checks/passive/grep/emails.rb +1 -1
  69. data/components/checks/passive/grep/form_upload.rb +3 -5
  70. data/components/checks/passive/grep/hsts.rb +1 -1
  71. data/components/checks/passive/grep/html_objects.rb +1 -1
  72. data/components/checks/passive/grep/http_only_cookies.rb +1 -1
  73. data/components/checks/passive/grep/insecure_cookies.rb +5 -5
  74. data/components/checks/passive/grep/insecure_cors_policy.rb +1 -1
  75. data/components/checks/passive/grep/mixed_resource.rb +4 -4
  76. data/components/checks/passive/grep/password_autocomplete.rb +1 -1
  77. data/components/checks/passive/grep/private_ip.rb +1 -1
  78. data/components/checks/passive/grep/ssn.rb +1 -1
  79. data/components/checks/passive/grep/unencrypted_password_forms.rb +3 -3
  80. data/components/checks/passive/grep/x_frame_options.rb +4 -4
  81. data/components/checks/passive/htaccess_limit.rb +1 -1
  82. data/components/checks/passive/http_put.rb +1 -1
  83. data/components/checks/passive/insecure_client_access_policy.rb +2 -2
  84. data/components/checks/passive/insecure_cross_domain_policy_access.rb +2 -2
  85. data/components/checks/passive/insecure_cross_domain_policy_headers.rb +2 -2
  86. data/components/checks/passive/interesting_responses.rb +1 -1
  87. data/components/checks/passive/localstart_asp.rb +1 -1
  88. data/components/checks/passive/origin_spoof_access_restriction_bypass.rb +1 -1
  89. data/components/checks/passive/webdav.rb +1 -1
  90. data/components/checks/passive/xst.rb +10 -12
  91. data/components/fingerprinters/frameworks/aspx_mvc.rb +1 -1
  92. data/components/fingerprinters/frameworks/cakephp.rb +1 -1
  93. data/components/fingerprinters/frameworks/cherrypy.rb +1 -1
  94. data/components/fingerprinters/frameworks/django.rb +1 -1
  95. data/components/fingerprinters/frameworks/jsf.rb +1 -1
  96. data/components/fingerprinters/frameworks/nette.rb +1 -1
  97. data/components/fingerprinters/frameworks/rack.rb +1 -1
  98. data/components/fingerprinters/frameworks/rails.rb +1 -1
  99. data/components/fingerprinters/frameworks/symfony.rb +1 -1
  100. data/components/fingerprinters/languages/asp.rb +1 -1
  101. data/components/fingerprinters/languages/aspx.rb +1 -1
  102. data/components/fingerprinters/languages/java.rb +1 -1
  103. data/components/fingerprinters/languages/php.rb +1 -1
  104. data/components/fingerprinters/languages/python.rb +1 -1
  105. data/components/fingerprinters/languages/ruby.rb +1 -1
  106. data/components/fingerprinters/os/bsd.rb +1 -1
  107. data/components/fingerprinters/os/linux.rb +1 -1
  108. data/components/fingerprinters/os/solaris.rb +1 -1
  109. data/components/fingerprinters/os/unix.rb +1 -1
  110. data/components/fingerprinters/os/windows.rb +1 -1
  111. data/components/fingerprinters/servers/apache.rb +1 -1
  112. data/components/fingerprinters/servers/gunicorn.rb +1 -1
  113. data/components/fingerprinters/servers/iis.rb +1 -1
  114. data/components/fingerprinters/servers/jetty.rb +1 -1
  115. data/components/fingerprinters/servers/nginx.rb +1 -1
  116. data/components/fingerprinters/servers/tomcat.rb +1 -1
  117. data/components/path_extractors/anchors.rb +3 -5
  118. data/components/path_extractors/areas.rb +3 -4
  119. data/components/path_extractors/comments.rb +4 -5
  120. data/components/path_extractors/data_url.rb +4 -5
  121. data/components/path_extractors/forms.rb +3 -4
  122. data/components/path_extractors/frames.rb +3 -5
  123. data/components/path_extractors/generic.rb +3 -1
  124. data/components/path_extractors/links.rb +3 -4
  125. data/components/path_extractors/meta_refresh.rb +11 -17
  126. data/components/path_extractors/scripts.rb +18 -15
  127. data/components/plugins/autologin.rb +3 -2
  128. data/components/plugins/beep_notify.rb +1 -1
  129. data/components/plugins/content_types.rb +1 -1
  130. data/components/plugins/cookie_collector.rb +1 -1
  131. data/components/plugins/debug/browser_cluster_job_monitor.rb +60 -0
  132. data/components/plugins/defaults/autothrottle.rb +1 -1
  133. data/components/plugins/defaults/healthmap.rb +3 -1
  134. data/components/plugins/defaults/meta/remedies/discovery.rb +1 -1
  135. data/components/plugins/defaults/meta/remedies/timing_attacks.rb +1 -1
  136. data/components/plugins/defaults/meta/uniformity.rb +1 -1
  137. data/components/plugins/email_notify.rb +26 -9
  138. data/components/plugins/exec.rb +1 -1
  139. data/components/plugins/form_dicattack.rb +3 -4
  140. data/components/plugins/headers_collector.rb +1 -1
  141. data/components/plugins/http_dicattack.rb +4 -5
  142. data/components/plugins/login_script.rb +2 -2
  143. data/components/plugins/metrics.rb +44 -18
  144. data/components/plugins/page_dump.rb +60 -0
  145. data/components/plugins/proxy/panel/verify_login_sequence.html.erb +1 -1
  146. data/components/plugins/proxy/template_scope.rb +6 -1
  147. data/components/plugins/proxy.rb +44 -31
  148. data/components/plugins/rate_limiter.rb +80 -0
  149. data/components/plugins/restrict_to_dom_state.rb +1 -1
  150. data/components/plugins/script.rb +1 -1
  151. data/components/plugins/uncommon_headers.rb +1 -1
  152. data/components/plugins/vector_collector.rb +1 -1
  153. data/components/plugins/vector_feed.rb +1 -1
  154. data/components/plugins/waf_detector.rb +3 -3
  155. data/components/plugins/webhook_notify.rb +99 -0
  156. data/components/reporters/ap.rb +1 -1
  157. data/components/reporters/html/default/configuration.erb +2 -0
  158. data/components/reporters/html/default.erb +3 -2
  159. data/components/reporters/html.rb +5 -8
  160. data/components/reporters/json.rb +1 -1
  161. data/components/reporters/marshal.rb +1 -1
  162. data/components/reporters/plugin_formatters/html/autologin.rb +1 -1
  163. data/components/reporters/plugin_formatters/html/content_types.rb +1 -1
  164. data/components/reporters/plugin_formatters/html/cookie_collector.rb +1 -1
  165. data/components/reporters/plugin_formatters/html/exec.rb +1 -1
  166. data/components/reporters/plugin_formatters/html/form_dicattack.rb +1 -1
  167. data/components/reporters/plugin_formatters/html/healthmap.rb +1 -1
  168. data/components/reporters/plugin_formatters/html/http_dicattack.rb +1 -1
  169. data/components/reporters/plugin_formatters/html/login_script.rb +1 -1
  170. data/components/reporters/plugin_formatters/html/metrics.rb +46 -1
  171. data/components/reporters/plugin_formatters/html/uncommon_headers.rb +1 -1
  172. data/components/reporters/plugin_formatters/html/uniformity.rb +1 -1
  173. data/components/reporters/plugin_formatters/html/vector_collector.rb +1 -1
  174. data/components/reporters/plugin_formatters/html/waf_detector.rb +1 -1
  175. data/components/reporters/plugin_formatters/stdout/autologin.rb +1 -1
  176. data/components/reporters/plugin_formatters/stdout/content_types.rb +1 -1
  177. data/components/reporters/plugin_formatters/stdout/cookie_collector.rb +1 -1
  178. data/components/reporters/plugin_formatters/stdout/exec.rb +1 -1
  179. data/components/reporters/plugin_formatters/stdout/form_dicattack.rb +1 -1
  180. data/components/reporters/plugin_formatters/stdout/healthmap.rb +1 -1
  181. data/components/reporters/plugin_formatters/stdout/http_dicattack.rb +1 -1
  182. data/components/reporters/plugin_formatters/stdout/login_script.rb +1 -1
  183. data/components/reporters/plugin_formatters/stdout/metrics.rb +11 -1
  184. data/components/reporters/plugin_formatters/stdout/uncommon_headers.rb +1 -1
  185. data/components/reporters/plugin_formatters/stdout/uniformity.rb +1 -1
  186. data/components/reporters/plugin_formatters/stdout/vector_collector.rb +1 -1
  187. data/components/reporters/plugin_formatters/stdout/waf_detector.rb +1 -1
  188. data/components/reporters/plugin_formatters/xml/autologin.rb +1 -1
  189. data/components/reporters/plugin_formatters/xml/content_types.rb +10 -7
  190. data/components/reporters/plugin_formatters/xml/cookie_collector.rb +6 -3
  191. data/components/reporters/plugin_formatters/xml/exec.rb +1 -1
  192. data/components/reporters/plugin_formatters/xml/form_dicattack.rb +1 -1
  193. data/components/reporters/plugin_formatters/xml/healthmap.rb +1 -1
  194. data/components/reporters/plugin_formatters/xml/http_dicattack.rb +1 -1
  195. data/components/reporters/plugin_formatters/xml/login_script.rb +1 -1
  196. data/components/reporters/plugin_formatters/xml/metrics.rb +1 -1
  197. data/components/reporters/plugin_formatters/xml/uncommon_headers.rb +5 -2
  198. data/components/reporters/plugin_formatters/xml/uniformity.rb +1 -1
  199. data/components/reporters/plugin_formatters/xml/vector_collector.rb +8 -5
  200. data/components/reporters/plugin_formatters/xml/waf_detector.rb +1 -1
  201. data/components/reporters/stdout.rb +3 -2
  202. data/components/reporters/txt.rb +1 -1
  203. data/components/reporters/xml/schema.xsd +29 -13
  204. data/components/reporters/xml.rb +40 -23
  205. data/components/reporters/yaml.rb +1 -1
  206. data/config/write_paths.yml +4 -0
  207. data/lib/arachni/banner.rb +1 -1
  208. data/lib/arachni/browser/element_locator.rb +9 -5
  209. data/lib/arachni/browser/javascript/dom_monitor.rb +1 -1
  210. data/lib/arachni/browser/javascript/proxy/stub.rb +1 -1
  211. data/lib/arachni/browser/javascript/proxy.rb +1 -1
  212. data/lib/arachni/browser/javascript/scripts/dom_monitor.js +329 -72
  213. data/lib/arachni/browser/javascript/scripts/polyfills.js +0 -28
  214. data/lib/arachni/browser/javascript/scripts/taint_tracer.js +81 -25
  215. data/lib/arachni/browser/javascript/taint_tracer/frame/called_function.rb +1 -1
  216. data/lib/arachni/browser/javascript/taint_tracer/frame.rb +1 -1
  217. data/lib/arachni/browser/javascript/taint_tracer/sink/base.rb +1 -1
  218. data/lib/arachni/browser/javascript/taint_tracer/sink/data_flow.rb +1 -1
  219. data/lib/arachni/browser/javascript/taint_tracer/sink/execution_flow.rb +1 -1
  220. data/lib/arachni/browser/javascript/taint_tracer.rb +1 -1
  221. data/lib/arachni/browser/javascript.rb +111 -198
  222. data/lib/arachni/browser.rb +309 -382
  223. data/lib/arachni/browser_cluster/job/result.rb +1 -1
  224. data/lib/arachni/browser_cluster/job.rb +9 -2
  225. data/lib/arachni/browser_cluster/jobs/browser_provider.rb +8 -2
  226. data/lib/arachni/browser_cluster/jobs/dom_exploration/event_trigger/result.rb +1 -1
  227. data/lib/arachni/browser_cluster/jobs/dom_exploration/event_trigger.rb +1 -1
  228. data/lib/arachni/browser_cluster/jobs/dom_exploration/result.rb +1 -1
  229. data/lib/arachni/browser_cluster/jobs/dom_exploration.rb +13 -1
  230. data/lib/arachni/browser_cluster/jobs/taint_trace/event_trigger/result.rb +1 -1
  231. data/lib/arachni/browser_cluster/jobs/taint_trace/event_trigger.rb +1 -1
  232. data/lib/arachni/browser_cluster/jobs/taint_trace/result.rb +1 -1
  233. data/lib/arachni/browser_cluster/jobs/taint_trace.rb +1 -1
  234. data/lib/arachni/browser_cluster/worker.rb +97 -87
  235. data/lib/arachni/browser_cluster.rb +79 -62
  236. data/lib/arachni/check/auditor.rb +161 -155
  237. data/lib/arachni/check/base.rb +1 -1
  238. data/lib/arachni/check/manager.rb +1 -1
  239. data/lib/arachni/check.rb +1 -1
  240. data/lib/arachni/component/base.rb +3 -1
  241. data/lib/arachni/component/manager.rb +1 -1
  242. data/lib/arachni/component/options/address.rb +1 -1
  243. data/lib/arachni/component/options/base.rb +1 -1
  244. data/lib/arachni/component/options/bool.rb +1 -1
  245. data/lib/arachni/component/options/float.rb +1 -1
  246. data/lib/arachni/component/options/int.rb +1 -1
  247. data/lib/arachni/component/options/multiple_choice.rb +1 -1
  248. data/lib/arachni/component/options/object.rb +1 -1
  249. data/lib/arachni/component/options/path.rb +1 -1
  250. data/lib/arachni/component/options/port.rb +1 -1
  251. data/lib/arachni/component/options/string.rb +1 -1
  252. data/lib/arachni/component/options/url.rb +1 -1
  253. data/lib/arachni/component/options.rb +1 -1
  254. data/lib/arachni/component/output.rb +8 -2
  255. data/lib/arachni/component/utilities.rb +1 -1
  256. data/lib/arachni/component.rb +1 -1
  257. data/lib/arachni/data/framework/rpc.rb +2 -2
  258. data/lib/arachni/data/framework.rb +3 -2
  259. data/lib/arachni/data/issues.rb +1 -1
  260. data/lib/arachni/data/plugins.rb +1 -1
  261. data/lib/arachni/data/session.rb +1 -1
  262. data/lib/arachni/data.rb +1 -1
  263. data/lib/arachni/element/base.rb +1 -1
  264. data/lib/arachni/element/body.rb +1 -1
  265. data/lib/arachni/element/capabilities/analyzable/differential.rb +142 -175
  266. data/lib/arachni/element/capabilities/analyzable/signature.rb +40 -18
  267. data/lib/arachni/element/capabilities/analyzable/timeout.rb +1 -1
  268. data/lib/arachni/element/capabilities/analyzable.rb +1 -1
  269. data/lib/arachni/element/capabilities/auditable/buffered.rb +92 -0
  270. data/lib/arachni/element/capabilities/auditable/line_buffered.rb +103 -0
  271. data/lib/arachni/element/capabilities/auditable.rb +2 -8
  272. data/lib/arachni/element/capabilities/dom_only.rb +1 -1
  273. data/lib/arachni/element/capabilities/inputtable.rb +6 -2
  274. data/lib/arachni/element/capabilities/mutable.rb +1 -1
  275. data/lib/arachni/element/capabilities/refreshable.rb +1 -1
  276. data/lib/arachni/element/capabilities/submittable.rb +1 -1
  277. data/lib/arachni/element/capabilities/with_auditor/output.rb +4 -3
  278. data/lib/arachni/element/capabilities/with_auditor.rb +1 -1
  279. data/lib/arachni/element/capabilities/with_dom.rb +1 -1
  280. data/lib/arachni/element/capabilities/with_node.rb +3 -3
  281. data/lib/arachni/element/capabilities/with_scope/scope.rb +1 -1
  282. data/lib/arachni/element/capabilities/with_scope.rb +1 -1
  283. data/lib/arachni/element/capabilities/with_source.rb +2 -2
  284. data/lib/arachni/element/cookie/capabilities/inputtable.rb +1 -1
  285. data/lib/arachni/element/cookie/capabilities/mutable.rb +1 -1
  286. data/lib/arachni/element/cookie/capabilities/with_dom.rb +1 -1
  287. data/lib/arachni/element/cookie/dom.rb +1 -1
  288. data/lib/arachni/element/cookie.rb +49 -24
  289. data/lib/arachni/element/dom/capabilities/auditable.rb +44 -3
  290. data/lib/arachni/element/dom/capabilities/inputtable.rb +1 -1
  291. data/lib/arachni/element/dom/capabilities/locatable.rb +1 -1
  292. data/lib/arachni/element/dom/capabilities/mutable.rb +7 -3
  293. data/lib/arachni/element/dom/capabilities/submittable.rb +51 -22
  294. data/lib/arachni/element/dom.rb +1 -1
  295. data/lib/arachni/element/form/capabilities/auditable.rb +1 -1
  296. data/lib/arachni/element/form/capabilities/mutable.rb +16 -11
  297. data/lib/arachni/element/form/capabilities/submittable.rb +1 -1
  298. data/lib/arachni/element/form/capabilities/with_dom.rb +1 -1
  299. data/lib/arachni/element/form/dom.rb +1 -1
  300. data/lib/arachni/element/form.rb +21 -32
  301. data/lib/arachni/element/generic_dom.rb +1 -1
  302. data/lib/arachni/element/header/capabilities/inputtable.rb +1 -1
  303. data/lib/arachni/element/header/capabilities/mutable.rb +1 -1
  304. data/lib/arachni/element/header.rb +3 -1
  305. data/lib/arachni/element/json/capabilities/inputtable.rb +1 -1
  306. data/lib/arachni/element/json/capabilities/mutable.rb +1 -1
  307. data/lib/arachni/element/json.rb +4 -8
  308. data/lib/arachni/element/link/capabilities/auditable.rb +1 -1
  309. data/lib/arachni/element/link/capabilities/submittable.rb +1 -1
  310. data/lib/arachni/element/link/capabilities/with_dom.rb +1 -1
  311. data/lib/arachni/element/link/dom/capabilities/submittable.rb +1 -1
  312. data/lib/arachni/element/link/dom.rb +1 -1
  313. data/lib/arachni/element/link.rb +11 -30
  314. data/lib/arachni/element/link_template/capabilities/auditable.rb +1 -1
  315. data/lib/arachni/element/link_template/capabilities/inputtable.rb +1 -1
  316. data/lib/arachni/element/link_template/capabilities/with_dom.rb +1 -1
  317. data/lib/arachni/element/link_template/dom/capabilities/submittable.rb +1 -1
  318. data/lib/arachni/element/link_template/dom.rb +2 -2
  319. data/lib/arachni/element/link_template.rb +10 -19
  320. data/lib/arachni/element/nested_cookie/capabilities/submittable.rb +35 -0
  321. data/lib/arachni/element/nested_cookie.rb +370 -0
  322. data/lib/arachni/element/path.rb +1 -1
  323. data/lib/arachni/element/server.rb +11 -11
  324. data/lib/arachni/element/ui_form/dom.rb +1 -1
  325. data/lib/arachni/element/ui_form.rb +5 -6
  326. data/lib/arachni/element/ui_input/dom.rb +1 -1
  327. data/lib/arachni/element/ui_input.rb +4 -6
  328. data/lib/arachni/element/xml/capabilities/inputtable.rb +1 -1
  329. data/lib/arachni/element/xml/capabilities/mutable.rb +1 -1
  330. data/lib/arachni/element/xml.rb +3 -7
  331. data/lib/arachni/element_filter.rb +1 -1
  332. data/lib/arachni/error.rb +1 -1
  333. data/lib/arachni/ethon/easy.rb +1 -1
  334. data/lib/arachni/framework/parts/audit.rb +6 -1
  335. data/lib/arachni/framework/parts/browser.rb +14 -14
  336. data/lib/arachni/framework/parts/check.rb +1 -1
  337. data/lib/arachni/framework/parts/data.rb +1 -1
  338. data/lib/arachni/framework/parts/platform.rb +1 -1
  339. data/lib/arachni/framework/parts/plugin.rb +1 -1
  340. data/lib/arachni/framework/parts/report.rb +3 -3
  341. data/lib/arachni/framework/parts/scope.rb +1 -1
  342. data/lib/arachni/framework/parts/state.rb +1 -1
  343. data/lib/arachni/framework.rb +1 -1
  344. data/lib/arachni/http/client/dynamic_404_handler.rb +74 -16
  345. data/lib/arachni/http/client.rb +38 -11
  346. data/lib/arachni/http/cookie_jar.rb +13 -8
  347. data/lib/arachni/http/headers.rb +11 -5
  348. data/lib/arachni/http/message/scope.rb +1 -1
  349. data/lib/arachni/http/message.rb +10 -9
  350. data/lib/arachni/http/proxy_server/connection.rb +110 -82
  351. data/lib/arachni/http/proxy_server/ssl-interceptor-cacert.pem +18 -32
  352. data/lib/arachni/http/proxy_server/ssl-interceptor-cakey.pem +28 -49
  353. data/lib/arachni/http/proxy_server/ssl_interceptor.rb +8 -6
  354. data/lib/arachni/http/proxy_server/tunnel.rb +4 -4
  355. data/lib/arachni/http/proxy_server.rb +44 -11
  356. data/lib/arachni/http/request/scope.rb +1 -1
  357. data/lib/arachni/http/request.rb +239 -41
  358. data/lib/arachni/http/response/scope.rb +1 -1
  359. data/lib/arachni/http/response.rb +73 -10
  360. data/lib/arachni/http.rb +1 -1
  361. data/lib/arachni/issue/severity/base.rb +1 -1
  362. data/lib/arachni/issue/severity.rb +1 -1
  363. data/lib/arachni/issue.rb +42 -14
  364. data/lib/arachni/option_group.rb +1 -1
  365. data/lib/arachni/option_groups/audit.rb +11 -2
  366. data/lib/arachni/option_groups/browser_cluster.rb +32 -4
  367. data/lib/arachni/option_groups/datastore.rb +1 -1
  368. data/lib/arachni/option_groups/dispatcher.rb +1 -1
  369. data/lib/arachni/option_groups/http.rb +39 -10
  370. data/lib/arachni/option_groups/input.rb +1 -1
  371. data/lib/arachni/option_groups/output.rb +1 -1
  372. data/lib/arachni/option_groups/paths.rb +12 -1
  373. data/lib/arachni/option_groups/rpc.rb +1 -1
  374. data/lib/arachni/option_groups/scope.rb +58 -4
  375. data/lib/arachni/option_groups/session.rb +1 -1
  376. data/lib/arachni/option_groups/snapshot.rb +1 -1
  377. data/lib/arachni/option_groups.rb +1 -1
  378. data/lib/arachni/options.rb +23 -4
  379. data/lib/arachni/page/dom/transition.rb +5 -2
  380. data/lib/arachni/page/dom.rb +46 -54
  381. data/lib/arachni/page/scope.rb +1 -1
  382. data/lib/arachni/page.rb +10 -8
  383. data/lib/arachni/parser/document.rb +34 -0
  384. data/lib/arachni/parser/extractors/base.rb +48 -0
  385. data/lib/arachni/parser/nodes/base.rb +22 -0
  386. data/lib/arachni/parser/nodes/comment.rb +32 -0
  387. data/lib/arachni/parser/nodes/element/with_attributes/attributes.rb +31 -0
  388. data/lib/arachni/parser/nodes/element/with_attributes.rb +35 -0
  389. data/lib/arachni/parser/nodes/element.rb +48 -0
  390. data/lib/arachni/parser/nodes/text.rb +32 -0
  391. data/lib/arachni/parser/nodes/with_value.rb +29 -0
  392. data/lib/arachni/parser/sax.rb +76 -0
  393. data/lib/arachni/parser/with_children/search.rb +92 -0
  394. data/lib/arachni/parser/with_children.rb +35 -0
  395. data/lib/arachni/parser.rb +181 -78
  396. data/lib/arachni/platform/fingerprinter.rb +1 -1
  397. data/lib/arachni/platform/list.rb +1 -1
  398. data/lib/arachni/platform/manager.rb +2 -2
  399. data/lib/arachni/platform.rb +1 -1
  400. data/lib/arachni/plugin/base.rb +2 -2
  401. data/lib/arachni/plugin/formatter.rb +1 -1
  402. data/lib/arachni/plugin/manager.rb +8 -5
  403. data/lib/arachni/plugin.rb +1 -1
  404. data/lib/arachni/processes/dispatchers.rb +1 -1
  405. data/lib/arachni/processes/executables/base.rb +2 -1
  406. data/lib/arachni/processes/executables/browser.rb +0 -2
  407. data/lib/arachni/processes/helpers/dispatchers.rb +1 -1
  408. data/lib/arachni/processes/helpers/instances.rb +1 -1
  409. data/lib/arachni/processes/helpers/processes.rb +1 -1
  410. data/lib/arachni/processes/helpers.rb +1 -1
  411. data/lib/arachni/processes/instances.rb +1 -1
  412. data/lib/arachni/processes/manager.rb +18 -9
  413. data/lib/arachni/processes.rb +1 -1
  414. data/lib/arachni/report.rb +8 -1
  415. data/lib/arachni/reporter/base.rb +1 -1
  416. data/lib/arachni/reporter/formatter_manager.rb +1 -1
  417. data/lib/arachni/reporter/manager.rb +1 -1
  418. data/lib/arachni/reporter/options.rb +1 -10
  419. data/lib/arachni/reporter.rb +1 -1
  420. data/lib/arachni/rest/server/instance_helpers.rb +10 -1
  421. data/lib/arachni/rest/server.rb +13 -1
  422. data/lib/arachni/rpc/client/base.rb +1 -1
  423. data/lib/arachni/rpc/client/dispatcher.rb +1 -1
  424. data/lib/arachni/rpc/client/instance/framework.rb +1 -1
  425. data/lib/arachni/rpc/client/instance/service.rb +1 -1
  426. data/lib/arachni/rpc/client/instance.rb +1 -1
  427. data/lib/arachni/rpc/serializer.rb +1 -1
  428. data/lib/arachni/rpc/server/active_options.rb +1 -1
  429. data/lib/arachni/rpc/server/base.rb +1 -1
  430. data/lib/arachni/rpc/server/check/manager.rb +1 -1
  431. data/lib/arachni/rpc/server/dispatcher/node.rb +1 -1
  432. data/lib/arachni/rpc/server/dispatcher/service.rb +1 -1
  433. data/lib/arachni/rpc/server/dispatcher.rb +1 -1
  434. data/lib/arachni/rpc/server/framework/distributor.rb +1 -1
  435. data/lib/arachni/rpc/server/framework/master.rb +1 -1
  436. data/lib/arachni/rpc/server/framework/multi_instance.rb +1 -1
  437. data/lib/arachni/rpc/server/framework/slave.rb +1 -1
  438. data/lib/arachni/rpc/server/framework.rb +1 -1
  439. data/lib/arachni/rpc/server/instance.rb +1 -1
  440. data/lib/arachni/rpc/server/output.rb +1 -1
  441. data/lib/arachni/rpc/server/plugin/manager.rb +1 -1
  442. data/lib/arachni/ruby/array.rb +1 -1
  443. data/lib/arachni/ruby/hash.rb +1 -1
  444. data/lib/arachni/ruby/object.rb +1 -1
  445. data/lib/arachni/ruby/set.rb +1 -1
  446. data/lib/arachni/ruby/string.rb +9 -5
  447. data/lib/arachni/ruby/webrick/cookie.rb +1 -1
  448. data/lib/arachni/ruby/webrick/httprequest.rb +1 -1
  449. data/lib/arachni/ruby/webrick.rb +1 -1
  450. data/lib/arachni/ruby.rb +1 -1
  451. data/lib/arachni/scope.rb +1 -1
  452. data/lib/arachni/selenium/webdriver/element.rb +4 -4
  453. data/lib/arachni/selenium/webdriver/remote/typhoeus.rb +59 -0
  454. data/lib/arachni/session.rb +32 -13
  455. data/lib/arachni/snapshot.rb +2 -2
  456. data/lib/arachni/state/audit.rb +1 -1
  457. data/lib/arachni/state/element_filter.rb +1 -1
  458. data/lib/arachni/state/framework/rpc.rb +1 -1
  459. data/lib/arachni/state/framework.rb +1 -1
  460. data/lib/arachni/state/http.rb +2 -2
  461. data/lib/arachni/state/options.rb +1 -1
  462. data/lib/arachni/state/plugins.rb +1 -1
  463. data/lib/arachni/state.rb +1 -1
  464. data/lib/arachni/support/buffer/autoflush.rb +1 -1
  465. data/lib/arachni/support/buffer/base.rb +1 -1
  466. data/lib/arachni/support/buffer.rb +1 -1
  467. data/lib/arachni/support/cache/base.rb +1 -1
  468. data/lib/arachni/support/cache/least_cost_replacement.rb +1 -1
  469. data/lib/arachni/support/cache/least_recently_pushed.rb +1 -1
  470. data/lib/arachni/support/cache/least_recently_used.rb +1 -1
  471. data/lib/arachni/support/cache/preference.rb +1 -1
  472. data/lib/arachni/support/cache/random_replacement.rb +1 -1
  473. data/lib/arachni/support/cache.rb +1 -1
  474. data/lib/arachni/support/crypto/rsa_aes_cbc.rb +1 -1
  475. data/lib/arachni/support/crypto.rb +1 -1
  476. data/lib/arachni/support/database/base.rb +16 -10
  477. data/lib/arachni/support/database/hash.rb +1 -1
  478. data/lib/arachni/support/database/queue.rb +1 -1
  479. data/lib/arachni/support/database.rb +1 -1
  480. data/lib/arachni/support/glob.rb +1 -1
  481. data/lib/arachni/support/lookup/base.rb +1 -1
  482. data/lib/arachni/support/lookup/hash_set.rb +1 -1
  483. data/lib/arachni/support/lookup/moolb.rb +1 -1
  484. data/lib/arachni/support/lookup.rb +1 -1
  485. data/lib/arachni/support/mixins/observable.rb +1 -1
  486. data/lib/arachni/support/mixins/terminal.rb +1 -1
  487. data/lib/arachni/support/mixins.rb +1 -1
  488. data/lib/arachni/support/profiler.rb +52 -13
  489. data/lib/arachni/support/signature.rb +18 -6
  490. data/lib/arachni/support.rb +1 -1
  491. data/lib/arachni/trainer.rb +55 -39
  492. data/lib/arachni/ui/foo/output.rb +1 -1
  493. data/lib/arachni/uri/scope.rb +15 -13
  494. data/lib/arachni/uri.rb +129 -103
  495. data/lib/arachni/utilities.rb +10 -10
  496. data/lib/arachni/version.rb +1 -1
  497. data/lib/arachni.rb +1 -7
  498. data/lib/version +1 -1
  499. data/spec/arachni/browser/element_locator_spec.rb +42 -18
  500. data/spec/arachni/browser/javascript/dom_monitor_spec.rb +264 -109
  501. data/spec/arachni/browser/javascript/polyfills_spec.rb +0 -15
  502. data/spec/arachni/browser/javascript/proxy_spec.rb +0 -10
  503. data/spec/arachni/browser/javascript/taint_tracer_spec.rb +43 -118
  504. data/spec/arachni/browser/javascript_spec.rb +95 -60
  505. data/spec/arachni/browser_cluster/job_spec.rb +23 -8
  506. data/spec/arachni/browser_cluster/jobs/dom_exploration_spec.rb +6 -1
  507. data/spec/arachni/browser_cluster/worker_spec.rb +29 -87
  508. data/spec/arachni/browser_cluster_spec.rb +124 -43
  509. data/spec/arachni/browser_spec.rb +463 -421
  510. data/spec/arachni/check/auditor_spec.rb +162 -198
  511. data/spec/arachni/data/framework/rpc_spec.rb +1 -1
  512. data/spec/arachni/data/framework_spec.rb +1 -1
  513. data/spec/arachni/element/capabilities/analyzable/signature_spec.rb +46 -3
  514. data/spec/arachni/element/cookie/dom_spec.rb +1 -1
  515. data/spec/arachni/element/cookie_spec.rb +159 -64
  516. data/spec/arachni/element/form/dom_spec.rb +1 -1
  517. data/spec/arachni/element/form_spec.rb +101 -54
  518. data/spec/arachni/element/header_spec.rb +3 -1
  519. data/spec/arachni/element/json_spec.rb +2 -0
  520. data/spec/arachni/element/link/dom_spec.rb +2 -2
  521. data/spec/arachni/element/link_spec.rb +46 -15
  522. data/spec/arachni/element/link_template/dom_spec.rb +1 -1
  523. data/spec/arachni/element/link_template_spec.rb +36 -12
  524. data/spec/arachni/element/nested_cookie_spec.rb +687 -0
  525. data/spec/arachni/element/server_spec.rb +22 -5
  526. data/spec/arachni/element/ui_form/dom_spec.rb +1 -1
  527. data/spec/arachni/element/ui_form_spec.rb +2 -2
  528. data/spec/arachni/element/ui_input/dom_spec.rb +1 -1
  529. data/spec/arachni/element/ui_input_spec.rb +1 -1
  530. data/spec/arachni/element/xml_spec.rb +5 -3
  531. data/spec/arachni/framework/parts/audit_spec.rb +2 -14
  532. data/spec/arachni/framework/parts/data_spec.rb +0 -6
  533. data/spec/arachni/http/client/dynamic_404_handlers_spec.rb +126 -0
  534. data/spec/arachni/http/client_spec.rb +96 -36
  535. data/spec/arachni/http/cookie_jar_spec.rb +2 -2
  536. data/spec/arachni/http/headers_spec.rb +59 -12
  537. data/spec/arachni/http/proxy_server_spec.rb +58 -25
  538. data/spec/arachni/http/request_spec.rb +382 -35
  539. data/spec/arachni/http/response_spec.rb +135 -7
  540. data/spec/arachni/issue_spec.rb +21 -2
  541. data/spec/arachni/option_groups/browser_cluster_spec.rb +17 -0
  542. data/spec/arachni/option_groups/http_spec.rb +21 -6
  543. data/spec/arachni/option_groups/paths_spec.rb +23 -1
  544. data/spec/arachni/option_groups/scope_spec.rb +27 -7
  545. data/spec/arachni/options_spec.rb +8 -1
  546. data/spec/arachni/page/dom_spec.rb +20 -6
  547. data/spec/arachni/page_spec.rb +8 -7
  548. data/spec/arachni/parser/document_spec.rb +49 -0
  549. data/spec/arachni/parser/nodes/comment_spec.rb +24 -0
  550. data/spec/arachni/parser/nodes/element/with_attributes/attributes_spec.rb +40 -0
  551. data/spec/arachni/parser/nodes/element/with_attributes_spec.rb +50 -0
  552. data/spec/arachni/parser/nodes/element_spec.rb +18 -0
  553. data/spec/arachni/parser/nodes/text_spec.rb +24 -0
  554. data/spec/arachni/parser/sax_spec.rb +88 -0
  555. data/spec/arachni/parser/with_children/search_spec.rb +146 -0
  556. data/spec/arachni/parser/with_children_spec.rb +37 -0
  557. data/spec/arachni/parser_spec.rb +211 -27
  558. data/spec/arachni/platform/list_spec.rb +1 -2
  559. data/spec/arachni/report_spec.rb +9 -2
  560. data/spec/arachni/reporter/options_spec.rb +0 -14
  561. data/spec/arachni/rest/server_spec.rb +91 -8
  562. data/spec/arachni/rpc/server/active_options_spec.rb +1 -1
  563. data/spec/arachni/rpc/server/framework/distributor_spec.rb +6 -6
  564. data/spec/arachni/ruby/string_spec.rb +6 -0
  565. data/spec/arachni/session_spec.rb +69 -8
  566. data/spec/arachni/snapshot_spec.rb +1 -1
  567. data/spec/arachni/state/framework_spec.rb +2 -2
  568. data/spec/arachni/support/signature_spec.rb +58 -0
  569. data/spec/arachni/trainer_spec.rb +102 -21
  570. data/spec/arachni/uri_spec.rb +11 -8
  571. data/spec/arachni/utilities_spec.rb +3 -3
  572. data/spec/components/checks/active/code_injection_spec.rb +12 -7
  573. data/spec/components/checks/active/code_injection_timing_spec.rb +4 -3
  574. data/spec/components/checks/active/csrf_spec.rb +1 -21
  575. data/spec/components/checks/active/file_inclusion_spec.rb +15 -10
  576. data/spec/components/checks/active/ldap_injection_spec.rb +5 -4
  577. data/spec/components/checks/active/no_sql_injection_differential_spec.rb +1 -1
  578. data/spec/components/checks/active/no_sql_injection_spec.rb +5 -4
  579. data/spec/components/checks/active/os_cmd_injection_spec.rb +6 -4
  580. data/spec/components/checks/active/os_cmd_injection_timing_spec.rb +4 -3
  581. data/spec/components/checks/active/path_traversal_spec.rb +18 -15
  582. data/spec/components/checks/active/response_splitting_spec.rb +5 -4
  583. data/spec/components/checks/active/rfi_spec.rb +9 -8
  584. data/spec/components/checks/active/source_code_disclosure_spec.rb +33 -10
  585. data/spec/components/checks/active/sql_injection_differential_spec.rb +1 -1
  586. data/spec/components/checks/active/sql_injection_spec.rb +61 -35
  587. data/spec/components/checks/active/sql_injection_timing_spec.rb +11 -8
  588. data/spec/components/checks/active/unvalidated_redirect_spec.rb +9 -8
  589. data/spec/components/checks/active/xpath_injection_spec.rb +5 -4
  590. data/spec/components/checks/active/xss_dom_script_context_spec.rb +6 -10
  591. data/spec/components/checks/active/xss_dom_spec.rb +2 -2
  592. data/spec/components/checks/active/xss_event_spec.rb +11 -3
  593. data/spec/components/checks/active/xss_script_context_spec.rb +8 -7
  594. data/spec/components/checks/active/xss_spec.rb +7 -6
  595. data/spec/components/checks/active/xss_tag_spec.rb +11 -3
  596. data/spec/components/checks/passive/backup_directories_spec.rb +3 -1
  597. data/spec/components/checks/passive/backup_files_spec.rb +4 -1
  598. data/spec/components/checks/passive/grep/insecure_cookies_spec.rb +2 -2
  599. data/spec/components/checks/passive/grep/x_frame_options_spec.rb +6 -0
  600. data/spec/components/path_extractors/comments_spec.rb +3 -1
  601. data/spec/components/path_extractors/data_url_spec.rb +6 -2
  602. data/spec/components/path_extractors/links_spec.rb +1 -1
  603. data/spec/components/plugins/autologin_spec.rb +2 -2
  604. data/spec/components/plugins/webhook_notify_spec.rb +69 -0
  605. data/spec/spec_helper.rb +2 -1
  606. data/spec/support/factories/http/response.rb +1 -1
  607. data/spec/support/factories/issue.rb +1 -2
  608. data/spec/support/factories/page/dom.rb +6 -0
  609. data/spec/support/factories/scan_report.rb +1 -0
  610. data/spec/support/factories/vector.rb +7 -3
  611. data/spec/support/fixtures/check_with_invalid_platforms/with_invalid_platforms.rb +1 -1
  612. data/spec/support/fixtures/checks/test.rb +4 -4
  613. data/spec/support/fixtures/checks/test2.rb +1 -1
  614. data/spec/support/fixtures/checks/test3.rb +1 -1
  615. data/spec/support/fixtures/cookies.txt +2 -2
  616. data/spec/support/fixtures/executables/node.rb +2 -3
  617. data/spec/support/fixtures/fingerprinters/test.rb +1 -1
  618. data/spec/support/fixtures/nested_cookies.txt +11 -0
  619. data/spec/support/fixtures/plugins/bad.rb +1 -1
  620. data/spec/support/fixtures/plugins/defaults/default.rb +1 -1
  621. data/spec/support/fixtures/plugins/distributable.rb +1 -1
  622. data/spec/support/fixtures/plugins/loop.rb +1 -1
  623. data/spec/support/fixtures/plugins/suspendable.rb +1 -1
  624. data/spec/support/fixtures/plugins/wait.rb +1 -1
  625. data/spec/support/fixtures/plugins/with_options.rb +1 -1
  626. data/spec/support/fixtures/plugins_with_priorities/p0.rb +1 -1
  627. data/spec/support/fixtures/plugins_with_priorities/p00.rb +1 -1
  628. data/spec/support/fixtures/plugins_with_priorities/p1.rb +1 -1
  629. data/spec/support/fixtures/plugins_with_priorities/p2.rb +1 -1
  630. data/spec/support/fixtures/plugins_with_priorities/p22.rb +1 -1
  631. data/spec/support/fixtures/plugins_with_priorities/p222.rb +1 -1
  632. data/spec/support/fixtures/plugins_with_priorities/p_nil.rb +1 -1
  633. data/spec/support/fixtures/plugins_with_priorities/p_nil2.rb +1 -1
  634. data/spec/support/fixtures/report.afr +0 -0
  635. data/spec/support/fixtures/reporters/base_spec/plugin_formatters/with_formatters/foobar.rb +1 -1
  636. data/spec/support/fixtures/reporters/base_spec/with_formatters.rb +1 -1
  637. data/spec/support/fixtures/reporters/base_spec/with_outfile.rb +1 -1
  638. data/spec/support/fixtures/reporters/base_spec/without_outfile.rb +1 -1
  639. data/spec/support/fixtures/reporters/manager_spec/afr.rb +1 -1
  640. data/spec/support/fixtures/reporters/manager_spec/error.rb +1 -1
  641. data/spec/support/fixtures/reporters/manager_spec/foo.rb +1 -1
  642. data/spec/support/fixtures/run_check/body.rb +1 -1
  643. data/spec/support/fixtures/run_check/cookies.rb +1 -1
  644. data/spec/support/fixtures/run_check/empty.rb +1 -1
  645. data/spec/support/fixtures/run_check/flch.rb +1 -1
  646. data/spec/support/fixtures/run_check/forms.rb +1 -1
  647. data/spec/support/fixtures/run_check/headers.rb +1 -1
  648. data/spec/support/fixtures/run_check/links.rb +1 -1
  649. data/spec/support/fixtures/run_check/nil.rb +1 -1
  650. data/spec/support/fixtures/run_check/path.rb +1 -1
  651. data/spec/support/fixtures/run_check/server.rb +1 -1
  652. data/spec/support/fixtures/signature_check/signature.rb +1 -1
  653. data/spec/support/fixtures/wait_check/wait.rb +1 -1
  654. data/spec/support/helpers/browser_cluster/jobs/taint_tracer.rb +0 -3
  655. data/spec/support/helpers/framework.rb +1 -1
  656. data/spec/support/helpers/misc.rb +1 -1
  657. data/spec/support/helpers/paths.rb +1 -1
  658. data/spec/support/helpers/requires.rb +1 -1
  659. data/spec/support/helpers/resets.rb +1 -1
  660. data/spec/support/helpers/web_server.rb +1 -1
  661. data/spec/support/lib/factory.rb +1 -1
  662. data/spec/support/lib/web_server_client.rb +1 -1
  663. data/spec/support/lib/web_server_dispatcher.rb +1 -1
  664. data/spec/support/lib/web_server_manager.rb +4 -2
  665. data/spec/support/servers/arachni/browser/javascript/dom_monitor.rb +48 -0
  666. data/spec/support/servers/arachni/browser/javascript/taint_tracer.rb +15 -3
  667. data/spec/support/servers/arachni/browser.rb +275 -4
  668. data/spec/support/servers/arachni/check/auditor.rb +9 -0
  669. data/spec/support/servers/arachni/element/cookie.rb +34 -0
  670. data/spec/support/servers/arachni/element/form/form_dom.rb +1 -0
  671. data/spec/support/servers/arachni/element/form.rb +36 -2
  672. data/spec/support/servers/arachni/element/header.rb +36 -1
  673. data/spec/support/servers/arachni/element/json.rb +33 -0
  674. data/spec/support/servers/arachni/element/link.rb +33 -1
  675. data/spec/support/servers/arachni/element/link_template.rb +37 -5
  676. data/spec/support/servers/arachni/element/nested_cookie.rb +84 -0
  677. data/spec/support/servers/arachni/element/xml.rb +33 -0
  678. data/spec/support/servers/arachni/http/client/dynamic_404_handler.rb +36 -0
  679. data/spec/support/servers/arachni/http/client/dynamic_404_handler_redirect_1.rb +18 -0
  680. data/spec/support/servers/arachni/http/client/dynamic_404_handler_redirect_2.rb +11 -0
  681. data/spec/support/servers/arachni/http/client.rb +43 -4
  682. data/spec/support/servers/arachni/http/proxy_server.rb +12 -0
  683. data/spec/support/servers/arachni/parser.rb +6 -0
  684. data/spec/support/servers/arachni/session.rb +24 -1
  685. data/spec/support/servers/checks/active/code_injection.rb +18 -0
  686. data/spec/support/servers/checks/active/code_injection_timing.rb +18 -0
  687. data/spec/support/servers/checks/active/csrf.rb +0 -76
  688. data/spec/support/servers/checks/active/file_inclusion.rb +19 -1
  689. data/spec/support/servers/checks/active/ldap_injection.rb +18 -0
  690. data/spec/support/servers/checks/active/no_sql_injection.rb +27 -0
  691. data/spec/support/servers/checks/active/no_sql_injection_differential.rb +19 -0
  692. data/spec/support/servers/checks/active/os_cmd_injection.rb +29 -0
  693. data/spec/support/servers/checks/active/os_cmd_injection_timing.rb +18 -1
  694. data/spec/support/servers/checks/active/path_traversal.rb +30 -3
  695. data/spec/support/servers/checks/active/response_splitting.rb +30 -1
  696. data/spec/support/servers/checks/active/rfi.rb +30 -2
  697. data/spec/support/servers/checks/active/session_fixation.rb +1 -3
  698. data/spec/support/servers/checks/active/source_code_disclosure.rb +16 -0
  699. data/spec/support/servers/checks/active/sql_injection/java +2 -0
  700. data/spec/support/servers/checks/active/sql_injection.rb +27 -0
  701. data/spec/support/servers/checks/active/sql_injection_differential.rb +19 -0
  702. data/spec/support/servers/checks/active/sql_injection_timing.rb +19 -1
  703. data/spec/support/servers/checks/active/unvalidated_redirect.rb +121 -1
  704. data/spec/support/servers/checks/active/xpath_injection.rb +27 -0
  705. data/spec/support/servers/checks/active/xss.rb +40 -0
  706. data/spec/support/servers/checks/active/xss_event.rb +23 -2
  707. data/spec/support/servers/checks/active/xss_script_context.rb +18 -0
  708. data/spec/support/servers/checks/active/xss_tag.rb +40 -0
  709. data/spec/support/servers/checks/passive/backup_files.rb +20 -1
  710. data/spec/support/servers/checks/passive/grep/cookie_set_for_parent_domain.rb +3 -5
  711. data/spec/support/servers/checks/passive/grep/insecure_cookies_https.rb +9 -0
  712. data/spec/support/servers/checks/passive/grep/x_frame_options.rb +5 -0
  713. data/spec/support/servers/plugins/autologin.rb +17 -1
  714. data/spec/support/servers/plugins/webhook_notify.rb +9 -0
  715. data/spec/support/shared/check.rb +1 -0
  716. data/spec/support/shared/element/capabilities/auditable/buffered.rb +791 -0
  717. data/spec/support/shared/element/capabilities/auditable/line_buffered.rb +797 -0
  718. data/spec/support/shared/element/capabilities/auditable.rb +28 -34
  719. data/spec/support/shared/element/capabilities/inputtable.rb +26 -0
  720. data/spec/support/shared/element/capabilities/with_node.rb +2 -2
  721. data/spec/support/shared/element/dom/submittable.rb +10 -10
  722. data/spec/support/shared/path_extractor.rb +17 -5
  723. data/ui/cli/framework/option_parser.rb +78 -13
  724. data/ui/cli/framework.rb +29 -8
  725. data/ui/cli/option_parser.rb +1 -1
  726. data/ui/cli/output.rb +10 -3
  727. data/ui/cli/reporter/option_parser.rb +1 -1
  728. data/ui/cli/reporter.rb +1 -1
  729. data/ui/cli/reproduce/option_parser.rb +90 -0
  730. data/ui/cli/reproduce.rb +228 -0
  731. data/ui/cli/rest/server/option_parser.rb +1 -1
  732. data/ui/cli/rest/server.rb +1 -1
  733. data/ui/cli/restored_framework/option_parser.rb +1 -1
  734. data/ui/cli/restored_framework.rb +1 -1
  735. data/ui/cli/rpc/client/dispatcher_monitor/option_parser.rb +1 -1
  736. data/ui/cli/rpc/client/dispatcher_monitor.rb +9 -11
  737. data/ui/cli/rpc/client/instance.rb +7 -4
  738. data/ui/cli/rpc/client/local/option_parser.rb +1 -1
  739. data/ui/cli/rpc/client/local.rb +1 -1
  740. data/ui/cli/rpc/client/remote/option_parser.rb +1 -1
  741. data/ui/cli/rpc/client/remote.rb +1 -1
  742. data/ui/cli/rpc/server/dispatcher/option_parser.rb +1 -1
  743. data/ui/cli/rpc/server/dispatcher.rb +1 -1
  744. data/ui/cli/utilities.rb +1 -1
  745. metadata +178 -79
  746. data/ACKNOWLEDGMENTS.md +0 -21
  747. data/AUTHORS.md +0 -3
  748. data/CONTRIBUTORS.md +0 -22
@@ -1,5 +1,5 @@
1
1
  =begin
2
- Copyright 2010-2016 Tasos Laskos <tasos.laskos@arachni-scanner.com>
2
+ Copyright 2010-2022 Ecsypno <http://www.ecsypno.com>
3
3
 
4
4
  This file is part of the Arachni Framework project and is subject to
5
5
  redistribution and commercial restrictions. Please see the Arachni Framework
@@ -7,15 +7,16 @@
7
7
  =end
8
8
 
9
9
  require 'childprocess'
10
- require 'watir-webdriver'
10
+ require 'watir'
11
11
  require_relative 'selenium/webdriver/element'
12
+ require_relative 'selenium/webdriver/remote/typhoeus'
12
13
  require_relative 'processes/manager'
13
14
  require_relative 'browser/element_locator'
14
15
  require_relative 'browser/javascript'
15
16
 
16
17
  module Arachni
17
18
 
18
- # @note Depends on PhantomJS 1.9.2.
19
+ # @note Depends on PhantomJS 2.1.1.
19
20
  #
20
21
  # Real browser driver providing DOM/JS/AJAX support.
21
22
  #
@@ -57,18 +58,20 @@ class Browser
57
58
  # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
58
59
  class Load < Error
59
60
  end
61
+
62
+ # @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
63
+ class MissingExecutable < Error
64
+ end
65
+
60
66
  end
61
67
 
62
68
  # How much time to wait for the PhantomJS process to spawn before respawning.
63
- PHANTOMJS_SPAWN_TIMEOUT = 4
69
+ BROWSER_SPAWN_TIMEOUT = 60
64
70
 
65
71
  # How much time to wait for a targeted HTML element to appear on the page
66
72
  # after the page is loaded.
67
73
  ELEMENT_APPEARANCE_TIMEOUT = 5
68
74
 
69
- # Let the browser take as long as it needs to complete an operation.
70
- WATIR_COM_TIMEOUT = 3600 # 1 hour.
71
-
72
75
  ASSET_EXTENSIONS = Set.new(%w( css js jpg jpeg png gif json ))
73
76
 
74
77
  INPUT_EVENTS = Set.new([
@@ -79,10 +82,8 @@ class Browser
79
82
  ])
80
83
 
81
84
  ASSET_EXTRACTORS = [
82
- /<\s*link.*?href=['"](.*?)['"].*?>/im,
83
- /<\s*script.*?src=['"](.*?)['"].*?>/im,
84
- /<\s*img.*?src=['"](.*?)['"].*?>/im,
85
- /<\s*input.*?src=['"](.*?)['"].*?>/im,
85
+ /<\s*link.*?href=\s*['"]?(.*?)?['"]?[\s>]/im,
86
+ /src\s*=\s*['"]?(.*?)?['"]?[\s>]/i,
86
87
  ]
87
88
 
88
89
  # @return [Array<Page::DOM::Transition>]
@@ -92,6 +93,8 @@ class Browser
92
93
  # Preloaded resources, by URL.
93
94
  attr_reader :preloads
94
95
 
96
+ attr_reader :proxy
97
+
95
98
  # @return [Watir::Browser]
96
99
  # Watir driver interface.
97
100
  attr_reader :watir
@@ -121,14 +124,6 @@ class Browser
121
124
  # @see #skip_state?
122
125
  attr_reader :skip_states
123
126
 
124
- # @return [Integer]
125
- # PID of the lifeline process managing the browser process.
126
- attr_reader :lifeline_pid
127
-
128
- # @return [Integer]
129
- # PID of the browser process.
130
- attr_reader :browser_pid
131
-
132
127
  attr_reader :last_url
133
128
 
134
129
  class <<self
@@ -136,13 +131,26 @@ class Browser
136
131
  # @return [Bool]
137
132
  # `true` if a supported browser is in the OS PATH, `false` otherwise.
138
133
  def has_executable?
139
- !!executable
134
+ executable
135
+ true
136
+ rescue Error::MissingExecutable
137
+ false
140
138
  end
141
139
 
142
140
  # @return [String]
143
141
  # Path to the PhantomJS executable.
144
142
  def executable
145
- Selenium::WebDriver::PhantomJS.path
143
+ @path ||= begin
144
+ path = Selenium::WebDriver::Platform.find_binary('chromedriver')
145
+ raise Error::MissingExecutable, 'chromedriver could not be found in PATH.' unless path
146
+ Selenium::WebDriver::Platform.assert_executable path
147
+ path
148
+ end
149
+ end
150
+
151
+ # @ private
152
+ def reset
153
+ @path = nil
146
154
  end
147
155
 
148
156
  def asset_domains
@@ -179,25 +187,9 @@ class Browser
179
187
  @width = options[:width] || 1600
180
188
  @height = options[:height] || 1200
181
189
 
182
- @proxy = HTTP::ProxyServer.new(
183
- concurrency: @options[:concurrency],
184
- address: '127.0.0.1',
185
- request_handler: proc do |request, response|
186
- exception_jail { request_handler( request, response ) }
187
- end,
188
- response_handler: proc do |request, response|
189
- exception_jail { response_handler( request, response ) }
190
- end
191
- )
192
-
193
190
  @options[:store_pages] = true if !@options.include?( :store_pages )
194
191
 
195
- @proxy.start_async
196
-
197
- @watir = ::Watir::Browser.new( selenium )
198
-
199
- # User-controlled response cache, by URL.
200
- @cache = Support::Cache::LeastRecentlyUsed.new( 200 )
192
+ start_webdriver
201
193
 
202
194
  # User-controlled preloaded responses, by URL.
203
195
  @preloads = {}
@@ -216,7 +208,6 @@ class Browser
216
208
  # Captures HTTP::Response objects per URL for open windows.
217
209
  @window_responses = {}
218
210
 
219
- @elements_with_events = {}
220
211
 
221
212
  # Keeps track of resources which should be skipped -- like already fired
222
213
  # events and clicked links etc.
@@ -234,9 +225,7 @@ class Browser
234
225
 
235
226
  def clear_buffers
236
227
  synchronize do
237
- @elements_with_events.clear
238
228
  @preloads.clear
239
- @cache.clear
240
229
  @captured_pages.clear
241
230
  @page_snapshots.clear
242
231
  @page_snapshots_with_sinks.clear
@@ -262,9 +251,11 @@ class Browser
262
251
 
263
252
  case resource
264
253
  when String
254
+ @transitions = []
265
255
  goto resource, options
266
256
 
267
257
  when HTTP::Response
258
+ @transitions = []
268
259
  goto preload( resource ), options
269
260
 
270
261
  when Page
@@ -288,8 +279,7 @@ class Browser
288
279
  self
289
280
  end
290
281
 
291
- # @note The preloaded resource will be removed once used, for a persistent
292
- # cache use {#cache}.
282
+ # @note The preloaded resource will be removed once used.
293
283
  #
294
284
  # @param [HTTP::Response, Page] resource
295
285
  # Preloads a resource to be instantly available by URL via {#load}.
@@ -303,7 +293,7 @@ class Browser
303
293
 
304
294
  else
305
295
  fail Error::Load,
306
- "Can't load resource of type #{resource.class}."
296
+ "Can't preload resource of type #{resource.class}."
307
297
  end
308
298
 
309
299
  save_response( response ) if !response.url.include?( request_token )
@@ -312,28 +302,6 @@ class Browser
312
302
  response.url
313
303
  end
314
304
 
315
- # @param [HTTP::Response, Page] resource
316
- # Cache a resource in order to be instantly available by URL via {#load}.
317
- def cache( resource = nil )
318
- return @cache if !resource
319
-
320
- response = case resource
321
- when HTTP::Response
322
- resource
323
-
324
- when Page
325
- resource.response
326
-
327
- else
328
- fail Error::Load,
329
- "Can't load resource of type #{resource.class}."
330
- end
331
-
332
- save_response response
333
- @cache[response.url] = response
334
- response.url
335
- end
336
-
337
305
  # @param [String] url
338
306
  # Loads the given URL in the browser.
339
307
  # @param [Hash] options
@@ -382,8 +350,8 @@ class Browser
382
350
  ).until { @selenium.find_element( :css, css ) }
383
351
 
384
352
  print_info "#{css.inspect} appeared for: #{url}"
385
- rescue Selenium::WebDriver::Error::TimeOutError
386
- print_bad "#{css.inspect} did not appeared for: #{url}"
353
+ rescue Selenium::WebDriver::Error::TimeoutError
354
+ print_bad "#{css.inspect} did not appear for: #{url}"
387
355
  end
388
356
 
389
357
  end
@@ -406,25 +374,13 @@ class Browser
406
374
  end
407
375
 
408
376
  def wait_till_ready
409
- print_debug_level_2 'Waiting for custom JS...'
410
377
  @javascript.wait_till_ready
411
- print_debug_level_2 '...done.'
412
378
 
413
- wait_for_timers
414
-
415
- wait_for_pending_requests
416
- end
417
-
418
- def shutdown
419
- begin
420
- watir.close if alive?
421
- # Bucnh of dirrent errors can be raised here, Selenium, HTTP client,
422
- # don't try to catch them by type because we'll probably miss some.
423
- rescue
379
+ if Options.browser_cluster.wait_for_timers?
380
+ wait_for_timers
424
381
  end
425
382
 
426
- kill_process
427
- @proxy.shutdown rescue Reactor::Error::NotRunning
383
+ wait_for_pending_requests
428
384
  end
429
385
 
430
386
  # @return [String]
@@ -436,7 +392,7 @@ class Browser
436
392
  # @return [String]
437
393
  # Current URL, as provided by the browser.
438
394
  def dom_url
439
- javascript.run( 'return document.URL;' )
395
+ @selenium.current_url
440
396
  end
441
397
 
442
398
  # Explores the browser's DOM tree and captures page snapshots for each
@@ -471,10 +427,10 @@ class Browser
471
427
  # @yield [ElementLocator,Array<Symbol>]
472
428
  # Element locator along with the element's applicable events along with
473
429
  # their handlers and attributes.
474
- def each_element_with_events
430
+ def each_element_with_events( whitelist = [])
475
431
  current_url = self.url
476
432
 
477
- javascript.dom_elements_with_events.each do |element|
433
+ javascript.each_dom_element_with_events whitelist do |element|
478
434
  tag_name = element['tag_name']
479
435
  attributes = element['attributes']
480
436
  events = element['events']
@@ -517,99 +473,6 @@ class Browser
517
473
  self
518
474
  end
519
475
 
520
- # @note The results will be cached, if direct access in necessary
521
- # use {#each_element_with_events}.
522
- #
523
- # @return [Hash<ElementLocator,Array<Symbol>>]
524
- # Element locator along with the element's applicable events along with
525
- # their handlers and attributes.
526
- def elements_with_events( clear_cache = false )
527
- current_url = self.url
528
-
529
- @elements_with_events.clear if clear_cache
530
-
531
- if @elements_with_events.include?( current_url )
532
- return @elements_with_events[current_url]
533
- end
534
-
535
- @elements_with_events.clear
536
- @elements_with_events[current_url] ||= {}
537
-
538
- each_element_with_events do |locator, events|
539
- @elements_with_events[current_url][locator] = events
540
- end
541
-
542
- @elements_with_events[current_url]
543
- end
544
-
545
- # @return [String]
546
- # Snapshot ID used to determine whether or not a page snapshot has already
547
- # been seen.
548
- #
549
- # Uses both elements and their DOM events and possible audit workload to
550
- # determine the ID, as page snapshots should be retained both when further
551
- # browser analysis can be performed and when new element audit workload
552
- # (but possibly without any DOM relevance) is available.
553
- def snapshot_id
554
- current_url = self.url
555
-
556
- id = Set.new
557
- javascript.dom_elements_with_events.each do |element|
558
- tag_name = element['tag_name']
559
- attributes = element['attributes']
560
- events = element['events']
561
- element_id = attributes['id'].to_s
562
-
563
- case tag_name
564
- when 'a'
565
- href = attributes['href'].to_s
566
- element_id << href
567
-
568
- if !href.empty?
569
- if href.downcase.start_with?( 'javascript:' )
570
- (events[:click] ||= []) << href
571
- else
572
- absolute = to_absolute( href, current_url )
573
- next if skip_path?( absolute )
574
-
575
- (events[:click] ||= []) << href
576
- end
577
- else
578
- (events[:click] ||= []) << current_url
579
- end
580
-
581
- when 'input', 'textarea', 'select'
582
- (events[:input] ||= []) << tag_name.to_sym
583
- element_id << attributes['name'].to_s
584
-
585
- when 'form'
586
- action = attributes['action'].to_s
587
- element_id << "#{action}#{attributes['name']}"
588
-
589
- if !action.empty?
590
- if action.downcase.start_with?( 'javascript:' )
591
- (events[:submit] ||= []) << action
592
- else
593
- absolute = to_absolute( action, current_url )
594
- if !skip_path?( absolute )
595
- (events[:submit] ||= []) << absolute
596
- end
597
- end
598
- else
599
- (events[:submit] ||= []) << current_url
600
- end
601
- end
602
-
603
- next if events.empty?
604
-
605
- id << "#{tag_name}:#{element_id}:#{events.keys.sort}".persistent_hash
606
- end
607
-
608
- id << [:cookies, cookies.map(&:name).sort].to_s.persistent_hash
609
-
610
- id.to_a.sort.map(&:to_s).join(':')
611
- end
612
-
613
476
  # Triggers all events on all elements (**once**) and captures
614
477
  # {#page_snapshots page snapshots}.
615
478
  #
@@ -617,14 +480,25 @@ class Browser
617
480
  # `self`
618
481
  def trigger_events
619
482
  dom = self.state
483
+ return self if !dom
620
484
 
621
- elements_with_events( true ).each do |locator, events|
622
- state = "#{locator.tag_name}:#{locator.attributes}:#{events.keys.sort}"
485
+ url = normalize_url( dom.url )
486
+
487
+ count = 1
488
+ each_element_with_events do |locator, events|
489
+ state = "#{url}:#{locator.tag_name}:#{locator.attributes}:#{events.keys.sort}"
623
490
  next if skip_state?( state )
624
491
  skip_state state
625
492
 
626
493
  events.each do |name, _|
494
+ if Options.scope.dom_event_limit_reached?( count )
495
+ print_debug "DOM event limit reached for: #{dom.url}"
496
+ next
497
+ end
498
+
627
499
  distribute_event( dom, locator, name.to_sym )
500
+
501
+ count += 1
628
502
  end
629
503
  end
630
504
 
@@ -654,13 +528,10 @@ class Browser
654
528
  # @param [Symbol] event
655
529
  # Event to trigger.
656
530
  def trigger_event( resource, element, event, restore = true )
657
- event = event.to_sym
658
531
  transition = fire_event( element, event )
659
532
 
660
533
  if !transition
661
- print_info "Could not trigger '#{event}' on '#{element}' because" <<
662
- ' the page has changed, capturing a new snapshot.'
663
- capture_snapshot
534
+ print_info "Could not trigger '#{event}' on: #{element}"
664
535
 
665
536
  if restore
666
537
  print_info 'Restoring page.'
@@ -723,21 +594,36 @@ class Browser
723
594
 
724
595
  notify_on_fire_event( element, event )
725
596
 
726
- pre_timeouts = javascript.timeouts
597
+ if Options.browser_cluster.wait_for_timers?
598
+ pre_timeouts = javascript.timeouts
599
+ end
727
600
 
728
601
  begin
729
602
  transition = Page::DOM::Transition.new( locator, event, options ) do
730
603
  force = true
731
604
 
732
- # It's better to use the Watir helpers whenever possible instead
733
- # of firing events manually.
605
+ # It's better to use the helpers whenever possible instead of
606
+ # firing events manually.
734
607
  if tag_name == :form
735
608
  fill_in_form_inputs( element, options[:inputs] )
736
609
 
610
+ if event == :fill
611
+ force = false
612
+ end
613
+
737
614
  if event == :submit
738
615
  force = false
739
616
 
740
- element.submit
617
+ begin
618
+ element.find_elements( :css,
619
+ "input[type='submit'], button[type='submit']"
620
+ ).first.click
621
+ rescue => e
622
+ print_debug "No submit button, will trigger 'submit' event."
623
+ print_debug_exception e
624
+
625
+ element.submit
626
+ end
741
627
  end
742
628
 
743
629
  elsif event == :click
@@ -767,24 +653,31 @@ class Browser
767
653
  wait_for_pending_requests
768
654
  print_debug_level_2 "[done waiting for requests]: #{event} (#{options}) #{locator}"
769
655
 
656
+ # Maybe we switched to a different page, wait until the custom
657
+ # JS env has been put in place.
658
+ javascript.wait_till_ready
659
+ javascript.set_element_ids
660
+
770
661
  update_cookies
771
662
  end
772
663
 
773
664
  print_debug_level_2 "[done in #{transition.time}s]: #{event} (#{options}) #{locator}"
774
665
 
775
- delay = (javascript.timeouts - pre_timeouts).compact.map { |t| t[1].to_i }.max
776
- if delay
777
- print_debug_level_2 "Found new timers with max #{delay}ms."
778
- delay = [Options.http.request_timeout, delay].min / 1000.0
666
+ if Options.browser_cluster.wait_for_timers?
667
+ delay = (javascript.timeouts - pre_timeouts).compact.map { |t| t[1].to_i }.max
668
+ if delay
669
+ print_debug_level_2 "Found new timers with max #{delay}ms."
670
+ delay = [Options.http.request_timeout, delay].min / 1000.0
779
671
 
780
- print_debug_level_2 "Will wait for #{delay}s."
781
- sleep delay
672
+ print_debug_level_2 "Will wait for #{delay}s."
673
+ sleep delay
674
+ end
782
675
  end
783
676
 
784
677
  transition
785
678
  rescue Selenium::WebDriver::Error::WebDriverError => e
786
679
 
787
- print_debug "Error when triggering event for: #{url}"
680
+ print_debug "Error when triggering event for: #{dom_url}"
788
681
  print_debug "-- '#{event}' on: #{opening_tag} -- #{locator.css}"
789
682
  print_debug
790
683
  print_debug_exception e
@@ -800,14 +693,19 @@ class Browser
800
693
  #
801
694
  # @param [Browser::ElementLocator] locator
802
695
  # @param [Symbol,String] event
803
- # @param [Bool] ret
804
- # Return JS result?
805
696
  # @param [Numeric] wait
806
697
  # Amount of time to wait (in seconds) after triggering the event.
807
- def fire_event_js( locator, event, ret: false, wait: 0.1 )
698
+ def fire_event_js( locator, event, wait: 0.1 )
808
699
  r = javascript.run <<-EOJS
809
700
  var element = document.querySelector( #{locator.css.inspect} );
810
- var event = document.createEvent( "Events" );
701
+
702
+ // Could not be found.
703
+ if( !element ) return false;
704
+
705
+ // Invisible.
706
+ if( element.offsetWidth <= 0 && element.offsetHeight <= 0 ) return false;
707
+
708
+ var event = document.createEvent( "Events" );
811
709
 
812
710
  event.initEvent( "#{event}", true, true );
813
711
 
@@ -819,10 +717,13 @@ class Browser
819
717
  event.keyCode = 0;
820
718
  event.charCode = 'a';
821
719
 
822
- #{'return' if ret} element.dispatchEvent( event );
720
+ element.dispatchEvent( event );
721
+
722
+ return true;
823
723
  EOJS
824
724
 
825
- sleep wait
725
+ sleep( wait ) if r
726
+
826
727
  r
827
728
  end
828
729
 
@@ -918,6 +819,7 @@ class Browser
918
819
  page = r.to_page
919
820
  page.body = source
920
821
  page.dom.url = d_url
822
+ page.dom.cookies = self.cookies
921
823
  page.dom.digest = @javascript.dom_digest
922
824
  page.dom.execution_flow_sinks = @javascript.execution_flow_sinks
923
825
  page.dom.data_flow_sinks = data_flow_sinks[@javascript.taint] || []
@@ -956,8 +858,22 @@ class Browser
956
858
 
957
859
  if Options.audit.cookie_doms?
958
860
  page.cookies.each do |cookie|
959
- next if data_flow_sinks.include?( cookie.name ) ||
960
- data_flow_sinks.include?( cookie.value )
861
+ if (sinks = data_flow_sinks[cookie.name] ||
862
+ data_flow_sinks[cookie.value])
863
+
864
+ # Don't be satisfied with just a taint match, make sure
865
+ # the full value is identical.
866
+ #
867
+ # For example, if a cookie has '1' as a name or value
868
+ # that's too generic and can match irrelevant data.
869
+ #
870
+ # The current approach isn't perfect of course, but it's
871
+ # the best we can do.
872
+ next if sinks.find do |sink|
873
+ sink.tainted_value == cookie.name ||
874
+ sink.tainted_value == cookie.value
875
+ end
876
+ end
961
877
 
962
878
  cookie.skip_dom = true
963
879
  end
@@ -987,7 +903,7 @@ class Browser
987
903
  # bother trying anything else.
988
904
  next if !response
989
905
 
990
- unique_id = self.snapshot_id
906
+ unique_id = javascript.dom_event_digest
991
907
  already_seen = skip_state?( unique_id )
992
908
  skip_state unique_id
993
909
 
@@ -1009,6 +925,13 @@ class Browser
1009
925
 
1010
926
  next if already_seen
1011
927
 
928
+ # Safegued against pages which generate an inf number of DOM
929
+ # states regardless of UI interactions.
930
+ transition_id ="#{page.dom.url}:#{page.dom.playable_transitions.map(&:hash)}"
931
+ transition_id_seen = skip_state?( transition_id )
932
+ skip_state transition_id
933
+ next if transition_id_seen
934
+
1012
935
  notify_on_new_page( page )
1013
936
 
1014
937
  if store_pages?
@@ -1103,7 +1026,6 @@ class Browser
1103
1026
  end
1104
1027
 
1105
1028
  def load_delay
1106
- #(intervals + timeouts).map { |t| t[1] }.max
1107
1029
  @javascript.timeouts.compact.map { |t| t[1].to_i }.max
1108
1030
  end
1109
1031
 
@@ -1111,9 +1033,10 @@ class Browser
1111
1033
  delay = load_delay
1112
1034
  return if !delay
1113
1035
 
1114
- print_debug_level_2 'Waiting for timers...'
1036
+ effective_delay = [Options.http.request_timeout, delay].min / 1000.0
1037
+ print_debug_level_2 "Waiting for max timer #{effective_delay}s (original was #{delay}ms)..."
1115
1038
 
1116
- sleep [Options.http.request_timeout, delay].min / 1000.0
1039
+ sleep effective_delay
1117
1040
 
1118
1041
  print_debug_level_2 '...done.'
1119
1042
  end
@@ -1125,7 +1048,7 @@ class Browser
1125
1048
  def response
1126
1049
  u = dom_url
1127
1050
 
1128
- if dom_url == 'about:blank'
1051
+ if u == 'about:blank'
1129
1052
  print_debug 'Blank page.'
1130
1053
  return
1131
1054
  end
@@ -1142,7 +1065,17 @@ class Browser
1142
1065
  if r
1143
1066
  print_debug "Origin server timed-out when requesting: #{u}"
1144
1067
  else
1145
- print_debug "Response never arrived: #{u}"
1068
+ print_debug "Response never arrived for: #{u}"
1069
+
1070
+ print_debug 'Available responses are:'
1071
+ @window_responses.each do |k, _|
1072
+ print_debug "-- #{k}"
1073
+ end
1074
+
1075
+ print_debug 'Tried:'
1076
+ print_debug "-- #{u}"
1077
+ print_debug "-- #{normalize_url( u )}"
1078
+ print_debug "-- #{normalize_watir_url( u )}"
1146
1079
  end
1147
1080
 
1148
1081
  nil
@@ -1153,28 +1086,82 @@ class Browser
1153
1086
  def selenium
1154
1087
  return @selenium if @selenium
1155
1088
 
1156
- client = Selenium::WebDriver::Remote::Http::Default.new
1157
- client.timeout = WATIR_COM_TIMEOUT
1089
+ start_proxy
1158
1090
 
1159
- @selenium = Selenium::WebDriver.for(
1160
- :remote,
1091
+ proxy_uri = URI( @proxy.url )
1161
1092
 
1162
- # We need to spawn our own PhantomJS process because Selenium's
1163
- # way sometimes gives us zombies.
1164
- url: spawn_browser,
1165
- desired_capabilities: capabilities,
1166
- http_client: client
1093
+ dir = "#{Options.paths.tmpdir}/Arachni_Chrome_#{self.object_id}/"
1094
+ FileUtils.rm_rf dir
1095
+ FileUtils.mkdir dir
1096
+ at_exit do
1097
+ FileUtils.rm_rf dir
1098
+ end
1099
+
1100
+ @selenium = Selenium::WebDriver.for(
1101
+ :chrome,
1102
+ capabilities: Selenium::WebDriver::Chrome::Options.new(
1103
+ emulation: {
1104
+ userAgent: Arachni::Options.http.user_agent
1105
+ },
1106
+ args: [
1107
+ '--allow-running-insecure-content',
1108
+ '--disable-web-security',
1109
+ '--reduce-security-for-testing',
1110
+ '--ignore-certificate-errors',
1111
+ '--disable-plugins',
1112
+ "--user-data-dir=#{dir}",
1113
+ "--proxy-server=#{proxy_uri.host}:#{proxy_uri.port}",
1114
+ "--buid=46464646",
1115
+ "--headless"
1116
+ ]
1117
+ ),
1118
+ http_client: Selenium::WebDriver::Remote::Http::Typhoeus.new
1167
1119
  )
1120
+
1121
+ rescue Selenium::WebDriver::Error::WebDriverError => e
1122
+ print_error "Please ensure that chromedriver and Chrome are the same" <<
1123
+ " version and in your PATH."
1124
+ raise e
1168
1125
  end
1169
1126
 
1170
- def alive?
1171
- @lifeline_pid && Processes::Manager.alive?( @lifeline_pid )
1127
+ def shutdown
1128
+ print_debug 'Shutting down...'
1129
+
1130
+ if @selenium
1131
+ @selenium.close
1132
+
1133
+ print_debug_level_2 'Quiting Selenium...'
1134
+ # So freaking hacky but @selenium.quit freezes if we don't detach first.
1135
+ @selenium.instance_eval do
1136
+ bridge.quit
1137
+
1138
+ @service.instance_eval do
1139
+ Process.detach @process.pid
1140
+ @process.stop
1141
+ end
1142
+ end
1143
+
1144
+ @selenium.quit rescue Errno::ECONNREFUSED
1145
+ # @selenium.quit rescue Selenium::WebDriver::Error::WebDriverError
1146
+ print_debug_level_2 '...done.'
1147
+
1148
+ end
1149
+
1150
+ if @proxy
1151
+ print_debug_level_2 'Shutting down proxy...'
1152
+ @proxy.shutdown rescue Reactor::Error::NotRunning
1153
+ print_debug_level_2 '...done.'
1154
+ end
1155
+
1156
+ @proxy = nil
1157
+ @watir = nil
1158
+ @selenium = nil
1159
+
1160
+ print_debug '...shutdown complete.'
1172
1161
  end
1173
1162
 
1174
1163
  def inspect
1175
1164
  s = "#<#{self.class} "
1176
- s << "pid=#{@lifeline_pid} "
1177
- s << "browser_pid=#{@browser_pid} "
1178
1165
  s << "last-url=#{@last_url.inspect} "
1179
1166
  s << "transitions=#{@transitions.size}"
1180
1167
  s << '>'
@@ -1263,117 +1250,30 @@ class Browser
1263
1250
  Options.input.value_for_name( name )
1264
1251
  end
1265
1252
 
1266
- def spawn_browser
1267
- if !spawn_phantomjs
1268
- fail Error::Spawn, 'Could not start the browser process.'
1269
- end
1270
-
1271
- @browser_url
1272
- end
1273
-
1274
- def spawn_phantomjs
1275
- return @browser_url if @browser_url
1276
-
1277
- print_debug 'Spawning PhantomJS...'
1278
-
1279
- ChildProcess.posix_spawn = true
1280
-
1281
- port = nil
1282
- output = ''
1283
-
1284
- 10.times do |i|
1285
- # Clear output of previous attempt.
1286
- output = ''
1287
- done = false
1288
- port = Utilities.available_port
1253
+ def start_proxy
1254
+ print_debug 'Booting up...'
1289
1255
 
1290
- print_debug "Attempt ##{i}, chose port number #{port}"
1291
-
1292
- begin
1293
- with_timeout 10 do
1294
- print_debug "Spawning process: #{self.class.executable}"
1295
-
1296
- r, w = IO.pipe
1297
- ri, @kill_process = IO.pipe
1298
-
1299
- @lifeline_pid = Processes::Manager.spawn(
1300
- :browser,
1301
- executable: self.class.executable,
1302
- without_arachni: true,
1303
- fork: false,
1304
- new_pgroup: true,
1305
- stdin: ri,
1306
- stdout: w,
1307
- stderr: w,
1308
- port: port,
1309
- proxy_url: @proxy.url
1310
- )
1311
-
1312
- w.close
1313
- ri.close
1314
-
1315
- print_debug 'Process spawned, waiting for it to boot-up...'
1316
-
1317
- # Wait for PhantomJS to initialize.
1318
- while !output.include?( 'running on port' )
1319
- begin
1320
- output << r.readpartial( 8192 )
1321
- # EOF or something, take a breather before retrying.
1322
- rescue
1323
- sleep 0.05
1324
- end
1325
- end
1326
-
1327
- @browser_pid = output.scan( /^PID: (\d+)/ ).flatten.first.to_i
1328
-
1329
- print_debug 'Boot-up complete.'
1330
- done = true
1331
- end
1332
- rescue Timeout::Error
1333
- print_debug 'Spawn timed-out.'
1334
- end
1335
-
1336
- if !output.empty?
1337
- print_debug output
1338
- end
1339
-
1340
- if done
1341
- print_debug 'PhantomJS is ready.'
1342
- break
1256
+ print_debug_level_2 'Starting proxy...'
1257
+ @proxy = HTTP::ProxyServer.new(
1258
+ concurrency: @options[:concurrency],
1259
+ address: '127.0.0.1',
1260
+ request_handler: proc do |request, response|
1261
+ exception_jail { request_handler( request, response ) }
1262
+ end,
1263
+ response_handler: proc do |request, response|
1264
+ exception_jail { response_handler( request, response ) }
1343
1265
  end
1344
-
1345
- print_debug 'Killing process.'
1346
- kill_process
1347
- end
1348
-
1349
- # Something went really bad, the browser couldn't be spawned even
1350
- # after our valiant efforts.
1351
- #
1352
- # Bail out for now and count on the BrowserCluster to retry to boot
1353
- # another process ass needed.
1354
- if !@lifeline_pid
1355
- log_error 'Could not spawn browser process.'
1356
- log_error output
1357
- return
1358
- end
1359
-
1360
- @browser_url = "http://127.0.0.1:#{port}"
1266
+ )
1267
+ @proxy.start_async
1268
+ print_debug_level_2 "... started proxy at: #{@proxy.url}"
1361
1269
  end
1362
1270
 
1363
- def kill_process
1364
- if @kill_process
1365
- begin
1366
- @kill_process.close
1367
- rescue
1368
- end
1369
- end
1271
+ def start_webdriver
1272
+ print_debug_level_2 'Starting WebDriver...'
1273
+ @watir = ::Watir::Browser.new( selenium )
1274
+ print_debug_level_2 "... started WebDriver."
1370
1275
 
1371
- @kill_process = nil
1372
- @watir = nil
1373
- @selenium = nil
1374
- @lifeline_pid = nil
1375
- @browser_pid = nil
1376
- @browser_url = nil
1276
+ print_debug '...boot-up completed.'
1377
1277
  end
1378
1278
 
1379
1279
  def store_pages?
@@ -1397,12 +1297,33 @@ class Browser
1397
1297
  end
1398
1298
 
1399
1299
  def wait_for_pending_requests
1400
- print_debug_level_2 "Waiting for #{@proxy.pending_requests} requests to complete..."
1300
+ sleep 0.2
1301
+
1302
+ t = Time.now
1303
+ last_connections = []
1304
+ while @proxy.has_pending_requests?
1305
+ connections = @proxy.active_connections
1306
+
1307
+ if last_connections != connections
1308
+ print_debug_level_2 "Waiting for #{@proxy.pending_requests} requests to complete:"
1309
+ connections.each do |connection|
1310
+ if connection.request
1311
+ print_debug_level_2 " * #{connection.request.url}"
1312
+ else
1313
+ print_debug_level_2 ' * Still reading request data.'
1314
+ end
1315
+ end
1316
+ end
1317
+ last_connections = connections
1401
1318
 
1402
- sleep 0.1
1403
- sleep 0.01 while @proxy.has_pending_requests?
1319
+ sleep 0.1
1404
1320
 
1405
- print_debug_level_2 '...done.'
1321
+ # If the browser sends incomplete data the connection will remain
1322
+ # open indefinitely.
1323
+ next if Time.now - t < Options.browser_cluster.job_timeout
1324
+ connections.each(&:close)
1325
+ break
1326
+ end
1406
1327
  end
1407
1328
 
1408
1329
  def load_cookies( url, cookies = {} )
@@ -1437,16 +1358,17 @@ class Browser
1437
1358
  end
1438
1359
 
1439
1360
  return if set_cookies.empty? &&
1440
- Arachni::Options::browser_cluster.local_storage.empty?
1361
+ Options.browser_cluster.local_storage.empty? &&
1362
+ Options.browser_cluster.session_storage.empty?
1441
1363
 
1442
1364
  set_cookie = set_cookies.values.map(&:to_set_cookie)
1443
1365
  print_debug_level_2 "Setting cookies: #{set_cookie}"
1444
1366
 
1445
1367
  body = ''
1446
- if Arachni::Options::browser_cluster.local_storage.any?
1447
- body = <<EOJS
1368
+ if Options.browser_cluster.local_storage.any?
1369
+ body << <<EOJS
1448
1370
  <script>
1449
- var data = #{Arachni::Options::browser_cluster.local_storage.to_json};
1371
+ var data = #{Options.browser_cluster.local_storage.to_json};
1450
1372
 
1451
1373
  for( prop in data ) {
1452
1374
  localStorage.setItem( prop, data[prop] );
@@ -1455,6 +1377,18 @@ class Browser
1455
1377
  EOJS
1456
1378
  end
1457
1379
 
1380
+ if Options.browser_cluster.session_storage.any?
1381
+ body << <<EOJS
1382
+ <script>
1383
+ var data = #{Options.browser_cluster.session_storage.to_json};
1384
+
1385
+ for( prop in data ) {
1386
+ sessionStorage.setItem( prop, data[prop] );
1387
+ }
1388
+ </script>
1389
+ EOJS
1390
+ end
1391
+
1458
1392
  @selenium.navigate.to preload( HTTP::Response.new(
1459
1393
  code: 200,
1460
1394
  url: "#{url}/set-cookies-#{request_token}",
@@ -1490,33 +1424,6 @@ EOJS
1490
1424
  @selenium.manage.window.resize_to( @width, @height )
1491
1425
  end
1492
1426
 
1493
- # # Firefox driver, only used for debugging.
1494
- # def firefox
1495
- # profile = Selenium::WebDriver::Firefox::Profile.new
1496
- # profile.proxy = Selenium::WebDriver::Proxy.new http: @proxy.address,
1497
- # ssl: @proxy.address
1498
- # [:firefox, profile: profile]
1499
- # end
1500
- #
1501
- # # Chrome driver, only used for debugging.
1502
- # def chrome
1503
- # [ :chrome, switches: [ "--proxy-server=#{@proxy.address}" ] ]
1504
- # end
1505
-
1506
- def capabilities
1507
- Selenium::WebDriver::Remote::Capabilities.phantomjs(
1508
- # Selenium tries to be helpful by including screenshots for errors
1509
- # in the JSON response. That's not gonna fly in this use case as
1510
- # parsing lots of massive JSON responses at the same time will
1511
- # have a significant impact on performance.
1512
- takes_screenshot: false,
1513
- 'phantomjs.page.settings.userAgent' => Options.http.user_agent,
1514
- 'phantomjs.page.customHeaders.X-Arachni-Browser-Auth' => auth_token,
1515
- 'phantomjs.page.settings.resourceTimeout' => Options.http.request_timeout,
1516
- 'phantomjs.page.settings.loadImages' => !Options.browser_cluster.ignore_images
1517
- )
1518
- end
1519
-
1520
1427
  def flush_request_transitions
1521
1428
  @request_transitions.dup
1522
1429
  ensure
@@ -1534,8 +1441,8 @@ EOJS
1534
1441
  def request_handler( request, response )
1535
1442
  request.performer = self
1536
1443
 
1537
- return if request.headers['X-Arachni-Browser-Auth'] != auth_token
1538
- request.headers.delete 'X-Arachni-Browser-Auth'
1444
+ # return if request.headers['X-Arachni-Browser-Auth'] != auth_token
1445
+ # request.headers.delete 'X-Arachni-Browser-Auth'
1539
1446
 
1540
1447
  print_debug_level_2 "Request: #{request.url}"
1541
1448
 
@@ -1544,19 +1451,19 @@ EOJS
1544
1451
  #
1545
1452
  # Still, it's a nice feature to have when requesting assets or anything
1546
1453
  # else.
1547
- if request.url == @last_url
1454
+ if !@last_url || request.url == @last_url
1548
1455
  request.headers.delete 'If-None-Match'
1549
1456
  request.headers.delete 'If-Modified-Since'
1550
1457
  end
1551
1458
 
1552
1459
  if @javascript.serve( request, response )
1553
- print_debug_level_2 "Serving local JS."
1460
+ print_debug_level_2 'Serving local JS.'
1554
1461
  return
1555
1462
  end
1556
1463
 
1557
1464
  if !request.url.include?( request_token )
1558
1465
  if ignore_request?( request )
1559
- print_debug_level_2 "Out of scope, ignoring."
1466
+ print_debug_level_2 'Out of scope, ignoring.'
1560
1467
  return
1561
1468
  end
1562
1469
 
@@ -1570,8 +1477,8 @@ EOJS
1570
1477
  end
1571
1478
 
1572
1479
  # Signal the proxy to not actually perform the request if we have a
1573
- # preloaded or cached response for it.
1574
- if from_preloads( request, response ) || from_cache( request, response )
1480
+ # preloaded response for it.
1481
+ if from_preloads( request, response )
1575
1482
  print_debug_level_2 'Resource has been preloaded.'
1576
1483
 
1577
1484
  # There may be taints or custom JS code that need to be updated.
@@ -1597,6 +1504,22 @@ EOJS
1597
1504
  def response_handler( request, response )
1598
1505
  return if request.url.include?( request_token )
1599
1506
 
1507
+ # Prevent PhantomJS from caching the root page, we need to have an
1508
+ # associated response.
1509
+ #
1510
+ # Also don't cache when we don't have a @last_url because this could
1511
+ # be driven directly from Selenium/Watir via a plugin and caching it
1512
+ # can ruin the scan.
1513
+ if !@last_url || @last_url == response.url
1514
+ response.headers.delete 'Cache-control'
1515
+ response.headers.delete 'Etag'
1516
+ response.headers.delete 'Date'
1517
+ response.headers.delete 'Last-Modified'
1518
+ end
1519
+
1520
+ # Allow our own scripts to run.
1521
+ response.headers.delete 'Content-Security-Policy'
1522
+
1600
1523
  print_debug_level_2 "Got response: #{response.url}"
1601
1524
 
1602
1525
  @request_transitions.each do |transition|
@@ -1700,6 +1623,10 @@ EOJS
1700
1623
 
1701
1624
  def whitelist_asset_domains( response )
1702
1625
  synchronize do
1626
+ @whitelist_asset_domains ||= Support::LookUp::HashSet.new
1627
+ return if @whitelist_asset_domains.include? response.body
1628
+ @whitelist_asset_domains << response.body
1629
+
1703
1630
  ASSET_EXTRACTORS.each do |regexp|
1704
1631
  response.body.scan( regexp ).flatten.compact.each do |url|
1705
1632
  next if !(domain = self.class.add_asset_domain( url ))
@@ -1748,6 +1675,16 @@ EOJS
1748
1675
  )
1749
1676
 
1750
1677
  when :post
1678
+ inputs = request.parsed_url.query_parameters
1679
+ if inputs.any?
1680
+ elements[:forms] << Form.new(
1681
+ url: @last_url,
1682
+ action: request.url,
1683
+ method: :get,
1684
+ inputs: inputs
1685
+ )
1686
+ end
1687
+
1751
1688
  if !found_element && (inputs = request.body_parameters).any?
1752
1689
  elements[:forms] << Form.new(
1753
1690
  url: @last_url,
@@ -1815,16 +1752,6 @@ EOJS
1815
1752
  end
1816
1753
  end
1817
1754
 
1818
- def from_cache( request, response )
1819
- synchronize do
1820
- return if !@cache.include?( request.url )
1821
-
1822
- copy_response_data( @cache[request.url], response )
1823
- response.request = request
1824
- save_response response
1825
- end
1826
- end
1827
-
1828
1755
  def copy_response_data( source, destination )
1829
1756
  [:code, :url, :body, :headers, :ip_address, :return_code,
1830
1757
  :return_message, :headers_string, :total_time, :time].each do |m|
@@ -1861,7 +1788,7 @@ EOJS
1861
1788
  end
1862
1789
 
1863
1790
  def normalize_watir_url( url )
1864
- normalize_url( ::URI.encode( url, ';' ) ).gsub( '%3B', '%253B' )
1791
+ normalize_url( url.gsub( ';', '%3B' ) ).gsub( '%3B', '%253B' )
1865
1792
  end
1866
1793
 
1867
1794
  end