arachni 1.2.1 → 1.3

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 (373) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -0
  3. data/Gemfile +1 -1
  4. data/README.md +16 -5
  5. data/components/checks/active/ldap_injection/errors.txt +1 -0
  6. data/components/checks/active/source_code_disclosure.rb +1 -1
  7. data/components/checks/active/unvalidated_redirect.rb +6 -6
  8. data/components/checks/active/unvalidated_redirect_dom.rb +10 -7
  9. data/components/checks/passive/grep/captcha.rb +14 -5
  10. data/components/checks/passive/grep/form_upload.rb +7 -3
  11. data/components/checks/passive/grep/hsts.rb +3 -3
  12. data/components/checks/passive/grep/html_objects.rb +2 -3
  13. data/components/checks/passive/grep/http_only_cookies.rb +2 -3
  14. data/components/checks/passive/grep/insecure_cookies.rb +1 -1
  15. data/components/checks/passive/grep/password_autocomplete.rb +2 -2
  16. data/components/checks/passive/grep/unencrypted_password_forms.rb +7 -7
  17. data/components/checks/passive/grep/x_frame_options.rb +2 -2
  18. data/components/checks/passive/http_put.rb +2 -3
  19. data/components/path_extractors/comments.rb +3 -3
  20. data/components/path_extractors/scripts.rb +10 -1
  21. data/components/plugins/defaults/autothrottle.rb +27 -18
  22. data/components/plugins/defaults/meta/remedies/discovery.rb +30 -33
  23. data/components/plugins/defaults/meta/remedies/timing_attacks.rb +7 -11
  24. data/components/plugins/login_script.rb +9 -3
  25. data/components/plugins/proxy.rb +4 -3
  26. data/components/reporters/html.rb +11 -14
  27. data/components/reporters/html/default/issue.erb +13 -38
  28. data/components/reporters/html/default/issue/info.erb +1 -1
  29. data/components/reporters/html/default/summary/issues/by_name.erb +3 -3
  30. data/components/reporters/stdout.rb +62 -71
  31. data/components/reporters/xml.rb +26 -40
  32. data/components/reporters/xml/schema.xsd +43 -89
  33. data/lib/arachni/browser.rb +52 -3
  34. data/lib/arachni/browser/javascript.rb +3 -3
  35. data/lib/arachni/browser/javascript/scripts/taint_tracer.js +46 -25
  36. data/lib/arachni/browser_cluster.rb +61 -0
  37. data/lib/arachni/browser_cluster/job.rb +21 -1
  38. data/lib/arachni/browser_cluster/jobs/browser_provider.rb +3 -1
  39. data/lib/arachni/browser_cluster/jobs/resource_exploration.rb +2 -1
  40. data/lib/arachni/browser_cluster/jobs/resource_exploration/event_trigger.rb +2 -1
  41. data/lib/arachni/browser_cluster/jobs/taint_trace.rb +3 -2
  42. data/lib/arachni/browser_cluster/jobs/taint_trace/event_trigger.rb +1 -1
  43. data/lib/arachni/browser_cluster/worker.rb +5 -0
  44. data/lib/arachni/check/auditor.rb +22 -12
  45. data/lib/arachni/data/framework.rb +13 -1
  46. data/lib/arachni/data/issues.rb +9 -25
  47. data/lib/arachni/element/base.rb +9 -3
  48. data/lib/arachni/element/capabilities/analyzable.rb +2 -6
  49. data/lib/arachni/element/capabilities/analyzable/differential.rb +24 -7
  50. data/lib/arachni/element/capabilities/analyzable/{taint.rb → signature.rb} +23 -23
  51. data/lib/arachni/element/capabilities/auditable.rb +0 -6
  52. data/lib/arachni/element/capabilities/dom_only.rb +61 -0
  53. data/lib/arachni/element/capabilities/with_dom.rb +3 -1
  54. data/lib/arachni/element/cookie.rb +35 -5
  55. data/lib/arachni/element/cookie/dom.rb +13 -4
  56. data/lib/arachni/element/{capabilities/auditable/dom.rb → dom.rb} +20 -68
  57. data/lib/arachni/element/dom/capabilities/auditable.rb +29 -0
  58. data/lib/arachni/element/dom/capabilities/inputtable.rb +27 -0
  59. data/lib/arachni/element/dom/capabilities/mutable.rb +21 -0
  60. data/lib/arachni/element/dom/capabilities/submittable.rb +52 -0
  61. data/lib/arachni/element/form.rb +12 -1
  62. data/lib/arachni/element/form/capabilities/mutable.rb +2 -1
  63. data/lib/arachni/element/form/capabilities/with_dom.rb +0 -1
  64. data/lib/arachni/element/form/dom.rb +9 -3
  65. data/lib/arachni/element/header.rb +14 -33
  66. data/lib/arachni/element/header/capabilities/inputtable.rb +29 -0
  67. data/lib/arachni/element/header/capabilities/mutable.rb +51 -0
  68. data/lib/arachni/element/input/dom.rb +71 -0
  69. data/lib/arachni/element/json.rb +2 -0
  70. data/lib/arachni/element/link.rb +3 -0
  71. data/lib/arachni/element/link/capabilities/with_dom.rb +0 -1
  72. data/lib/arachni/element/link/dom.rb +16 -3
  73. data/lib/arachni/element/link/dom/capabilities/submittable.rb +29 -0
  74. data/lib/arachni/element/link_template.rb +3 -5
  75. data/lib/arachni/element/link_template/capabilities/inputtable.rb +5 -0
  76. data/lib/arachni/element/link_template/capabilities/with_dom.rb +0 -1
  77. data/lib/arachni/element/link_template/dom.rb +16 -3
  78. data/lib/arachni/element/link_template/dom/capabilities/submittable.rb +29 -0
  79. data/lib/arachni/element/server.rb +3 -5
  80. data/lib/arachni/element/ui_form.rb +106 -0
  81. data/lib/arachni/element/ui_form/dom.rb +107 -0
  82. data/lib/arachni/element/ui_input.rb +62 -0
  83. data/lib/arachni/element/xml.rb +2 -1
  84. data/lib/arachni/framework.rb +7 -5
  85. data/lib/arachni/framework/parts/audit.rb +0 -1
  86. data/lib/arachni/framework/parts/check.rb +1 -0
  87. data/lib/arachni/framework/parts/data.rb +4 -0
  88. data/lib/arachni/framework/parts/state.rb +0 -2
  89. data/lib/arachni/http/client.rb +17 -6
  90. data/lib/arachni/http/proxy_server.rb +52 -5
  91. data/lib/arachni/http/request.rb +1 -1
  92. data/lib/arachni/issue.rb +34 -179
  93. data/lib/arachni/issue/severity.rb +2 -0
  94. data/lib/arachni/option_groups/audit.rb +22 -2
  95. data/lib/arachni/option_groups/browser_cluster.rb +15 -0
  96. data/lib/arachni/page.rb +3 -2
  97. data/lib/arachni/parser.rb +24 -5
  98. data/lib/arachni/platform/manager.rb +1 -2
  99. data/lib/arachni/rpc/server/framework.rb +3 -4
  100. data/lib/arachni/rpc/server/framework/multi_instance.rb +2 -1
  101. data/lib/arachni/session.rb +1 -1
  102. data/lib/arachni/trainer.rb +4 -7
  103. data/lib/arachni/watir/element.rb +12 -1
  104. data/lib/version +1 -1
  105. data/spec/arachni/browser/element_locator_spec.rb +43 -43
  106. data/spec/arachni/browser/javascript/dom_monitor_spec.rb +44 -44
  107. data/spec/arachni/browser/javascript/proxy/stub_spec.rb +17 -14
  108. data/spec/arachni/browser/javascript/proxy_spec.rb +24 -24
  109. data/spec/arachni/browser/javascript/taint_tracer/frame/called_function_spec.rb +11 -11
  110. data/spec/arachni/browser/javascript/taint_tracer/frame_spec.rb +7 -7
  111. data/spec/arachni/browser/javascript/taint_tracer/sink/data_flow_spec.rb +13 -13
  112. data/spec/arachni/browser/javascript/taint_tracer/sink/execution_flow_spec.rb +7 -7
  113. data/spec/arachni/browser/javascript/taint_tracer_spec.rb +568 -558
  114. data/spec/arachni/browser/javascript_spec.rb +73 -63
  115. data/spec/arachni/browser_cluster/job/result_spec.rb +3 -3
  116. data/spec/arachni/browser_cluster/job_spec.rb +68 -48
  117. data/spec/arachni/browser_cluster/jobs/resource_exploration/event_trigger/result_spec.rb +2 -2
  118. data/spec/arachni/browser_cluster/jobs/resource_exploration/event_trigger_spec.rb +5 -4
  119. data/spec/arachni/browser_cluster/jobs/resource_exploration/result_spec.rb +2 -2
  120. data/spec/arachni/browser_cluster/jobs/resource_exploration_spec.rb +5 -5
  121. data/spec/arachni/browser_cluster/worker_spec.rb +87 -70
  122. data/spec/arachni/browser_cluster_spec.rb +64 -39
  123. data/spec/arachni/browser_spec.rb +692 -527
  124. data/spec/arachni/check/auditor_spec.rb +177 -147
  125. data/spec/arachni/check/base_spec.rb +33 -33
  126. data/spec/arachni/check/manager_spec.rb +15 -15
  127. data/spec/arachni/component/base_spec.rb +8 -8
  128. data/spec/arachni/component/manager_spec.rb +100 -99
  129. data/spec/arachni/component/options/address_spec.rb +3 -3
  130. data/spec/arachni/component/options/base_spec.rb +7 -7
  131. data/spec/arachni/component/options/bool_spec.rb +9 -9
  132. data/spec/arachni/component/options/float_spec.rb +6 -6
  133. data/spec/arachni/component/options/int_spec.rb +5 -5
  134. data/spec/arachni/component/options/multiple_choice_spec.rb +12 -12
  135. data/spec/arachni/component/options/object_spec.rb +2 -2
  136. data/spec/arachni/component/options/path_spec.rb +3 -3
  137. data/spec/arachni/component/options/port_spec.rb +5 -5
  138. data/spec/arachni/component/options/string_spec.rb +3 -3
  139. data/spec/arachni/component/options/url_spec.rb +4 -4
  140. data/spec/arachni/component/utilities_spec.rb +2 -2
  141. data/spec/arachni/data/framework/rpc_spec.rb +10 -9
  142. data/spec/arachni/data/framework_spec.rb +65 -46
  143. data/spec/arachni/data/issues_spec.rb +39 -77
  144. data/spec/arachni/data/plugins_spec.rb +11 -11
  145. data/spec/arachni/data/session_spec.rb +6 -6
  146. data/spec/arachni/data_spec.rb +8 -8
  147. data/spec/arachni/element/body_spec.rb +10 -10
  148. data/spec/arachni/element/capabilities/analyzable/differential_spec.rb +39 -21
  149. data/spec/arachni/element/capabilities/analyzable/{taint_spec.rb → signature_spec.rb} +63 -63
  150. data/spec/arachni/element/capabilities/analyzable/timeout_spec.rb +51 -51
  151. data/spec/arachni/element/capabilities/with_scope/scope_spec.rb +5 -5
  152. data/spec/arachni/element/cookie/dom_spec.rb +37 -18
  153. data/spec/arachni/element/cookie_spec.rb +206 -139
  154. data/spec/arachni/element/form/dom_spec.rb +36 -19
  155. data/spec/arachni/element/form_spec.rb +210 -187
  156. data/spec/arachni/element/generic_dom_spec.rb +14 -14
  157. data/spec/arachni/element/header_spec.rb +35 -17
  158. data/spec/arachni/element/json_spec.rb +53 -31
  159. data/spec/arachni/element/link/dom_spec.rb +46 -28
  160. data/spec/arachni/element/link_spec.rb +58 -40
  161. data/spec/arachni/element/link_template/dom_spec.rb +47 -29
  162. data/spec/arachni/element/link_template_spec.rb +79 -61
  163. data/spec/arachni/element/path_spec.rb +1 -1
  164. data/spec/arachni/element/server_spec.rb +33 -32
  165. data/spec/arachni/element/ui_form/ui_form_dom_spec.rb +164 -0
  166. data/spec/arachni/element/ui_form_spec.rb +242 -0
  167. data/spec/arachni/element/ui_input/dom_spec.rb +157 -0
  168. data/spec/arachni/element/ui_input_spec.rb +136 -0
  169. data/spec/arachni/element/xml_spec.rb +42 -24
  170. data/spec/arachni/element_filter_spec.rb +49 -48
  171. data/spec/arachni/error_spec.rb +3 -3
  172. data/spec/arachni/framework/parts/audit_spec.rb +64 -63
  173. data/spec/arachni/framework/parts/browser_spec.rb +16 -16
  174. data/spec/arachni/framework/parts/check_spec.rb +3 -3
  175. data/spec/arachni/framework/parts/data_spec.rb +48 -48
  176. data/spec/arachni/framework/parts/platform_spec.rb +3 -3
  177. data/spec/arachni/framework/parts/plugin_spec.rb +7 -6
  178. data/spec/arachni/framework/parts/report_spec.rb +7 -7
  179. data/spec/arachni/framework/parts/scope_spec.rb +16 -16
  180. data/spec/arachni/framework/parts/state_spec.rb +68 -69
  181. data/spec/arachni/framework_spec.rb +39 -31
  182. data/spec/arachni/http/client/dynamic_404_handlers_spec.rb +32 -32
  183. data/spec/arachni/http/client_spec.rb +219 -208
  184. data/spec/arachni/http/cookie_jar_spec.rb +72 -72
  185. data/spec/arachni/http/headers_spec.rb +14 -14
  186. data/spec/arachni/http/proxy_server_spec.rb +43 -42
  187. data/spec/arachni/http/request_spec.rb +105 -103
  188. data/spec/arachni/http/response/scope_spec.rb +24 -24
  189. data/spec/arachni/http/response_spec.rb +50 -49
  190. data/spec/arachni/issue/severity_spec.rb +10 -9
  191. data/spec/arachni/issue_spec.rb +71 -369
  192. data/spec/arachni/option_groups/audit_spec.rb +114 -114
  193. data/spec/arachni/option_groups/browser_cluster_spec.rb +20 -3
  194. data/spec/arachni/option_groups/datastore_spec.rb +6 -6
  195. data/spec/arachni/option_groups/dispatcher_spec.rb +19 -19
  196. data/spec/arachni/option_groups/http_spec.rb +11 -11
  197. data/spec/arachni/option_groups/input_spec.rb +31 -27
  198. data/spec/arachni/option_groups/output_spec.rb +2 -2
  199. data/spec/arachni/option_groups/paths_spec.rb +17 -17
  200. data/spec/arachni/option_groups/rpc_spec.rb +2 -2
  201. data/spec/arachni/option_groups/scope_spec.rb +40 -40
  202. data/spec/arachni/option_groups/session_spec.rb +6 -5
  203. data/spec/arachni/option_groups/snapshot_spec.rb +4 -4
  204. data/spec/arachni/options_spec.rb +46 -45
  205. data/spec/arachni/page/dom/transition_spec.rb +74 -72
  206. data/spec/arachni/page/dom_spec.rb +35 -35
  207. data/spec/arachni/page/scope_spec.rb +15 -15
  208. data/spec/arachni/page_spec.rb +217 -217
  209. data/spec/arachni/parser_spec.rb +106 -104
  210. data/spec/arachni/platform/fingerprinter_spec.rb +17 -14
  211. data/spec/arachni/platform/list_spec.rb +33 -33
  212. data/spec/arachni/platform/manager_spec.rb +67 -64
  213. data/spec/arachni/plugin/base_spec.rb +10 -10
  214. data/spec/arachni/plugin/manager_spec.rb +38 -37
  215. data/spec/arachni/report_spec.rb +43 -40
  216. data/spec/arachni/reporter/base_spec.rb +15 -15
  217. data/spec/arachni/reporter/manager_spec.rb +4 -4
  218. data/spec/arachni/reporter/options_spec.rb +6 -6
  219. data/spec/arachni/rpc/client/base_spec.rb +6 -6
  220. data/spec/arachni/rpc/client/dispatcher_spec.rb +2 -2
  221. data/spec/arachni/rpc/client/instance_spec.rb +6 -6
  222. data/spec/arachni/rpc/server/active_options_spec.rb +11 -8
  223. data/spec/arachni/rpc/server/base_spec.rb +5 -5
  224. data/spec/arachni/rpc/server/checks/manager_spec.rb +8 -8
  225. data/spec/arachni/rpc/server/dispatcher/node_spec.rb +37 -37
  226. data/spec/arachni/rpc/server/dispatcher/service_spec.rb +15 -14
  227. data/spec/arachni/rpc/server/dispatcher_spec.rb +36 -35
  228. data/spec/arachni/rpc/server/framework/distributor_spec.rb +36 -36
  229. data/spec/arachni/rpc/server/framework_multi_spec.rb +340 -336
  230. data/spec/arachni/rpc/server/framework_spec.rb +90 -85
  231. data/spec/arachni/rpc/server/instance_spec.rb +126 -107
  232. data/spec/arachni/rpc/server/output_spec.rb +1 -1
  233. data/spec/arachni/rpc/server/plugin/manager_spec.rb +6 -6
  234. data/spec/arachni/ruby/array_spec.rb +42 -42
  235. data/spec/arachni/ruby/hash_spec.rb +20 -18
  236. data/spec/arachni/ruby/io_spec.rb +2 -2
  237. data/spec/arachni/ruby/object_spec.rb +1 -1
  238. data/spec/arachni/ruby/set_spec.rb +3 -3
  239. data/spec/arachni/ruby/string_spec.rb +30 -30
  240. data/spec/arachni/ruby/webrick_spec.rb +2 -2
  241. data/spec/arachni/scope_spec.rb +1 -1
  242. data/spec/arachni/session_spec.rb +67 -64
  243. data/spec/arachni/snapshot_spec.rb +15 -15
  244. data/spec/arachni/state/audit_spec.rb +11 -11
  245. data/spec/arachni/state/element_filter_spec.rb +6 -6
  246. data/spec/arachni/state/framework/rpc_spec.rb +12 -12
  247. data/spec/arachni/state/framework_spec.rb +125 -121
  248. data/spec/arachni/state/http_spec.rb +7 -7
  249. data/spec/arachni/state/options_spec.rb +7 -7
  250. data/spec/arachni/state/plugins_spec.rb +8 -8
  251. data/spec/arachni/state_spec.rb +10 -10
  252. data/spec/arachni/support/buffer/autoflush_spec.rb +16 -16
  253. data/spec/arachni/support/buffer/base_spec.rb +39 -39
  254. data/spec/arachni/support/cache/least_cost_replacement_spec.rb +18 -18
  255. data/spec/arachni/support/cache/least_recently_pushed_spec.rb +24 -24
  256. data/spec/arachni/support/cache/least_recently_used_spec.rb +20 -20
  257. data/spec/arachni/support/cache/preference_spec.rb +4 -4
  258. data/spec/arachni/support/cache/random_replacement_spec.rb +8 -8
  259. data/spec/arachni/support/crypto/rsa_aes_cbc_spec.rb +1 -1
  260. data/spec/arachni/support/database/hash_spec.rb +44 -43
  261. data/spec/arachni/support/database/queue_spec.rb +27 -27
  262. data/spec/arachni/support/lookup/hash_set_spec.rb +8 -8
  263. data/spec/arachni/support/lookup/moolb_spec.rb +3 -3
  264. data/spec/arachni/support/mixins/observable_spec.rb +6 -6
  265. data/spec/arachni/support/signature_spec.rb +19 -19
  266. data/spec/arachni/trainer_spec.rb +39 -39
  267. data/spec/arachni/typhoeus/hydra_spec.rb +2 -2
  268. data/spec/arachni/uri/scope_spec.rb +66 -66
  269. data/spec/arachni/uri_spec.rb +107 -105
  270. data/spec/arachni/utilities_spec.rb +40 -40
  271. data/spec/components/checks/active/csrf_spec.rb +8 -8
  272. data/spec/components/checks/active/no_sql_injection_spec.rb +1 -1
  273. data/spec/components/checks/active/sql_injection_spec.rb +16 -16
  274. data/spec/components/checks/active/trainer_spec.rb +4 -4
  275. data/spec/components/checks/active/unvalidated_redirect_dom_spec.rb +4 -2
  276. data/spec/components/checks/active/xpath_injection_spec.rb +1 -1
  277. data/spec/components/checks/active/xss_dom_script_context_spec.rb +51 -21
  278. data/spec/components/checks/active/xss_dom_spec.rb +46 -24
  279. data/spec/components/checks/passive/allowed_methods_spec.rb +1 -1
  280. data/spec/components/checks/passive/grep/cookie_set_for_parent_domain_spec.rb +1 -1
  281. data/spec/components/checks/passive/grep/hsts_spec.rb +2 -2
  282. data/spec/components/checks/passive/grep/http_only_cookies_spec.rb +1 -1
  283. data/spec/components/checks/passive/grep/insecure_cookies_spec.rb +1 -1
  284. data/spec/components/checks/passive/grep/insecure_cors_policy_spec.rb +2 -2
  285. data/spec/components/checks/passive/grep/password_autocomplete_spec.rb +1 -1
  286. data/spec/components/checks/passive/grep/private_ip_spec.rb +3 -3
  287. data/spec/components/checks/passive/grep/unencrypted_password_forms_spec.rb +1 -1
  288. data/spec/components/checks/passive/grep/x_frame_options_spec.rb +2 -2
  289. data/spec/components/checks/passive/interesting_responses_spec.rb +2 -2
  290. data/spec/components/checks/passive/webdav_spec.rb +1 -1
  291. data/spec/components/checks/passive/xst_spec.rb +1 -1
  292. data/spec/components/fingerprinters/servers/apache_spec.rb +2 -2
  293. data/spec/components/path_extractors/comments_spec.rb +5 -1
  294. data/spec/components/path_extractors/scripts_spec.rb +5 -2
  295. data/spec/components/plugins/autologin_spec.rb +22 -22
  296. data/spec/components/plugins/autothrottle_spec.rb +6 -5
  297. data/spec/components/plugins/content_types_spec.rb +4 -4
  298. data/spec/components/plugins/cookie_collector_spec.rb +5 -5
  299. data/spec/components/plugins/exec_spec.rb +12 -12
  300. data/spec/components/plugins/form_dicattack_spec.rb +3 -3
  301. data/spec/components/plugins/headers_collector_spec.rb +8 -8
  302. data/spec/components/plugins/healthmap_spec.rb +3 -3
  303. data/spec/components/plugins/http_dicattack_spec.rb +3 -3
  304. data/spec/components/plugins/login_script_spec.rb +79 -22
  305. data/spec/components/plugins/meta/remedies/discovery_spec.rb +3 -2
  306. data/spec/components/plugins/meta/remedies/timing_attacks_spec.rb +3 -3
  307. data/spec/components/plugins/meta/uniformity_spec.rb +2 -2
  308. data/spec/components/plugins/restrict_to_dom_state_spec.rb +1 -1
  309. data/spec/components/plugins/script_spec.rb +1 -1
  310. data/spec/components/plugins/uncommon_headers_spec.rb +2 -2
  311. data/spec/components/plugins/vector_collector_spec.rb +2 -2
  312. data/spec/components/plugins/vector_feed_spec.rb +40 -40
  313. data/spec/components/plugins/waf_detector_spec.rb +6 -6
  314. data/spec/components/reporters/json_spec.rb +4 -4
  315. data/spec/components/reporters/marshal_spec.rb +2 -2
  316. data/spec/components/reporters/yaml_spec.rb +3 -2
  317. data/spec/external/wavsep/active/sqli_spec.rb +1 -3
  318. data/spec/spec_helper.rb +4 -0
  319. data/spec/support/factories/element/ui_form.rb +14 -0
  320. data/spec/support/factories/element/ui_input.rb +13 -0
  321. data/spec/support/factories/issue.rb +0 -13
  322. data/spec/support/fixtures/report.afr +0 -0
  323. data/spec/support/fixtures/{taint_check/taint.rb → signature_check/signature.rb} +2 -2
  324. data/spec/support/helpers/browser_cluster/jobs/taint_tracer.rb +11 -11
  325. data/spec/support/helpers/framework.rb +1 -1
  326. data/spec/support/helpers/pages.rb +2 -2
  327. data/spec/support/servers/arachni/browser.rb +139 -0
  328. data/spec/support/servers/arachni/browser/javascript/taint_tracer.rb +40 -0
  329. data/spec/support/servers/arachni/element/capabilities/analyzable/{taint.rb → signature.rb} +0 -0
  330. data/spec/support/servers/arachni/element/input/input_dom.rb +102 -0
  331. data/spec/support/servers/arachni/element/ui_form/ui_form_dom.rb +238 -0
  332. data/spec/support/servers/checks/active/trainer_check.rb +7 -7
  333. data/spec/support/servers/checks/active/unvalidated_redirect_dom.rb +22 -6
  334. data/spec/support/servers/checks/active/xss_dom.rb +50 -0
  335. data/spec/support/servers/checks/active/xss_dom_script_context.rb +53 -0
  336. data/spec/support/shared/browser/javascript/taint_tracer/sink/base.rb +6 -6
  337. data/spec/support/shared/check.rb +10 -12
  338. data/spec/support/shared/component/options/base.rb +24 -24
  339. data/spec/support/shared/element/base.rb +25 -25
  340. data/spec/support/shared/element/capabilities/auditable.rb +116 -140
  341. data/spec/support/shared/element/capabilities/dom_only.rb +65 -0
  342. data/spec/support/shared/element/capabilities/inputtable.rb +71 -86
  343. data/spec/support/shared/element/capabilities/mutable.rb +122 -111
  344. data/spec/support/shared/element/capabilities/refreshable.rb +10 -10
  345. data/spec/support/shared/element/capabilities/{submitable.rb → submittable.rb} +26 -26
  346. data/spec/support/shared/element/capabilities/with_auditor.rb +10 -10
  347. data/spec/support/shared/element/capabilities/with_dom.rb +8 -8
  348. data/spec/support/shared/element/capabilities/with_node.rb +4 -6
  349. data/spec/support/shared/element/capabilities/with_scope.rb +2 -2
  350. data/spec/support/shared/element/capabilities/with_source.rb +6 -8
  351. data/spec/support/shared/element/dom.rb +144 -0
  352. data/spec/support/shared/element/dom/auditable.rb +42 -0
  353. data/spec/support/shared/element/dom/inputtable.rb +5 -0
  354. data/spec/support/shared/element/dom/mutable.rb +3 -0
  355. data/spec/support/shared/element/dom/submittable.rb +119 -0
  356. data/spec/support/shared/external/wavsep.rb +3 -3
  357. data/spec/support/shared/fingerprinter.rb +2 -2
  358. data/spec/support/shared/framework.rb +1 -1
  359. data/spec/support/shared/http/message.rb +9 -9
  360. data/spec/support/shared/option_group.rb +17 -17
  361. data/spec/support/shared/path_extractor.rb +1 -1
  362. data/spec/support/shared/plugin.rb +2 -2
  363. data/spec/support/shared/support/cache.rb +57 -57
  364. data/spec/support/shared/support/lookup.rb +25 -25
  365. data/ui/cli/framework.rb +22 -11
  366. data/ui/cli/framework/option_parser.rb +15 -0
  367. data/ui/cli/option_parser.rb +8 -1
  368. data/ui/cli/output.rb +2 -1
  369. metadata +54 -20
  370. data/components/checks/active/xss_dom_inputs.rb +0 -236
  371. data/spec/components/checks/active/xss_dom_inputs_spec.rb +0 -30
  372. data/spec/support/servers/checks/active/xss_dom_inputs.rb +0 -59
  373. data/spec/support/shared/element/capabilities/auditable/dom.rb +0 -322
