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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +66 -0
- data/Gemfile +1 -1
- data/README.md +16 -5
- data/components/checks/active/ldap_injection/errors.txt +1 -0
- data/components/checks/active/source_code_disclosure.rb +1 -1
- data/components/checks/active/unvalidated_redirect.rb +6 -6
- data/components/checks/active/unvalidated_redirect_dom.rb +10 -7
- data/components/checks/passive/grep/captcha.rb +14 -5
- data/components/checks/passive/grep/form_upload.rb +7 -3
- data/components/checks/passive/grep/hsts.rb +3 -3
- data/components/checks/passive/grep/html_objects.rb +2 -3
- data/components/checks/passive/grep/http_only_cookies.rb +2 -3
- data/components/checks/passive/grep/insecure_cookies.rb +1 -1
- data/components/checks/passive/grep/password_autocomplete.rb +2 -2
- data/components/checks/passive/grep/unencrypted_password_forms.rb +7 -7
- data/components/checks/passive/grep/x_frame_options.rb +2 -2
- data/components/checks/passive/http_put.rb +2 -3
- data/components/path_extractors/comments.rb +3 -3
- data/components/path_extractors/scripts.rb +10 -1
- data/components/plugins/defaults/autothrottle.rb +27 -18
- data/components/plugins/defaults/meta/remedies/discovery.rb +30 -33
- data/components/plugins/defaults/meta/remedies/timing_attacks.rb +7 -11
- data/components/plugins/login_script.rb +9 -3
- data/components/plugins/proxy.rb +4 -3
- data/components/reporters/html.rb +11 -14
- data/components/reporters/html/default/issue.erb +13 -38
- data/components/reporters/html/default/issue/info.erb +1 -1
- data/components/reporters/html/default/summary/issues/by_name.erb +3 -3
- data/components/reporters/stdout.rb +62 -71
- data/components/reporters/xml.rb +26 -40
- data/components/reporters/xml/schema.xsd +43 -89
- data/lib/arachni/browser.rb +52 -3
- data/lib/arachni/browser/javascript.rb +3 -3
- data/lib/arachni/browser/javascript/scripts/taint_tracer.js +46 -25
- data/lib/arachni/browser_cluster.rb +61 -0
- data/lib/arachni/browser_cluster/job.rb +21 -1
- data/lib/arachni/browser_cluster/jobs/browser_provider.rb +3 -1
- data/lib/arachni/browser_cluster/jobs/resource_exploration.rb +2 -1
- data/lib/arachni/browser_cluster/jobs/resource_exploration/event_trigger.rb +2 -1
- data/lib/arachni/browser_cluster/jobs/taint_trace.rb +3 -2
- data/lib/arachni/browser_cluster/jobs/taint_trace/event_trigger.rb +1 -1
- data/lib/arachni/browser_cluster/worker.rb +5 -0
- data/lib/arachni/check/auditor.rb +22 -12
- data/lib/arachni/data/framework.rb +13 -1
- data/lib/arachni/data/issues.rb +9 -25
- data/lib/arachni/element/base.rb +9 -3
- data/lib/arachni/element/capabilities/analyzable.rb +2 -6
- data/lib/arachni/element/capabilities/analyzable/differential.rb +24 -7
- data/lib/arachni/element/capabilities/analyzable/{taint.rb → signature.rb} +23 -23
- data/lib/arachni/element/capabilities/auditable.rb +0 -6
- data/lib/arachni/element/capabilities/dom_only.rb +61 -0
- data/lib/arachni/element/capabilities/with_dom.rb +3 -1
- data/lib/arachni/element/cookie.rb +35 -5
- data/lib/arachni/element/cookie/dom.rb +13 -4
- data/lib/arachni/element/{capabilities/auditable/dom.rb → dom.rb} +20 -68
- data/lib/arachni/element/dom/capabilities/auditable.rb +29 -0
- data/lib/arachni/element/dom/capabilities/inputtable.rb +27 -0
- data/lib/arachni/element/dom/capabilities/mutable.rb +21 -0
- data/lib/arachni/element/dom/capabilities/submittable.rb +52 -0
- data/lib/arachni/element/form.rb +12 -1
- data/lib/arachni/element/form/capabilities/mutable.rb +2 -1
- data/lib/arachni/element/form/capabilities/with_dom.rb +0 -1
- data/lib/arachni/element/form/dom.rb +9 -3
- data/lib/arachni/element/header.rb +14 -33
- data/lib/arachni/element/header/capabilities/inputtable.rb +29 -0
- data/lib/arachni/element/header/capabilities/mutable.rb +51 -0
- data/lib/arachni/element/input/dom.rb +71 -0
- data/lib/arachni/element/json.rb +2 -0
- data/lib/arachni/element/link.rb +3 -0
- data/lib/arachni/element/link/capabilities/with_dom.rb +0 -1
- data/lib/arachni/element/link/dom.rb +16 -3
- data/lib/arachni/element/link/dom/capabilities/submittable.rb +29 -0
- data/lib/arachni/element/link_template.rb +3 -5
- data/lib/arachni/element/link_template/capabilities/inputtable.rb +5 -0
- data/lib/arachni/element/link_template/capabilities/with_dom.rb +0 -1
- data/lib/arachni/element/link_template/dom.rb +16 -3
- data/lib/arachni/element/link_template/dom/capabilities/submittable.rb +29 -0
- data/lib/arachni/element/server.rb +3 -5
- data/lib/arachni/element/ui_form.rb +106 -0
- data/lib/arachni/element/ui_form/dom.rb +107 -0
- data/lib/arachni/element/ui_input.rb +62 -0
- data/lib/arachni/element/xml.rb +2 -1
- data/lib/arachni/framework.rb +7 -5
- data/lib/arachni/framework/parts/audit.rb +0 -1
- data/lib/arachni/framework/parts/check.rb +1 -0
- data/lib/arachni/framework/parts/data.rb +4 -0
- data/lib/arachni/framework/parts/state.rb +0 -2
- data/lib/arachni/http/client.rb +17 -6
- data/lib/arachni/http/proxy_server.rb +52 -5
- data/lib/arachni/http/request.rb +1 -1
- data/lib/arachni/issue.rb +34 -179
- data/lib/arachni/issue/severity.rb +2 -0
- data/lib/arachni/option_groups/audit.rb +22 -2
- data/lib/arachni/option_groups/browser_cluster.rb +15 -0
- data/lib/arachni/page.rb +3 -2
- data/lib/arachni/parser.rb +24 -5
- data/lib/arachni/platform/manager.rb +1 -2
- data/lib/arachni/rpc/server/framework.rb +3 -4
- data/lib/arachni/rpc/server/framework/multi_instance.rb +2 -1
- data/lib/arachni/session.rb +1 -1
- data/lib/arachni/trainer.rb +4 -7
- data/lib/arachni/watir/element.rb +12 -1
- data/lib/version +1 -1
- data/spec/arachni/browser/element_locator_spec.rb +43 -43
- data/spec/arachni/browser/javascript/dom_monitor_spec.rb +44 -44
- data/spec/arachni/browser/javascript/proxy/stub_spec.rb +17 -14
- data/spec/arachni/browser/javascript/proxy_spec.rb +24 -24
- data/spec/arachni/browser/javascript/taint_tracer/frame/called_function_spec.rb +11 -11
- data/spec/arachni/browser/javascript/taint_tracer/frame_spec.rb +7 -7
- data/spec/arachni/browser/javascript/taint_tracer/sink/data_flow_spec.rb +13 -13
- data/spec/arachni/browser/javascript/taint_tracer/sink/execution_flow_spec.rb +7 -7
- data/spec/arachni/browser/javascript/taint_tracer_spec.rb +568 -558
- data/spec/arachni/browser/javascript_spec.rb +73 -63
- data/spec/arachni/browser_cluster/job/result_spec.rb +3 -3
- data/spec/arachni/browser_cluster/job_spec.rb +68 -48
- data/spec/arachni/browser_cluster/jobs/resource_exploration/event_trigger/result_spec.rb +2 -2
- data/spec/arachni/browser_cluster/jobs/resource_exploration/event_trigger_spec.rb +5 -4
- data/spec/arachni/browser_cluster/jobs/resource_exploration/result_spec.rb +2 -2
- data/spec/arachni/browser_cluster/jobs/resource_exploration_spec.rb +5 -5
- data/spec/arachni/browser_cluster/worker_spec.rb +87 -70
- data/spec/arachni/browser_cluster_spec.rb +64 -39
- data/spec/arachni/browser_spec.rb +692 -527
- data/spec/arachni/check/auditor_spec.rb +177 -147
- data/spec/arachni/check/base_spec.rb +33 -33
- data/spec/arachni/check/manager_spec.rb +15 -15
- data/spec/arachni/component/base_spec.rb +8 -8
- data/spec/arachni/component/manager_spec.rb +100 -99
- data/spec/arachni/component/options/address_spec.rb +3 -3
- data/spec/arachni/component/options/base_spec.rb +7 -7
- data/spec/arachni/component/options/bool_spec.rb +9 -9
- data/spec/arachni/component/options/float_spec.rb +6 -6
- data/spec/arachni/component/options/int_spec.rb +5 -5
- data/spec/arachni/component/options/multiple_choice_spec.rb +12 -12
- data/spec/arachni/component/options/object_spec.rb +2 -2
- data/spec/arachni/component/options/path_spec.rb +3 -3
- data/spec/arachni/component/options/port_spec.rb +5 -5
- data/spec/arachni/component/options/string_spec.rb +3 -3
- data/spec/arachni/component/options/url_spec.rb +4 -4
- data/spec/arachni/component/utilities_spec.rb +2 -2
- data/spec/arachni/data/framework/rpc_spec.rb +10 -9
- data/spec/arachni/data/framework_spec.rb +65 -46
- data/spec/arachni/data/issues_spec.rb +39 -77
- data/spec/arachni/data/plugins_spec.rb +11 -11
- data/spec/arachni/data/session_spec.rb +6 -6
- data/spec/arachni/data_spec.rb +8 -8
- data/spec/arachni/element/body_spec.rb +10 -10
- data/spec/arachni/element/capabilities/analyzable/differential_spec.rb +39 -21
- data/spec/arachni/element/capabilities/analyzable/{taint_spec.rb → signature_spec.rb} +63 -63
- data/spec/arachni/element/capabilities/analyzable/timeout_spec.rb +51 -51
- data/spec/arachni/element/capabilities/with_scope/scope_spec.rb +5 -5
- data/spec/arachni/element/cookie/dom_spec.rb +37 -18
- data/spec/arachni/element/cookie_spec.rb +206 -139
- data/spec/arachni/element/form/dom_spec.rb +36 -19
- data/spec/arachni/element/form_spec.rb +210 -187
- data/spec/arachni/element/generic_dom_spec.rb +14 -14
- data/spec/arachni/element/header_spec.rb +35 -17
- data/spec/arachni/element/json_spec.rb +53 -31
- data/spec/arachni/element/link/dom_spec.rb +46 -28
- data/spec/arachni/element/link_spec.rb +58 -40
- data/spec/arachni/element/link_template/dom_spec.rb +47 -29
- data/spec/arachni/element/link_template_spec.rb +79 -61
- data/spec/arachni/element/path_spec.rb +1 -1
- data/spec/arachni/element/server_spec.rb +33 -32
- data/spec/arachni/element/ui_form/ui_form_dom_spec.rb +164 -0
- data/spec/arachni/element/ui_form_spec.rb +242 -0
- data/spec/arachni/element/ui_input/dom_spec.rb +157 -0
- data/spec/arachni/element/ui_input_spec.rb +136 -0
- data/spec/arachni/element/xml_spec.rb +42 -24
- data/spec/arachni/element_filter_spec.rb +49 -48
- data/spec/arachni/error_spec.rb +3 -3
- data/spec/arachni/framework/parts/audit_spec.rb +64 -63
- data/spec/arachni/framework/parts/browser_spec.rb +16 -16
- data/spec/arachni/framework/parts/check_spec.rb +3 -3
- data/spec/arachni/framework/parts/data_spec.rb +48 -48
- data/spec/arachni/framework/parts/platform_spec.rb +3 -3
- data/spec/arachni/framework/parts/plugin_spec.rb +7 -6
- data/spec/arachni/framework/parts/report_spec.rb +7 -7
- data/spec/arachni/framework/parts/scope_spec.rb +16 -16
- data/spec/arachni/framework/parts/state_spec.rb +68 -69
- data/spec/arachni/framework_spec.rb +39 -31
- data/spec/arachni/http/client/dynamic_404_handlers_spec.rb +32 -32
- data/spec/arachni/http/client_spec.rb +219 -208
- data/spec/arachni/http/cookie_jar_spec.rb +72 -72
- data/spec/arachni/http/headers_spec.rb +14 -14
- data/spec/arachni/http/proxy_server_spec.rb +43 -42
- data/spec/arachni/http/request_spec.rb +105 -103
- data/spec/arachni/http/response/scope_spec.rb +24 -24
- data/spec/arachni/http/response_spec.rb +50 -49
- data/spec/arachni/issue/severity_spec.rb +10 -9
- data/spec/arachni/issue_spec.rb +71 -369
- data/spec/arachni/option_groups/audit_spec.rb +114 -114
- data/spec/arachni/option_groups/browser_cluster_spec.rb +20 -3
- data/spec/arachni/option_groups/datastore_spec.rb +6 -6
- data/spec/arachni/option_groups/dispatcher_spec.rb +19 -19
- data/spec/arachni/option_groups/http_spec.rb +11 -11
- data/spec/arachni/option_groups/input_spec.rb +31 -27
- data/spec/arachni/option_groups/output_spec.rb +2 -2
- data/spec/arachni/option_groups/paths_spec.rb +17 -17
- data/spec/arachni/option_groups/rpc_spec.rb +2 -2
- data/spec/arachni/option_groups/scope_spec.rb +40 -40
- data/spec/arachni/option_groups/session_spec.rb +6 -5
- data/spec/arachni/option_groups/snapshot_spec.rb +4 -4
- data/spec/arachni/options_spec.rb +46 -45
- data/spec/arachni/page/dom/transition_spec.rb +74 -72
- data/spec/arachni/page/dom_spec.rb +35 -35
- data/spec/arachni/page/scope_spec.rb +15 -15
- data/spec/arachni/page_spec.rb +217 -217
- data/spec/arachni/parser_spec.rb +106 -104
- data/spec/arachni/platform/fingerprinter_spec.rb +17 -14
- data/spec/arachni/platform/list_spec.rb +33 -33
- data/spec/arachni/platform/manager_spec.rb +67 -64
- data/spec/arachni/plugin/base_spec.rb +10 -10
- data/spec/arachni/plugin/manager_spec.rb +38 -37
- data/spec/arachni/report_spec.rb +43 -40
- data/spec/arachni/reporter/base_spec.rb +15 -15
- data/spec/arachni/reporter/manager_spec.rb +4 -4
- data/spec/arachni/reporter/options_spec.rb +6 -6
- data/spec/arachni/rpc/client/base_spec.rb +6 -6
- data/spec/arachni/rpc/client/dispatcher_spec.rb +2 -2
- data/spec/arachni/rpc/client/instance_spec.rb +6 -6
- data/spec/arachni/rpc/server/active_options_spec.rb +11 -8
- data/spec/arachni/rpc/server/base_spec.rb +5 -5
- data/spec/arachni/rpc/server/checks/manager_spec.rb +8 -8
- data/spec/arachni/rpc/server/dispatcher/node_spec.rb +37 -37
- data/spec/arachni/rpc/server/dispatcher/service_spec.rb +15 -14
- data/spec/arachni/rpc/server/dispatcher_spec.rb +36 -35
- data/spec/arachni/rpc/server/framework/distributor_spec.rb +36 -36
- data/spec/arachni/rpc/server/framework_multi_spec.rb +340 -336
- data/spec/arachni/rpc/server/framework_spec.rb +90 -85
- data/spec/arachni/rpc/server/instance_spec.rb +126 -107
- data/spec/arachni/rpc/server/output_spec.rb +1 -1
- data/spec/arachni/rpc/server/plugin/manager_spec.rb +6 -6
- data/spec/arachni/ruby/array_spec.rb +42 -42
- data/spec/arachni/ruby/hash_spec.rb +20 -18
- data/spec/arachni/ruby/io_spec.rb +2 -2
- data/spec/arachni/ruby/object_spec.rb +1 -1
- data/spec/arachni/ruby/set_spec.rb +3 -3
- data/spec/arachni/ruby/string_spec.rb +30 -30
- data/spec/arachni/ruby/webrick_spec.rb +2 -2
- data/spec/arachni/scope_spec.rb +1 -1
- data/spec/arachni/session_spec.rb +67 -64
- data/spec/arachni/snapshot_spec.rb +15 -15
- data/spec/arachni/state/audit_spec.rb +11 -11
- data/spec/arachni/state/element_filter_spec.rb +6 -6
- data/spec/arachni/state/framework/rpc_spec.rb +12 -12
- data/spec/arachni/state/framework_spec.rb +125 -121
- data/spec/arachni/state/http_spec.rb +7 -7
- data/spec/arachni/state/options_spec.rb +7 -7
- data/spec/arachni/state/plugins_spec.rb +8 -8
- data/spec/arachni/state_spec.rb +10 -10
- data/spec/arachni/support/buffer/autoflush_spec.rb +16 -16
- data/spec/arachni/support/buffer/base_spec.rb +39 -39
- data/spec/arachni/support/cache/least_cost_replacement_spec.rb +18 -18
- data/spec/arachni/support/cache/least_recently_pushed_spec.rb +24 -24
- data/spec/arachni/support/cache/least_recently_used_spec.rb +20 -20
- data/spec/arachni/support/cache/preference_spec.rb +4 -4
- data/spec/arachni/support/cache/random_replacement_spec.rb +8 -8
- data/spec/arachni/support/crypto/rsa_aes_cbc_spec.rb +1 -1
- data/spec/arachni/support/database/hash_spec.rb +44 -43
- data/spec/arachni/support/database/queue_spec.rb +27 -27
- data/spec/arachni/support/lookup/hash_set_spec.rb +8 -8
- data/spec/arachni/support/lookup/moolb_spec.rb +3 -3
- data/spec/arachni/support/mixins/observable_spec.rb +6 -6
- data/spec/arachni/support/signature_spec.rb +19 -19
- data/spec/arachni/trainer_spec.rb +39 -39
- data/spec/arachni/typhoeus/hydra_spec.rb +2 -2
- data/spec/arachni/uri/scope_spec.rb +66 -66
- data/spec/arachni/uri_spec.rb +107 -105
- data/spec/arachni/utilities_spec.rb +40 -40
- data/spec/components/checks/active/csrf_spec.rb +8 -8
- data/spec/components/checks/active/no_sql_injection_spec.rb +1 -1
- data/spec/components/checks/active/sql_injection_spec.rb +16 -16
- data/spec/components/checks/active/trainer_spec.rb +4 -4
- data/spec/components/checks/active/unvalidated_redirect_dom_spec.rb +4 -2
- data/spec/components/checks/active/xpath_injection_spec.rb +1 -1
- data/spec/components/checks/active/xss_dom_script_context_spec.rb +51 -21
- data/spec/components/checks/active/xss_dom_spec.rb +46 -24
- data/spec/components/checks/passive/allowed_methods_spec.rb +1 -1
- data/spec/components/checks/passive/grep/cookie_set_for_parent_domain_spec.rb +1 -1
- data/spec/components/checks/passive/grep/hsts_spec.rb +2 -2
- data/spec/components/checks/passive/grep/http_only_cookies_spec.rb +1 -1
- data/spec/components/checks/passive/grep/insecure_cookies_spec.rb +1 -1
- data/spec/components/checks/passive/grep/insecure_cors_policy_spec.rb +2 -2
- data/spec/components/checks/passive/grep/password_autocomplete_spec.rb +1 -1
- data/spec/components/checks/passive/grep/private_ip_spec.rb +3 -3
- data/spec/components/checks/passive/grep/unencrypted_password_forms_spec.rb +1 -1
- data/spec/components/checks/passive/grep/x_frame_options_spec.rb +2 -2
- data/spec/components/checks/passive/interesting_responses_spec.rb +2 -2
- data/spec/components/checks/passive/webdav_spec.rb +1 -1
- data/spec/components/checks/passive/xst_spec.rb +1 -1
- data/spec/components/fingerprinters/servers/apache_spec.rb +2 -2
- data/spec/components/path_extractors/comments_spec.rb +5 -1
- data/spec/components/path_extractors/scripts_spec.rb +5 -2
- data/spec/components/plugins/autologin_spec.rb +22 -22
- data/spec/components/plugins/autothrottle_spec.rb +6 -5
- data/spec/components/plugins/content_types_spec.rb +4 -4
- data/spec/components/plugins/cookie_collector_spec.rb +5 -5
- data/spec/components/plugins/exec_spec.rb +12 -12
- data/spec/components/plugins/form_dicattack_spec.rb +3 -3
- data/spec/components/plugins/headers_collector_spec.rb +8 -8
- data/spec/components/plugins/healthmap_spec.rb +3 -3
- data/spec/components/plugins/http_dicattack_spec.rb +3 -3
- data/spec/components/plugins/login_script_spec.rb +79 -22
- data/spec/components/plugins/meta/remedies/discovery_spec.rb +3 -2
- data/spec/components/plugins/meta/remedies/timing_attacks_spec.rb +3 -3
- data/spec/components/plugins/meta/uniformity_spec.rb +2 -2
- data/spec/components/plugins/restrict_to_dom_state_spec.rb +1 -1
- data/spec/components/plugins/script_spec.rb +1 -1
- data/spec/components/plugins/uncommon_headers_spec.rb +2 -2
- data/spec/components/plugins/vector_collector_spec.rb +2 -2
- data/spec/components/plugins/vector_feed_spec.rb +40 -40
- data/spec/components/plugins/waf_detector_spec.rb +6 -6
- data/spec/components/reporters/json_spec.rb +4 -4
- data/spec/components/reporters/marshal_spec.rb +2 -2
- data/spec/components/reporters/yaml_spec.rb +3 -2
- data/spec/external/wavsep/active/sqli_spec.rb +1 -3
- data/spec/spec_helper.rb +4 -0
- data/spec/support/factories/element/ui_form.rb +14 -0
- data/spec/support/factories/element/ui_input.rb +13 -0
- data/spec/support/factories/issue.rb +0 -13
- data/spec/support/fixtures/report.afr +0 -0
- data/spec/support/fixtures/{taint_check/taint.rb → signature_check/signature.rb} +2 -2
- data/spec/support/helpers/browser_cluster/jobs/taint_tracer.rb +11 -11
- data/spec/support/helpers/framework.rb +1 -1
- data/spec/support/helpers/pages.rb +2 -2
- data/spec/support/servers/arachni/browser.rb +139 -0
- data/spec/support/servers/arachni/browser/javascript/taint_tracer.rb +40 -0
- data/spec/support/servers/arachni/element/capabilities/analyzable/{taint.rb → signature.rb} +0 -0
- data/spec/support/servers/arachni/element/input/input_dom.rb +102 -0
- data/spec/support/servers/arachni/element/ui_form/ui_form_dom.rb +238 -0
- data/spec/support/servers/checks/active/trainer_check.rb +7 -7
- data/spec/support/servers/checks/active/unvalidated_redirect_dom.rb +22 -6
- data/spec/support/servers/checks/active/xss_dom.rb +50 -0
- data/spec/support/servers/checks/active/xss_dom_script_context.rb +53 -0
- data/spec/support/shared/browser/javascript/taint_tracer/sink/base.rb +6 -6
- data/spec/support/shared/check.rb +10 -12
- data/spec/support/shared/component/options/base.rb +24 -24
- data/spec/support/shared/element/base.rb +25 -25
- data/spec/support/shared/element/capabilities/auditable.rb +116 -140
- data/spec/support/shared/element/capabilities/dom_only.rb +65 -0
- data/spec/support/shared/element/capabilities/inputtable.rb +71 -86
- data/spec/support/shared/element/capabilities/mutable.rb +122 -111
- data/spec/support/shared/element/capabilities/refreshable.rb +10 -10
- data/spec/support/shared/element/capabilities/{submitable.rb → submittable.rb} +26 -26
- data/spec/support/shared/element/capabilities/with_auditor.rb +10 -10
- data/spec/support/shared/element/capabilities/with_dom.rb +8 -8
- data/spec/support/shared/element/capabilities/with_node.rb +4 -6
- data/spec/support/shared/element/capabilities/with_scope.rb +2 -2
- data/spec/support/shared/element/capabilities/with_source.rb +6 -8
- data/spec/support/shared/element/dom.rb +144 -0
- data/spec/support/shared/element/dom/auditable.rb +42 -0
- data/spec/support/shared/element/dom/inputtable.rb +5 -0
- data/spec/support/shared/element/dom/mutable.rb +3 -0
- data/spec/support/shared/element/dom/submittable.rb +119 -0
- data/spec/support/shared/external/wavsep.rb +3 -3
- data/spec/support/shared/fingerprinter.rb +2 -2
- data/spec/support/shared/framework.rb +1 -1
- data/spec/support/shared/http/message.rb +9 -9
- data/spec/support/shared/option_group.rb +17 -17
- data/spec/support/shared/path_extractor.rb +1 -1
- data/spec/support/shared/plugin.rb +2 -2
- data/spec/support/shared/support/cache.rb +57 -57
- data/spec/support/shared/support/lookup.rb +25 -25
- data/ui/cli/framework.rb +22 -11
- data/ui/cli/framework/option_parser.rb +15 -0
- data/ui/cli/option_parser.rb +8 -1
- data/ui/cli/output.rb +2 -1
- metadata +54 -20
- data/components/checks/active/xss_dom_inputs.rb +0 -236
- data/spec/components/checks/active/xss_dom_inputs_spec.rb +0 -30
- data/spec/support/servers/checks/active/xss_dom_inputs.rb +0 -59
- data/spec/support/shared/element/capabilities/auditable/dom.rb +0 -322
@@ -13,16 +13,14 @@ module Check
|
|
13
13
|
#
|
14
14
|
# There are 3 main types of audit and analysis techniques available:
|
15
15
|
#
|
16
|
-
# * {Arachni::Element::Capabilities::Analyzable::
|
16
|
+
# * {Arachni::Element::Capabilities::Analyzable::Signature Signature analysis}
|
17
17
|
# -- {#audit}
|
18
18
|
# * {Arachni::Element::Capabilities::Analyzable::Timeout Timeout analysis}
|
19
19
|
# -- {#audit_timeout}
|
20
20
|
# * {Arachni::Element::Capabilities::Analyzable::Differential Differential analysis}
|
21
21
|
# -- {#audit_differential}
|
22
22
|
#
|
23
|
-
# It should be noted that actual analysis takes place at the element level
|
24
|
-
# and to be more specific, the {Arachni::Element::Capabilities::Auditable}
|
25
|
-
# element level.
|
23
|
+
# It should be noted that actual analysis takes place at the {Arachni::Element element} level.
|
26
24
|
#
|
27
25
|
# It also provides:
|
28
26
|
#
|
@@ -119,6 +117,12 @@ module Auditor
|
|
119
117
|
proc { audit.jsons? && page.jsons.find { |e| e.inputs.any? } },
|
120
118
|
Element::XML =>
|
121
119
|
proc { audit.xmls? && page.xmls.find { |e| e.inputs.any? } },
|
120
|
+
Element::UIInput => false,
|
121
|
+
Element::UIInput::DOM =>
|
122
|
+
proc { audit.ui_inputs? && page.ui_inputs.any? },
|
123
|
+
Element::UIForm => false,
|
124
|
+
Element::UIForm::DOM =>
|
125
|
+
proc { audit.ui_forms? && page.ui_forms.any? },
|
122
126
|
Element::Body => !page.body.empty?,
|
123
127
|
Element::GenericDOM => page.has_script?,
|
124
128
|
Element::Path => true,
|
@@ -189,7 +193,7 @@ module Auditor
|
|
189
193
|
# Auditable DOM elements.
|
190
194
|
DOM_ELEMENTS_WITH_INPUTS = [
|
191
195
|
Element::Link::DOM, Element::Form::DOM, Element::Cookie::DOM,
|
192
|
-
Element::LinkTemplate::DOM
|
196
|
+
Element::LinkTemplate::DOM, Element::UIInput::DOM, Element::UIForm::DOM
|
193
197
|
]
|
194
198
|
|
195
199
|
# Default audit options.
|
@@ -523,6 +527,12 @@ module Auditor
|
|
523
527
|
when Element::LinkTemplate::DOM.type
|
524
528
|
prepare_each_dom_element( page.link_templates, &block )
|
525
529
|
|
530
|
+
when Element::UIInput::DOM.type
|
531
|
+
prepare_each_dom_element( page.ui_inputs, &block )
|
532
|
+
|
533
|
+
when Element::UIForm::DOM.type
|
534
|
+
prepare_each_dom_element( page.ui_forms, &block )
|
535
|
+
|
526
536
|
else
|
527
537
|
fail ArgumentError, "Unknown DOM element: #{elem}"
|
528
538
|
end
|
@@ -530,17 +540,17 @@ module Auditor
|
|
530
540
|
end
|
531
541
|
|
532
542
|
# If a block has been provided it calls {Arachni::Element::Capabilities::Auditable#audit}
|
533
|
-
# for every element, otherwise, it defaults to {#
|
543
|
+
# for every element, otherwise, it defaults to {#audit_signature}.
|
534
544
|
#
|
535
545
|
# Uses {#each_candidate_element} to decide which elements to audit.
|
536
546
|
#
|
537
547
|
# @see OPTIONS
|
538
548
|
# @see Arachni::Element::Capabilities::Auditable#audit
|
539
|
-
# @see #
|
549
|
+
# @see #audit_signature
|
540
550
|
def audit( payloads, opts = {}, &block )
|
541
551
|
opts = OPTIONS.merge( opts )
|
542
552
|
if !block_given?
|
543
|
-
|
553
|
+
audit_signature( payloads, opts )
|
544
554
|
else
|
545
555
|
each_candidate_element( opts[:elements] ) do |e|
|
546
556
|
e.audit( payloads, opts, &block )
|
@@ -549,17 +559,17 @@ module Auditor
|
|
549
559
|
end
|
550
560
|
end
|
551
561
|
|
552
|
-
# Provides easy access to element auditing using simple
|
562
|
+
# Provides easy access to element auditing using simple signature analysis
|
553
563
|
# and automatically logs results.
|
554
564
|
#
|
555
565
|
# Uses {#each_candidate_element} to decide which elements to audit.
|
556
566
|
#
|
557
567
|
# @see OPTIONS
|
558
|
-
# @see Arachni::Element::Capabilities::Analyzable::
|
559
|
-
def
|
568
|
+
# @see Arachni::Element::Capabilities::Analyzable::Signature
|
569
|
+
def audit_signature( payloads, opts = {} )
|
560
570
|
opts = OPTIONS.merge( opts )
|
561
571
|
each_candidate_element( opts[:elements] )do |e|
|
562
|
-
e.
|
572
|
+
e.signature_analysis( payloads, opts )
|
563
573
|
audited( e.coverage_id )
|
564
574
|
end
|
565
575
|
end
|
@@ -89,7 +89,19 @@ class Framework
|
|
89
89
|
# @param [Page] page
|
90
90
|
# Page with which to update the {#sitemap}.
|
91
91
|
def add_page_to_sitemap( page )
|
92
|
-
|
92
|
+
update_sitemap( page.dom.url => page.code )
|
93
|
+
end
|
94
|
+
|
95
|
+
def update_sitemap( entries )
|
96
|
+
entries.each do |url, code|
|
97
|
+
# Feedback from the trainer or whatever, don't include it in the
|
98
|
+
# sitemap, it'll just add noise.
|
99
|
+
next if url.include?( Utilities.random_seed )
|
100
|
+
|
101
|
+
@sitemap[url] = code
|
102
|
+
end
|
103
|
+
|
104
|
+
@sitemap
|
93
105
|
end
|
94
106
|
|
95
107
|
def dump( directory )
|
data/lib/arachni/data/issues.rb
CHANGED
@@ -34,8 +34,7 @@ class Issues
|
|
34
34
|
def initialize
|
35
35
|
super
|
36
36
|
|
37
|
-
# Stores all issues with Issue#digest as the key as a way to deduplicate
|
38
|
-
# and group variations.
|
37
|
+
# Stores all issues with Issue#digest as the key as a way to deduplicate.
|
39
38
|
@collection = {}
|
40
39
|
|
41
40
|
# We also use this Set for deduplication in case #do_not_store has been
|
@@ -92,24 +91,11 @@ class Issues
|
|
92
91
|
end
|
93
92
|
|
94
93
|
# @return [Array<Issue>]
|
95
|
-
# All logged issues
|
94
|
+
# All logged issues.
|
96
95
|
def all
|
97
96
|
@collection.values
|
98
97
|
end
|
99
98
|
|
100
|
-
# @return [Array<Issue>]
|
101
|
-
# First variation of all issues (as solo issues) sorted by severity.
|
102
|
-
def summary
|
103
|
-
all.map { |issue| issue.variations.first.to_solo issue }.flatten.
|
104
|
-
sort_by(&:severity).reverse
|
105
|
-
end
|
106
|
-
|
107
|
-
# @return [Array<Issue>]
|
108
|
-
# All logged issues as solo objects, without variations.
|
109
|
-
def flatten
|
110
|
-
all.map { |issue| issue.variations.map { |v| v.to_solo issue } }.flatten
|
111
|
-
end
|
112
|
-
|
113
99
|
def each( &block )
|
114
100
|
all.each( &block )
|
115
101
|
end
|
@@ -124,7 +110,7 @@ class Issues
|
|
124
110
|
@digests.include? issue.digest
|
125
111
|
end
|
126
112
|
|
127
|
-
# @note Will deduplicate
|
113
|
+
# @note Will deduplicate issues.
|
128
114
|
#
|
129
115
|
# @param [Issue] issue
|
130
116
|
# Issue to push to the collection.
|
@@ -133,18 +119,16 @@ class Issues
|
|
133
119
|
def <<( issue )
|
134
120
|
notify_on_new_pre_deduplication( issue )
|
135
121
|
|
136
|
-
|
137
|
-
return self if include?( issue ) && issue.active?
|
122
|
+
return self if include?( issue )
|
138
123
|
|
139
|
-
|
124
|
+
digest = issue.digest
|
125
|
+
@digests << digest
|
140
126
|
|
141
127
|
synchronize do
|
142
128
|
notify_on_new( issue )
|
143
129
|
|
144
130
|
if store?
|
145
|
-
|
146
|
-
@collection[id] ||= issue.with_variations
|
147
|
-
@collection[id].variations << issue.as_variation
|
131
|
+
@collection[digest] = issue
|
148
132
|
end
|
149
133
|
end
|
150
134
|
|
@@ -165,11 +149,11 @@ class Issues
|
|
165
149
|
end
|
166
150
|
|
167
151
|
def first
|
168
|
-
|
152
|
+
all.first
|
169
153
|
end
|
170
154
|
|
171
155
|
def last
|
172
|
-
|
156
|
+
all.last
|
173
157
|
end
|
174
158
|
|
175
159
|
def any?
|
data/lib/arachni/element/base.rb
CHANGED
@@ -24,9 +24,13 @@ module Capabilities
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
file = File.dirname( __FILE__ )
|
28
|
+
# Need to be loaded in order.
|
29
|
+
%w(inputtable submittable mutable auditable analyzable).each do |name|
|
30
|
+
require_relative "#{file}/capabilities/#{name}.rb"
|
31
|
+
end
|
32
|
+
# Load the rest automatically.
|
33
|
+
Dir.glob( "#{file}/capabilities/*.rb" ).each { |f| require f }
|
30
34
|
|
31
35
|
# Base class for all element types.
|
32
36
|
#
|
@@ -166,6 +170,8 @@ class Base
|
|
166
170
|
data.delete 'scope'
|
167
171
|
data['class'] = self.class.to_s
|
168
172
|
|
173
|
+
data['initialization_options'] = initialization_options
|
174
|
+
|
169
175
|
if data['initialization_options'].is_a? Hash
|
170
176
|
data['initialization_options'] =
|
171
177
|
data['initialization_options'].my_stringify_keys(false)
|
@@ -6,19 +6,15 @@
|
|
6
6
|
web site for more information on licensing and terms of use.
|
7
7
|
=end
|
8
8
|
|
9
|
-
require_relative 'auditable'
|
10
|
-
|
11
9
|
module Arachni
|
12
10
|
module Element::Capabilities
|
13
11
|
|
14
12
|
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
15
13
|
module Analyzable
|
16
|
-
|
17
|
-
|
18
|
-
# Load and include all available analysis/audit techniques.
|
14
|
+
# Load all available analysis techniques.
|
19
15
|
Dir.glob( File.dirname( __FILE__ ) + '/analyzable/*.rb' ).each { |f| require f }
|
20
16
|
|
21
|
-
include
|
17
|
+
include Signature
|
22
18
|
include Timeout
|
23
19
|
include Differential
|
24
20
|
|
@@ -6,6 +6,8 @@
|
|
6
6
|
web site for more information on licensing and terms of use.
|
7
7
|
=end
|
8
8
|
|
9
|
+
require_relative '../mutable'
|
10
|
+
|
9
11
|
module Arachni
|
10
12
|
module Element::Capabilities
|
11
13
|
module Analyzable
|
@@ -28,7 +30,7 @@ module Differential
|
|
28
30
|
end
|
29
31
|
|
30
32
|
DIFFERENTIAL_OPTIONS = {
|
31
|
-
format: [Mutable::Format::STRAIGHT],
|
33
|
+
format: [Arachni::Element::Capabilities::Mutable::Format::STRAIGHT],
|
32
34
|
|
33
35
|
# Amount of refinement operations to remove context-irrelevant dynamic
|
34
36
|
# content -- like banners etc.
|
@@ -104,6 +106,12 @@ module Differential
|
|
104
106
|
def differential_analysis( opts = {} )
|
105
107
|
return if self.inputs.empty?
|
106
108
|
|
109
|
+
with_missing_values = Set.new( self.inputs.select { |k, v| v.to_s.empty? }.keys )
|
110
|
+
if self.inputs.size == with_missing_values.size
|
111
|
+
print_debug 'Differential analysis: Inputs are missing default values.'
|
112
|
+
return false
|
113
|
+
end
|
114
|
+
|
107
115
|
return false if audited? audit_id
|
108
116
|
audited audit_id
|
109
117
|
|
@@ -115,6 +123,9 @@ module Differential
|
|
115
123
|
|
116
124
|
@differential_analysis_options = opts.dup
|
117
125
|
opts = self.class::MUTATION_OPTIONS.merge( DIFFERENTIAL_OPTIONS.merge( opts ) )
|
126
|
+
opts[:skip_like] = proc do |mutation|
|
127
|
+
with_missing_values.include? mutation.affected_input_name
|
128
|
+
end
|
118
129
|
|
119
130
|
mutations_size = 0
|
120
131
|
each_mutation( opts[:false], opts ) { mutations_size += 1 }
|
@@ -196,13 +207,15 @@ module Differential
|
|
196
207
|
@data_gathering[:controls][altered_hash] = true
|
197
208
|
end
|
198
209
|
|
210
|
+
body = res.body.gsub( elem.seed, '' )
|
211
|
+
|
199
212
|
# Create a signature from the response body and refine it with
|
200
213
|
# subsequent ones to remove noise (like context-irrelevant dynamic
|
201
214
|
# content such as banners etc.).
|
202
215
|
signatures[:controls][altered_hash] =
|
203
216
|
signatures[:controls][altered_hash] ?
|
204
|
-
signatures[:controls][altered_hash].refine!(
|
205
|
-
Support::Signature.new(
|
217
|
+
signatures[:controls][altered_hash].refine!(body) :
|
218
|
+
Support::Signature.new(body)
|
206
219
|
|
207
220
|
increase_received_responses( opts, signatures )
|
208
221
|
end
|
@@ -264,13 +277,15 @@ module Differential
|
|
264
277
|
|
265
278
|
signatures[pair_hash][altered_hash][:injected_string] ||= expr
|
266
279
|
|
280
|
+
body = res.body.gsub( elem.seed, '' )
|
281
|
+
|
267
282
|
# Create a signature from the response body and refine it with
|
268
283
|
# subsequent ones to remove noise (like context-irrelevant dynamic
|
269
284
|
# content such as banners etc.).
|
270
285
|
signatures[pair_hash][altered_hash][bool] =
|
271
286
|
signatures[pair_hash][altered_hash][bool] ?
|
272
|
-
signatures[pair_hash][altered_hash][bool].refine!(
|
273
|
-
Support::Signature.new(
|
287
|
+
signatures[pair_hash][altered_hash][bool].refine!(body) :
|
288
|
+
Support::Signature.new(body)
|
274
289
|
|
275
290
|
signature_sieve( altered_hash, signatures, pair_hash )
|
276
291
|
|
@@ -326,13 +341,15 @@ module Differential
|
|
326
341
|
" action '#{elem.action}'."
|
327
342
|
end
|
328
343
|
|
344
|
+
body = res.body.gsub( elem.seed, '' )
|
345
|
+
|
329
346
|
# Create a signature from the response body and refine it with
|
330
347
|
# subsequent ones to remove noise (like context-irrelevant dynamic
|
331
348
|
# content such as banners etc.).
|
332
349
|
signatures[:controls_verification][altered_hash] =
|
333
350
|
signatures[:controls_verification][altered_hash] ?
|
334
|
-
signatures[:controls_verification][altered_hash].refine!(
|
335
|
-
Support::Signature.new(
|
351
|
+
signatures[:controls_verification][altered_hash].refine!(body) :
|
352
|
+
Support::Signature.new(body)
|
336
353
|
|
337
354
|
received_responses += 1
|
338
355
|
next if received_responses != @data_gathering[:mutations_size]
|
@@ -13,13 +13,13 @@ module Analyzable
|
|
13
13
|
# Looks for specific substrings or patterns in response bodies.
|
14
14
|
#
|
15
15
|
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
16
|
-
module
|
16
|
+
module Signature
|
17
17
|
|
18
|
-
|
18
|
+
SIGNATURE_CACHE = {
|
19
19
|
match: Support::Cache::LeastRecentlyPushed.new( 10_000 )
|
20
20
|
}
|
21
21
|
|
22
|
-
|
22
|
+
SIGNATURE_OPTIONS = {
|
23
23
|
# The regular expression to match against the response body.
|
24
24
|
#
|
25
25
|
# Alternatively, you can use the :substring option.
|
@@ -43,13 +43,13 @@ module Taint
|
|
43
43
|
longest_word_optimization: false
|
44
44
|
}
|
45
45
|
|
46
|
-
# Performs
|
46
|
+
# Performs signatures analysis and logs an issue should there be one.
|
47
47
|
#
|
48
48
|
# It logs an issue when:
|
49
49
|
#
|
50
50
|
# * `:match` == nil AND `:regexp` matches the response body
|
51
|
-
# * `:match
|
52
|
-
# * `:substring`exists in the response body
|
51
|
+
# * `:match` != nil AND `:regexp` match == `:match`
|
52
|
+
# * `:substring` exists in the response body
|
53
53
|
#
|
54
54
|
# @param [String, Array<String>, Hash{Symbol => <String, Array<String>>}] payloads
|
55
55
|
# Payloads to inject, if given:
|
@@ -63,16 +63,16 @@ module Taint
|
|
63
63
|
# for the {Element::Capabilities::Submittable#action resource} to be audited.
|
64
64
|
# @param [Hash] opts
|
65
65
|
# Options as described in {Arachni::Check::Auditor::OPTIONS} and
|
66
|
-
# {
|
66
|
+
# {SIGNATURE_OPTIONS}.
|
67
67
|
#
|
68
68
|
# @return [Bool]
|
69
69
|
# `true` if the audit was scheduled successfully, `false` otherwise (like
|
70
70
|
# if the resource is out of scope).
|
71
|
-
def
|
71
|
+
def signature_analysis( payloads, opts = { } )
|
72
72
|
return false if self.inputs.empty?
|
73
73
|
|
74
74
|
if scope.out?
|
75
|
-
print_debug '
|
75
|
+
print_debug 'Signature analysis: Element is out of scope,' <<
|
76
76
|
" skipping: #{audit_id}"
|
77
77
|
return false
|
78
78
|
end
|
@@ -81,8 +81,8 @@ module Taint
|
|
81
81
|
# we've evaluated our control response.
|
82
82
|
@candidate_issues = []
|
83
83
|
|
84
|
-
# Perform the
|
85
|
-
opts = self.class::OPTIONS.merge(
|
84
|
+
# Perform the analysis.
|
85
|
+
opts = self.class::OPTIONS.merge( SIGNATURE_OPTIONS.merge( opts ) )
|
86
86
|
audit( payloads, opts ) { |response| get_matches( response ) }
|
87
87
|
end
|
88
88
|
|
@@ -104,7 +104,7 @@ module Taint
|
|
104
104
|
|
105
105
|
def match_patterns( patterns, matcher, response, opts )
|
106
106
|
k = [patterns, response.body]
|
107
|
-
return if
|
107
|
+
return if SIGNATURE_CACHE[:match][k] == false
|
108
108
|
|
109
109
|
if opts[:longest_word_optimization]
|
110
110
|
opts[:downcased_body] = response.body.downcase
|
@@ -114,7 +114,7 @@ module Taint
|
|
114
114
|
when Regexp, String, Array
|
115
115
|
[patterns].flatten.compact.each do |pattern|
|
116
116
|
res = matcher.call( pattern, response, opts )
|
117
|
-
|
117
|
+
SIGNATURE_CACHE[:match][k] ||= !!res
|
118
118
|
end
|
119
119
|
|
120
120
|
when Hash
|
@@ -122,7 +122,7 @@ module Taint
|
|
122
122
|
[patterns[opts[:platform]]].flatten.compact.each do |p|
|
123
123
|
[p].flatten.compact.each do |pattern|
|
124
124
|
res = matcher.call( pattern, response, opts )
|
125
|
-
|
125
|
+
SIGNATURE_CACHE[:match][k] ||= !!res
|
126
126
|
end
|
127
127
|
end
|
128
128
|
|
@@ -133,7 +133,7 @@ module Taint
|
|
133
133
|
|
134
134
|
[p].flatten.compact.each do |pattern|
|
135
135
|
res = matcher.call( pattern, response, dopts )
|
136
|
-
|
136
|
+
SIGNATURE_CACHE[:match][k] ||= !!res
|
137
137
|
end
|
138
138
|
end
|
139
139
|
end
|
@@ -149,7 +149,7 @@ module Taint
|
|
149
149
|
|
150
150
|
[p].flatten.compact.each do |pattern|
|
151
151
|
res = matcher.call( pattern, response, dopts )
|
152
|
-
|
152
|
+
SIGNATURE_CACHE[:match][k] ||= !!res
|
153
153
|
end
|
154
154
|
end
|
155
155
|
end
|
@@ -159,9 +159,9 @@ module Taint
|
|
159
159
|
return if substring.to_s.empty?
|
160
160
|
|
161
161
|
k = [substring, response.body]
|
162
|
-
return if
|
162
|
+
return if SIGNATURE_CACHE[:match][k] == false
|
163
163
|
|
164
|
-
|
164
|
+
SIGNATURE_CACHE[:match][k] = includes = response.body.include?( substring )
|
165
165
|
return if !includes || ignore?( response, opts )
|
166
166
|
|
167
167
|
@candidate_issues << {
|
@@ -178,7 +178,7 @@ module Taint
|
|
178
178
|
|
179
179
|
def match_regexp_and_log( regexp, response, opts )
|
180
180
|
k = [regexp, response.body]
|
181
|
-
return if
|
181
|
+
return if SIGNATURE_CACHE[:match][k] == false
|
182
182
|
|
183
183
|
regexp = regexp.is_a?( Regexp ) ? regexp :
|
184
184
|
Regexp.new( regexp.to_s, Regexp::IGNORECASE )
|
@@ -192,7 +192,7 @@ module Taint
|
|
192
192
|
|
193
193
|
match_data = match_data[0].to_s
|
194
194
|
|
195
|
-
|
195
|
+
SIGNATURE_CACHE[:match][k] = !match_data.empty?
|
196
196
|
|
197
197
|
return if match_data.empty? || ignore?( response, opts )
|
198
198
|
|
@@ -220,13 +220,13 @@ module Taint
|
|
220
220
|
return if @setup_verification_callbacks
|
221
221
|
@setup_verification_callbacks = true
|
222
222
|
|
223
|
-
# Go over the issues
|
224
|
-
#
|
223
|
+
# Go over the issues to ensure that the signature that identified them
|
224
|
+
# does not match by default.
|
225
225
|
http.after_run do
|
226
226
|
@setup_verification_callbacks = false
|
227
227
|
next if @candidate_issues.empty?
|
228
228
|
|
229
|
-
# Grab
|
229
|
+
# Grab the default response.
|
230
230
|
submit do |response|
|
231
231
|
# Something has gone wrong, timed-out request or closed connection.
|
232
232
|
# If we can't verify the issue bail out...
|