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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +195 -0
- data/Gemfile +4 -4
- data/LICENSE.md +1 -1
- data/README.md +7 -3
- data/Rakefile +1 -43
- data/arachni.gemspec +35 -30
- data/bin/arachni +1 -1
- data/bin/arachni_console +1 -1
- data/bin/arachni_multi +6 -1
- data/bin/arachni_reporter +1 -1
- data/bin/arachni_reproduce +12 -0
- data/bin/arachni_rest_server +1 -1
- data/bin/arachni_restore +1 -1
- data/bin/arachni_rpc +6 -1
- data/bin/arachni_rpcd +1 -1
- data/bin/arachni_rpcd_monitor +6 -1
- data/bin/arachni_script +1 -1
- data/components/checks/active/code_injection.rb +1 -1
- data/components/checks/active/code_injection_php_input_wrapper.rb +1 -1
- data/components/checks/active/code_injection_timing.rb +1 -1
- data/components/checks/active/csrf.rb +20 -75
- data/components/checks/active/file_inclusion.rb +1 -1
- data/components/checks/active/ldap_injection.rb +1 -1
- data/components/checks/active/no_sql_injection.rb +1 -1
- data/components/checks/active/no_sql_injection_differential.rb +3 -3
- data/components/checks/active/os_cmd_injection.rb +1 -1
- data/components/checks/active/os_cmd_injection_timing.rb +1 -1
- data/components/checks/active/path_traversal.rb +3 -3
- data/components/checks/active/response_splitting.rb +1 -1
- data/components/checks/active/rfi.rb +1 -1
- data/components/checks/active/session_fixation.rb +1 -1
- data/components/checks/active/source_code_disclosure.rb +1 -1
- data/components/checks/active/sql_injection/regexps/hsqldb.yaml +1 -0
- data/components/checks/active/sql_injection/substrings/hsqldb +1 -0
- data/components/checks/active/sql_injection/substrings/java +4 -0
- data/components/checks/active/sql_injection/substrings/oracle +0 -1
- data/components/checks/active/sql_injection/substrings/sqlite +1 -0
- data/components/checks/active/sql_injection.rb +1 -1
- data/components/checks/active/sql_injection_differential.rb +3 -3
- data/components/checks/active/sql_injection_timing.rb +1 -1
- data/components/checks/active/trainer.rb +1 -1
- data/components/checks/active/unvalidated_redirect.rb +34 -11
- data/components/checks/active/unvalidated_redirect_dom.rb +4 -4
- data/components/checks/active/xpath_injection.rb +1 -1
- data/components/checks/active/xss.rb +54 -29
- data/components/checks/active/xss_dom.rb +15 -11
- data/components/checks/active/xss_dom_script_context.rb +4 -6
- data/components/checks/active/xss_event.rb +46 -34
- data/components/checks/active/xss_path.rb +9 -6
- data/components/checks/active/xss_script_context.rb +100 -47
- data/components/checks/active/xss_tag.rb +41 -15
- data/components/checks/active/xxe.rb +1 -1
- data/components/checks/passive/allowed_methods.rb +1 -1
- data/components/checks/passive/backdoors.rb +1 -1
- data/components/checks/passive/backup_directories.rb +15 -3
- data/components/checks/passive/backup_files.rb +39 -6
- data/components/checks/passive/common_admin_interfaces/admin-panels.txt +1 -0
- data/components/checks/passive/common_admin_interfaces.rb +1 -1
- data/components/checks/passive/common_directories/directories.txt +1 -0
- data/components/checks/passive/common_directories.rb +1 -1
- data/components/checks/passive/common_files.rb +1 -1
- data/components/checks/passive/directory_listing.rb +1 -1
- data/components/checks/passive/grep/captcha.rb +8 -9
- data/components/checks/passive/grep/cookie_set_for_parent_domain.rb +1 -1
- data/components/checks/passive/grep/credit_card.rb +1 -1
- data/components/checks/passive/grep/cvs_svn_users.rb +1 -1
- data/components/checks/passive/grep/emails.rb +1 -1
- data/components/checks/passive/grep/form_upload.rb +3 -5
- data/components/checks/passive/grep/hsts.rb +1 -1
- data/components/checks/passive/grep/html_objects.rb +1 -1
- data/components/checks/passive/grep/http_only_cookies.rb +1 -1
- data/components/checks/passive/grep/insecure_cookies.rb +5 -5
- data/components/checks/passive/grep/insecure_cors_policy.rb +1 -1
- data/components/checks/passive/grep/mixed_resource.rb +4 -4
- data/components/checks/passive/grep/password_autocomplete.rb +1 -1
- data/components/checks/passive/grep/private_ip.rb +1 -1
- data/components/checks/passive/grep/ssn.rb +1 -1
- data/components/checks/passive/grep/unencrypted_password_forms.rb +3 -3
- data/components/checks/passive/grep/x_frame_options.rb +4 -4
- data/components/checks/passive/htaccess_limit.rb +1 -1
- data/components/checks/passive/http_put.rb +1 -1
- data/components/checks/passive/insecure_client_access_policy.rb +2 -2
- data/components/checks/passive/insecure_cross_domain_policy_access.rb +2 -2
- data/components/checks/passive/insecure_cross_domain_policy_headers.rb +2 -2
- data/components/checks/passive/interesting_responses.rb +1 -1
- data/components/checks/passive/localstart_asp.rb +1 -1
- data/components/checks/passive/origin_spoof_access_restriction_bypass.rb +1 -1
- data/components/checks/passive/webdav.rb +1 -1
- data/components/checks/passive/xst.rb +10 -12
- data/components/fingerprinters/frameworks/aspx_mvc.rb +1 -1
- data/components/fingerprinters/frameworks/cakephp.rb +1 -1
- data/components/fingerprinters/frameworks/cherrypy.rb +1 -1
- data/components/fingerprinters/frameworks/django.rb +1 -1
- data/components/fingerprinters/frameworks/jsf.rb +1 -1
- data/components/fingerprinters/frameworks/nette.rb +1 -1
- data/components/fingerprinters/frameworks/rack.rb +1 -1
- data/components/fingerprinters/frameworks/rails.rb +1 -1
- data/components/fingerprinters/frameworks/symfony.rb +1 -1
- data/components/fingerprinters/languages/asp.rb +1 -1
- data/components/fingerprinters/languages/aspx.rb +1 -1
- data/components/fingerprinters/languages/java.rb +1 -1
- data/components/fingerprinters/languages/php.rb +1 -1
- data/components/fingerprinters/languages/python.rb +1 -1
- data/components/fingerprinters/languages/ruby.rb +1 -1
- data/components/fingerprinters/os/bsd.rb +1 -1
- data/components/fingerprinters/os/linux.rb +1 -1
- data/components/fingerprinters/os/solaris.rb +1 -1
- data/components/fingerprinters/os/unix.rb +1 -1
- data/components/fingerprinters/os/windows.rb +1 -1
- data/components/fingerprinters/servers/apache.rb +1 -1
- data/components/fingerprinters/servers/gunicorn.rb +1 -1
- data/components/fingerprinters/servers/iis.rb +1 -1
- data/components/fingerprinters/servers/jetty.rb +1 -1
- data/components/fingerprinters/servers/nginx.rb +1 -1
- data/components/fingerprinters/servers/tomcat.rb +1 -1
- data/components/path_extractors/anchors.rb +3 -5
- data/components/path_extractors/areas.rb +3 -4
- data/components/path_extractors/comments.rb +4 -5
- data/components/path_extractors/data_url.rb +4 -5
- data/components/path_extractors/forms.rb +3 -4
- data/components/path_extractors/frames.rb +3 -5
- data/components/path_extractors/generic.rb +3 -1
- data/components/path_extractors/links.rb +3 -4
- data/components/path_extractors/meta_refresh.rb +11 -17
- data/components/path_extractors/scripts.rb +18 -15
- data/components/plugins/autologin.rb +3 -2
- data/components/plugins/beep_notify.rb +1 -1
- data/components/plugins/content_types.rb +1 -1
- data/components/plugins/cookie_collector.rb +1 -1
- data/components/plugins/debug/browser_cluster_job_monitor.rb +60 -0
- data/components/plugins/defaults/autothrottle.rb +1 -1
- data/components/plugins/defaults/healthmap.rb +3 -1
- data/components/plugins/defaults/meta/remedies/discovery.rb +1 -1
- data/components/plugins/defaults/meta/remedies/timing_attacks.rb +1 -1
- data/components/plugins/defaults/meta/uniformity.rb +1 -1
- data/components/plugins/email_notify.rb +26 -9
- data/components/plugins/exec.rb +1 -1
- data/components/plugins/form_dicattack.rb +3 -4
- data/components/plugins/headers_collector.rb +1 -1
- data/components/plugins/http_dicattack.rb +4 -5
- data/components/plugins/login_script.rb +2 -2
- data/components/plugins/metrics.rb +44 -18
- data/components/plugins/page_dump.rb +60 -0
- data/components/plugins/proxy/panel/verify_login_sequence.html.erb +1 -1
- data/components/plugins/proxy/template_scope.rb +6 -1
- data/components/plugins/proxy.rb +44 -31
- data/components/plugins/rate_limiter.rb +80 -0
- data/components/plugins/restrict_to_dom_state.rb +1 -1
- data/components/plugins/script.rb +1 -1
- data/components/plugins/uncommon_headers.rb +1 -1
- data/components/plugins/vector_collector.rb +1 -1
- data/components/plugins/vector_feed.rb +1 -1
- data/components/plugins/waf_detector.rb +3 -3
- data/components/plugins/webhook_notify.rb +99 -0
- data/components/reporters/ap.rb +1 -1
- data/components/reporters/html/default/configuration.erb +2 -0
- data/components/reporters/html/default.erb +3 -2
- data/components/reporters/html.rb +5 -8
- data/components/reporters/json.rb +1 -1
- data/components/reporters/marshal.rb +1 -1
- data/components/reporters/plugin_formatters/html/autologin.rb +1 -1
- data/components/reporters/plugin_formatters/html/content_types.rb +1 -1
- data/components/reporters/plugin_formatters/html/cookie_collector.rb +1 -1
- data/components/reporters/plugin_formatters/html/exec.rb +1 -1
- data/components/reporters/plugin_formatters/html/form_dicattack.rb +1 -1
- data/components/reporters/plugin_formatters/html/healthmap.rb +1 -1
- data/components/reporters/plugin_formatters/html/http_dicattack.rb +1 -1
- data/components/reporters/plugin_formatters/html/login_script.rb +1 -1
- data/components/reporters/plugin_formatters/html/metrics.rb +46 -1
- data/components/reporters/plugin_formatters/html/uncommon_headers.rb +1 -1
- data/components/reporters/plugin_formatters/html/uniformity.rb +1 -1
- data/components/reporters/plugin_formatters/html/vector_collector.rb +1 -1
- data/components/reporters/plugin_formatters/html/waf_detector.rb +1 -1
- data/components/reporters/plugin_formatters/stdout/autologin.rb +1 -1
- data/components/reporters/plugin_formatters/stdout/content_types.rb +1 -1
- data/components/reporters/plugin_formatters/stdout/cookie_collector.rb +1 -1
- data/components/reporters/plugin_formatters/stdout/exec.rb +1 -1
- data/components/reporters/plugin_formatters/stdout/form_dicattack.rb +1 -1
- data/components/reporters/plugin_formatters/stdout/healthmap.rb +1 -1
- data/components/reporters/plugin_formatters/stdout/http_dicattack.rb +1 -1
- data/components/reporters/plugin_formatters/stdout/login_script.rb +1 -1
- data/components/reporters/plugin_formatters/stdout/metrics.rb +11 -1
- data/components/reporters/plugin_formatters/stdout/uncommon_headers.rb +1 -1
- data/components/reporters/plugin_formatters/stdout/uniformity.rb +1 -1
- data/components/reporters/plugin_formatters/stdout/vector_collector.rb +1 -1
- data/components/reporters/plugin_formatters/stdout/waf_detector.rb +1 -1
- data/components/reporters/plugin_formatters/xml/autologin.rb +1 -1
- data/components/reporters/plugin_formatters/xml/content_types.rb +10 -7
- data/components/reporters/plugin_formatters/xml/cookie_collector.rb +6 -3
- data/components/reporters/plugin_formatters/xml/exec.rb +1 -1
- data/components/reporters/plugin_formatters/xml/form_dicattack.rb +1 -1
- data/components/reporters/plugin_formatters/xml/healthmap.rb +1 -1
- data/components/reporters/plugin_formatters/xml/http_dicattack.rb +1 -1
- data/components/reporters/plugin_formatters/xml/login_script.rb +1 -1
- data/components/reporters/plugin_formatters/xml/metrics.rb +1 -1
- data/components/reporters/plugin_formatters/xml/uncommon_headers.rb +5 -2
- data/components/reporters/plugin_formatters/xml/uniformity.rb +1 -1
- data/components/reporters/plugin_formatters/xml/vector_collector.rb +8 -5
- data/components/reporters/plugin_formatters/xml/waf_detector.rb +1 -1
- data/components/reporters/stdout.rb +3 -2
- data/components/reporters/txt.rb +1 -1
- data/components/reporters/xml/schema.xsd +29 -13
- data/components/reporters/xml.rb +40 -23
- data/components/reporters/yaml.rb +1 -1
- data/config/write_paths.yml +4 -0
- data/lib/arachni/banner.rb +1 -1
- data/lib/arachni/browser/element_locator.rb +9 -5
- data/lib/arachni/browser/javascript/dom_monitor.rb +1 -1
- data/lib/arachni/browser/javascript/proxy/stub.rb +1 -1
- data/lib/arachni/browser/javascript/proxy.rb +1 -1
- data/lib/arachni/browser/javascript/scripts/dom_monitor.js +329 -72
- data/lib/arachni/browser/javascript/scripts/polyfills.js +0 -28
- data/lib/arachni/browser/javascript/scripts/taint_tracer.js +81 -25
- data/lib/arachni/browser/javascript/taint_tracer/frame/called_function.rb +1 -1
- data/lib/arachni/browser/javascript/taint_tracer/frame.rb +1 -1
- data/lib/arachni/browser/javascript/taint_tracer/sink/base.rb +1 -1
- data/lib/arachni/browser/javascript/taint_tracer/sink/data_flow.rb +1 -1
- data/lib/arachni/browser/javascript/taint_tracer/sink/execution_flow.rb +1 -1
- data/lib/arachni/browser/javascript/taint_tracer.rb +1 -1
- data/lib/arachni/browser/javascript.rb +111 -198
- data/lib/arachni/browser.rb +309 -382
- data/lib/arachni/browser_cluster/job/result.rb +1 -1
- data/lib/arachni/browser_cluster/job.rb +9 -2
- data/lib/arachni/browser_cluster/jobs/browser_provider.rb +8 -2
- data/lib/arachni/browser_cluster/jobs/dom_exploration/event_trigger/result.rb +1 -1
- data/lib/arachni/browser_cluster/jobs/dom_exploration/event_trigger.rb +1 -1
- data/lib/arachni/browser_cluster/jobs/dom_exploration/result.rb +1 -1
- data/lib/arachni/browser_cluster/jobs/dom_exploration.rb +13 -1
- data/lib/arachni/browser_cluster/jobs/taint_trace/event_trigger/result.rb +1 -1
- data/lib/arachni/browser_cluster/jobs/taint_trace/event_trigger.rb +1 -1
- data/lib/arachni/browser_cluster/jobs/taint_trace/result.rb +1 -1
- data/lib/arachni/browser_cluster/jobs/taint_trace.rb +1 -1
- data/lib/arachni/browser_cluster/worker.rb +97 -87
- data/lib/arachni/browser_cluster.rb +79 -62
- data/lib/arachni/check/auditor.rb +161 -155
- data/lib/arachni/check/base.rb +1 -1
- data/lib/arachni/check/manager.rb +1 -1
- data/lib/arachni/check.rb +1 -1
- data/lib/arachni/component/base.rb +3 -1
- data/lib/arachni/component/manager.rb +1 -1
- data/lib/arachni/component/options/address.rb +1 -1
- data/lib/arachni/component/options/base.rb +1 -1
- data/lib/arachni/component/options/bool.rb +1 -1
- data/lib/arachni/component/options/float.rb +1 -1
- data/lib/arachni/component/options/int.rb +1 -1
- data/lib/arachni/component/options/multiple_choice.rb +1 -1
- data/lib/arachni/component/options/object.rb +1 -1
- data/lib/arachni/component/options/path.rb +1 -1
- data/lib/arachni/component/options/port.rb +1 -1
- data/lib/arachni/component/options/string.rb +1 -1
- data/lib/arachni/component/options/url.rb +1 -1
- data/lib/arachni/component/options.rb +1 -1
- data/lib/arachni/component/output.rb +8 -2
- data/lib/arachni/component/utilities.rb +1 -1
- data/lib/arachni/component.rb +1 -1
- data/lib/arachni/data/framework/rpc.rb +2 -2
- data/lib/arachni/data/framework.rb +3 -2
- data/lib/arachni/data/issues.rb +1 -1
- data/lib/arachni/data/plugins.rb +1 -1
- data/lib/arachni/data/session.rb +1 -1
- data/lib/arachni/data.rb +1 -1
- data/lib/arachni/element/base.rb +1 -1
- data/lib/arachni/element/body.rb +1 -1
- data/lib/arachni/element/capabilities/analyzable/differential.rb +142 -175
- data/lib/arachni/element/capabilities/analyzable/signature.rb +40 -18
- data/lib/arachni/element/capabilities/analyzable/timeout.rb +1 -1
- data/lib/arachni/element/capabilities/analyzable.rb +1 -1
- data/lib/arachni/element/capabilities/auditable/buffered.rb +92 -0
- data/lib/arachni/element/capabilities/auditable/line_buffered.rb +103 -0
- data/lib/arachni/element/capabilities/auditable.rb +2 -8
- data/lib/arachni/element/capabilities/dom_only.rb +1 -1
- data/lib/arachni/element/capabilities/inputtable.rb +6 -2
- data/lib/arachni/element/capabilities/mutable.rb +1 -1
- data/lib/arachni/element/capabilities/refreshable.rb +1 -1
- data/lib/arachni/element/capabilities/submittable.rb +1 -1
- data/lib/arachni/element/capabilities/with_auditor/output.rb +4 -3
- data/lib/arachni/element/capabilities/with_auditor.rb +1 -1
- data/lib/arachni/element/capabilities/with_dom.rb +1 -1
- data/lib/arachni/element/capabilities/with_node.rb +3 -3
- data/lib/arachni/element/capabilities/with_scope/scope.rb +1 -1
- data/lib/arachni/element/capabilities/with_scope.rb +1 -1
- data/lib/arachni/element/capabilities/with_source.rb +2 -2
- data/lib/arachni/element/cookie/capabilities/inputtable.rb +1 -1
- data/lib/arachni/element/cookie/capabilities/mutable.rb +1 -1
- data/lib/arachni/element/cookie/capabilities/with_dom.rb +1 -1
- data/lib/arachni/element/cookie/dom.rb +1 -1
- data/lib/arachni/element/cookie.rb +49 -24
- data/lib/arachni/element/dom/capabilities/auditable.rb +44 -3
- data/lib/arachni/element/dom/capabilities/inputtable.rb +1 -1
- data/lib/arachni/element/dom/capabilities/locatable.rb +1 -1
- data/lib/arachni/element/dom/capabilities/mutable.rb +7 -3
- data/lib/arachni/element/dom/capabilities/submittable.rb +51 -22
- data/lib/arachni/element/dom.rb +1 -1
- data/lib/arachni/element/form/capabilities/auditable.rb +1 -1
- data/lib/arachni/element/form/capabilities/mutable.rb +16 -11
- data/lib/arachni/element/form/capabilities/submittable.rb +1 -1
- data/lib/arachni/element/form/capabilities/with_dom.rb +1 -1
- data/lib/arachni/element/form/dom.rb +1 -1
- data/lib/arachni/element/form.rb +21 -32
- data/lib/arachni/element/generic_dom.rb +1 -1
- data/lib/arachni/element/header/capabilities/inputtable.rb +1 -1
- data/lib/arachni/element/header/capabilities/mutable.rb +1 -1
- data/lib/arachni/element/header.rb +3 -1
- data/lib/arachni/element/json/capabilities/inputtable.rb +1 -1
- data/lib/arachni/element/json/capabilities/mutable.rb +1 -1
- data/lib/arachni/element/json.rb +4 -8
- data/lib/arachni/element/link/capabilities/auditable.rb +1 -1
- data/lib/arachni/element/link/capabilities/submittable.rb +1 -1
- data/lib/arachni/element/link/capabilities/with_dom.rb +1 -1
- data/lib/arachni/element/link/dom/capabilities/submittable.rb +1 -1
- data/lib/arachni/element/link/dom.rb +1 -1
- data/lib/arachni/element/link.rb +11 -30
- data/lib/arachni/element/link_template/capabilities/auditable.rb +1 -1
- data/lib/arachni/element/link_template/capabilities/inputtable.rb +1 -1
- data/lib/arachni/element/link_template/capabilities/with_dom.rb +1 -1
- data/lib/arachni/element/link_template/dom/capabilities/submittable.rb +1 -1
- data/lib/arachni/element/link_template/dom.rb +2 -2
- data/lib/arachni/element/link_template.rb +10 -19
- data/lib/arachni/element/nested_cookie/capabilities/submittable.rb +35 -0
- data/lib/arachni/element/nested_cookie.rb +370 -0
- data/lib/arachni/element/path.rb +1 -1
- data/lib/arachni/element/server.rb +11 -11
- data/lib/arachni/element/ui_form/dom.rb +1 -1
- data/lib/arachni/element/ui_form.rb +5 -6
- data/lib/arachni/element/ui_input/dom.rb +1 -1
- data/lib/arachni/element/ui_input.rb +4 -6
- data/lib/arachni/element/xml/capabilities/inputtable.rb +1 -1
- data/lib/arachni/element/xml/capabilities/mutable.rb +1 -1
- data/lib/arachni/element/xml.rb +3 -7
- data/lib/arachni/element_filter.rb +1 -1
- data/lib/arachni/error.rb +1 -1
- data/lib/arachni/ethon/easy.rb +1 -1
- data/lib/arachni/framework/parts/audit.rb +6 -1
- data/lib/arachni/framework/parts/browser.rb +14 -14
- data/lib/arachni/framework/parts/check.rb +1 -1
- data/lib/arachni/framework/parts/data.rb +1 -1
- data/lib/arachni/framework/parts/platform.rb +1 -1
- data/lib/arachni/framework/parts/plugin.rb +1 -1
- data/lib/arachni/framework/parts/report.rb +3 -3
- data/lib/arachni/framework/parts/scope.rb +1 -1
- data/lib/arachni/framework/parts/state.rb +1 -1
- data/lib/arachni/framework.rb +1 -1
- data/lib/arachni/http/client/dynamic_404_handler.rb +74 -16
- data/lib/arachni/http/client.rb +38 -11
- data/lib/arachni/http/cookie_jar.rb +13 -8
- data/lib/arachni/http/headers.rb +11 -5
- data/lib/arachni/http/message/scope.rb +1 -1
- data/lib/arachni/http/message.rb +10 -9
- data/lib/arachni/http/proxy_server/connection.rb +110 -82
- data/lib/arachni/http/proxy_server/ssl-interceptor-cacert.pem +18 -32
- data/lib/arachni/http/proxy_server/ssl-interceptor-cakey.pem +28 -49
- data/lib/arachni/http/proxy_server/ssl_interceptor.rb +8 -6
- data/lib/arachni/http/proxy_server/tunnel.rb +4 -4
- data/lib/arachni/http/proxy_server.rb +44 -11
- data/lib/arachni/http/request/scope.rb +1 -1
- data/lib/arachni/http/request.rb +239 -41
- data/lib/arachni/http/response/scope.rb +1 -1
- data/lib/arachni/http/response.rb +73 -10
- data/lib/arachni/http.rb +1 -1
- data/lib/arachni/issue/severity/base.rb +1 -1
- data/lib/arachni/issue/severity.rb +1 -1
- data/lib/arachni/issue.rb +42 -14
- data/lib/arachni/option_group.rb +1 -1
- data/lib/arachni/option_groups/audit.rb +11 -2
- data/lib/arachni/option_groups/browser_cluster.rb +32 -4
- data/lib/arachni/option_groups/datastore.rb +1 -1
- data/lib/arachni/option_groups/dispatcher.rb +1 -1
- data/lib/arachni/option_groups/http.rb +39 -10
- data/lib/arachni/option_groups/input.rb +1 -1
- data/lib/arachni/option_groups/output.rb +1 -1
- data/lib/arachni/option_groups/paths.rb +12 -1
- data/lib/arachni/option_groups/rpc.rb +1 -1
- data/lib/arachni/option_groups/scope.rb +58 -4
- data/lib/arachni/option_groups/session.rb +1 -1
- data/lib/arachni/option_groups/snapshot.rb +1 -1
- data/lib/arachni/option_groups.rb +1 -1
- data/lib/arachni/options.rb +23 -4
- data/lib/arachni/page/dom/transition.rb +5 -2
- data/lib/arachni/page/dom.rb +46 -54
- data/lib/arachni/page/scope.rb +1 -1
- data/lib/arachni/page.rb +10 -8
- data/lib/arachni/parser/document.rb +34 -0
- data/lib/arachni/parser/extractors/base.rb +48 -0
- data/lib/arachni/parser/nodes/base.rb +22 -0
- data/lib/arachni/parser/nodes/comment.rb +32 -0
- data/lib/arachni/parser/nodes/element/with_attributes/attributes.rb +31 -0
- data/lib/arachni/parser/nodes/element/with_attributes.rb +35 -0
- data/lib/arachni/parser/nodes/element.rb +48 -0
- data/lib/arachni/parser/nodes/text.rb +32 -0
- data/lib/arachni/parser/nodes/with_value.rb +29 -0
- data/lib/arachni/parser/sax.rb +76 -0
- data/lib/arachni/parser/with_children/search.rb +92 -0
- data/lib/arachni/parser/with_children.rb +35 -0
- data/lib/arachni/parser.rb +181 -78
- data/lib/arachni/platform/fingerprinter.rb +1 -1
- data/lib/arachni/platform/list.rb +1 -1
- data/lib/arachni/platform/manager.rb +2 -2
- data/lib/arachni/platform.rb +1 -1
- data/lib/arachni/plugin/base.rb +2 -2
- data/lib/arachni/plugin/formatter.rb +1 -1
- data/lib/arachni/plugin/manager.rb +8 -5
- data/lib/arachni/plugin.rb +1 -1
- data/lib/arachni/processes/dispatchers.rb +1 -1
- data/lib/arachni/processes/executables/base.rb +2 -1
- data/lib/arachni/processes/executables/browser.rb +0 -2
- data/lib/arachni/processes/helpers/dispatchers.rb +1 -1
- data/lib/arachni/processes/helpers/instances.rb +1 -1
- data/lib/arachni/processes/helpers/processes.rb +1 -1
- data/lib/arachni/processes/helpers.rb +1 -1
- data/lib/arachni/processes/instances.rb +1 -1
- data/lib/arachni/processes/manager.rb +18 -9
- data/lib/arachni/processes.rb +1 -1
- data/lib/arachni/report.rb +8 -1
- data/lib/arachni/reporter/base.rb +1 -1
- data/lib/arachni/reporter/formatter_manager.rb +1 -1
- data/lib/arachni/reporter/manager.rb +1 -1
- data/lib/arachni/reporter/options.rb +1 -10
- data/lib/arachni/reporter.rb +1 -1
- data/lib/arachni/rest/server/instance_helpers.rb +10 -1
- data/lib/arachni/rest/server.rb +13 -1
- data/lib/arachni/rpc/client/base.rb +1 -1
- data/lib/arachni/rpc/client/dispatcher.rb +1 -1
- data/lib/arachni/rpc/client/instance/framework.rb +1 -1
- data/lib/arachni/rpc/client/instance/service.rb +1 -1
- data/lib/arachni/rpc/client/instance.rb +1 -1
- data/lib/arachni/rpc/serializer.rb +1 -1
- data/lib/arachni/rpc/server/active_options.rb +1 -1
- data/lib/arachni/rpc/server/base.rb +1 -1
- data/lib/arachni/rpc/server/check/manager.rb +1 -1
- data/lib/arachni/rpc/server/dispatcher/node.rb +1 -1
- data/lib/arachni/rpc/server/dispatcher/service.rb +1 -1
- data/lib/arachni/rpc/server/dispatcher.rb +1 -1
- data/lib/arachni/rpc/server/framework/distributor.rb +1 -1
- data/lib/arachni/rpc/server/framework/master.rb +1 -1
- data/lib/arachni/rpc/server/framework/multi_instance.rb +1 -1
- data/lib/arachni/rpc/server/framework/slave.rb +1 -1
- data/lib/arachni/rpc/server/framework.rb +1 -1
- data/lib/arachni/rpc/server/instance.rb +1 -1
- data/lib/arachni/rpc/server/output.rb +1 -1
- data/lib/arachni/rpc/server/plugin/manager.rb +1 -1
- data/lib/arachni/ruby/array.rb +1 -1
- data/lib/arachni/ruby/hash.rb +1 -1
- data/lib/arachni/ruby/object.rb +1 -1
- data/lib/arachni/ruby/set.rb +1 -1
- data/lib/arachni/ruby/string.rb +9 -5
- data/lib/arachni/ruby/webrick/cookie.rb +1 -1
- data/lib/arachni/ruby/webrick/httprequest.rb +1 -1
- data/lib/arachni/ruby/webrick.rb +1 -1
- data/lib/arachni/ruby.rb +1 -1
- data/lib/arachni/scope.rb +1 -1
- data/lib/arachni/selenium/webdriver/element.rb +4 -4
- data/lib/arachni/selenium/webdriver/remote/typhoeus.rb +59 -0
- data/lib/arachni/session.rb +32 -13
- data/lib/arachni/snapshot.rb +2 -2
- data/lib/arachni/state/audit.rb +1 -1
- data/lib/arachni/state/element_filter.rb +1 -1
- data/lib/arachni/state/framework/rpc.rb +1 -1
- data/lib/arachni/state/framework.rb +1 -1
- data/lib/arachni/state/http.rb +2 -2
- data/lib/arachni/state/options.rb +1 -1
- data/lib/arachni/state/plugins.rb +1 -1
- data/lib/arachni/state.rb +1 -1
- data/lib/arachni/support/buffer/autoflush.rb +1 -1
- data/lib/arachni/support/buffer/base.rb +1 -1
- data/lib/arachni/support/buffer.rb +1 -1
- data/lib/arachni/support/cache/base.rb +1 -1
- data/lib/arachni/support/cache/least_cost_replacement.rb +1 -1
- data/lib/arachni/support/cache/least_recently_pushed.rb +1 -1
- data/lib/arachni/support/cache/least_recently_used.rb +1 -1
- data/lib/arachni/support/cache/preference.rb +1 -1
- data/lib/arachni/support/cache/random_replacement.rb +1 -1
- data/lib/arachni/support/cache.rb +1 -1
- data/lib/arachni/support/crypto/rsa_aes_cbc.rb +1 -1
- data/lib/arachni/support/crypto.rb +1 -1
- data/lib/arachni/support/database/base.rb +16 -10
- data/lib/arachni/support/database/hash.rb +1 -1
- data/lib/arachni/support/database/queue.rb +1 -1
- data/lib/arachni/support/database.rb +1 -1
- data/lib/arachni/support/glob.rb +1 -1
- data/lib/arachni/support/lookup/base.rb +1 -1
- data/lib/arachni/support/lookup/hash_set.rb +1 -1
- data/lib/arachni/support/lookup/moolb.rb +1 -1
- data/lib/arachni/support/lookup.rb +1 -1
- data/lib/arachni/support/mixins/observable.rb +1 -1
- data/lib/arachni/support/mixins/terminal.rb +1 -1
- data/lib/arachni/support/mixins.rb +1 -1
- data/lib/arachni/support/profiler.rb +52 -13
- data/lib/arachni/support/signature.rb +18 -6
- data/lib/arachni/support.rb +1 -1
- data/lib/arachni/trainer.rb +55 -39
- data/lib/arachni/ui/foo/output.rb +1 -1
- data/lib/arachni/uri/scope.rb +15 -13
- data/lib/arachni/uri.rb +129 -103
- data/lib/arachni/utilities.rb +10 -10
- data/lib/arachni/version.rb +1 -1
- data/lib/arachni.rb +1 -7
- data/lib/version +1 -1
- data/spec/arachni/browser/element_locator_spec.rb +42 -18
- data/spec/arachni/browser/javascript/dom_monitor_spec.rb +264 -109
- data/spec/arachni/browser/javascript/polyfills_spec.rb +0 -15
- data/spec/arachni/browser/javascript/proxy_spec.rb +0 -10
- data/spec/arachni/browser/javascript/taint_tracer_spec.rb +43 -118
- data/spec/arachni/browser/javascript_spec.rb +95 -60
- data/spec/arachni/browser_cluster/job_spec.rb +23 -8
- data/spec/arachni/browser_cluster/jobs/dom_exploration_spec.rb +6 -1
- data/spec/arachni/browser_cluster/worker_spec.rb +29 -87
- data/spec/arachni/browser_cluster_spec.rb +124 -43
- data/spec/arachni/browser_spec.rb +463 -421
- data/spec/arachni/check/auditor_spec.rb +162 -198
- data/spec/arachni/data/framework/rpc_spec.rb +1 -1
- data/spec/arachni/data/framework_spec.rb +1 -1
- data/spec/arachni/element/capabilities/analyzable/signature_spec.rb +46 -3
- data/spec/arachni/element/cookie/dom_spec.rb +1 -1
- data/spec/arachni/element/cookie_spec.rb +159 -64
- data/spec/arachni/element/form/dom_spec.rb +1 -1
- data/spec/arachni/element/form_spec.rb +101 -54
- data/spec/arachni/element/header_spec.rb +3 -1
- data/spec/arachni/element/json_spec.rb +2 -0
- data/spec/arachni/element/link/dom_spec.rb +2 -2
- data/spec/arachni/element/link_spec.rb +46 -15
- data/spec/arachni/element/link_template/dom_spec.rb +1 -1
- data/spec/arachni/element/link_template_spec.rb +36 -12
- data/spec/arachni/element/nested_cookie_spec.rb +687 -0
- data/spec/arachni/element/server_spec.rb +22 -5
- data/spec/arachni/element/ui_form/dom_spec.rb +1 -1
- data/spec/arachni/element/ui_form_spec.rb +2 -2
- data/spec/arachni/element/ui_input/dom_spec.rb +1 -1
- data/spec/arachni/element/ui_input_spec.rb +1 -1
- data/spec/arachni/element/xml_spec.rb +5 -3
- data/spec/arachni/framework/parts/audit_spec.rb +2 -14
- data/spec/arachni/framework/parts/data_spec.rb +0 -6
- data/spec/arachni/http/client/dynamic_404_handlers_spec.rb +126 -0
- data/spec/arachni/http/client_spec.rb +96 -36
- data/spec/arachni/http/cookie_jar_spec.rb +2 -2
- data/spec/arachni/http/headers_spec.rb +59 -12
- data/spec/arachni/http/proxy_server_spec.rb +58 -25
- data/spec/arachni/http/request_spec.rb +382 -35
- data/spec/arachni/http/response_spec.rb +135 -7
- data/spec/arachni/issue_spec.rb +21 -2
- data/spec/arachni/option_groups/browser_cluster_spec.rb +17 -0
- data/spec/arachni/option_groups/http_spec.rb +21 -6
- data/spec/arachni/option_groups/paths_spec.rb +23 -1
- data/spec/arachni/option_groups/scope_spec.rb +27 -7
- data/spec/arachni/options_spec.rb +8 -1
- data/spec/arachni/page/dom_spec.rb +20 -6
- data/spec/arachni/page_spec.rb +8 -7
- data/spec/arachni/parser/document_spec.rb +49 -0
- data/spec/arachni/parser/nodes/comment_spec.rb +24 -0
- data/spec/arachni/parser/nodes/element/with_attributes/attributes_spec.rb +40 -0
- data/spec/arachni/parser/nodes/element/with_attributes_spec.rb +50 -0
- data/spec/arachni/parser/nodes/element_spec.rb +18 -0
- data/spec/arachni/parser/nodes/text_spec.rb +24 -0
- data/spec/arachni/parser/sax_spec.rb +88 -0
- data/spec/arachni/parser/with_children/search_spec.rb +146 -0
- data/spec/arachni/parser/with_children_spec.rb +37 -0
- data/spec/arachni/parser_spec.rb +211 -27
- data/spec/arachni/platform/list_spec.rb +1 -2
- data/spec/arachni/report_spec.rb +9 -2
- data/spec/arachni/reporter/options_spec.rb +0 -14
- data/spec/arachni/rest/server_spec.rb +91 -8
- data/spec/arachni/rpc/server/active_options_spec.rb +1 -1
- data/spec/arachni/rpc/server/framework/distributor_spec.rb +6 -6
- data/spec/arachni/ruby/string_spec.rb +6 -0
- data/spec/arachni/session_spec.rb +69 -8
- data/spec/arachni/snapshot_spec.rb +1 -1
- data/spec/arachni/state/framework_spec.rb +2 -2
- data/spec/arachni/support/signature_spec.rb +58 -0
- data/spec/arachni/trainer_spec.rb +102 -21
- data/spec/arachni/uri_spec.rb +11 -8
- data/spec/arachni/utilities_spec.rb +3 -3
- data/spec/components/checks/active/code_injection_spec.rb +12 -7
- data/spec/components/checks/active/code_injection_timing_spec.rb +4 -3
- data/spec/components/checks/active/csrf_spec.rb +1 -21
- data/spec/components/checks/active/file_inclusion_spec.rb +15 -10
- data/spec/components/checks/active/ldap_injection_spec.rb +5 -4
- data/spec/components/checks/active/no_sql_injection_differential_spec.rb +1 -1
- data/spec/components/checks/active/no_sql_injection_spec.rb +5 -4
- data/spec/components/checks/active/os_cmd_injection_spec.rb +6 -4
- data/spec/components/checks/active/os_cmd_injection_timing_spec.rb +4 -3
- data/spec/components/checks/active/path_traversal_spec.rb +18 -15
- data/spec/components/checks/active/response_splitting_spec.rb +5 -4
- data/spec/components/checks/active/rfi_spec.rb +9 -8
- data/spec/components/checks/active/source_code_disclosure_spec.rb +33 -10
- data/spec/components/checks/active/sql_injection_differential_spec.rb +1 -1
- data/spec/components/checks/active/sql_injection_spec.rb +61 -35
- data/spec/components/checks/active/sql_injection_timing_spec.rb +11 -8
- data/spec/components/checks/active/unvalidated_redirect_spec.rb +9 -8
- data/spec/components/checks/active/xpath_injection_spec.rb +5 -4
- data/spec/components/checks/active/xss_dom_script_context_spec.rb +6 -10
- data/spec/components/checks/active/xss_dom_spec.rb +2 -2
- data/spec/components/checks/active/xss_event_spec.rb +11 -3
- data/spec/components/checks/active/xss_script_context_spec.rb +8 -7
- data/spec/components/checks/active/xss_spec.rb +7 -6
- data/spec/components/checks/active/xss_tag_spec.rb +11 -3
- data/spec/components/checks/passive/backup_directories_spec.rb +3 -1
- data/spec/components/checks/passive/backup_files_spec.rb +4 -1
- data/spec/components/checks/passive/grep/insecure_cookies_spec.rb +2 -2
- data/spec/components/checks/passive/grep/x_frame_options_spec.rb +6 -0
- data/spec/components/path_extractors/comments_spec.rb +3 -1
- data/spec/components/path_extractors/data_url_spec.rb +6 -2
- data/spec/components/path_extractors/links_spec.rb +1 -1
- data/spec/components/plugins/autologin_spec.rb +2 -2
- data/spec/components/plugins/webhook_notify_spec.rb +69 -0
- data/spec/spec_helper.rb +2 -1
- data/spec/support/factories/http/response.rb +1 -1
- data/spec/support/factories/issue.rb +1 -2
- data/spec/support/factories/page/dom.rb +6 -0
- data/spec/support/factories/scan_report.rb +1 -0
- data/spec/support/factories/vector.rb +7 -3
- data/spec/support/fixtures/check_with_invalid_platforms/with_invalid_platforms.rb +1 -1
- data/spec/support/fixtures/checks/test.rb +4 -4
- data/spec/support/fixtures/checks/test2.rb +1 -1
- data/spec/support/fixtures/checks/test3.rb +1 -1
- data/spec/support/fixtures/cookies.txt +2 -2
- data/spec/support/fixtures/executables/node.rb +2 -3
- data/spec/support/fixtures/fingerprinters/test.rb +1 -1
- data/spec/support/fixtures/nested_cookies.txt +11 -0
- data/spec/support/fixtures/plugins/bad.rb +1 -1
- data/spec/support/fixtures/plugins/defaults/default.rb +1 -1
- data/spec/support/fixtures/plugins/distributable.rb +1 -1
- data/spec/support/fixtures/plugins/loop.rb +1 -1
- data/spec/support/fixtures/plugins/suspendable.rb +1 -1
- data/spec/support/fixtures/plugins/wait.rb +1 -1
- data/spec/support/fixtures/plugins/with_options.rb +1 -1
- data/spec/support/fixtures/plugins_with_priorities/p0.rb +1 -1
- data/spec/support/fixtures/plugins_with_priorities/p00.rb +1 -1
- data/spec/support/fixtures/plugins_with_priorities/p1.rb +1 -1
- data/spec/support/fixtures/plugins_with_priorities/p2.rb +1 -1
- data/spec/support/fixtures/plugins_with_priorities/p22.rb +1 -1
- data/spec/support/fixtures/plugins_with_priorities/p222.rb +1 -1
- data/spec/support/fixtures/plugins_with_priorities/p_nil.rb +1 -1
- data/spec/support/fixtures/plugins_with_priorities/p_nil2.rb +1 -1
- data/spec/support/fixtures/report.afr +0 -0
- data/spec/support/fixtures/reporters/base_spec/plugin_formatters/with_formatters/foobar.rb +1 -1
- data/spec/support/fixtures/reporters/base_spec/with_formatters.rb +1 -1
- data/spec/support/fixtures/reporters/base_spec/with_outfile.rb +1 -1
- data/spec/support/fixtures/reporters/base_spec/without_outfile.rb +1 -1
- data/spec/support/fixtures/reporters/manager_spec/afr.rb +1 -1
- data/spec/support/fixtures/reporters/manager_spec/error.rb +1 -1
- data/spec/support/fixtures/reporters/manager_spec/foo.rb +1 -1
- data/spec/support/fixtures/run_check/body.rb +1 -1
- data/spec/support/fixtures/run_check/cookies.rb +1 -1
- data/spec/support/fixtures/run_check/empty.rb +1 -1
- data/spec/support/fixtures/run_check/flch.rb +1 -1
- data/spec/support/fixtures/run_check/forms.rb +1 -1
- data/spec/support/fixtures/run_check/headers.rb +1 -1
- data/spec/support/fixtures/run_check/links.rb +1 -1
- data/spec/support/fixtures/run_check/nil.rb +1 -1
- data/spec/support/fixtures/run_check/path.rb +1 -1
- data/spec/support/fixtures/run_check/server.rb +1 -1
- data/spec/support/fixtures/signature_check/signature.rb +1 -1
- data/spec/support/fixtures/wait_check/wait.rb +1 -1
- data/spec/support/helpers/browser_cluster/jobs/taint_tracer.rb +0 -3
- data/spec/support/helpers/framework.rb +1 -1
- data/spec/support/helpers/misc.rb +1 -1
- data/spec/support/helpers/paths.rb +1 -1
- data/spec/support/helpers/requires.rb +1 -1
- data/spec/support/helpers/resets.rb +1 -1
- data/spec/support/helpers/web_server.rb +1 -1
- data/spec/support/lib/factory.rb +1 -1
- data/spec/support/lib/web_server_client.rb +1 -1
- data/spec/support/lib/web_server_dispatcher.rb +1 -1
- data/spec/support/lib/web_server_manager.rb +4 -2
- data/spec/support/servers/arachni/browser/javascript/dom_monitor.rb +48 -0
- data/spec/support/servers/arachni/browser/javascript/taint_tracer.rb +15 -3
- data/spec/support/servers/arachni/browser.rb +275 -4
- data/spec/support/servers/arachni/check/auditor.rb +9 -0
- data/spec/support/servers/arachni/element/cookie.rb +34 -0
- data/spec/support/servers/arachni/element/form/form_dom.rb +1 -0
- data/spec/support/servers/arachni/element/form.rb +36 -2
- data/spec/support/servers/arachni/element/header.rb +36 -1
- data/spec/support/servers/arachni/element/json.rb +33 -0
- data/spec/support/servers/arachni/element/link.rb +33 -1
- data/spec/support/servers/arachni/element/link_template.rb +37 -5
- data/spec/support/servers/arachni/element/nested_cookie.rb +84 -0
- data/spec/support/servers/arachni/element/xml.rb +33 -0
- data/spec/support/servers/arachni/http/client/dynamic_404_handler.rb +36 -0
- data/spec/support/servers/arachni/http/client/dynamic_404_handler_redirect_1.rb +18 -0
- data/spec/support/servers/arachni/http/client/dynamic_404_handler_redirect_2.rb +11 -0
- data/spec/support/servers/arachni/http/client.rb +43 -4
- data/spec/support/servers/arachni/http/proxy_server.rb +12 -0
- data/spec/support/servers/arachni/parser.rb +6 -0
- data/spec/support/servers/arachni/session.rb +24 -1
- data/spec/support/servers/checks/active/code_injection.rb +18 -0
- data/spec/support/servers/checks/active/code_injection_timing.rb +18 -0
- data/spec/support/servers/checks/active/csrf.rb +0 -76
- data/spec/support/servers/checks/active/file_inclusion.rb +19 -1
- data/spec/support/servers/checks/active/ldap_injection.rb +18 -0
- data/spec/support/servers/checks/active/no_sql_injection.rb +27 -0
- data/spec/support/servers/checks/active/no_sql_injection_differential.rb +19 -0
- data/spec/support/servers/checks/active/os_cmd_injection.rb +29 -0
- data/spec/support/servers/checks/active/os_cmd_injection_timing.rb +18 -1
- data/spec/support/servers/checks/active/path_traversal.rb +30 -3
- data/spec/support/servers/checks/active/response_splitting.rb +30 -1
- data/spec/support/servers/checks/active/rfi.rb +30 -2
- data/spec/support/servers/checks/active/session_fixation.rb +1 -3
- data/spec/support/servers/checks/active/source_code_disclosure.rb +16 -0
- data/spec/support/servers/checks/active/sql_injection/java +2 -0
- data/spec/support/servers/checks/active/sql_injection.rb +27 -0
- data/spec/support/servers/checks/active/sql_injection_differential.rb +19 -0
- data/spec/support/servers/checks/active/sql_injection_timing.rb +19 -1
- data/spec/support/servers/checks/active/unvalidated_redirect.rb +121 -1
- data/spec/support/servers/checks/active/xpath_injection.rb +27 -0
- data/spec/support/servers/checks/active/xss.rb +40 -0
- data/spec/support/servers/checks/active/xss_event.rb +23 -2
- data/spec/support/servers/checks/active/xss_script_context.rb +18 -0
- data/spec/support/servers/checks/active/xss_tag.rb +40 -0
- data/spec/support/servers/checks/passive/backup_files.rb +20 -1
- data/spec/support/servers/checks/passive/grep/cookie_set_for_parent_domain.rb +3 -5
- data/spec/support/servers/checks/passive/grep/insecure_cookies_https.rb +9 -0
- data/spec/support/servers/checks/passive/grep/x_frame_options.rb +5 -0
- data/spec/support/servers/plugins/autologin.rb +17 -1
- data/spec/support/servers/plugins/webhook_notify.rb +9 -0
- data/spec/support/shared/check.rb +1 -0
- data/spec/support/shared/element/capabilities/auditable/buffered.rb +791 -0
- data/spec/support/shared/element/capabilities/auditable/line_buffered.rb +797 -0
- data/spec/support/shared/element/capabilities/auditable.rb +28 -34
- data/spec/support/shared/element/capabilities/inputtable.rb +26 -0
- data/spec/support/shared/element/capabilities/with_node.rb +2 -2
- data/spec/support/shared/element/dom/submittable.rb +10 -10
- data/spec/support/shared/path_extractor.rb +17 -5
- data/ui/cli/framework/option_parser.rb +78 -13
- data/ui/cli/framework.rb +29 -8
- data/ui/cli/option_parser.rb +1 -1
- data/ui/cli/output.rb +10 -3
- data/ui/cli/reporter/option_parser.rb +1 -1
- data/ui/cli/reporter.rb +1 -1
- data/ui/cli/reproduce/option_parser.rb +90 -0
- data/ui/cli/reproduce.rb +228 -0
- data/ui/cli/rest/server/option_parser.rb +1 -1
- data/ui/cli/rest/server.rb +1 -1
- data/ui/cli/restored_framework/option_parser.rb +1 -1
- data/ui/cli/restored_framework.rb +1 -1
- data/ui/cli/rpc/client/dispatcher_monitor/option_parser.rb +1 -1
- data/ui/cli/rpc/client/dispatcher_monitor.rb +9 -11
- data/ui/cli/rpc/client/instance.rb +7 -4
- data/ui/cli/rpc/client/local/option_parser.rb +1 -1
- data/ui/cli/rpc/client/local.rb +1 -1
- data/ui/cli/rpc/client/remote/option_parser.rb +1 -1
- data/ui/cli/rpc/client/remote.rb +1 -1
- data/ui/cli/rpc/server/dispatcher/option_parser.rb +1 -1
- data/ui/cli/rpc/server/dispatcher.rb +1 -1
- data/ui/cli/utilities.rb +1 -1
- metadata +178 -79
- data/ACKNOWLEDGMENTS.md +0 -21
- data/AUTHORS.md +0 -3
- data/CONTRIBUTORS.md +0 -22
data/lib/arachni/browser.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
Copyright 2010-
|
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
|
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.
|
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
|
-
|
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
|
83
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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::
|
386
|
-
print_bad "#{css.inspect} did not
|
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
|
-
|
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
|
-
|
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.
|
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
|
-
|
622
|
-
|
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
|
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
|
-
|
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
|
733
|
-
#
|
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
|
-
|
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
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
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
|
-
|
781
|
-
|
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: #{
|
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,
|
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
|
-
|
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
|
-
|
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
|
-
|
960
|
-
data_flow_sinks
|
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 =
|
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
|
-
|
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
|
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
|
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
|
-
|
1157
|
-
client.timeout = WATIR_COM_TIMEOUT
|
1089
|
+
start_proxy
|
1158
1090
|
|
1159
|
-
|
1160
|
-
:remote,
|
1091
|
+
proxy_uri = URI( @proxy.url )
|
1161
1092
|
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
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
|
1171
|
-
|
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
|
1267
|
-
|
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
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
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
|
-
|
1346
|
-
|
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
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
1403
|
-
sleep 0.01 while @proxy.has_pending_requests?
|
1319
|
+
sleep 0.1
|
1404
1320
|
|
1405
|
-
|
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
|
-
|
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
|
1447
|
-
body
|
1368
|
+
if Options.browser_cluster.local_storage.any?
|
1369
|
+
body << <<EOJS
|
1448
1370
|
<script>
|
1449
|
-
var data = #{
|
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
|
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
|
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
|
1574
|
-
if from_preloads( 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(
|
1791
|
+
normalize_url( url.gsub( ';', '%3B' ) ).gsub( '%3B', '%253B' )
|
1865
1792
|
end
|
1866
1793
|
|
1867
1794
|
end
|