@@ -27,7 +27,7 @@ describe Arachni::Element::Path do
27
27
 
28
28
  describe '#action' do
29
29
  it 'delegates to #url' do
30
- subject.action.should == subject.url
30
+ expect(subject.action).to eq(subject.url)
31
31
  end
32
32
  end
33
33
  end
@@ -52,13 +52,13 @@ describe Arachni::Element::Server do
52
52
 
53
53
  context 'when given an invalid URL' do
54
54
  it 'returns false' do
55
- auditable.log_remote_file_if_exists( '433' ).should be_false
55
+ expect(auditable.log_remote_file_if_exists( '433' )).to be_falsey
56
56
  end
57
57
  end
58
58
 
59
59
  context 'when given a valid URL' do
60
60
  it 'returns true' do
61
- auditable.log_remote_file_if_exists( @base_url ).should be_true
61
+ expect(auditable.log_remote_file_if_exists( @base_url )).to be_truthy
62
62
  end
63
63
  end
64
64
 
@@ -69,24 +69,25 @@ describe Arachni::Element::Server do
69
69
  @framework.http.run
70
70
 
71
71
  logged_issue = Arachni::Data.issues.first
72
- logged_issue.vector.url.split( '?' ).first.should == file
73
- logged_issue.vector.class.should == Arachni::Element::Server
74
- logged_issue.check.should == {
72
+ expect(logged_issue.vector.url.split( '?' ).first).to eq(file)
73
+ expect(logged_issue.vector.class).to eq(Arachni::Element::Server)
74
+ expect(logged_issue.check).to eq({
75
75
  name: 'Auditor',
76
76
  shortname: 'auditor_test'
77
- }
78
- logged_issue.variations.first.proof.should ==
79
- logged_issue.variations.first.page.response.status_line
77
+ })
78
+ expect(logged_issue.proof).to eq(
79
+ logged_issue.page.response.status_line
80
+ )
80
81
 
