arachni 1.3.2 → 1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +108 -0
- data/Gemfile +2 -6
- data/LICENSE.md +1 -1
- data/README.md +34 -16
- data/Rakefile +1 -1
- data/arachni.gemspec +28 -20
- data/bin/arachni +1 -1
- data/bin/arachni_console +1 -1
- data/bin/arachni_multi +1 -1
- data/bin/arachni_reporter +1 -1
- data/bin/arachni_rest_server +13 -0
- data/bin/arachni_restore +1 -1
- data/bin/arachni_rpc +1 -1
- data/bin/arachni_rpcd +1 -1
- data/bin/arachni_rpcd_monitor +1 -1
- data/bin/arachni_script +1 -1
- data/components/checks/active/code_injection.rb +8 -10
- data/components/checks/active/code_injection_php_input_wrapper.rb +5 -6
- data/components/checks/active/code_injection_timing.rb +1 -1
- data/components/checks/active/csrf.rb +1 -1
- data/components/checks/active/file_inclusion.rb +20 -26
- data/components/checks/active/ldap_injection.rb +4 -5
- data/components/checks/active/no_sql_injection.rb +11 -20
- data/components/checks/active/no_sql_injection/substrings/mongodb +1 -0
- data/components/checks/active/no_sql_injection_differential.rb +3 -4
- data/components/checks/active/os_cmd_injection.rb +5 -9
- data/components/checks/active/os_cmd_injection_timing.rb +1 -1
- data/components/checks/active/path_traversal.rb +4 -17
- data/components/checks/active/response_splitting.rb +8 -2
- data/components/checks/active/rfi.rb +4 -5
- data/components/checks/active/session_fixation.rb +9 -3
- data/components/checks/active/source_code_disclosure.rb +5 -20
- data/components/checks/active/sql_injection.rb +30 -18
- data/components/checks/active/sql_injection/{regexp_ignore.txt → ignore_substrings} +0 -0
- data/components/checks/active/sql_injection/regexps/db2.yaml +2 -0
- data/components/checks/active/sql_injection/regexps/frontbase.yaml +1 -0
- data/components/checks/active/sql_injection/regexps/informix.yaml +1 -0
- data/components/checks/active/sql_injection/regexps/ingres.yaml +2 -0
- data/components/checks/active/sql_injection/regexps/maxdb.yaml +2 -0
- data/components/checks/active/sql_injection/regexps/mssql.yaml +8 -0
- data/components/checks/active/sql_injection/regexps/mysql.yaml +4 -0
- data/components/checks/active/sql_injection/regexps/oracle.yaml +4 -0
- data/components/checks/active/sql_injection/regexps/pgsql.yaml +3 -0
- data/components/checks/active/sql_injection/regexps/sqlite.yaml +2 -0
- data/components/checks/active/sql_injection/regexps/sybase.yaml +2 -0
- data/components/checks/active/sql_injection/substrings/access +3 -0
- data/components/checks/active/sql_injection/substrings/db2 +2 -0
- data/components/checks/active/sql_injection/{patterns → substrings}/emc +1 -1
- data/components/checks/active/sql_injection/{patterns → substrings}/firebird +0 -1
- data/components/checks/active/sql_injection/substrings/hsqldb +1 -0
- data/components/checks/active/sql_injection/{patterns → substrings}/informix +1 -2
- data/components/checks/active/sql_injection/substrings/ingres +1 -0
- data/components/checks/active/sql_injection/{patterns → substrings}/interbase +0 -0
- data/components/checks/active/sql_injection/substrings/mssql +17 -0
- data/components/checks/active/sql_injection/{patterns → substrings}/mysql +3 -6
- data/components/checks/active/sql_injection/substrings/oracle +2 -0
- data/components/checks/active/sql_injection/{patterns → substrings}/pgsql +3 -6
- data/components/checks/active/sql_injection/substrings/sqlite +3 -0
- data/components/checks/active/sql_injection/substrings/sybase +1 -0
- data/components/checks/active/sql_injection_differential.rb +5 -7
- data/components/checks/active/sql_injection_differential/payloads.txt +1 -1
- data/components/checks/active/sql_injection_timing.rb +1 -1
- data/components/checks/active/trainer.rb +5 -4
- data/components/checks/active/unvalidated_redirect.rb +1 -1
- data/components/checks/active/unvalidated_redirect_dom.rb +1 -1
- data/components/checks/active/xpath_injection.rb +3 -4
- data/components/checks/active/xss.rb +33 -12
- data/components/checks/active/xss_dom.rb +7 -4
- data/components/checks/active/xss_dom_script_context.rb +1 -1
- data/components/checks/active/xss_event.rb +43 -20
- data/components/checks/active/xss_path.rb +5 -4
- data/components/checks/active/xss_script_context.rb +41 -11
- data/components/checks/active/xss_tag.rb +14 -15
- data/components/checks/active/xxe.rb +5 -16
- data/components/checks/passive/allowed_methods.rb +1 -1
- data/components/checks/passive/backdoors.rb +4 -2
- data/components/checks/passive/backup_directories.rb +4 -2
- data/components/checks/passive/backup_files.rb +4 -2
- data/components/checks/passive/common_admin_interfaces.rb +4 -3
- data/components/checks/passive/common_directories.rb +3 -1
- data/components/checks/passive/common_files.rb +3 -1
- data/components/checks/passive/directory_listing.rb +4 -4
- data/components/checks/passive/grep/captcha.rb +1 -1
- data/components/checks/passive/grep/cookie_set_for_parent_domain.rb +1 -1
- data/components/checks/passive/grep/credit_card.rb +5 -7
- data/components/checks/passive/grep/cvs_svn_users.rb +1 -1
- data/components/checks/passive/grep/emails.rb +135 -8
- data/components/checks/passive/grep/form_upload.rb +1 -1
- data/components/checks/passive/grep/hsts.rb +4 -3
- data/components/checks/passive/grep/html_objects.rb +1 -1
- data/components/checks/passive/grep/http_only_cookies.rb +5 -3
- data/components/checks/passive/grep/insecure_cookies.rb +5 -3
- data/components/checks/passive/grep/insecure_cors_policy.rb +1 -1
- data/components/checks/passive/grep/mixed_resource.rb +1 -1
- 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 +6 -3
- data/components/checks/passive/grep/unencrypted_password_forms.rb +1 -1
- data/components/checks/passive/grep/x_frame_options.rb +4 -3
- 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 +1 -1
- 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 +1 -1
- data/components/path_extractors/areas.rb +1 -1
- data/components/path_extractors/comments.rb +1 -1
- data/components/path_extractors/data_url.rb +1 -1
- data/components/path_extractors/forms.rb +1 -1
- data/components/path_extractors/frames.rb +1 -1
- data/components/path_extractors/generic.rb +1 -1
- data/components/path_extractors/links.rb +1 -1
- data/components/path_extractors/meta_refresh.rb +3 -3
- data/components/path_extractors/scripts.rb +1 -1
- data/components/plugins/autologin.rb +16 -24
- 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/defaults/autothrottle.rb +1 -1
- data/components/plugins/defaults/healthmap.rb +1 -1
- data/components/plugins/defaults/meta/remedies/discovery.rb +10 -9
- 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 +3 -5
- data/components/plugins/exec.rb +1 -1
- data/components/plugins/form_dicattack.rb +1 -1
- data/components/plugins/headers_collector.rb +1 -1
- data/components/plugins/http_dicattack.rb +1 -1
- data/components/plugins/login_script.rb +47 -22
- data/components/plugins/metrics.rb +1 -1
- data/components/plugins/proxy.rb +69 -44
- data/components/plugins/proxy/panel/help.html.erb +1 -18
- data/components/plugins/proxy/panel/inspect.html.erb +4 -3
- data/components/plugins/proxy/panel/page_accordion.html.erb +78 -43
- data/components/plugins/proxy/panel/panel.html.erb +2 -7
- data/components/plugins/proxy/template_scope.rb +1 -1
- data/components/plugins/restrict_to_dom_state.rb +3 -15
- 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 +3 -11
- data/components/plugins/waf_detector.rb +1 -1
- data/components/reporters/ap.rb +1 -1
- data/components/reporters/html.rb +2 -2
- 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 +1 -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 +1 -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 +1 -1
- data/components/reporters/plugin_formatters/xml/cookie_collector.rb +1 -1
- 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 +1 -1
- data/components/reporters/plugin_formatters/xml/uniformity.rb +1 -1
- data/components/reporters/plugin_formatters/xml/vector_collector.rb +1 -1
- data/components/reporters/plugin_formatters/xml/waf_detector.rb +1 -1
- data/components/reporters/stdout.rb +1 -1
- data/components/reporters/txt.rb +1 -1
- data/components/reporters/xml.rb +29 -4
- data/components/reporters/yaml.rb +1 -1
- data/lib/arachni.rb +48 -3
- data/lib/arachni/banner.rb +1 -1
- data/lib/arachni/browser.rb +601 -358
- data/lib/arachni/browser/element_locator.rb +25 -6
- data/lib/arachni/browser/javascript.rb +103 -35
- data/lib/arachni/browser/javascript/dom_monitor.rb +1 -1
- data/lib/arachni/browser/javascript/proxy.rb +28 -16
- data/lib/arachni/browser/javascript/proxy/stub.rb +1 -1
- data/lib/arachni/browser/javascript/scripts/dom_monitor.js +138 -67
- data/lib/arachni/browser/javascript/scripts/polyfills.js +28 -0
- data/lib/arachni/browser/javascript/scripts/taint_tracer.js +27 -6
- data/lib/arachni/browser/javascript/taint_tracer.rb +1 -1
- data/lib/arachni/browser/javascript/taint_tracer/frame.rb +1 -1
- data/lib/arachni/browser/javascript/taint_tracer/frame/called_function.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_cluster.rb +10 -14
- data/lib/arachni/browser_cluster/job.rb +1 -1
- data/lib/arachni/browser_cluster/job/result.rb +1 -1
- data/lib/arachni/browser_cluster/jobs/browser_provider.rb +1 -1
- data/lib/arachni/browser_cluster/jobs/{resource_exploration.rb → dom_exploration.rb} +5 -5
- data/lib/arachni/browser_cluster/jobs/{resource_exploration → dom_exploration}/event_trigger.rb +7 -4
- data/lib/arachni/browser_cluster/jobs/{resource_exploration → dom_exploration}/event_trigger/result.rb +3 -3
- data/lib/arachni/browser_cluster/jobs/{resource_exploration → dom_exploration}/result.rb +2 -2
- data/lib/arachni/browser_cluster/jobs/taint_trace.rb +3 -3
- data/lib/arachni/browser_cluster/jobs/taint_trace/event_trigger.rb +2 -2
- data/lib/arachni/browser_cluster/jobs/taint_trace/event_trigger/result.rb +2 -2
- data/lib/arachni/browser_cluster/jobs/taint_trace/result.rb +1 -1
- data/lib/arachni/browser_cluster/worker.rb +12 -40
- data/lib/arachni/check.rb +1 -1
- data/lib/arachni/check/auditor.rb +15 -1
- data/lib/arachni/check/base.rb +1 -1
- data/lib/arachni/check/manager.rb +1 -1
- data/lib/arachni/component.rb +1 -1
- data/lib/arachni/component/base.rb +5 -5
- data/lib/arachni/component/manager.rb +39 -13
- data/lib/arachni/component/options.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/output.rb +1 -1
- data/lib/arachni/component/utilities.rb +1 -1
- data/lib/arachni/data.rb +1 -1
- data/lib/arachni/data/framework.rb +1 -1
- data/lib/arachni/data/framework/rpc.rb +1 -1
- 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/element/base.rb +19 -5
- data/lib/arachni/element/body.rb +1 -1
- data/lib/arachni/element/capabilities/analyzable.rb +1 -1
- data/lib/arachni/element/capabilities/analyzable/differential.rb +15 -5
- data/lib/arachni/element/capabilities/analyzable/signature.rb +147 -89
- data/lib/arachni/element/capabilities/analyzable/timeout.rb +43 -16
- data/lib/arachni/element/capabilities/auditable.rb +20 -15
- data/lib/arachni/element/capabilities/dom_only.rb +5 -4
- data/lib/arachni/element/capabilities/inputtable.rb +62 -12
- data/lib/arachni/element/capabilities/mutable.rb +74 -13
- data/lib/arachni/element/capabilities/refreshable.rb +1 -1
- data/lib/arachni/element/capabilities/submittable.rb +5 -2
- data/lib/arachni/element/capabilities/with_auditor.rb +1 -1
- data/lib/arachni/element/capabilities/with_auditor/output.rb +5 -5
- data/lib/arachni/element/capabilities/with_dom.rb +1 -1
- data/lib/arachni/element/capabilities/with_node.rb +2 -2
- data/lib/arachni/element/capabilities/with_scope.rb +1 -1
- data/lib/arachni/element/capabilities/with_scope/scope.rb +1 -1
- data/lib/arachni/element/capabilities/with_source.rb +4 -4
- data/lib/arachni/element/cookie.rb +57 -34
- data/lib/arachni/element/cookie/capabilities/inputtable.rb +1 -1
- data/lib/arachni/element/cookie/capabilities/mutable.rb +10 -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/dom.rb +1 -15
- data/lib/arachni/element/dom/capabilities/auditable.rb +1 -1
- data/lib/arachni/element/dom/capabilities/inputtable.rb +1 -1
- data/lib/arachni/element/dom/capabilities/locatable.rb +29 -0
- data/lib/arachni/element/dom/capabilities/mutable.rb +11 -1
- data/lib/arachni/element/dom/capabilities/submittable.rb +2 -2
- data/lib/arachni/element/form.rb +33 -14
- data/lib/arachni/element/form/capabilities/auditable.rb +1 -1
- data/lib/arachni/element/form/capabilities/mutable.rb +18 -17
- data/lib/arachni/element/form/capabilities/submittable.rb +1 -1
- data/lib/arachni/element/form/capabilities/with_dom.rb +2 -1
- data/lib/arachni/element/form/dom.rb +3 -2
- data/lib/arachni/element/generic_dom.rb +1 -1
- data/lib/arachni/element/header.rb +16 -4
- data/lib/arachni/element/header/capabilities/inputtable.rb +1 -1
- data/lib/arachni/element/header/capabilities/mutable.rb +11 -1
- data/lib/arachni/element/json.rb +2 -2
- data/lib/arachni/element/json/capabilities/inputtable.rb +1 -1
- data/lib/arachni/element/json/capabilities/mutable.rb +8 -2
- data/lib/arachni/element/link.rb +14 -7
- 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 +8 -1
- data/lib/arachni/element/link/dom.rb +2 -1
- data/lib/arachni/element/link/dom/capabilities/submittable.rb +1 -1
- data/lib/arachni/element/link_template.rb +8 -3
- 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.rb +2 -1
- data/lib/arachni/element/link_template/dom/capabilities/submittable.rb +1 -1
- data/lib/arachni/element/path.rb +1 -1
- data/lib/arachni/element/server.rb +3 -3
- data/lib/arachni/element/ui_form.rb +24 -21
- data/lib/arachni/element/ui_form/dom.rb +12 -3
- data/lib/arachni/element/ui_input.rb +17 -11
- data/lib/arachni/element/{input → ui_input}/dom.rb +11 -2
- data/lib/arachni/element/xml.rb +3 -3
- data/lib/arachni/element/xml/capabilities/inputtable.rb +7 -1
- data/lib/arachni/element/xml/capabilities/mutable.rb +7 -13
- 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.rb +2 -5
- data/lib/arachni/framework/parts/audit.rb +8 -2
- data/lib/arachni/framework/parts/browser.rb +8 -9
- data/lib/arachni/framework/parts/check.rb +2 -6
- data/lib/arachni/framework/parts/data.rb +23 -8
- data/lib/arachni/framework/parts/platform.rb +1 -1
- data/lib/arachni/framework/parts/plugin.rb +2 -8
- data/lib/arachni/framework/parts/report.rb +3 -9
- data/lib/arachni/framework/parts/scope.rb +1 -1
- data/lib/arachni/framework/parts/state.rb +8 -8
- data/lib/arachni/http.rb +1 -1
- data/lib/arachni/http/client.rb +72 -68
- data/lib/arachni/http/client/dynamic_404_handler.rb +85 -60
- data/lib/arachni/http/cookie_jar.rb +48 -27
- data/lib/arachni/http/headers.rb +4 -3
- data/lib/arachni/http/message.rb +17 -3
- data/lib/arachni/http/message/scope.rb +1 -1
- data/lib/arachni/http/proxy_server.rb +46 -344
- data/lib/arachni/http/proxy_server/connection.rb +316 -0
- data/lib/arachni/http/proxy_server/ssl_interceptor.rb +102 -0
- data/lib/arachni/http/proxy_server/tunnel.rb +54 -0
- data/lib/arachni/http/request.rb +126 -29
- data/lib/arachni/http/request/scope.rb +1 -1
- data/lib/arachni/http/response.rb +42 -12
- data/lib/arachni/http/response/scope.rb +1 -1
- data/lib/arachni/issue.rb +2 -2
- data/lib/arachni/issue/severity.rb +1 -1
- data/lib/arachni/issue/severity/base.rb +1 -1
- data/lib/arachni/option_group.rb +1 -1
- data/lib/arachni/option_groups.rb +1 -1
- data/lib/arachni/option_groups/audit.rb +20 -4
- data/lib/arachni/option_groups/browser_cluster.rb +8 -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 +2 -2
- data/lib/arachni/option_groups/input.rb +6 -3
- data/lib/arachni/option_groups/output.rb +1 -1
- data/lib/arachni/option_groups/paths.rb +10 -3
- data/lib/arachni/option_groups/rpc.rb +1 -1
- data/lib/arachni/option_groups/scope.rb +35 -6
- data/lib/arachni/option_groups/session.rb +1 -1
- data/lib/arachni/option_groups/snapshot.rb +1 -1
- data/lib/arachni/options.rb +1 -1
- data/lib/arachni/page.rb +26 -12
- data/lib/arachni/page/dom.rb +29 -22
- data/lib/arachni/page/dom/transition.rb +2 -2
- data/lib/arachni/page/scope.rb +1 -1
- data/lib/arachni/parser.rb +42 -5
- data/lib/arachni/platform.rb +1 -1
- 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/plugin.rb +1 -1
- data/lib/arachni/plugin/base.rb +1 -1
- data/lib/arachni/plugin/formatter.rb +1 -1
- data/lib/arachni/plugin/manager.rb +7 -13
- data/lib/arachni/processes.rb +1 -1
- data/lib/arachni/processes/dispatchers.rb +2 -2
- data/lib/arachni/processes/executables/base.rb +45 -4
- data/lib/arachni/processes/executables/browser.rb +91 -0
- data/lib/arachni/processes/executables/rest_service.rb +14 -0
- data/lib/arachni/processes/helpers.rb +1 -1
- 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/instances.rb +5 -5
- data/lib/arachni/processes/manager.rb +68 -9
- data/lib/arachni/report.rb +1 -1
- data/lib/arachni/reporter.rb +1 -1
- data/lib/arachni/reporter/base.rb +1 -1
- data/lib/arachni/reporter/formatter_manager.rb +4 -2
- data/lib/arachni/reporter/manager.rb +3 -2
- data/lib/arachni/reporter/options.rb +1 -1
- data/lib/arachni/rest/server.rb +231 -0
- data/lib/arachni/rest/server/instance_helpers.rb +37 -0
- data/lib/arachni/rpc/client/base.rb +1 -1
- data/lib/arachni/rpc/client/dispatcher.rb +1 -1
- data/lib/arachni/rpc/client/instance.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/serializer.rb +1 -1
- data/lib/arachni/rpc/server/active_options.rb +20 -3
- 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.rb +4 -4
- 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/framework.rb +3 -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/instance.rb +1 -3
- data/lib/arachni/rpc/server/output.rb +1 -1
- data/lib/arachni/rpc/server/plugin/manager.rb +1 -1
- data/lib/arachni/ruby.rb +1 -2
- data/lib/arachni/ruby/array.rb +1 -1
- data/lib/arachni/ruby/hash.rb +1 -1
- data/lib/arachni/ruby/object.rb +15 -1
- data/lib/arachni/ruby/set.rb +1 -1
- data/lib/arachni/ruby/string.rb +23 -4
- data/lib/arachni/ruby/webrick.rb +1 -1
- data/lib/arachni/ruby/webrick/cookie.rb +1 -1
- data/lib/arachni/ruby/webrick/httprequest.rb +1 -1
- data/lib/arachni/scope.rb +1 -1
- data/lib/arachni/{watir → selenium/webdriver}/element.rb +12 -13
- data/lib/arachni/session.rb +19 -4
- data/lib/arachni/snapshot.rb +9 -5
- data/lib/arachni/state.rb +1 -1
- data/lib/arachni/state/audit.rb +1 -1
- data/lib/arachni/state/element_filter.rb +1 -1
- data/lib/arachni/state/framework.rb +1 -1
- data/lib/arachni/state/framework/rpc.rb +1 -1
- data/lib/arachni/state/http.rb +1 -1
- data/lib/arachni/state/options.rb +1 -1
- data/lib/arachni/state/plugins.rb +1 -1
- data/lib/arachni/support.rb +2 -1
- data/lib/arachni/support/buffer.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/cache.rb +1 -1
- data/lib/arachni/support/cache/base.rb +20 -8
- 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 +8 -9
- data/lib/arachni/support/cache/preference.rb +7 -20
- data/lib/arachni/support/cache/random_replacement.rb +1 -1
- data/lib/arachni/support/crypto.rb +1 -1
- data/lib/arachni/support/crypto/rsa_aes_cbc.rb +1 -1
- data/lib/arachni/support/database.rb +1 -1
- data/lib/arachni/support/database/base.rb +2 -2
- data/lib/arachni/support/database/hash.rb +1 -1
- data/lib/arachni/support/database/queue.rb +1 -1
- data/lib/arachni/support/glob.rb +35 -0
- data/lib/arachni/support/lookup.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/mixins.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/profiler.rb +12 -10
- data/lib/arachni/support/signature.rb +12 -5
- data/lib/arachni/trainer.rb +18 -4
- data/lib/arachni/ui/foo/output.rb +17 -1
- data/lib/arachni/uri.rb +285 -203
- data/lib/arachni/uri/scope.rb +13 -2
- data/lib/arachni/utilities.rb +22 -5
- data/lib/arachni/version.rb +1 -1
- data/lib/version +1 -1
- data/spec/arachni/browser/element_locator_spec.rb +42 -14
- data/spec/arachni/browser/javascript/dom_monitor_spec.rb +34 -304
- data/spec/arachni/browser/javascript/polyfills_spec.rb +35 -0
- data/spec/arachni/browser/javascript/taint_tracer_spec.rb +24 -4
- data/spec/arachni/browser/javascript_spec.rb +92 -65
- data/spec/arachni/browser_cluster/job_spec.rb +3 -3
- data/spec/arachni/browser_cluster/jobs/{resource_exploration → dom_exploration}/event_trigger/result_spec.rb +1 -1
- data/spec/arachni/browser_cluster/jobs/{resource_exploration → dom_exploration}/event_trigger_spec.rb +4 -4
- data/spec/arachni/browser_cluster/jobs/{resource_exploration → dom_exploration}/result_spec.rb +1 -1
- data/spec/arachni/browser_cluster/jobs/{resource_exploration_spec.rb → dom_exploration_spec.rb} +4 -4
- data/spec/arachni/browser_cluster/jobs/taint_tracer_spec.rb +9 -9
- data/spec/arachni/browser_cluster/worker_spec.rb +46 -67
- data/spec/arachni/browser_cluster_spec.rb +19 -17
- data/spec/arachni/browser_spec.rb +506 -183
- data/spec/arachni/check/auditor_spec.rb +70 -25
- data/spec/arachni/component/manager_spec.rb +19 -20
- data/spec/arachni/data/framework/rpc_spec.rb +1 -1
- data/spec/arachni/data/framework_spec.rb +1 -1
- data/spec/arachni/data/issues_spec.rb +3 -3
- data/spec/arachni/element/capabilities/analyzable/differential_spec.rb +44 -0
- data/spec/arachni/element/capabilities/analyzable/signature_spec.rb +33 -162
- data/spec/arachni/element/capabilities/analyzable/timeout_spec.rb +4 -4
- data/spec/arachni/element/cookie_spec.rb +98 -49
- data/spec/arachni/element/form/dom_spec.rb +1 -22
- data/spec/arachni/element/form_spec.rb +7 -7
- data/spec/arachni/element/header_spec.rb +2 -2
- data/spec/arachni/element/json_spec.rb +2 -2
- data/spec/arachni/element/link/dom_spec.rb +1 -22
- data/spec/arachni/element/link_spec.rb +17 -1
- data/spec/arachni/element/link_template/dom_spec.rb +1 -22
- data/spec/arachni/element/link_template_spec.rb +3 -3
- data/spec/arachni/element/ui_form/{ui_form_dom_spec.rb → dom_spec.rb} +72 -22
- data/spec/arachni/element/ui_form_spec.rb +1 -0
- data/spec/arachni/element/ui_input/dom_spec.rb +64 -22
- data/spec/arachni/element/ui_input_spec.rb +1 -0
- data/spec/arachni/element/xml_spec.rb +1 -0
- data/spec/arachni/framework/parts/audit_spec.rb +7 -5
- data/spec/arachni/framework/parts/browser_spec.rb +8 -8
- data/spec/arachni/framework/parts/check_spec.rb +1 -1
- data/spec/arachni/framework/parts/data_spec.rb +4 -4
- data/spec/arachni/framework/parts/scope_spec.rb +2 -2
- data/spec/arachni/framework_spec.rb +1 -1
- data/spec/arachni/http/client/dynamic_404_handlers_spec.rb +26 -13
- data/spec/arachni/http/client_spec.rb +80 -45
- data/spec/arachni/http/cookie_jar_spec.rb +6 -6
- data/spec/arachni/http/proxy_server_spec.rb +69 -66
- data/spec/arachni/http/request_spec.rb +147 -23
- data/spec/arachni/http/response/scope_spec.rb +12 -12
- data/spec/arachni/http/response_spec.rb +62 -4
- data/spec/arachni/issue_spec.rb +6 -6
- data/spec/arachni/option_groups/audit_spec.rb +25 -8
- data/spec/arachni/option_groups/browser_cluster_spec.rb +27 -1
- data/spec/arachni/option_groups/dispatcher_spec.rb +3 -3
- data/spec/arachni/option_groups/input_spec.rb +9 -9
- data/spec/arachni/option_groups/paths_spec.rb +2 -2
- data/spec/arachni/option_groups/scope_spec.rb +32 -16
- data/spec/arachni/options_spec.rb +4 -4
- data/spec/arachni/page/dom/transition_spec.rb +17 -10
- data/spec/arachni/page/dom_spec.rb +19 -0
- data/spec/arachni/page/scope_spec.rb +4 -4
- data/spec/arachni/page_spec.rb +15 -15
- data/spec/arachni/platform/manager_spec.rb +2 -2
- data/spec/arachni/plugin/base_spec.rb +1 -0
- data/spec/arachni/reporter/base_spec.rb +2 -2
- data/spec/arachni/reporter/manager_spec.rb +2 -2
- data/spec/arachni/rest/server_spec.rb +495 -0
- data/spec/arachni/rpc/server/active_options_spec.rb +63 -12
- data/spec/arachni/rpc/server/base_spec.rb +1 -1
- data/spec/arachni/rpc/server/framework/distributor_spec.rb +2 -2
- data/spec/arachni/rpc/server/framework_multi_spec.rb +6 -6
- data/spec/arachni/rpc/server/framework_spec.rb +4 -4
- data/spec/arachni/rpc/server/instance_spec.rb +24 -24
- data/spec/arachni/ruby/array_spec.rb +2 -2
- data/spec/arachni/ruby/string_spec.rb +52 -0
- data/spec/arachni/session_spec.rb +19 -2
- data/spec/arachni/snapshot_spec.rb +1 -1
- data/spec/arachni/state/audit_spec.rb +1 -1
- data/spec/arachni/state/framework_spec.rb +2 -2
- data/spec/arachni/support/cache/least_recently_used_spec.rb +0 -2
- data/spec/arachni/support/glob_spec.rb +75 -0
- data/spec/arachni/support/lookup/hash_set_spec.rb +1 -1
- data/spec/arachni/support/lookup/moolb_spec.rb +2 -2
- data/spec/arachni/support/signature_spec.rb +4 -4
- data/spec/arachni/trainer_spec.rb +48 -4
- data/spec/arachni/uri/scope_spec.rb +54 -10
- data/spec/arachni/uri_spec.rb +110 -89
- data/spec/arachni/utilities_spec.rb +8 -8
- data/spec/components/checks/active/code_injection_spec.rb +9 -9
- data/spec/components/checks/active/file_inclusion_spec.rb +20 -20
- data/spec/components/checks/active/ldap_injection_spec.rb +1 -1
- data/spec/components/checks/active/no_sql_injection_spec.rb +1 -1
- data/spec/components/checks/active/os_cmd_injection_spec.rb +3 -3
- data/spec/components/checks/active/path_traversal_spec.rb +11 -11
- data/spec/components/checks/active/response_splitting_spec.rb +2 -2
- data/spec/components/checks/active/rfi_spec.rb +3 -3
- data/spec/components/checks/active/session_fixation_spec.rb +1 -1
- data/spec/components/checks/active/source_code_disclosure_spec.rb +4 -4
- data/spec/components/checks/active/sql_injection_spec.rb +58 -59
- data/spec/components/checks/active/unvalidated_redirect_spec.rb +2 -2
- data/spec/components/checks/active/xpath_injection_spec.rb +3 -3
- data/spec/components/checks/active/xss_dom_script_context_spec.rb +1 -1
- data/spec/components/checks/active/xss_dom_spec.rb +1 -1
- data/spec/components/checks/active/xss_script_context_spec.rb +5 -5
- data/spec/components/checks/active/xss_spec.rb +5 -5
- data/spec/components/checks/passive/grep/credit_card_spec.rb +1 -1
- data/spec/components/checks/passive/grep/emails_spec.rb +12 -2
- data/spec/components/checks/passive/grep/ssn_spec.rb +1 -1
- data/spec/components/path_extractors/meta_refresh_spec.rb +3 -1
- data/spec/components/plugins/exec_spec.rb +2 -2
- data/spec/components/plugins/login_script_spec.rb +22 -2
- data/spec/components/plugins/vector_feed_spec.rb +3 -3
- data/spec/spec_helper.rb +10 -4
- data/spec/support/factories/browser_cluster/job.rb +1 -0
- data/spec/support/fixtures/check_with_invalid_platforms/with_invalid_platforms.rb +1 -1
- data/spec/support/fixtures/checks/test.rb +1 -1
- data/spec/support/fixtures/checks/test2.rb +1 -1
- data/spec/support/fixtures/checks/test3.rb +1 -1
- data/spec/support/fixtures/fingerprinters/test.rb +1 -1
- 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/framework.rb +1 -1
- data/spec/support/helpers/misc.rb +1 -1
- data/spec/support/helpers/paths.rb +1 -1
- data/spec/support/helpers/request_helpers.rb +38 -0
- 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 +2 -2
- data/spec/support/servers/arachni/browser.rb +182 -15
- data/spec/support/servers/arachni/browser/javascript/angular-1.2.8.js +1 -1
- data/spec/support/servers/arachni/browser/javascript/angular-route.js +1 -1
- data/spec/support/servers/arachni/browser/javascript/dom_monitor.rb +27 -4
- data/spec/support/servers/arachni/element/capabilities/analyzable/differential.rb +103 -0
- data/spec/support/servers/arachni/element/capabilities/analyzable/timeout.rb +5 -2
- data/spec/support/servers/arachni/element/header.rb +1 -1
- data/spec/support/servers/arachni/http/client.rb +46 -0
- data/spec/support/servers/arachni/http/client/dynamic_404_handler.rb +7 -1
- data/spec/support/servers/checks/active/code_injection.rb +5 -5
- data/spec/support/servers/checks/active/no_sql_injection.rb +0 -6
- data/spec/support/servers/checks/active/no_sql_injection_differential.rb +1 -1
- data/spec/support/servers/checks/active/sql_injection.rb +5 -2
- data/spec/support/servers/checks/active/sql_injection_differential.rb +1 -1
- data/spec/support/servers/checks/active/trainer_check.rb +6 -6
- data/spec/support/servers/checks/passive/backdoors.rb +1 -0
- data/spec/support/servers/checks/passive/backup_directories.rb +2 -0
- data/spec/support/servers/checks/passive/backup_files.rb +2 -0
- data/spec/support/servers/checks/passive/grep/emails.rb +6 -6
- data/spec/support/shared/check.rb +28 -0
- data/spec/support/shared/element/capabilities/auditable.rb +76 -13
- data/spec/support/shared/element/capabilities/dom_only.rb +5 -6
- data/spec/support/shared/element/capabilities/inputtable.rb +74 -4
- data/spec/support/shared/element/capabilities/mutable.rb +86 -14
- data/spec/support/shared/element/capabilities/submittable.rb +12 -0
- data/spec/support/shared/element/capabilities/with_dom.rb +13 -4
- data/spec/support/shared/element/capabilities/with_node.rb +1 -1
- data/spec/support/shared/element/capabilities/with_source.rb +1 -6
- data/spec/support/shared/element/dom/locatable.rb +20 -0
- data/spec/support/shared/element/dom/submittable.rb +4 -17
- data/spec/support/shared/http/message.rb +37 -5
- data/spec/support/shared/support/cache.rb +5 -4
- data/ui/cli/framework.rb +4 -3
- data/ui/cli/framework/option_parser.rb +20 -8
- data/ui/cli/option_parser.rb +1 -1
- data/ui/cli/output.rb +40 -4
- data/ui/cli/reporter.rb +1 -1
- data/ui/cli/reporter/option_parser.rb +4 -4
- data/ui/cli/rest/server.rb +43 -0
- data/ui/cli/rest/server/option_parser.rb +115 -0
- data/ui/cli/restored_framework.rb +1 -1
- data/ui/cli/restored_framework/option_parser.rb +1 -1
- data/ui/cli/rpc/client/dispatcher_monitor.rb +1 -1
- data/ui/cli/rpc/client/dispatcher_monitor/option_parser.rb +1 -1
- data/ui/cli/rpc/client/instance.rb +1 -1
- data/ui/cli/rpc/client/local.rb +1 -1
- data/ui/cli/rpc/client/local/option_parser.rb +1 -1
- data/ui/cli/rpc/client/remote.rb +1 -1
- data/ui/cli/rpc/client/remote/option_parser.rb +1 -1
- data/ui/cli/rpc/server/dispatcher.rb +1 -1
- data/ui/cli/rpc/server/dispatcher/option_parser.rb +1 -1
- data/ui/cli/utilities.rb +1 -1
- metadata +197 -84
- data/components/checks/active/no_sql_injection/patterns/mongodb +0 -1
- data/components/checks/active/no_sql_injection/regexp_ignore.txt +0 -0
- data/components/checks/active/sql_injection/patterns/access +0 -3
- data/components/checks/active/sql_injection/patterns/db2 +0 -5
- data/components/checks/active/sql_injection/patterns/frontbase +0 -1
- data/components/checks/active/sql_injection/patterns/hsqldb +0 -1
- data/components/checks/active/sql_injection/patterns/ingres +0 -3
- data/components/checks/active/sql_injection/patterns/maxdb +0 -2
- data/components/checks/active/sql_injection/patterns/mssql +0 -25
- data/components/checks/active/sql_injection/patterns/oracle +0 -6
- data/components/checks/active/sql_injection/patterns/sqlite +0 -5
- data/components/checks/active/sql_injection/patterns/sybase +0 -3
- data/lib/arachni/ruby/io.rb +0 -39
- data/lib/arachni/selenium/webdriver/remote/http/typhoeus.rb +0 -63
- data/spec/arachni/ruby/io_spec.rb +0 -26
@@ -1,5 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
Copyright 2010-
|
2
|
+
Copyright 2010-2016 Tasos Laskos <tasos.laskos@arachni-scanner.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
|
@@ -1,5 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
Copyright 2010-
|
2
|
+
Copyright 2010-2016 Tasos Laskos <tasos.laskos@arachni-scanner.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
|
@@ -1,5 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
Copyright 2010-
|
2
|
+
Copyright 2010-2016 Tasos Laskos <tasos.laskos@arachni-scanner.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
|
@@ -1,5 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
Copyright 2010-
|
2
|
+
Copyright 2010-2016 Tasos Laskos <tasos.laskos@arachni-scanner.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
|
@@ -1,5 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
Copyright 2010-
|
2
|
+
Copyright 2010-2016 Tasos Laskos <tasos.laskos@arachni-scanner.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
|
@@ -1,5 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
Copyright 2010-
|
2
|
+
Copyright 2010-2016 Tasos Laskos <tasos.laskos@arachni-scanner.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
|
@@ -1,5 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
Copyright 2010-
|
2
|
+
Copyright 2010-2016 Tasos Laskos <tasos.laskos@arachni-scanner.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
|
@@ -1,5 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
Copyright 2010-
|
2
|
+
Copyright 2010-2016 Tasos Laskos <tasos.laskos@arachni-scanner.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
|
data/components/reporters/txt.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
Copyright 2010-
|
2
|
+
Copyright 2010-2016 Tasos Laskos <tasos.laskos@arachni-scanner.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
|
data/components/reporters/xml.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
Copyright 2010-
|
2
|
+
Copyright 2010-2016 Tasos Laskos <tasos.laskos@arachni-scanner.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
|
@@ -11,7 +11,6 @@ require 'nokogiri'
|
|
11
11
|
# Creates an XML report of the audit.
|
12
12
|
#
|
13
13
|
# @author Tasos "Zapotek" Laskos <tasos.laskos@arachni-scanner.com>
|
14
|
-
# @version 0.3.4
|
15
14
|
class Arachni::Reporters::XML < Arachni::Reporter::Base
|
16
15
|
|
17
16
|
LOCAL_SCHEMA = File.dirname( __FILE__ ) + '/xml/schema.xsd'
|
@@ -136,11 +135,37 @@ class Arachni::Reporters::XML < Arachni::Reporter::Base
|
|
136
135
|
has_errors = false
|
137
136
|
xsd.validate( Nokogiri::XML( xml ) ).each do |error|
|
138
137
|
puts error.message
|
139
|
-
|
138
|
+
puts " -- Line #{error.line}, column #{error.column}, level #{error.level}."
|
139
|
+
puts '-' * 100
|
140
|
+
|
141
|
+
justify = (error.line+10).to_s.size
|
142
|
+
lines = xml.lines
|
143
|
+
((error.line-10)..(error.line+10)).each do |i|
|
144
|
+
line = lines[i]
|
145
|
+
next if i < 0 || !line
|
146
|
+
i = i + 1
|
147
|
+
|
148
|
+
printf( "%#{justify}s | %s", i, line )
|
149
|
+
|
150
|
+
if i == error.line
|
151
|
+
printf( "%#{justify}s |", i )
|
152
|
+
line.size.times.each do |c|
|
153
|
+
print error.column == c ? '^' : '-'
|
154
|
+
end
|
155
|
+
puts
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
puts '-' * 100
|
160
|
+
puts
|
161
|
+
|
140
162
|
has_errors = true
|
141
163
|
end
|
142
164
|
|
143
|
-
|
165
|
+
if has_errors
|
166
|
+
print_error 'Report could not be validated against the XSD due to the above errors.'
|
167
|
+
return
|
168
|
+
end
|
144
169
|
|
145
170
|
IO.binwrite( outfile, xml )
|
146
171
|
print_status "Saved in '#{outfile}'."
|
@@ -1,5 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
Copyright 2010-
|
2
|
+
Copyright 2010-2016 Tasos Laskos <tasos.laskos@arachni-scanner.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
|
data/lib/arachni.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
Copyright 2010-
|
2
|
+
Copyright 2010-2016 Tasos Laskos <tasos.laskos@arachni-scanner.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
|
@@ -8,8 +8,9 @@
|
|
8
8
|
|
9
9
|
require 'rubygems'
|
10
10
|
require 'bundler/setup'
|
11
|
-
|
12
|
-
require '
|
11
|
+
require 'concurrent'
|
12
|
+
require 'pp'
|
13
|
+
require 'ap'
|
13
14
|
|
14
15
|
def ap( obj )
|
15
16
|
super obj, raw: true
|
@@ -19,6 +20,20 @@ module Arachni
|
|
19
20
|
|
20
21
|
class <<self
|
21
22
|
|
23
|
+
# Runs a minor GC to collect young, short-lived objects.
|
24
|
+
#
|
25
|
+
# Generally called after analysis operations that generate a lot of
|
26
|
+
# new temporary objects.
|
27
|
+
def collect_young_objects
|
28
|
+
GC.start( full_mark: false )
|
29
|
+
end
|
30
|
+
|
31
|
+
def tmpdir
|
32
|
+
# On MS Windows Dir.tmpdir can return the path with a shortname,
|
33
|
+
# better avoid that as it can be insonsistent with other paths.
|
34
|
+
get_long_win32_filename( Dir.tmpdir )
|
35
|
+
end
|
36
|
+
|
22
37
|
def null_device
|
23
38
|
Gem.win_platform? ? 'NUL' : '/dev/null'
|
24
39
|
end
|
@@ -39,10 +54,40 @@ module Arachni
|
|
39
54
|
!!ENV['ARACHNI_PROFILER']
|
40
55
|
end
|
41
56
|
|
57
|
+
if Arachni.windows?
|
58
|
+
require 'find'
|
59
|
+
require 'fileutils'
|
60
|
+
require 'Win32API'
|
61
|
+
require 'win32ole'
|
62
|
+
|
63
|
+
def get_long_win32_filename( short_name )
|
64
|
+
short_name = short_name.dup
|
65
|
+
max_path = 1024
|
66
|
+
long_name = ' ' * max_path
|
67
|
+
|
68
|
+
lfn_size = Win32API.new(
|
69
|
+
"kernel32",
|
70
|
+
"GetLongPathName",
|
71
|
+
['P','P','L'],
|
72
|
+
'L'
|
73
|
+
).call( short_name, long_name, max_path )
|
74
|
+
|
75
|
+
(1..max_path).include?( lfn_size ) ?
|
76
|
+
long_name[0..lfn_size-1] : short_name
|
77
|
+
end
|
78
|
+
else
|
79
|
+
def get_long_win32_filename( short_name )
|
80
|
+
short_name
|
81
|
+
end
|
82
|
+
end
|
42
83
|
end
|
43
84
|
|
44
85
|
end
|
45
86
|
|
87
|
+
if !Arachni.jruby?
|
88
|
+
require 'oj_mimic_json'
|
89
|
+
end
|
90
|
+
|
46
91
|
require_relative 'arachni/version'
|
47
92
|
require_relative 'arachni/banner'
|
48
93
|
|
data/lib/arachni/banner.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
Copyright 2010-
|
2
|
+
Copyright 2010-2016 Tasos Laskos <tasos.laskos@arachni-scanner.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
|
data/lib/arachni/browser.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
Copyright 2010-
|
2
|
+
Copyright 2010-2016 Tasos Laskos <tasos.laskos@arachni-scanner.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
|
@@ -8,8 +8,7 @@
|
|
8
8
|
|
9
9
|
require 'childprocess'
|
10
10
|
require 'watir-webdriver'
|
11
|
-
require_relative '
|
12
|
-
require_relative 'selenium/webdriver/remote/http/typhoeus'
|
11
|
+
require_relative 'selenium/webdriver/element'
|
13
12
|
require_relative 'processes/manager'
|
14
13
|
require_relative 'browser/element_locator'
|
15
14
|
require_relative 'browser/javascript'
|
@@ -70,9 +69,14 @@ class Browser
|
|
70
69
|
# Let the browser take as long as it needs to complete an operation.
|
71
70
|
WATIR_COM_TIMEOUT = 3600 # 1 hour.
|
72
71
|
|
73
|
-
ASSET_EXTENSIONS = Set.new(
|
74
|
-
|
75
|
-
|
72
|
+
ASSET_EXTENSIONS = Set.new(%w( css js jpg jpeg png gif json ))
|
73
|
+
|
74
|
+
INPUT_EVENTS = Set.new([
|
75
|
+
:change, :blur, :focus, :select, :keyup, :keypress, :keydown, :input
|
76
|
+
])
|
77
|
+
INPUT_EVENTS_TO_FORCE = Set.new([
|
78
|
+
:focus, :change, :blur, :select
|
79
|
+
])
|
76
80
|
|
77
81
|
ASSET_EXTRACTORS = [
|
78
82
|
/<\s*link.*?href=['"](.*?)['"].*?>/im,
|
@@ -92,6 +96,10 @@ class Browser
|
|
92
96
|
# Watir driver interface.
|
93
97
|
attr_reader :watir
|
94
98
|
|
99
|
+
# @return [Selenium::WebDriver]
|
100
|
+
# Selenium driver interface.
|
101
|
+
attr_reader :selenium
|
102
|
+
|
95
103
|
# @return [Array<Page>]
|
96
104
|
# Same as {#page_snapshots} but it doesn't deduplicate and only contains
|
97
105
|
# pages with sink ({Page::DOM#data_flow_sinks} or {Page::DOM#execution_flow_sinks})
|
@@ -114,36 +122,44 @@ class Browser
|
|
114
122
|
attr_reader :skip_states
|
115
123
|
|
116
124
|
# @return [Integer]
|
117
|
-
|
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
|
118
131
|
|
119
132
|
attr_reader :last_url
|
120
133
|
|
121
|
-
|
134
|
+
class <<self
|
122
135
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
136
|
+
# @return [Bool]
|
137
|
+
# `true` if a supported browser is in the OS PATH, `false` otherwise.
|
138
|
+
def has_executable?
|
139
|
+
!!executable
|
140
|
+
end
|
128
141
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
142
|
+
# @return [String]
|
143
|
+
# Path to the PhantomJS executable.
|
144
|
+
def executable
|
145
|
+
Selenium::WebDriver::PhantomJS.path
|
146
|
+
end
|
134
147
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
asset_domains
|
148
|
+
def asset_domains
|
149
|
+
@asset_domains ||= Set.new
|
150
|
+
end
|
139
151
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
152
|
+
def add_asset_domain( url )
|
153
|
+
return if url.to_s.empty?
|
154
|
+
return if !(curl = Arachni::URI( url ))
|
155
|
+
return if !(domain = curl.domain)
|
156
|
+
|
157
|
+
asset_domains << domain
|
158
|
+
domain
|
159
|
+
end
|
144
160
|
|
145
|
-
asset_domains << domain
|
146
161
|
end
|
162
|
+
asset_domains
|
147
163
|
|
148
164
|
# @param [Hash] options
|
149
165
|
# @option options [Integer] :concurrency
|
@@ -167,10 +183,10 @@ class Browser
|
|
167
183
|
concurrency: @options[:concurrency],
|
168
184
|
address: '127.0.0.1',
|
169
185
|
request_handler: proc do |request, response|
|
170
|
-
|
186
|
+
exception_jail { request_handler( request, response ) }
|
171
187
|
end,
|
172
188
|
response_handler: proc do |request, response|
|
173
|
-
|
189
|
+
exception_jail { response_handler( request, response ) }
|
174
190
|
end
|
175
191
|
)
|
176
192
|
|
@@ -200,6 +216,8 @@ class Browser
|
|
200
216
|
# Captures HTTP::Response objects per URL for open windows.
|
201
217
|
@window_responses = {}
|
202
218
|
|
219
|
+
@elements_with_events = {}
|
220
|
+
|
203
221
|
# Keeps track of resources which should be skipped -- like already fired
|
204
222
|
# events and clicked links etc.
|
205
223
|
@skip_states = Support::LookUp::HashSet.new( hasher: :persistent_hash )
|
@@ -212,12 +230,11 @@ class Browser
|
|
212
230
|
@last_url = nil
|
213
231
|
|
214
232
|
@javascript = Javascript.new( self )
|
215
|
-
|
216
|
-
ensure_open_window
|
217
233
|
end
|
218
234
|
|
219
235
|
def clear_buffers
|
220
236
|
synchronize do
|
237
|
+
@elements_with_events.clear
|
221
238
|
@preloads.clear
|
222
239
|
@cache.clear
|
223
240
|
@captured_pages.clear
|
@@ -235,14 +252,13 @@ class Browser
|
|
235
252
|
end.join
|
236
253
|
end
|
237
254
|
|
238
|
-
# @param [String, HTTP::Response, Page] resource
|
255
|
+
# @param [String, HTTP::Response, Page, Page:::DOM] resource
|
239
256
|
# Loads the given resource in the browser. If it is a string it will be
|
240
257
|
# treated like a URL.
|
241
258
|
#
|
242
259
|
# @return [Browser]
|
243
260
|
# `self`
|
244
261
|
def load( resource, options = {} )
|
245
|
-
@last_dom_url = nil
|
246
262
|
|
247
263
|
case resource
|
248
264
|
when String
|
@@ -254,13 +270,14 @@ class Browser
|
|
254
270
|
when Page
|
255
271
|
HTTP::Client.update_cookies resource.cookie_jar
|
256
272
|
|
257
|
-
|
258
|
-
update_skip_states resource.dom.skip_states
|
273
|
+
load resource.dom
|
259
274
|
|
260
|
-
|
275
|
+
when Page::DOM
|
276
|
+
@transitions = resource.transitions.dup
|
277
|
+
update_skip_states resource.skip_states
|
261
278
|
|
262
279
|
@add_request_transitions = false if @transitions.any?
|
263
|
-
resource.
|
280
|
+
resource.restore self
|
264
281
|
@add_request_transitions = true
|
265
282
|
|
266
283
|
else
|
@@ -350,22 +367,22 @@ class Browser
|
|
350
367
|
url: url,
|
351
368
|
cookies: extra_cookies
|
352
369
|
) do
|
353
|
-
print_debug_level_2 "Loading
|
354
|
-
|
355
|
-
print_debug_level_2 '
|
370
|
+
print_debug_level_2 "Loading #{url} ..."
|
371
|
+
@selenium.navigate.to url
|
372
|
+
print_debug_level_2 '...done.'
|
356
373
|
|
357
374
|
wait_till_ready
|
358
375
|
|
359
|
-
url = watir.url
|
360
376
|
Options.browser_cluster.css_to_wait_for( url ).each do |css|
|
361
377
|
print_info "Waiting for #{css.inspect} to appear for: #{url}"
|
362
378
|
|
363
379
|
begin
|
364
|
-
|
365
|
-
|
380
|
+
Selenium::WebDriver::Wait.new(
|
381
|
+
timeout: Options.browser_cluster.job_timeout
|
382
|
+
).until { @selenium.find_element( :css, css ) }
|
366
383
|
|
367
384
|
print_info "#{css.inspect} appeared for: #{url}"
|
368
|
-
rescue
|
385
|
+
rescue Selenium::WebDriver::Error::TimeOutError
|
369
386
|
print_bad "#{css.inspect} did not appeared for: #{url}"
|
370
387
|
end
|
371
388
|
|
@@ -380,7 +397,7 @@ class Browser
|
|
380
397
|
|
381
398
|
@add_request_transitions = pre_add_request_transitions
|
382
399
|
|
383
|
-
|
400
|
+
update_cookies
|
384
401
|
|
385
402
|
# Capture the page at its initial state.
|
386
403
|
capture_snapshot if take_snapshot
|
@@ -391,32 +408,35 @@ class Browser
|
|
391
408
|
def wait_till_ready
|
392
409
|
print_debug_level_2 'Waiting for custom JS...'
|
393
410
|
@javascript.wait_till_ready
|
394
|
-
print_debug_level_2 '
|
411
|
+
print_debug_level_2 '...done.'
|
395
412
|
|
396
|
-
print_debug_level_2 "Waiting for #{@proxy.active_connections} connections to close.."
|
397
|
-
wait_for_pending_requests
|
398
|
-
print_debug_level_2 'Done'
|
399
|
-
|
400
|
-
print_debug_level_2 'Waiting for timers...'
|
401
413
|
wait_for_timers
|
402
|
-
|
414
|
+
|
415
|
+
wait_for_pending_requests
|
403
416
|
end
|
404
417
|
|
405
418
|
def shutdown
|
406
419
|
begin
|
407
|
-
watir.close if
|
408
|
-
|
409
|
-
|
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
|
410
424
|
end
|
411
425
|
|
412
426
|
kill_process
|
413
|
-
@proxy.shutdown
|
427
|
+
@proxy.shutdown rescue Reactor::Error::NotRunning
|
414
428
|
end
|
415
429
|
|
416
430
|
# @return [String]
|
417
|
-
# Current URL.
|
431
|
+
# Current URL, noralized via #{Arachni::URI.}
|
418
432
|
def url
|
419
|
-
normalize_url
|
433
|
+
normalize_url dom_url
|
434
|
+
end
|
435
|
+
|
436
|
+
# @return [String]
|
437
|
+
# Current URL, as provided by the browser.
|
438
|
+
def dom_url
|
439
|
+
javascript.run( 'return document.URL;' )
|
420
440
|
end
|
421
441
|
|
422
442
|
# Explores the browser's DOM tree and captures page snapshots for each
|
@@ -448,15 +468,11 @@ class Browser
|
|
448
468
|
# Iterates over all elements which have events and passes their info to the
|
449
469
|
# given block.
|
450
470
|
#
|
451
|
-
# @param [Bool] mark_state
|
452
|
-
# Mark each element/events as visited and skip it if it has already been
|
453
|
-
# seen.
|
454
|
-
#
|
455
471
|
# @yield [ElementLocator,Array<Symbol>]
|
456
|
-
#
|
457
|
-
#
|
458
|
-
def each_element_with_events
|
459
|
-
current_url = url
|
472
|
+
# Element locator along with the element's applicable events along with
|
473
|
+
# their handlers and attributes.
|
474
|
+
def each_element_with_events
|
475
|
+
current_url = self.url
|
460
476
|
|
461
477
|
javascript.dom_elements_with_events.each do |element|
|
462
478
|
tag_name = element['tag_name']
|
@@ -469,7 +485,7 @@ class Browser
|
|
469
485
|
|
470
486
|
if !href.empty?
|
471
487
|
if href.downcase.start_with?( 'javascript:' )
|
472
|
-
events
|
488
|
+
(events[:click] ||= []) << href
|
473
489
|
else
|
474
490
|
next if skip_path?( to_absolute( href, current_url ) )
|
475
491
|
end
|
@@ -477,7 +493,7 @@ class Browser
|
|
477
493
|
|
478
494
|
when 'input'
|
479
495
|
if attributes['type'].to_s.downcase == 'image'
|
480
|
-
events
|
496
|
+
(events[:click] ||= []) << 'image'
|
481
497
|
end
|
482
498
|
|
483
499
|
when 'form'
|
@@ -485,7 +501,7 @@ class Browser
|
|
485
501
|
|
486
502
|
if !action.empty?
|
487
503
|
if action.downcase.start_with?( 'javascript:' )
|
488
|
-
events
|
504
|
+
(events[:submit] ||= []) << action
|
489
505
|
else
|
490
506
|
next if skip_path?( to_absolute( action, current_url ) )
|
491
507
|
end
|
@@ -494,12 +510,6 @@ class Browser
|
|
494
510
|
|
495
511
|
next if events.empty?
|
496
512
|
|
497
|
-
if mark_state
|
498
|
-
state = "#{tag_name}#{attributes}#{events}"
|
499
|
-
next if skip_state?( state )
|
500
|
-
skip_state state
|
501
|
-
end
|
502
|
-
|
503
513
|
yield ElementLocator.new( tag_name: tag_name, attributes: attributes ),
|
504
514
|
events
|
505
515
|
end
|
@@ -507,21 +517,47 @@ class Browser
|
|
507
517
|
self
|
508
518
|
end
|
509
519
|
|
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
|
+
|
510
545
|
# @return [String]
|
511
546
|
# Snapshot ID used to determine whether or not a page snapshot has already
|
512
|
-
# been seen.
|
513
|
-
#
|
514
|
-
#
|
515
|
-
#
|
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.
|
516
553
|
def snapshot_id
|
517
|
-
current_url = url
|
554
|
+
current_url = self.url
|
518
555
|
|
519
|
-
id =
|
556
|
+
id = Set.new
|
520
557
|
javascript.dom_elements_with_events.each do |element|
|
521
558
|
tag_name = element['tag_name']
|
522
559
|
attributes = element['attributes']
|
523
|
-
events = element['events']
|
524
|
-
Javascript.select_event_attributes( attributes ).to_a
|
560
|
+
events = element['events']
|
525
561
|
element_id = attributes['id'].to_s
|
526
562
|
|
527
563
|
case tag_name
|
@@ -531,19 +567,19 @@ class Browser
|
|
531
567
|
|
532
568
|
if !href.empty?
|
533
569
|
if href.downcase.start_with?( 'javascript:' )
|
534
|
-
events
|
570
|
+
(events[:click] ||= []) << href
|
535
571
|
else
|
536
572
|
absolute = to_absolute( href, current_url )
|
537
573
|
next if skip_path?( absolute )
|
538
574
|
|
539
|
-
events
|
575
|
+
(events[:click] ||= []) << href
|
540
576
|
end
|
541
577
|
else
|
542
|
-
events
|
578
|
+
(events[:click] ||= []) << current_url
|
543
579
|
end
|
544
580
|
|
545
581
|
when 'input', 'textarea', 'select'
|
546
|
-
events
|
582
|
+
(events[:input] ||= []) << tag_name.to_sym
|
547
583
|
element_id << attributes['name'].to_s
|
548
584
|
|
549
585
|
when 'form'
|
@@ -552,24 +588,26 @@ class Browser
|
|
552
588
|
|
553
589
|
if !action.empty?
|
554
590
|
if action.downcase.start_with?( 'javascript:' )
|
555
|
-
events
|
591
|
+
(events[:submit] ||= []) << action
|
556
592
|
else
|
557
593
|
absolute = to_absolute( action, current_url )
|
558
594
|
if !skip_path?( absolute )
|
559
|
-
events
|
595
|
+
(events[:submit] ||= []) << absolute
|
560
596
|
end
|
561
597
|
end
|
562
598
|
else
|
563
|
-
events
|
599
|
+
(events[:submit] ||= []) << current_url
|
564
600
|
end
|
565
601
|
end
|
566
602
|
|
567
603
|
next if events.empty?
|
568
604
|
|
569
|
-
id << "#{tag_name}:#{element_id}:#{events}"
|
605
|
+
id << "#{tag_name}:#{element_id}:#{events.keys.sort}".persistent_hash
|
570
606
|
end
|
571
607
|
|
572
|
-
id.
|
608
|
+
id << [:cookies, cookies.map(&:name).sort].to_s.persistent_hash
|
609
|
+
|
610
|
+
id.to_a.sort.map(&:to_s).join(':')
|
573
611
|
end
|
574
612
|
|
575
613
|
# Triggers all events on all elements (**once**) and captures
|
@@ -578,11 +616,15 @@ class Browser
|
|
578
616
|
# @return [Browser]
|
579
617
|
# `self`
|
580
618
|
def trigger_events
|
581
|
-
|
619
|
+
dom = self.state
|
620
|
+
|
621
|
+
elements_with_events( true ).each do |locator, events|
|
622
|
+
state = "#{locator.tag_name}:#{locator.attributes}:#{events.keys.sort}"
|
623
|
+
next if skip_state?( state )
|
624
|
+
skip_state state
|
582
625
|
|
583
|
-
each_element_with_events do |locator, events|
|
584
626
|
events.each do |name, _|
|
585
|
-
distribute_event(
|
627
|
+
distribute_event( dom, locator, name.to_sym )
|
586
628
|
end
|
587
629
|
end
|
588
630
|
|
@@ -595,23 +637,23 @@ class Browser
|
|
595
637
|
# Distributes the triggering of `event` on the element at `element_index`
|
596
638
|
# on `page`.
|
597
639
|
#
|
598
|
-
# @param [Page]
|
640
|
+
# @param [String, Page, Page::DOM, HTTP::Response] resource
|
599
641
|
# @param [ElementLocator] locator
|
600
642
|
# @param [Symbol] event
|
601
|
-
def distribute_event(
|
602
|
-
trigger_event(
|
643
|
+
def distribute_event( resource, locator, event )
|
644
|
+
trigger_event( resource, locator, event )
|
603
645
|
end
|
604
646
|
|
605
647
|
# @note Captures page {#page_snapshots}.
|
606
648
|
#
|
607
649
|
# Triggers `event` on the element described by `tag` on `page`.
|
608
650
|
#
|
609
|
-
# @param [Page]
|
651
|
+
# @param [String, Page, Page::DOM, HTTP::Response] resource
|
610
652
|
# Page containing the element's `tag`.
|
611
653
|
# @param [ElementLocator] element
|
612
654
|
# @param [Symbol] event
|
613
655
|
# Event to trigger.
|
614
|
-
def trigger_event(
|
656
|
+
def trigger_event( resource, element, event, restore = true )
|
615
657
|
event = event.to_sym
|
616
658
|
transition = fire_event( element, event )
|
617
659
|
|
@@ -620,18 +662,21 @@ class Browser
|
|
620
662
|
' the page has changed, capturing a new snapshot.'
|
621
663
|
capture_snapshot
|
622
664
|
|
623
|
-
|
624
|
-
|
665
|
+
if restore
|
666
|
+
print_info 'Restoring page.'
|
667
|
+
restore( resource )
|
668
|
+
end
|
669
|
+
|
625
670
|
return
|
626
671
|
end
|
627
672
|
|
628
673
|
capture_snapshot( transition )
|
629
|
-
restore
|
674
|
+
restore( resource ) if restore
|
630
675
|
end
|
631
676
|
|
632
677
|
# Triggers `event` on `element`.
|
633
678
|
#
|
634
|
-
# @param [
|
679
|
+
# @param [Selenium::WebDriver::Element, ElementLocator] element
|
635
680
|
# @param [Symbol] event
|
636
681
|
# @param [Hash] options
|
637
682
|
# @option options [Hash<Symbol,String=>String>] :inputs
|
@@ -651,10 +696,10 @@ class Browser
|
|
651
696
|
locator = element
|
652
697
|
|
653
698
|
begin
|
654
|
-
|
655
|
-
|
656
|
-
Watir::Exception::Error => e
|
699
|
+
Selenium::WebDriver::Wait.new( timeout: ELEMENT_APPEARANCE_TIMEOUT ).
|
700
|
+
until { element = element.locate( self ) }
|
657
701
|
|
702
|
+
rescue Selenium::WebDriver::Error::WebDriverError => e
|
658
703
|
print_debug "Element '#{element.inspect}' could not be " <<
|
659
704
|
"located for triggering '#{event}'."
|
660
705
|
print_debug
|
@@ -663,23 +708,6 @@ class Browser
|
|
663
708
|
end
|
664
709
|
end
|
665
710
|
|
666
|
-
# The page may need a bit to settle and the element is lazily located
|
667
|
-
# by Watir so give it a few tries.
|
668
|
-
begin
|
669
|
-
with_timeout ELEMENT_APPEARANCE_TIMEOUT do
|
670
|
-
sleep 0.1 while !element.exists?
|
671
|
-
end
|
672
|
-
rescue Timeout::Error
|
673
|
-
print_debug_level_2 "#{element.inspect} did not appear in " <<
|
674
|
-
"#{ELEMENT_APPEARANCE_TIMEOUT}."
|
675
|
-
return
|
676
|
-
end
|
677
|
-
|
678
|
-
if !element.visible?
|
679
|
-
print_debug_level_2 "#{element.inspect} is not visible, skipping..."
|
680
|
-
return
|
681
|
-
end
|
682
|
-
|
683
711
|
if locator
|
684
712
|
opening_tag = locator.to_s
|
685
713
|
tag_name = locator.tag_name
|
@@ -689,71 +717,115 @@ class Browser
|
|
689
717
|
locator = ElementLocator.from_html( opening_tag )
|
690
718
|
end
|
691
719
|
|
692
|
-
print_debug_level_2 "
|
720
|
+
print_debug_level_2 "[start]: #{event} (#{options}) #{locator}"
|
693
721
|
|
694
722
|
tag_name = tag_name.to_sym
|
695
723
|
|
696
724
|
notify_on_fire_event( element, event )
|
697
725
|
|
698
|
-
|
726
|
+
pre_timeouts = javascript.timeouts
|
727
|
+
|
699
728
|
begin
|
700
|
-
Page::DOM::Transition.new( locator, event, options ) do
|
701
|
-
|
729
|
+
transition = Page::DOM::Transition.new( locator, event, options ) do
|
730
|
+
force = true
|
702
731
|
|
732
|
+
# It's better to use the Watir helpers whenever possible instead
|
733
|
+
# of firing events manually.
|
703
734
|
if tag_name == :form
|
704
735
|
fill_in_form_inputs( element, options[:inputs] )
|
705
736
|
|
706
737
|
if event == :submit
|
707
|
-
|
708
|
-
|
738
|
+
force = false
|
739
|
+
|
740
|
+
element.submit
|
709
741
|
end
|
710
|
-
elsif tag_name == :input && event == :click &&
|
711
|
-
element.attribute_value(:type) == 'image'
|
712
742
|
|
713
|
-
|
714
|
-
|
743
|
+
elsif event == :click
|
744
|
+
force = false
|
715
745
|
|
716
|
-
|
746
|
+
element.click
|
717
747
|
|
718
|
-
|
719
|
-
|
748
|
+
elsif INPUT_EVENTS.include? event
|
749
|
+
force = INPUT_EVENTS_TO_FORCE.include?( event )
|
720
750
|
|
721
751
|
# Send keys will append to the existing value, so we need to
|
722
|
-
# clear it first. The receiving input may support values
|
752
|
+
# clear it first. The receiving input may not support values
|
723
753
|
# though, so watch out.
|
724
|
-
if
|
725
|
-
subtype.value = ''
|
726
|
-
end
|
754
|
+
element.clear if [:input, :textarea].include?( tag_name )
|
727
755
|
|
756
|
+
# Simulates real text input and will trigger associated events.
|
757
|
+
# Except for INPUT_EVENTS_TO_FORCE of course.
|
728
758
|
element.send_keys( (options[:value] || value_for( element )).to_s )
|
729
759
|
end
|
730
760
|
|
731
|
-
|
761
|
+
if force
|
762
|
+
print_debug_level_2 "[forcing event]: #{event} (#{options}) #{locator}"
|
763
|
+
fire_event_js locator, event
|
764
|
+
end
|
732
765
|
|
733
|
-
print_debug_level_2 "
|
766
|
+
print_debug_level_2 "[waiting for requests]: #{event} (#{options}) #{locator}"
|
734
767
|
wait_for_pending_requests
|
735
|
-
print_debug_level_2 "
|
768
|
+
print_debug_level_2 "[done waiting for requests]: #{event} (#{options}) #{locator}"
|
736
769
|
|
737
|
-
|
738
|
-
print_debug_level_2 "#{__method__} [done]: #{event} (#{options}) #{locator}"
|
770
|
+
update_cookies
|
739
771
|
end
|
740
|
-
rescue Selenium::WebDriver::Error::WebDriverError,
|
741
|
-
Watir::Exception::Error => e
|
742
772
|
|
743
|
-
|
773
|
+
print_debug_level_2 "[done in #{transition.time}s]: #{event} (#{options}) #{locator}"
|
774
|
+
|
775
|
+
delay = (javascript.timeouts - pre_timeouts).compact.map { |t| t[1].to_i }.max
|
776
|
+
if delay
|
777
|
+
print_debug_level_2 "Found new timers with max #{delay}ms."
|
778
|
+
delay = [Options.http.request_timeout, delay].min / 1000.0
|
779
|
+
|
780
|
+
print_debug_level_2 "Will wait for #{delay}s."
|
781
|
+
sleep delay
|
782
|
+
end
|
744
783
|
|
745
|
-
|
746
|
-
|
784
|
+
transition
|
785
|
+
rescue Selenium::WebDriver::Error::WebDriverError => e
|
747
786
|
|
748
787
|
print_debug "Error when triggering event for: #{url}"
|
749
|
-
print_debug "-- '#{event}' on: #{opening_tag}"
|
788
|
+
print_debug "-- '#{event}' on: #{opening_tag} -- #{locator.css}"
|
750
789
|
print_debug
|
751
790
|
print_debug_exception e
|
752
|
-
|
753
791
|
nil
|
754
792
|
end
|
755
793
|
end
|
756
794
|
|
795
|
+
# This is essentially the same thing as Watir::Element#fire_event
|
796
|
+
# but 10 times faster.
|
797
|
+
#
|
798
|
+
# Does not perform any sort of sanitization nor sanity checking, it will
|
799
|
+
# just try to trigger the event.
|
800
|
+
#
|
801
|
+
# @param [Browser::ElementLocator] locator
|
802
|
+
# @param [Symbol,String] event
|
803
|
+
# @param [Bool] ret
|
804
|
+
# Return JS result?
|
805
|
+
# @param [Numeric] wait
|
806
|
+
# Amount of time to wait (in seconds) after triggering the event.
|
807
|
+
def fire_event_js( locator, event, ret: false, wait: 0.1 )
|
808
|
+
r = javascript.run <<-EOJS
|
809
|
+
var element = document.querySelector( #{locator.css.inspect} );
|
810
|
+
var event = document.createEvent( "Events" );
|
811
|
+
|
812
|
+
event.initEvent( "#{event}", true, true );
|
813
|
+
|
814
|
+
event.view = window;
|
815
|
+
event.altKey = false;
|
816
|
+
event.ctrlKey = false;
|
817
|
+
event.shiftKey = false;
|
818
|
+
event.metaKey = false;
|
819
|
+
event.keyCode = 0;
|
820
|
+
event.charCode = 'a';
|
821
|
+
|
822
|
+
#{'return' if ret} element.dispatchEvent( event );
|
823
|
+
EOJS
|
824
|
+
|
825
|
+
sleep wait
|
826
|
+
r
|
827
|
+
end
|
828
|
+
|
757
829
|
# Starts capturing requests and parses them into elements of pages,
|
758
830
|
# accessible via {#captured_pages}.
|
759
831
|
#
|
@@ -805,13 +877,29 @@ class Browser
|
|
805
877
|
@captured_pages
|
806
878
|
end
|
807
879
|
|
880
|
+
# @return [Page::DOM]
|
881
|
+
def state
|
882
|
+
d_url = dom_url
|
883
|
+
|
884
|
+
return if !response
|
885
|
+
|
886
|
+
Page::DOM.new(
|
887
|
+
url: d_url,
|
888
|
+
transitions: @transitions.dup,
|
889
|
+
digest: @javascript.dom_digest,
|
890
|
+
skip_states: skip_states.dup
|
891
|
+
)
|
892
|
+
end
|
893
|
+
|
808
894
|
# @return [Page]
|
809
895
|
# Converts the current browser window to a {Page page}.
|
810
896
|
def to_page
|
897
|
+
d_url = dom_url
|
898
|
+
|
811
899
|
if !(r = response)
|
812
900
|
return Page.from_data(
|
813
901
|
dom: {
|
814
|
-
url:
|
902
|
+
url: d_url
|
815
903
|
},
|
816
904
|
response: {
|
817
905
|
code: 0,
|
@@ -820,12 +908,19 @@ class Browser
|
|
820
908
|
)
|
821
909
|
end
|
822
910
|
|
911
|
+
# We need sink data for both the current taint and to determine cookie
|
912
|
+
# usage, so grab all of the data-flow sinks once.
|
913
|
+
data_flow_sinks = {}
|
914
|
+
if @javascript.supported?
|
915
|
+
data_flow_sinks = @javascript.taint_tracer.data_flow_sinks
|
916
|
+
end
|
917
|
+
|
823
918
|
page = r.to_page
|
824
919
|
page.body = source
|
825
|
-
page.dom.url =
|
920
|
+
page.dom.url = d_url
|
826
921
|
page.dom.digest = @javascript.dom_digest
|
827
922
|
page.dom.execution_flow_sinks = @javascript.execution_flow_sinks
|
828
|
-
page.dom.data_flow_sinks = @javascript.
|
923
|
+
page.dom.data_flow_sinks = data_flow_sinks[@javascript.taint] || []
|
829
924
|
page.dom.transitions = @transitions.dup
|
830
925
|
page.dom.skip_states = skip_states.dup
|
831
926
|
|
@@ -860,10 +955,9 @@ class Browser
|
|
860
955
|
end
|
861
956
|
|
862
957
|
if Options.audit.cookie_doms?
|
863
|
-
sinks = @javascript.taint_tracer.data_flow_sinks
|
864
958
|
page.cookies.each do |cookie|
|
865
|
-
next if
|
866
|
-
|
959
|
+
next if data_flow_sinks.include?( cookie.name ) ||
|
960
|
+
data_flow_sinks.include?( cookie.value )
|
867
961
|
|
868
962
|
cookie.skip_dom = true
|
869
963
|
end
|
@@ -881,32 +975,46 @@ class Browser
|
|
881
975
|
request_transitions = flush_request_transitions
|
882
976
|
transitions = ([transition] + request_transitions).flatten.compact
|
883
977
|
|
978
|
+
window_handles = @selenium.window_handles
|
979
|
+
|
884
980
|
begin
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
if pages.empty?
|
891
|
-
transitions.each do |t|
|
892
|
-
@transitions << t
|
893
|
-
page.dom.push_transition t
|
894
|
-
end
|
895
|
-
end
|
981
|
+
window_handles.each do |handle|
|
982
|
+
if window_handles.size > 1
|
983
|
+
@selenium.switch_to.window( handle )
|
984
|
+
end
|
896
985
|
|
897
|
-
|
986
|
+
# We don't even have an HTTP response for the page, don't
|
987
|
+
# bother trying anything else.
|
988
|
+
next if !response
|
898
989
|
|
899
|
-
|
900
|
-
|
901
|
-
|
990
|
+
unique_id = self.snapshot_id
|
991
|
+
already_seen = skip_state?( unique_id )
|
992
|
+
skip_state unique_id
|
902
993
|
|
903
|
-
|
994
|
+
with_sinks = javascript.has_sinks?
|
904
995
|
|
905
|
-
|
906
|
-
|
907
|
-
|
996
|
+
# Avoid a #to_page call if at all possible because it'll generate
|
997
|
+
# loads of data.
|
998
|
+
next if (already_seen && !with_sinks) ||
|
999
|
+
(page = to_page).code == 0
|
1000
|
+
|
1001
|
+
if pages.empty?
|
1002
|
+
transitions.each do |t|
|
1003
|
+
@transitions << t
|
1004
|
+
page.dom.push_transition t
|
908
1005
|
end
|
909
1006
|
end
|
1007
|
+
|
1008
|
+
capture_snapshot_with_sink( page )
|
1009
|
+
|
1010
|
+
next if already_seen
|
1011
|
+
|
1012
|
+
notify_on_new_page( page )
|
1013
|
+
|
1014
|
+
if store_pages?
|
1015
|
+
@page_snapshots[unique_id] = page
|
1016
|
+
pages << page
|
1017
|
+
end
|
910
1018
|
end
|
911
1019
|
rescue => e
|
912
1020
|
print_debug "Could not capture snapshot for: #{@last_url}"
|
@@ -917,6 +1025,8 @@ class Browser
|
|
917
1025
|
|
918
1026
|
print_debug
|
919
1027
|
print_debug_exception e
|
1028
|
+
ensure
|
1029
|
+
@selenium.switch_to.default_content
|
920
1030
|
end
|
921
1031
|
|
922
1032
|
pages
|
@@ -947,33 +1057,49 @@ class Browser
|
|
947
1057
|
end
|
948
1058
|
|
949
1059
|
# @return [Array<Cookie>]
|
950
|
-
#
|
1060
|
+
# Cookies visible to JS.
|
951
1061
|
def cookies
|
952
1062
|
js_cookies = begin
|
953
|
-
|
954
|
-
|
1063
|
+
# Watir doesn't tell us if cookies are HttpOnly, so we need to figure
|
1064
|
+
# this out ourselves, by checking for JS visibility.
|
955
1065
|
javascript.run( 'return document.cookie' )
|
956
1066
|
# We may not have a page.
|
957
1067
|
rescue Selenium::WebDriver::Error::WebDriverError
|
958
1068
|
''
|
959
1069
|
end
|
960
1070
|
|
961
|
-
|
962
|
-
|
1071
|
+
# The domain attribute cannot be trusted, PhantomJS thinks all cookies
|
1072
|
+
# are for subdomains too.
|
1073
|
+
# Do not try to hack around this because it'll be a waste of time,
|
1074
|
+
# leading to confusion and duplicate cookies.
|
1075
|
+
#
|
1076
|
+
# Still, we ask Selenium for cookies instead of parsing the JS ones
|
1077
|
+
# and merging with the HTTP cookiejar because this allows us to get
|
1078
|
+
# a path attribute for JS cookies.
|
1079
|
+
@selenium.manage.all_cookies.map do |c|
|
1080
|
+
|
1081
|
+
c[:httponly] = !js_cookies.include?( c[:name].to_s )
|
1082
|
+
c[:path] = c[:path].gsub( /\/+/, '/' )
|
1083
|
+
c[:expires] = Time.parse( c[:expires].to_s ) if c[:expires]
|
963
1084
|
|
964
|
-
c[:
|
965
|
-
c[:
|
966
|
-
c[:value] = Cookie.value_to_v0( c[:value].to_s )
|
967
|
-
c[:httponly] = !js_cookies.include?( original_name )
|
1085
|
+
c[:raw_name] = c[:name].to_s
|
1086
|
+
c[:raw_value] = c[:value].to_s
|
968
1087
|
|
969
|
-
Cookie.
|
1088
|
+
c[:name] = Cookie.decode( c[:name].to_s )
|
1089
|
+
c[:value] = Cookie.value_to_v0( c[:value].to_s )
|
1090
|
+
|
1091
|
+
Cookie.new c.merge( url: @last_url || self.url )
|
970
1092
|
end
|
971
1093
|
end
|
972
1094
|
|
1095
|
+
def update_cookies
|
1096
|
+
HTTP::Client.update_cookies self.cookies
|
1097
|
+
end
|
1098
|
+
|
973
1099
|
# @return [String]
|
974
1100
|
# HTML code of the evaluated (DOM/JS/AJAX) page.
|
975
1101
|
def source
|
976
|
-
|
1102
|
+
@selenium.page_source
|
977
1103
|
end
|
978
1104
|
|
979
1105
|
def load_delay
|
@@ -985,7 +1111,11 @@ class Browser
|
|
985
1111
|
delay = load_delay
|
986
1112
|
return if !delay
|
987
1113
|
|
1114
|
+
print_debug_level_2 'Waiting for timers...'
|
1115
|
+
|
988
1116
|
sleep [Options.http.request_timeout, delay].min / 1000.0
|
1117
|
+
|
1118
|
+
print_debug_level_2 '...done.'
|
989
1119
|
end
|
990
1120
|
|
991
1121
|
def skip_path?( path )
|
@@ -993,22 +1123,26 @@ class Browser
|
|
993
1123
|
end
|
994
1124
|
|
995
1125
|
def response
|
996
|
-
u =
|
1126
|
+
u = dom_url
|
997
1127
|
|
998
|
-
|
1128
|
+
if dom_url == 'about:blank'
|
1129
|
+
print_debug 'Blank page.'
|
1130
|
+
return
|
1131
|
+
end
|
999
1132
|
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
end
|
1133
|
+
if skip_path?( u )
|
1134
|
+
print_debug "Response is out of scope: #{u}"
|
1135
|
+
return
|
1136
|
+
end
|
1005
1137
|
|
1006
|
-
|
1138
|
+
r = get_response( u )
|
1007
1139
|
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
print_debug "
|
1140
|
+
return r if r && r.code != 504
|
1141
|
+
|
1142
|
+
if r
|
1143
|
+
print_debug "Origin server timed-out when requesting: #{u}"
|
1144
|
+
else
|
1145
|
+
print_debug "Response never arrived: #{u}"
|
1012
1146
|
end
|
1013
1147
|
|
1014
1148
|
nil
|
@@ -1019,7 +1153,7 @@ class Browser
|
|
1019
1153
|
def selenium
|
1020
1154
|
return @selenium if @selenium
|
1021
1155
|
|
1022
|
-
client = Selenium::WebDriver::Remote::Http::
|
1156
|
+
client = Selenium::WebDriver::Remote::Http::Default.new
|
1023
1157
|
client.timeout = WATIR_COM_TIMEOUT
|
1024
1158
|
|
1025
1159
|
@selenium = Selenium::WebDriver.for(
|
@@ -1033,47 +1167,53 @@ class Browser
|
|
1033
1167
|
)
|
1034
1168
|
end
|
1035
1169
|
|
1170
|
+
def alive?
|
1171
|
+
@lifeline_pid && Processes::Manager.alive?( @lifeline_pid )
|
1172
|
+
end
|
1173
|
+
|
1036
1174
|
def inspect
|
1037
1175
|
s = "#<#{self.class} "
|
1038
|
-
s << "pid=#{@
|
1176
|
+
s << "pid=#{@lifeline_pid} "
|
1177
|
+
s << "browser_pid=#{@browser_pid} "
|
1039
1178
|
s << "last-url=#{@last_url.inspect} "
|
1040
1179
|
s << "transitions=#{@transitions.size}"
|
1041
1180
|
s << '>'
|
1042
1181
|
end
|
1043
1182
|
|
1044
|
-
def filter_events( element, events )
|
1045
|
-
supported = Set.new( Arachni::Browser::Javascript.events_for( element ) )
|
1046
|
-
events.reject { |name, _| !supported.include? ('on' + name.to_s.gsub( /^on/, '' )).to_sym }
|
1047
|
-
end
|
1048
|
-
|
1049
1183
|
private
|
1050
1184
|
|
1051
1185
|
def fill_in_form_inputs( form, inputs = nil )
|
1052
|
-
form.
|
1186
|
+
form.find_elements( :css, 'input, textarea' ).each do |input|
|
1053
1187
|
name_or_id = name_or_id_for( input )
|
1054
|
-
value = inputs ? inputs[name_or_id] :
|
1188
|
+
value = inputs ? inputs[name_or_id] : value_for_name( name_or_id )
|
1055
1189
|
|
1056
1190
|
begin
|
1057
|
-
input.
|
1191
|
+
input.clear
|
1192
|
+
input.send_keys( value.to_s.recode )
|
1058
1193
|
# Disabled inputs and such...
|
1059
|
-
rescue Selenium::WebDriver::Error::WebDriverError
|
1060
|
-
Watir::Exception::Error => e
|
1194
|
+
rescue Selenium::WebDriver::Error::WebDriverError => e
|
1061
1195
|
print_debug_level_2 "Could not fill in form input '#{name_or_id}'" <<
|
1062
1196
|
" because: #{e} [#{e.class}"
|
1063
1197
|
end
|
1064
1198
|
end
|
1065
1199
|
|
1066
|
-
form.
|
1067
|
-
name_or_id = name_or_id_for(
|
1068
|
-
value = inputs ? inputs[name_or_id] :
|
1200
|
+
form.find_elements( :tag_name, 'select' ).each do |select|
|
1201
|
+
name_or_id = name_or_id_for( select )
|
1202
|
+
value = inputs ? inputs[name_or_id] : value_for_name( name_or_id )
|
1069
1203
|
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1204
|
+
options = select.find_elements( tag_name: 'option' )
|
1205
|
+
options.each do |option|
|
1206
|
+
|
1207
|
+
begin
|
1208
|
+
if option[:value] == value || option.text == value
|
1209
|
+
option.click
|
1210
|
+
return
|
1211
|
+
end
|
1212
|
+
# Disabled inputs and such...
|
1213
|
+
rescue Selenium::WebDriver::Error::WebDriverError => e
|
1214
|
+
print_debug_level_2 "Could not fill in form select '#{name_or_id}'" <<
|
1215
|
+
" because: #{e} [#{e.class}"
|
1216
|
+
end
|
1077
1217
|
end
|
1078
1218
|
end
|
1079
1219
|
end
|
@@ -1091,10 +1231,10 @@ class Browser
|
|
1091
1231
|
end
|
1092
1232
|
|
1093
1233
|
def name_or_id_for( element )
|
1094
|
-
name = element
|
1234
|
+
name = element[:name].to_s
|
1095
1235
|
return name if !name.empty?
|
1096
1236
|
|
1097
|
-
id = element
|
1237
|
+
id = element[:id].to_s
|
1098
1238
|
return id if !id.empty?
|
1099
1239
|
|
1100
1240
|
nil
|
@@ -1119,6 +1259,10 @@ class Browser
|
|
1119
1259
|
Options.input.value_for_name( name_or_id_for( element ) )
|
1120
1260
|
end
|
1121
1261
|
|
1262
|
+
def value_for_name( name )
|
1263
|
+
Options.input.value_for_name( name )
|
1264
|
+
end
|
1265
|
+
|
1122
1266
|
def spawn_browser
|
1123
1267
|
if !spawn_phantomjs
|
1124
1268
|
fail Error::Spawn, 'Could not start the browser process.'
|
@@ -1134,11 +1278,14 @@ class Browser
|
|
1134
1278
|
|
1135
1279
|
ChildProcess.posix_spawn = true
|
1136
1280
|
|
1137
|
-
port
|
1138
|
-
|
1281
|
+
port = nil
|
1282
|
+
output = ''
|
1283
|
+
|
1139
1284
|
10.times do |i|
|
1140
|
-
|
1141
|
-
|
1285
|
+
# Clear output of previous attempt.
|
1286
|
+
output = ''
|
1287
|
+
done = false
|
1288
|
+
port = Utilities.available_port
|
1142
1289
|
|
1143
1290
|
print_debug "Attempt ##{i}, chose port number #{port}"
|
1144
1291
|
|
@@ -1146,52 +1293,48 @@ class Browser
|
|
1146
1293
|
with_timeout 10 do
|
1147
1294
|
print_debug "Spawning process: #{self.class.executable}"
|
1148
1295
|
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1296
|
+
r, w = IO.pipe
|
1297
|
+
ri, @kill_process = IO.pipe
|
1298
|
+
|
1299
|
+
@lifeline_pid = Processes::Manager.spawn(
|
1300
|
+
:browser,
|
1301
|
+
executable: self.class.executable,
|
1302
|
+
without_arachni: true,
|
1303
|
+
fork: false,
|
1304
|
+
new_pgroup: true,
|
1305
|
+
stdin: ri,
|
1306
|
+
stdout: w,
|
1307
|
+
stderr: w,
|
1308
|
+
port: port,
|
1309
|
+
proxy_url: @proxy.url
|
1162
1310
|
)
|
1163
|
-
# @process.leader = true
|
1164
|
-
@process.detach = true
|
1165
1311
|
|
1166
|
-
|
1167
|
-
|
1168
|
-
@process.io.stderr.sync = @process.io.stdout.sync = true
|
1312
|
+
w.close
|
1313
|
+
ri.close
|
1169
1314
|
|
1170
|
-
@process.start
|
1171
1315
|
print_debug 'Process spawned, waiting for it to boot-up...'
|
1172
1316
|
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
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
|
1180
1324
|
end
|
1325
|
+
end
|
1181
1326
|
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1327
|
+
@browser_pid = output.scan( /^PID: (\d+)/ ).flatten.first.to_i
|
1328
|
+
|
1329
|
+
print_debug 'Boot-up complete.'
|
1330
|
+
done = true
|
1186
1331
|
end
|
1187
1332
|
rescue Timeout::Error
|
1188
1333
|
print_debug 'Spawn timed-out.'
|
1189
1334
|
end
|
1190
1335
|
|
1191
|
-
if
|
1192
|
-
|
1193
|
-
print_debug last_attempt_output
|
1194
|
-
@process.io.stdout.close!
|
1336
|
+
if !output.empty?
|
1337
|
+
print_debug output
|
1195
1338
|
end
|
1196
1339
|
|
1197
1340
|
if done
|
@@ -1208,42 +1351,29 @@ class Browser
|
|
1208
1351
|
#
|
1209
1352
|
# Bail out for now and count on the BrowserCluster to retry to boot
|
1210
1353
|
# another process ass needed.
|
1211
|
-
if !@
|
1354
|
+
if !@lifeline_pid
|
1212
1355
|
log_error 'Could not spawn browser process.'
|
1213
|
-
log_error
|
1356
|
+
log_error output
|
1214
1357
|
return
|
1215
1358
|
end
|
1216
1359
|
|
1217
|
-
begin
|
1218
|
-
@pid = @process.pid
|
1219
|
-
# Not supported on JRuby on MS Windows.
|
1220
|
-
rescue NotImplementedError
|
1221
|
-
end
|
1222
|
-
|
1223
1360
|
@browser_url = "http://127.0.0.1:#{port}"
|
1224
1361
|
end
|
1225
1362
|
|
1226
1363
|
def kill_process
|
1227
|
-
|
1228
|
-
|
1229
|
-
@
|
1230
|
-
|
1364
|
+
if @kill_process
|
1365
|
+
begin
|
1366
|
+
@kill_process.close
|
1367
|
+
rescue
|
1231
1368
|
end
|
1232
|
-
rescue Errno::ECHILD
|
1233
|
-
false
|
1234
1369
|
end
|
1235
1370
|
|
1236
|
-
@
|
1237
|
-
@watir
|
1238
|
-
@selenium
|
1239
|
-
@
|
1240
|
-
@
|
1241
|
-
|
1242
|
-
|
1243
|
-
def browser_alive?
|
1244
|
-
@watir && @process && @process.alive?
|
1245
|
-
rescue Errno::ECHILD
|
1246
|
-
false
|
1371
|
+
@kill_process = nil
|
1372
|
+
@watir = nil
|
1373
|
+
@selenium = nil
|
1374
|
+
@lifeline_pid = nil
|
1375
|
+
@browser_pid = nil
|
1376
|
+
@browser_url = nil
|
1247
1377
|
end
|
1248
1378
|
|
1249
1379
|
def store_pages?
|
@@ -1267,52 +1397,50 @@ class Browser
|
|
1267
1397
|
end
|
1268
1398
|
|
1269
1399
|
def wait_for_pending_requests
|
1270
|
-
|
1271
|
-
# to wait a split second to give the browser time to initialize
|
1272
|
-
# a connection.
|
1273
|
-
#
|
1274
|
-
# TODO: Add XMLHttpRequest.send() overrides to the DOMMonitor so
|
1275
|
-
# that we'll know for sure when to wait.
|
1276
|
-
sleep 0.1
|
1400
|
+
print_debug_level_2 "Waiting for #{@proxy.pending_requests} requests to complete..."
|
1277
1401
|
|
1278
|
-
|
1279
|
-
|
1280
|
-
# The HTTP timeout option already guards us against this but I don't
|
1281
|
-
# fully trust the proxy so we're using #with_timeout as a fallback.
|
1282
|
-
with_timeout Options.http.request_timeout / 1_000 do
|
1283
|
-
sleep 0.1 while @proxy.has_connections?
|
1284
|
-
end
|
1402
|
+
sleep 0.1
|
1403
|
+
sleep 0.01 while @proxy.has_pending_requests?
|
1285
1404
|
|
1286
|
-
|
1287
|
-
rescue Timeout::Error
|
1288
|
-
#ap 'PENDING REQUESTS TIMEOUT'
|
1289
|
-
#ap caller
|
1290
|
-
false
|
1405
|
+
print_debug_level_2 '...done.'
|
1291
1406
|
end
|
1292
1407
|
|
1293
1408
|
def load_cookies( url, cookies = {} )
|
1294
1409
|
# First clears the browser's cookies and then tricks it into accepting
|
1295
1410
|
# the system cookies for its cookie-jar.
|
1411
|
+
#
|
1412
|
+
# Well, it doesn't really clear the browser's cookie-jar, but that's
|
1413
|
+
# not necessary because whatever cookies the browser has have already
|
1414
|
+
# gotten into the system-wide cookiejar, and since we're passing
|
1415
|
+
# all applicable cookies to the browser the end result will be that
|
1416
|
+
# it'll have the wanted values.
|
1296
1417
|
|
1297
1418
|
url = normalize_url( url )
|
1298
|
-
watir.cookies.clear
|
1299
1419
|
|
1300
1420
|
set_cookies = {}
|
1301
1421
|
HTTP::Client.cookie_jar.for_url( url ).each do |cookie|
|
1302
1422
|
cookie = cookie.dup
|
1303
|
-
cookie.data.delete :domain
|
1304
1423
|
set_cookies[cookie.name] = cookie
|
1305
1424
|
end
|
1425
|
+
|
1306
1426
|
cookies.each do |name, value|
|
1307
1427
|
if set_cookies[name]
|
1308
1428
|
set_cookies[name] = set_cookies[name].dup
|
1429
|
+
|
1430
|
+
# Don't forget this, otherwise the #to_set_cookie call will
|
1431
|
+
# return the original raw data.
|
1432
|
+
set_cookies[name].affected_input_name = name
|
1309
1433
|
set_cookies[name].update( name => value )
|
1310
1434
|
else
|
1311
1435
|
set_cookies[name] = Cookie.new( url: url, inputs: { name => value } )
|
1312
1436
|
end
|
1313
1437
|
end
|
1314
1438
|
|
1315
|
-
|
1439
|
+
return if set_cookies.empty? &&
|
1440
|
+
Arachni::Options::browser_cluster.local_storage.empty?
|
1441
|
+
|
1442
|
+
set_cookie = set_cookies.values.map(&:to_set_cookie)
|
1443
|
+
print_debug_level_2 "Setting cookies: #{set_cookie}"
|
1316
1444
|
|
1317
1445
|
body = ''
|
1318
1446
|
if Arachni::Options::browser_cluster.local_storage.any?
|
@@ -1327,11 +1455,12 @@ class Browser
|
|
1327
1455
|
EOJS
|
1328
1456
|
end
|
1329
1457
|
|
1330
|
-
|
1331
|
-
|
1458
|
+
@selenium.navigate.to preload( HTTP::Response.new(
|
1459
|
+
code: 200,
|
1460
|
+
url: "#{url}/set-cookies-#{request_token}",
|
1332
1461
|
body: body,
|
1333
1462
|
headers: {
|
1334
|
-
'Set-Cookie' =>
|
1463
|
+
'Set-Cookie' => set_cookie
|
1335
1464
|
}
|
1336
1465
|
))
|
1337
1466
|
end
|
@@ -1339,12 +1468,26 @@ EOJS
|
|
1339
1468
|
# Makes sure we have at least 2 windows open so that we can switch to the
|
1340
1469
|
# last available one in case there's some JS in the page that closes one.
|
1341
1470
|
def ensure_open_window
|
1342
|
-
|
1471
|
+
window_handles = @selenium.window_handles
|
1472
|
+
|
1473
|
+
if window_handles.size == 0
|
1474
|
+
@javascript.run( 'window.open()' )
|
1475
|
+
@selenium.switch_to.window( @selenium.window_handles.last )
|
1476
|
+
else
|
1477
|
+
if window_handles.size > 1
|
1478
|
+
# Keep the first
|
1479
|
+
window_handles[1..-1].each do |handle|
|
1480
|
+
@selenium.switch_to.window( handle )
|
1481
|
+
@selenium.close
|
1482
|
+
end
|
1343
1483
|
|
1344
|
-
|
1345
|
-
|
1484
|
+
@selenium.switch_to.window( @selenium.window_handles.first )
|
1485
|
+
end
|
1346
1486
|
|
1347
|
-
|
1487
|
+
@selenium.navigate.to 'about:blank'
|
1488
|
+
end
|
1489
|
+
|
1490
|
+
@selenium.manage.window.resize_to( @width, @height )
|
1348
1491
|
end
|
1349
1492
|
|
1350
1493
|
# # Firefox driver, only used for debugging.
|
@@ -1362,6 +1505,11 @@ EOJS
|
|
1362
1505
|
|
1363
1506
|
def capabilities
|
1364
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,
|
1365
1513
|
'phantomjs.page.settings.userAgent' => Options.http.user_agent,
|
1366
1514
|
'phantomjs.page.customHeaders.X-Arachni-Browser-Auth' => auth_token,
|
1367
1515
|
'phantomjs.page.settings.resourceTimeout' => Options.http.request_timeout,
|
@@ -1389,6 +1537,8 @@ EOJS
|
|
1389
1537
|
return if request.headers['X-Arachni-Browser-Auth'] != auth_token
|
1390
1538
|
request.headers.delete 'X-Arachni-Browser-Auth'
|
1391
1539
|
|
1540
|
+
print_debug_level_2 "Request: #{request.url}"
|
1541
|
+
|
1392
1542
|
# We can't have 304 page responses in the framework, we need full request
|
1393
1543
|
# and response data, the browser cache doesn't help us here.
|
1394
1544
|
#
|
@@ -1399,41 +1549,47 @@ EOJS
|
|
1399
1549
|
request.headers.delete 'If-Modified-Since'
|
1400
1550
|
end
|
1401
1551
|
|
1402
|
-
|
1552
|
+
if @javascript.serve( request, response )
|
1553
|
+
print_debug_level_2 "Serving local JS."
|
1554
|
+
return
|
1555
|
+
end
|
1403
1556
|
|
1404
1557
|
if !request.url.include?( request_token )
|
1405
|
-
|
1558
|
+
if ignore_request?( request )
|
1559
|
+
print_debug_level_2 "Out of scope, ignoring."
|
1560
|
+
return
|
1561
|
+
end
|
1406
1562
|
|
1407
1563
|
if @add_request_transitions
|
1408
|
-
|
1564
|
+
synchronize do
|
1565
|
+
@request_transitions << Page::DOM::Transition.new(
|
1566
|
+
request.url, :request
|
1567
|
+
)
|
1568
|
+
end
|
1409
1569
|
end
|
1410
1570
|
end
|
1411
1571
|
|
1412
1572
|
# Signal the proxy to not actually perform the request if we have a
|
1413
1573
|
# preloaded or cached response for it.
|
1414
1574
|
if from_preloads( request, response ) || from_cache( request, response )
|
1575
|
+
print_debug_level_2 'Resource has been preloaded.'
|
1576
|
+
|
1415
1577
|
# There may be taints or custom JS code that need to be updated.
|
1416
1578
|
javascript.inject response
|
1417
1579
|
return
|
1418
1580
|
end
|
1419
1581
|
|
1582
|
+
print_debug_level_2 'Request can proceed to origin.'
|
1583
|
+
|
1420
1584
|
# Capture the request as elements of pages -- let's us grab AJAX and
|
1421
1585
|
# other browser requests and convert them into elements we can analyze
|
1422
1586
|
# and audit.
|
1423
|
-
|
1587
|
+
if request.scope.in?
|
1588
|
+
capture( request )
|
1589
|
+
end
|
1424
1590
|
|
1425
1591
|
request.headers['user-agent'] = Options.http.user_agent
|
1426
1592
|
|
1427
|
-
# The proxy has an unlimited response_max_size so if we're not requesting
|
1428
|
-
# an asset remove the response_max_size option so that it'll end up using
|
1429
|
-
# the system settings.
|
1430
|
-
#
|
1431
|
-
# However, this is not foolproof, a lot of assets don't have the expected
|
1432
|
-
# extension.
|
1433
|
-
if !request_for_asset?( request )
|
1434
|
-
request.response_max_size = nil
|
1435
|
-
end
|
1436
|
-
|
1437
1593
|
# Signal the proxy to continue with its request to the origin server.
|
1438
1594
|
true
|
1439
1595
|
end
|
@@ -1441,41 +1597,99 @@ EOJS
|
|
1441
1597
|
def response_handler( request, response )
|
1442
1598
|
return if request.url.include?( request_token )
|
1443
1599
|
|
1600
|
+
print_debug_level_2 "Got response: #{response.url}"
|
1601
|
+
|
1444
1602
|
@request_transitions.each do |transition|
|
1445
1603
|
next if !transition.running? || transition.element != request.url
|
1446
1604
|
transition.complete
|
1447
1605
|
end
|
1448
1606
|
|
1449
|
-
|
1607
|
+
# If we abort the request because it's out of scope we need to emulate
|
1608
|
+
# an OK response because we **do** want to be able to grab a page with
|
1609
|
+
# the out of scope URL, even if it's empty.
|
1610
|
+
# For example, unvalidated_redirect checks need this.
|
1611
|
+
if response.code == 0
|
1612
|
+
if enforce_scope? && response.scope.out?
|
1613
|
+
response.code = 200
|
1614
|
+
end
|
1615
|
+
else
|
1616
|
+
if javascript.inject( response )
|
1617
|
+
print_debug_level_2 'Injected custom JS.'
|
1618
|
+
end
|
1619
|
+
end
|
1450
1620
|
|
1451
1621
|
# Don't store assets, the browsers will cache them accordingly.
|
1452
|
-
|
1622
|
+
if request_for_asset?( request ) || !response.text?
|
1623
|
+
print_debug_level_2 'Asset detected, will not store.'
|
1624
|
+
return
|
1625
|
+
end
|
1453
1626
|
|
1454
1627
|
# No-matter the scope, don't store resources for external domains.
|
1455
|
-
|
1628
|
+
if !response.scope.in_domain?
|
1629
|
+
print_debug_level_2 'Outside of domain scope, will not store.'
|
1630
|
+
return
|
1631
|
+
end
|
1456
1632
|
|
1457
|
-
|
1633
|
+
if enforce_scope? && response.scope.out?
|
1634
|
+
print_debug_level_2 'Outside of general scope, will not store.'
|
1635
|
+
return
|
1636
|
+
end
|
1458
1637
|
|
1459
1638
|
whitelist_asset_domains( response )
|
1460
1639
|
save_response response
|
1461
1640
|
|
1641
|
+
print_debug_level_2 'Stored.'
|
1642
|
+
|
1462
1643
|
nil
|
1463
1644
|
end
|
1464
1645
|
|
1465
1646
|
def ignore_request?( request )
|
1466
|
-
|
1647
|
+
print_debug_level_2 "Checking: #{request.url}"
|
1648
|
+
|
1649
|
+
if !enforce_scope?
|
1650
|
+
print_debug_level_2 'Allow: Scope enforcement disabled.'
|
1651
|
+
return
|
1652
|
+
end
|
1653
|
+
|
1654
|
+
if request_for_asset?( request )
|
1655
|
+
print_debug_level_2 'Allow: Asset detected.'
|
1656
|
+
return false
|
1657
|
+
end
|
1658
|
+
|
1659
|
+
if !request.scope.follow_protocol?
|
1660
|
+
print_debug_level_2 'Disallow: Cannot follow protocol.'
|
1661
|
+
return true
|
1662
|
+
end
|
1663
|
+
|
1664
|
+
if !request.scope.in_domain?
|
1665
|
+
if self.class.asset_domains.include?( request.parsed_url.domain )
|
1666
|
+
print_debug_level_2 'Allow: Out of scope but in CDN list.'
|
1667
|
+
return false
|
1668
|
+
end
|
1467
1669
|
|
1468
|
-
|
1670
|
+
print_debug_level_2 'Disallow: Domain out of scope and not in CDN list.'
|
1671
|
+
return true
|
1672
|
+
end
|
1469
1673
|
|
1470
|
-
|
1674
|
+
if request.scope.too_deep?
|
1675
|
+
print_debug_level_2 'Disallow: Too deep.'
|
1676
|
+
return true
|
1677
|
+
end
|
1471
1678
|
|
1472
|
-
|
1473
|
-
|
1679
|
+
if !request.scope.include?
|
1680
|
+
print_debug_level_2 'Disallow: Does not match inclusion rules.'
|
1681
|
+
return true
|
1682
|
+
end
|
1474
1683
|
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1684
|
+
if request.scope.exclude?
|
1685
|
+
print_debug_level_2 'Disallow: Matches exclusion rules.'
|
1686
|
+
return true
|
1687
|
+
end
|
1688
|
+
|
1689
|
+
if request.scope.redundant?
|
1690
|
+
print_debug_level_2 'Disallow: Matches redundant rules.'
|
1691
|
+
return true
|
1692
|
+
end
|
1479
1693
|
|
1480
1694
|
false
|
1481
1695
|
end
|
@@ -1485,9 +1699,13 @@ EOJS
|
|
1485
1699
|
end
|
1486
1700
|
|
1487
1701
|
def whitelist_asset_domains( response )
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1702
|
+
synchronize do
|
1703
|
+
ASSET_EXTRACTORS.each do |regexp|
|
1704
|
+
response.body.scan( regexp ).flatten.compact.each do |url|
|
1705
|
+
next if !(domain = self.class.add_asset_domain( url ))
|
1706
|
+
|
1707
|
+
print_debug_level_2 "#{domain} from #{url} based on #{regexp.source}"
|
1708
|
+
end
|
1491
1709
|
end
|
1492
1710
|
end
|
1493
1711
|
end
|
@@ -1504,11 +1722,15 @@ EOJS
|
|
1504
1722
|
found_element = false
|
1505
1723
|
|
1506
1724
|
if (json = JSON.from_request( @last_url, request ))
|
1725
|
+
print_debug_level_2 "Extracted JSON input:\n#{json.source}"
|
1726
|
+
|
1507
1727
|
elements[:jsons] << json
|
1508
1728
|
found_element = true
|
1509
1729
|
end
|
1510
1730
|
|
1511
1731
|
if !found_element && (xml = XML.from_request( @last_url, request ))
|
1732
|
+
print_debug_level_2 "Extracted XML input:\n#{xml.source}"
|
1733
|
+
|
1512
1734
|
elements[:xmls] << xml
|
1513
1735
|
found_element = true
|
1514
1736
|
end
|
@@ -1539,13 +1761,30 @@ EOJS
|
|
1539
1761
|
return
|
1540
1762
|
end
|
1541
1763
|
|
1764
|
+
if (form = elements[:forms].last)
|
1765
|
+
print_debug_level_2 "Extracted form input:\n" <<
|
1766
|
+
"#{form.method.to_s.upcase} #{form.action} #{form.inputs}"
|
1767
|
+
end
|
1768
|
+
|
1542
1769
|
el = elements.values.flatten
|
1543
1770
|
|
1771
|
+
if el.empty?
|
1772
|
+
print_debug_level_2 'No elements found.'
|
1773
|
+
return
|
1774
|
+
end
|
1775
|
+
|
1544
1776
|
# Don't bother if the system in general has already seen the vectors.
|
1545
|
-
|
1777
|
+
if el.empty? || !el.find { |e| !ElementFilter.include?( e ) }
|
1778
|
+
print_debug_level_2 'Ignoring, already seen.'
|
1779
|
+
return
|
1780
|
+
end
|
1546
1781
|
|
1547
1782
|
begin
|
1548
|
-
|
1783
|
+
if !el.find { |e| !skip_state?( e ) }
|
1784
|
+
print_debug_level_2 'Ignoring, already seen.'
|
1785
|
+
return
|
1786
|
+
end
|
1787
|
+
|
1549
1788
|
el.each { |e| skip_state e.id }
|
1550
1789
|
# This could be an orphaned HTTP request, without a job, if running in
|
1551
1790
|
# BrowserCluster::Worker.
|
@@ -1565,21 +1804,25 @@ EOJS
|
|
1565
1804
|
end
|
1566
1805
|
|
1567
1806
|
def from_preloads( request, response )
|
1568
|
-
|
1807
|
+
synchronize do
|
1808
|
+
return if !(preloaded = preloads.delete( request.url ))
|
1569
1809
|
|
1570
|
-
|
1571
|
-
|
1572
|
-
|
1810
|
+
copy_response_data( preloaded, response )
|
1811
|
+
response.request = request
|
1812
|
+
save_response( response ) if !preloaded.url.include?( request_token )
|
1573
1813
|
|
1574
|
-
|
1814
|
+
preloaded
|
1815
|
+
end
|
1575
1816
|
end
|
1576
1817
|
|
1577
1818
|
def from_cache( request, response )
|
1578
|
-
|
1819
|
+
synchronize do
|
1820
|
+
return if !@cache.include?( request.url )
|
1579
1821
|
|
1580
|
-
|
1581
|
-
|
1582
|
-
|
1822
|
+
copy_response_data( @cache[request.url], response )
|
1823
|
+
response.request = request
|
1824
|
+
save_response response
|
1825
|
+
end
|
1583
1826
|
end
|
1584
1827
|
|
1585
1828
|
def copy_response_data( source, destination )
|