arachni 1.3.2 → 1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +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 )
|