81
- logged_issue.name.should == @auditor.class.info[:issue][:name]
82
- logged_issue.trusted.should be_true
82
+ expect(logged_issue.name).to eq(@auditor.class.info[:issue][:name])
83
+ expect(logged_issue.trusted).to be_truthy
83
84
  end
84
85
 
85
86
  context 'when one issue is logged' do
86
87
  it "does not push the response to the #{Arachni::Trainer}" do
87
88
  auditable.log_remote_file_if_exists( @base_url + 'true' )
88
89
 
89
- @framework.trainer.should_not receive(:push)
90
+ expect(@framework.trainer).not_to receive(:push)
90
91
  @framework.http.run
91
92
  end
92
93
  end
@@ -96,7 +97,7 @@ describe Arachni::Element::Server do
96
97
  auditable.log_remote_file_if_exists( @base_url + 'true' )
97
98
  auditable.log_remote_file_if_exists( "#{url}/each_candidate_dom_element" )
98
99
 
99
- @framework.trainer.should receive(:push).twice
100
+ expect(@framework.trainer).to receive(:push).twice
100
101
  @framework.http.run
101
102
  end
102
103
  end
@@ -106,13 +107,13 @@ describe Arachni::Element::Server do
106
107
  it 'does not log an issue' do
107
108
  auditable.log_remote_file_if_exists( @base_url + 'false' )
108
109
  @framework.http.run
109
- Arachni::Data.issues.should be_empty
110
+ expect(Arachni::Data.issues).to be_empty
110
111
  end
111
112
 
112
113
  it "does not push the responses to the #{Arachni::Trainer}" do
113
114
  auditable.log_remote_file_if_exists( @base_url + 'false' )
114
115
 
115
- @framework.trainer.should_not receive(:push)
116
+ expect(@framework.trainer).not_to receive(:push)
116
117
  @framework.http.run
117
118
  end
118
119
  end
@@ -124,9 +125,9 @@ describe Arachni::Element::Server do
124
125
  10.times { auditable.log_remote_file_if_exists( check_url ) }
125
126
  @framework.http.run
126
127
 
127
- issues.should be_any
128
+ expect(issues).to be_any
128
129
  issues.each do |issue|
129
- issue.should be_untrusted
130
+ expect(issue).to be_untrusted
130
131
  end
131
132
  end
132
133
 
@@ -134,17 +135,17 @@ describe Arachni::Element::Server do
134
135
  10.times { auditable.log_remote_file_if_exists( check_url ) }
135
136
  @framework.http.run
136
137
 
137
- issues.should be_any
138
+ expect(issues).to be_any
138
139
 
139
140
  issues.each do |issue|
140
- issue.remarks[:meta_analysis].should == [described_class::REMARK]
141
+ expect(issue.remarks[:meta_analysis]).to eq([described_class::REMARK])
141
142
  end
142
143
  end
143
144
 
144
145
  it "does not push the responses to the #{Arachni::Trainer}" do
145
146
  10.times { auditable.log_remote_file_if_exists( url ) }
146
147
 
147
- @framework.trainer.should_not receive(:push)
148
+ expect(@framework.trainer).not_to receive(:push)
148
149
  @framework.http.run
149
150
  end
150
151
  end
@@ -157,13 +158,13 @@ describe Arachni::Element::Server do
157
158
 
158
159
  context 'when given an invalid URL' do
159
160
  it 'returns false' do
160
- auditable.remote_file_exist?( '433' ).should be_false
161
+ expect(auditable.remote_file_exist?( '433' )).to be_falsey
161
162
  end
162
163
  end
163
164
 
164
165
  context 'when given a valid URL' do
165
166
  it 'returns true' do
166
- auditable.remote_file_exist?( @base_url ).should be_true
167
+ expect(auditable.remote_file_exist?( @base_url )).to be_truthy
167
168
  end
168
169
  end
169
170
 
@@ -186,7 +187,7 @@ describe Arachni::Element::Server do
186
187
  auditable.remote_file_exist?( url ) {}
187
188
  @framework.http.run
188
189
 
189
- request.fingerprint?.should be_true
190
+ expect(request.fingerprint?).to be_truthy
190
191
  end
191
192
 
192
193
  context 'when a remote file exists' do
@@ -194,7 +195,7 @@ describe Arachni::Element::Server do
194
195
  exists = false
195
196
  auditable.remote_file_exist?( @base_url + 'true' ) { |bool| exists = bool }
196
197
  @framework.http.run
197
- exists.should be_true
198
+ expect(exists).to be_truthy
198
199
  end
199
200
 
200
201
  context 'on subsequent calls' do
@@ -203,10 +204,10 @@ describe Arachni::Element::Server do
203
204
  @framework.http.run
204
205
 
205
206
  exists = false
206
- @framework.http.should_not receive(:custom_404?)
207
+ expect(@framework.http).not_to receive(:custom_404?)
207
208
  auditable.remote_file_exist?( @base_url + 'true' ) { |bool| exists = bool }
208
209
  @framework.http.run
209
- exists.should be_true
210
+ expect(exists).to be_truthy
210
211
  end
211
212
  end
212
213
  end
@@ -216,7 +217,7 @@ describe Arachni::Element::Server do
216
217
  exists = true
217
218
  auditable.remote_file_exist?( @base_url + 'false' ) { |bool| exists = bool }
218
219
  @framework.http.run
219
- exists.should be_false
220
+ expect(exists).to be_falsey
220
221
  end
221
222
  end
222
223
 
@@ -225,7 +226,7 @@ describe Arachni::Element::Server do
225
226
  exists = true
226
227
  auditable.remote_file_exist?( @base_url + 'redirect' ) { |bool| exists = bool }
227
228
  @framework.http.run
228
- exists.should be_false
229
+ expect(exists).to be_falsey
229
230
  end
230
231
  end
231
232
  end
@@ -251,7 +252,7 @@ describe Arachni::Element::Server do
251
252
  auditable.remote_file_exist?( url ) {}
252
253
  @framework.http.run
253
254
 
254
- request.fingerprint?.should be_false
255
+ expect(request.fingerprint?).to be_falsey
255
256
  end
256
257
 
257
258
  context 'and the response' do
@@ -261,7 +262,7 @@ describe Arachni::Element::Server do
261
262
  url = @_404_url + 'static/this_does_not_exist'
262
263
  auditable.remote_file_exist?( url ) { |bool| exists = bool }
263
264
  @framework.http.run
264
- exists.should be_false
265
+ expect(exists).to be_falsey
265
266
  end
266
267
  end
267
268
 
@@ -272,7 +273,7 @@ describe Arachni::Element::Server do
272
273
  url = @_404_url + 'invalid/this_does_not_exist'
273
274
  auditable.remote_file_exist?( url ) { |bool| exists = bool }
274
275
  @framework.http.run
275
- exists.should be_false
276
+ expect(exists).to be_falsey
276
277
  end
277
278
  end
278
279
 
@@ -282,7 +283,7 @@ describe Arachni::Element::Server do
282
283
  url = @_404_url + 'dynamic/this_does_not_exist'
283
284
  auditable.remote_file_exist?( url ) { |bool| exists = bool }
284
285
  @framework.http.run
285
- exists.should be_false
286
+ expect(exists).to be_falsey
286
287
  end
287
288
  end
288
289
 
@@ -294,7 +295,7 @@ describe Arachni::Element::Server do
294
295
  auditable.remote_file_exist?( url ) { |bool| exist << bool }
295
296
  }
296
297
  @framework.http.run
297
- exist.include?( true ).should be_false
298
+ expect(exist.include?( true )).to be_falsey
298
299
  end
299
300
  end
300
301
  end
@@ -0,0 +1,164 @@
1
+ require 'spec_helper'
2
+
3
+ describe Arachni::Element::UIForm::DOM do
4
+ inputs = { 'my-input' => 'stuff' }
5
+
6
+ it_should_behave_like 'element_dom'
7
+
8
+ it_should_behave_like 'with_node'
9
+ it_should_behave_like 'with_auditor'
10
+
11
+ it_should_behave_like 'submittable_dom'
12
+ it_should_behave_like 'inputtable_dom', inputs: inputs
13
+ it_should_behave_like 'mutable_dom', inputs: inputs
14
+ it_should_behave_like 'auditable_dom'
15
+
16
+ def run
17
+ auditor.browser_cluster.wait
18
+ end
19
+
20
+ def auditable_extract_parameters( page )
21
+ {
22
+ 'my-input' => page.document.css('#container').text.strip
23
+ }
24
+ end
25
+
26
+ def element( inputs )
27
+ e = Arachni::Element::UIForm.new(
28
+ method: 'click',
29
+ action: @page.url,
30
+ source: '<button id="insert">Insert into DOM</button>',
31
+ inputs: inputs,
32
+ opening_tags: {
33
+ 'my-input' => "<input id=\"my-input\" type=\"text\" value=\"stuff\">"
34
+ }
35
+ ).dom
36
+ e.page = @page
37
+ e.auditor = @auditor
38
+ e
39
+ end
40
+
41
+ before :each do
42
+ @framework = Arachni::Framework.new
43
+ @page = Arachni::Page.from_url( url )
44
+ @auditor = Auditor.new( @page, @framework )
45
+ end
46
+
47
+ after :each do
48
+ @framework.clean_up
49
+ @framework.reset
50
+ end
51
+
52
+ subject { element( inputs ) }
53
+ let(:parent) { subject.parent }
54
+ let(:url) { web_server_url_for( :ui_form_dom ) }
55
+ let(:auditor) { @auditor }
56
+ let(:inputtable) { element( inputs ) }
57
+
58
+ describe '#type' do
59
+ it 'returns :ui_form_dom' do
60
+ expect(subject.type).to eq(:ui_form_dom)
61
+ end
62
+ end
63
+
64
+ describe '.type' do
65
+ it 'returns :ui_form_dom' do
66
+ expect(described_class.type).to eq(:ui_form_dom)
67
+ end
68
+ end
69
+
70
+ describe '#parent' do
71
+ it 'returns the parent element' do
72
+ expect(subject.parent).to be_kind_of Arachni::Element::UIForm
73
+ end
74
+ end
75
+
76
+ describe '#inputs' do
77
+ it 'returns the parent inputs' do
78
+ expect(subject.inputs).to eq subject.parent.inputs
79
+ end
80
+ end
81
+
82
+ describe '#locate' do
83
+ it 'locates the live element' do
84
+ called = false
85
+ subject.with_browser do |browser|
86
+ subject.browser = browser
87
+ browser.load subject.page
88
+
89
+ element = subject.locate
90
+ expect(element).to be_kind_of Watir::HTMLElement
91
+
92
+ expect(Arachni::Browser::ElementLocator.
93
+ from_html( element.opening_tag ).attributes
94
+ ).to eq(subject.locator.attributes)
95
+
96
+ called = true
97
+ end
98
+
99
+ subject.auditor.browser_cluster.wait
100
+ expect(called).to be_truthy
101
+ end
102
+ end
103
+
104
+ describe '#trigger' do
105
+ let(:new_inputs) { { subject.inputs.keys.first => 'The.Dude' } }
106
+
107
+ it 'triggers the event required to submit the element' do
108
+ subject.update new_inputs
109
+
110
+ called = false
111
+ subject.with_browser do |browser|
112
+ subject.browser = browser
113
+ browser.load subject.page
114
+
115
+ subject.trigger
116
+
117
+ page = browser.to_page
118
+
119
+ expect(subject.inputs).to eq(auditable_extract_parameters( page ))
120
+ called = true
121
+ end
122
+
123
+ subject.auditor.browser_cluster.wait
124
+ expect(called).to be_truthy
125
+ end
126
+
127
+ it 'returns a playable transition' do
128
+ subject.update new_inputs
129
+
130
+ transitions = []
131
+ called = false
132
+ subject.with_browser do |browser|
133
+ subject.browser = browser
134
+ browser.load subject.page
135
+
136
+ transitions = subject.trigger
137
+
138
+ page = browser.to_page
139
+
140
+ expect(subject.inputs).to eq(auditable_extract_parameters( page ))
141
+ called = true
142
+ end
143
+
144
+ subject.auditor.browser_cluster.wait
145
+ expect(called).to be_truthy
146
+
147
+ called = false
148
+ auditor.with_browser do |browser|
149
+ browser.load subject.page
150
+ expect(auditable_extract_parameters( browser.to_page ).values.first).to eq ''
151
+
152
+ transitions.each do |transition|
153
+ transition.play browser
154
+ end
155
+
156
+ expect(auditable_extract_parameters( browser.to_page )).to eq(new_inputs)
157
+ called = true
158
+ end
159
+ auditor.browser_cluster.wait
160
+ expect(called).to be_truthy
161
+ end
162
+ end
163
+
164
+ end
@@ -0,0 +1,242 @@
1
+ require 'spec_helper'
2
+
3
+ describe Arachni::Element::UIForm do
4
+ html = '<button id="insert">Insert into DOM</button'
5
+
6
+ it_should_behave_like 'dom_only', html
7
+
8
+ def new_element( source )
9
+ described_class.new(
10
+ method: 'click',
11
+ action: page.url,
12
+ source: source,
13
+ inputs: { 'my-input' => 'stuff' },
14
+ opening_tags: {
15
+ 'my-input' => "<input id=\"my-input\" type=\"text\" value=\"stuff\">"
16
+ }
17
+ )
18
+ end
19
+
20
+ subject do
21
+ new_element( html )
22
+ end
23
+ let(:inputtable) do
24
+ new_element( html )
25
+ end
26
+ let(:url) { "#{web_server_url_for( :ui_form_dom )}/" }
27
+
28
+ let(:browser) { @browser }
29
+ let(:page) { Arachni::Page.from_url( url ) }
30
+
31
+ describe '.new' do
32
+ describe ':opening_tags' do
33
+ it 'sets the #opening_tags' do
34
+ expect(subject.opening_tags).to eq(
35
+ 'my-input' => "<input id=\"my-input\" type=\"text\" value=\"stuff\">"
36
+ )
37
+ end
38
+
39
+ context 'when nil' do
40
+ subject do
41
+ described_class.new(
42
+ method: 'click',
43
+ action: page.url
44
+ )
45
+ end
46
+
47
+ it 'sets the empty array' do
48
+ expect(subject.opening_tags).to eq([])
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ describe '#dup' do
55
+ it 'duplicated #opening_tags' do
56
+ dupped = subject.dup
57
+ expect(dupped.opening_tags).to eq subject.opening_tags
58
+ end
59
+ end
60
+
61
+ describe '.from_browser' do
62
+ before :each do
63
+ @browser = Arachni::Browser.new
64
+ @browser.load page
65
+ end
66
+
67
+ after :each do
68
+ @browser.shutdown
69
+ end
70
+
71
+ context 'when there no buttons' do
72
+ let(:url) { "#{super()}/without-button" }
73
+
74
+ it 'returns empty array' do
75
+ expect(described_class.from_browser( @browser, page )).to be_empty
76
+ end
77
+ end
78
+
79
+ context 'when there are buttons' do
80
+ context 'as <button>' do
81
+ let(:url) { "#{super()}/button" }
82
+ let(:source) { '<button id="insert">' }
83
+
84
+ context 'without inputs' do
85
+ let(:url) { "#{super()}/without-inputs" }
86
+
87
+ it 'returns empty array' do
88
+ expect(described_class.from_browser( @browser, page )).to be_empty
89
+ end
90
+ end
91
+
92
+ context 'with inputs as' do
93
+ context '<input type="text">' do
94
+ let(:url) { "#{super()}/input/type/text" }
95
+
96
+ context 'and buttons with events' do
97
+ let(:url) { "#{super()}/with_events" }
98
+
99
+ it 'returns array of elements' do
100
+ form = described_class.from_browser( @browser, page ).first
101
+
102
+ expect(form.source).to eq source
103
+ expect(form.url).to eq page.url
104
+ expect(form.action).to eq page.url
105
+ expect(form.method).to eq :click
106
+ expect(form.inputs).to eq( 'my-input' => 'stuff' )
107
+ end
108
+ end
109
+
110
+ context 'and buttons without events' do
111
+ let(:url) { "#{super()}/without_events" }
112
+
113
+ it 'returns empty array' do
114
+ expect(described_class.from_browser( @browser, page )).to be_empty
115
+ end
116
+ end
117
+ end
118
+
119
+ context '<input>' do
120
+ let(:url) { "#{super()}/input/type/none" }
121
+
122
+ context 'and buttons with events' do
123
+ let(:url) { "#{super()}/with_events" }
124
+
125
+ it 'returns array of elements' do
126
+ form = described_class.from_browser( @browser, page ).first
127
+
128
+ expect(form.source).to eq source
129
+ expect(form.url).to eq page.url
130
+ expect(form.action).to eq page.url
131
+ expect(form.method).to eq :click
132
+ expect(form.inputs).to eq( 'my-input' => 'stuff' )
133
+ end
134
+ end
135
+
136
+ context 'and buttons without events' do
137
+ let(:url) { "#{super()}/without_events" }
138
+
139
+ it 'returns empty array' do
140
+ expect(described_class.from_browser( @browser, page )).to be_empty
141
+ end
142
+ end
143
+ end
144
+
145
+ context '<textarea>' do
146
+ let(:url) { "#{super()}/textarea" }
147
+
148
+ context 'and buttons with events' do
149
+ let(:url) { "#{super()}/with_events" }
150
+
151
+ it 'returns array of elements' do
152
+ form = described_class.from_browser( @browser, page ).first
153
+
154
+ expect(form.source).to eq source
155
+ expect(form.url).to eq page.url
156
+ expect(form.action).to eq page.url
157
+ expect(form.method).to eq :click
158
+ expect(form.inputs).to eq( 'my-input' => 'stuff' )
159
+ end
160
+ end
161
+
162
+ context 'and buttons without events' do
163
+ let(:url) { "#{super()}/without_events" }
164
+
165
+ it 'returns empty array' do
166
+ expect(described_class.from_browser( @browser, page )).to be_empty
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+
173
+ context 'as <input type="button">' do
174
+ let(:url) { "#{super()}/input-button" }
175
+ let(:source) { '<input type="button" id="insert" value="Insert into DOM">' }
176
+
177
+ context 'without inputs' do
178
+ let(:url) { "#{super()}/without-inputs" }
179
+
180
+ it 'returns empty array' do
181
+ expect(described_class.from_browser( @browser, page )).to be_empty
182
+ end
183
+ end
184
+
185
+ context 'with inputs as' do
186
+ context '<input>' do
187
+ let(:url) { "#{super()}/input" }
188
+
189
+ context 'and buttons with events' do
190
+ let(:url) { "#{super()}/with_events" }
191
+
192
+ it 'returns array of elements' do
193
+ form = described_class.from_browser( @browser, page ).first
194
+
195
+ expect(form.source).to eq source
196
+ expect(form.url).to eq page.url
197
+ expect(form.action).to eq page.url
198
+ expect(form.method).to eq :click
199
+ expect(form.inputs).to eq( 'my-input' => 'stuff' )
200
+ end
201
+ end
202
+
203
+ context 'and buttons without events' do
204
+ let(:url) { "#{super()}/without_events" }
205
+
206
+ it 'returns array of elements' do
207
+ expect(described_class.from_browser( @browser, page )).to be_empty
208
+ end
209
+ end
210
+ end
211
+
212
+ context '<textarea>' do
213
+ let(:url) { "#{super()}/textarea" }
214
+
215
+ context 'and buttons with events' do
216
+ let(:url) { "#{super()}/with_events" }
217
+
218
+ it 'returns array of elements' do
219
+ form = described_class.from_browser( @browser, page ).first
220
+
221
+ expect(form.source).to eq source
222
+ expect(form.url).to eq page.url
223
+ expect(form.action).to eq page.url
224
+ expect(form.method).to eq :click
225
+ expect(form.inputs).to eq( 'my-input' => 'stuff' )
226
+ end
227
+ end
228
+
229
+ context 'and buttons without events' do
230
+ let(:url) { "#{super()}/without_events" }
231
+
232
+ it 'returns array of elements' do
233
+ expect(described_class.from_browser( @browser, page )).to be_empty
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end
241
+
242
+ end