arachni 1.1 → 1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +159 -0
- data/LICENSE.md +126 -196
- data/README.md +32 -24
- data/arachni.gemspec +7 -7
- data/components/checks/active/code_injection_timing.rb +3 -3
- data/components/checks/active/csrf.rb +2 -2
- data/components/checks/active/file_inclusion.rb +6 -7
- data/components/checks/active/os_cmd_injection.rb +3 -3
- data/components/checks/active/path_traversal.rb +7 -7
- data/components/checks/active/response_splitting.rb +9 -4
- data/components/checks/active/session_fixation.rb +7 -3
- data/components/checks/active/source_code_disclosure.rb +5 -5
- data/components/checks/active/unvalidated_redirect.rb +12 -3
- data/components/checks/active/unvalidated_redirect_dom.rb +3 -3
- data/components/checks/active/xss.rb +23 -10
- data/components/checks/active/xss_dom_inputs.rb +113 -11
- data/components/checks/active/xxe.rb +3 -3
- data/components/checks/passive/backdoors.rb +6 -5
- data/components/checks/passive/backup_directories.rb +6 -6
- data/components/checks/passive/backup_files.rb +6 -6
- data/components/checks/passive/common_admin_interfaces.rb +58 -0
- data/components/checks/passive/common_admin_interfaces/admin-panels.txt +49 -0
- data/components/checks/passive/common_directories/directories.txt +0 -16
- data/components/checks/passive/common_files.rb +6 -5
- data/components/checks/passive/common_files/filenames.txt +0 -2
- data/components/checks/passive/directory_listing.rb +6 -6
- data/components/checks/passive/grep/cookie_set_for_parent_domain.rb +3 -3
- data/components/checks/passive/grep/hsts.rb +6 -3
- data/components/checks/passive/grep/http_only_cookies.rb +3 -3
- data/components/checks/passive/grep/insecure_cookies.rb +2 -2
- data/components/checks/passive/grep/insecure_cors_policy.rb +6 -4
- data/components/checks/passive/grep/x_frame_options.rb +6 -4
- data/components/checks/passive/htaccess_limit.rb +6 -2
- data/components/checks/passive/http_put.rb +8 -4
- data/components/checks/passive/interesting_responses.rb +3 -2
- data/components/checks/passive/localstart_asp.rb +6 -2
- data/components/checks/passive/origin_spoof_access_restriction_bypass.rb +5 -1
- data/components/checks/passive/xst.rb +6 -2
- data/components/fingerprinters/frameworks/aspx_mvc.rb +43 -0
- data/components/fingerprinters/frameworks/cakephp.rb +28 -0
- data/components/fingerprinters/frameworks/cherrypy.rb +31 -0
- data/components/fingerprinters/frameworks/django.rb +33 -0
- data/components/fingerprinters/frameworks/jsf.rb +30 -0
- data/components/fingerprinters/frameworks/rack.rb +5 -7
- data/components/fingerprinters/frameworks/rails.rb +43 -0
- data/components/fingerprinters/languages/aspx.rb +11 -11
- data/components/fingerprinters/languages/{jsp.rb → java.rb} +11 -7
- data/components/fingerprinters/languages/php.rb +6 -6
- data/components/fingerprinters/languages/python.rb +14 -6
- data/components/fingerprinters/languages/ruby.rb +3 -5
- data/components/fingerprinters/servers/apache.rb +5 -4
- data/components/fingerprinters/servers/gunicorn.rb +33 -0
- data/components/fingerprinters/servers/jetty.rb +1 -1
- data/components/fingerprinters/servers/tomcat.rb +11 -4
- data/components/path_extractors/anchors.rb +5 -12
- data/components/path_extractors/areas.rb +5 -13
- data/components/path_extractors/comments.rb +5 -3
- data/components/path_extractors/data_url.rb +21 -0
- data/components/path_extractors/forms.rb +5 -13
- data/components/path_extractors/frames.rb +6 -13
- data/components/path_extractors/generic.rb +3 -12
- data/components/path_extractors/links.rb +5 -13
- data/components/path_extractors/meta_refresh.rb +5 -13
- data/components/path_extractors/scripts.rb +8 -14
- data/components/plugins/autologin.rb +17 -5
- data/components/plugins/defaults/meta/remedies/discovery.rb +11 -29
- data/components/plugins/login_script.rb +40 -10
- data/components/plugins/metrics.rb +235 -0
- data/components/plugins/proxy.rb +21 -4
- data/components/plugins/proxy/panel/page_accordion.html.erb +34 -2
- data/components/plugins/restrict_to_dom_state.rb +70 -0
- data/components/plugins/vector_feed.rb +38 -9
- data/components/reporters/plugin_formatters/html/metrics.rb +290 -0
- data/components/reporters/plugin_formatters/stdout/metrics.rb +80 -0
- data/components/reporters/plugin_formatters/xml/metrics.rb +29 -0
- data/components/reporters/stdout.rb +4 -2
- data/components/reporters/xml.rb +4 -4
- data/components/reporters/xml/schema.xsd +95 -0
- data/lib/arachni.rb +2 -0
- data/lib/arachni/browser.rb +132 -77
- data/lib/arachni/browser/javascript.rb +173 -45
- data/lib/arachni/browser/javascript/scripts/dom_monitor.js +81 -6
- data/lib/arachni/browser/javascript/scripts/taint_tracer.js +31 -3
- data/lib/arachni/browser_cluster.rb +41 -15
- data/lib/arachni/browser_cluster/job.rb +4 -0
- data/lib/arachni/browser_cluster/jobs/resource_exploration.rb +0 -9
- data/lib/arachni/browser_cluster/worker.rb +8 -5
- data/lib/arachni/check/auditor.rb +20 -8
- data/lib/arachni/check/base.rb +38 -6
- data/lib/arachni/element/base.rb +18 -1
- data/lib/arachni/element/capabilities/analyzable/differential.rb +0 -1
- data/lib/arachni/element/capabilities/analyzable/taint.rb +40 -10
- data/lib/arachni/element/capabilities/analyzable/timeout.rb +27 -23
- data/lib/arachni/element/capabilities/auditable/dom.rb +22 -0
- data/lib/arachni/element/capabilities/inputtable.rb +6 -2
- data/lib/arachni/element/capabilities/submittable.rb +1 -1
- data/lib/arachni/element/cookie.rb +37 -23
- data/lib/arachni/element/cookie/capabilities/mutable.rb +6 -6
- data/lib/arachni/element/cookie/dom.rb +0 -8
- data/lib/arachni/element/form.rb +28 -14
- data/lib/arachni/element/form/capabilities/auditable.rb +2 -2
- data/lib/arachni/element/form/capabilities/mutable.rb +5 -5
- data/lib/arachni/element/form/dom.rb +0 -8
- data/lib/arachni/element/generic_dom.rb +1 -1
- data/lib/arachni/element/json.rb +2 -1
- data/lib/arachni/element/json/capabilities/inputtable.rb +6 -6
- data/lib/arachni/element/json/capabilities/mutable.rb +1 -1
- data/lib/arachni/element/link.rb +13 -16
- data/lib/arachni/element/link/dom.rb +1 -14
- data/lib/arachni/element/link_template.rb +3 -2
- data/lib/arachni/element/link_template/dom.rb +0 -16
- data/lib/arachni/element/server.rb +51 -9
- data/lib/arachni/element/xml.rb +1 -0
- data/lib/arachni/ethon/easy.rb +4 -1
- data/lib/arachni/framework/parts/audit.rb +26 -77
- data/lib/arachni/framework/parts/browser.rb +50 -55
- data/lib/arachni/framework/parts/check.rb +4 -3
- data/lib/arachni/framework/parts/data.rb +41 -6
- data/lib/arachni/framework/parts/state.rb +16 -7
- data/lib/arachni/http/client.rb +66 -38
- data/lib/arachni/http/client/dynamic_404_handler.rb +46 -14
- data/lib/arachni/http/headers.rb +22 -10
- data/lib/arachni/http/proxy_server.rb +67 -22
- data/lib/arachni/http/proxy_server/ssl-interceptor-cacert.pem +34 -0
- data/lib/arachni/http/proxy_server/ssl-interceptor-cakey.pem +51 -0
- data/lib/arachni/http/request.rb +71 -18
- data/lib/arachni/issue.rb +17 -3
- data/lib/arachni/option_groups/browser_cluster.rb +34 -1
- data/lib/arachni/option_groups/http.rb +1 -1
- data/lib/arachni/page.rb +26 -13
- data/lib/arachni/page/dom/transition.rb +2 -2
- data/lib/arachni/parser.rb +28 -11
- data/lib/arachni/platform/fingerprinter.rb +5 -0
- data/lib/arachni/platform/manager.rb +65 -32
- data/lib/arachni/plugin/base.rb +8 -0
- data/lib/arachni/processes/instances.rb +25 -11
- data/lib/arachni/reporter/manager.rb +2 -2
- data/lib/arachni/rpc/client/instance.rb +4 -0
- data/lib/arachni/rpc/server/framework/master.rb +3 -3
- data/lib/arachni/rpc/server/framework/multi_instance.rb +0 -8
- data/lib/arachni/rpc/server/instance.rb +2 -1
- data/lib/arachni/ruby/array.rb +5 -0
- data/lib/arachni/ruby/hash.rb +5 -0
- data/lib/arachni/ruby/string.rb +2 -3
- data/lib/arachni/session.rb +32 -6
- data/lib/arachni/state/framework.rb +6 -2
- data/lib/arachni/support/cache.rb +1 -0
- data/lib/arachni/support/cache/base.rb +12 -8
- data/lib/arachni/support/cache/least_recently_pushed.rb +29 -0
- data/lib/arachni/support/cache/least_recently_used.rb +5 -8
- data/lib/arachni/support/cache/preference.rb +1 -1
- data/lib/arachni/support/cache/random_replacement.rb +1 -25
- data/lib/arachni/support/database/queue.rb +21 -8
- data/lib/arachni/support/lookup/base.rb +7 -1
- data/lib/arachni/support/mixins/observable.rb +3 -1
- data/lib/arachni/support/profiler.rb +51 -10
- data/lib/arachni/support/signature.rb +11 -2
- data/lib/arachni/trainer.rb +8 -2
- data/lib/arachni/uri.rb +28 -25
- data/lib/arachni/uri/scope.rb +1 -1
- data/lib/arachni/utilities.rb +8 -0
- data/lib/arachni/watir/element.rb +1 -1
- data/lib/version +1 -1
- data/spec/arachni/browser/javascript/dom_monitor_spec.rb +388 -53
- data/spec/arachni/browser/javascript/taint_tracer_spec.rb +41 -0
- data/spec/arachni/browser/javascript_spec.rb +235 -61
- data/spec/arachni/browser_cluster/jobs/resource_exploration_spec.rb +0 -9
- data/spec/arachni/browser_cluster_spec.rb +58 -10
- data/spec/arachni/browser_spec.rb +170 -26
- data/spec/arachni/check/auditor_spec.rb +22 -3
- data/spec/arachni/check/base_spec.rb +84 -0
- data/spec/arachni/element/body_spec.rb +1 -1
- data/spec/arachni/element/capabilities/analyzable/taint_spec.rb +3 -3
- data/spec/arachni/element/capabilities/analyzable/timeout_spec.rb +1 -1
- data/spec/arachni/element/cookie/dom_spec.rb +0 -9
- data/spec/arachni/element/cookie_spec.rb +85 -0
- data/spec/arachni/element/form/dom_spec.rb +0 -9
- data/spec/arachni/element/form_spec.rb +46 -3
- data/spec/arachni/element/json_spec.rb +20 -0
- data/spec/arachni/element/link/dom_spec.rb +0 -9
- data/spec/arachni/element/link_spec.rb +40 -15
- data/spec/arachni/element/link_template/dom_spec.rb +0 -8
- data/spec/arachni/element/link_template_spec.rb +2 -6
- data/spec/arachni/element/server_spec.rb +94 -8
- data/spec/arachni/element/xml_spec.rb +20 -0
- data/spec/arachni/framework/parts/audit_spec.rb +12 -14
- data/spec/arachni/framework/parts/browser_spec.rb +0 -171
- data/spec/arachni/framework/parts/platform_spec.rb +14 -8
- data/spec/arachni/framework/parts/report_spec.rb +1 -1
- data/spec/arachni/framework/parts/state_spec.rb +0 -9
- data/spec/arachni/http/client/dynamic_404_handlers_spec.rb +19 -0
- data/spec/arachni/http/client_spec.rb +169 -42
- data/spec/arachni/http/headers_spec.rb +18 -0
- data/spec/arachni/http/request_spec.rb +23 -0
- data/spec/arachni/issue_spec.rb +17 -6
- data/spec/arachni/page_spec.rb +22 -2
- data/spec/arachni/parser_spec.rb +5 -0
- data/spec/arachni/platform/manager_spec.rb +57 -25
- data/spec/arachni/reporter/manager_spec.rb +26 -0
- data/spec/arachni/rpc/server/active_options_spec.rb +9 -4
- data/spec/arachni/state/framework_spec.rb +2 -8
- data/spec/arachni/support/cache/least_recently_pushed_spec.rb +90 -0
- data/spec/arachni/support/cache/least_recently_used_spec.rb +5 -13
- data/spec/arachni/support/database/queue_spec.rb +7 -0
- data/spec/arachni/support/mixins/observable_spec.rb +15 -1
- data/spec/arachni/trainer_spec.rb +2 -2
- data/spec/components/checks/active/code_injection_timing_spec.rb +1 -1
- data/spec/components/checks/active/file_inclusion_spec.rb +6 -6
- data/spec/components/checks/active/path_traversal_spec.rb +2 -2
- data/spec/components/checks/active/source_code_disclosure_spec.rb +2 -2
- data/spec/components/checks/active/unvalidated_redirect_spec.rb +6 -6
- data/spec/components/checks/active/xss_dom_inputs_spec.rb +3 -5
- data/spec/components/checks/active/xss_dom_script_context_spec.rb +1 -1
- data/spec/components/checks/active/xss_spec.rb +5 -5
- data/spec/components/checks/passive/common_admin_interfaces_spec.rb +15 -0
- data/spec/components/checks/passive/interesting_responses_spec.rb +14 -1
- data/spec/components/fingerprinters/frameworks/aspx_mvc_spec.rb +31 -0
- data/spec/components/fingerprinters/frameworks/cakephp_spec.rb +22 -0
- data/spec/components/fingerprinters/frameworks/cherrypy_spec.rb +28 -0
- data/spec/components/fingerprinters/frameworks/django_spec.rb +37 -0
- data/spec/components/fingerprinters/frameworks/jsf_spec.rb +27 -0
- data/spec/components/fingerprinters/frameworks/rack_spec.rb +11 -14
- data/spec/components/fingerprinters/frameworks/rails_spec.rb +53 -0
- data/spec/components/fingerprinters/languages/asp_spec.rb +7 -9
- data/spec/components/fingerprinters/languages/aspx_spec.rb +10 -24
- data/spec/components/fingerprinters/languages/java_spec.rb +88 -0
- data/spec/components/fingerprinters/languages/php_spec.rb +19 -12
- data/spec/components/fingerprinters/languages/python_spec.rb +22 -9
- data/spec/components/fingerprinters/languages/ruby.rb +6 -4
- data/spec/components/fingerprinters/os/bsd_spec.rb +6 -4
- data/spec/components/fingerprinters/os/linux_spec.rb +6 -4
- data/spec/components/fingerprinters/os/solaris_spec.rb +6 -4
- data/spec/components/fingerprinters/os/unix_spec.rb +6 -4
- data/spec/components/fingerprinters/os/windows_spec.rb +6 -4
- data/spec/components/fingerprinters/servers/apache_spec.rb +15 -4
- data/spec/components/fingerprinters/servers/gunicorn_spec.rb +28 -0
- data/spec/components/fingerprinters/servers/iis_spec.rb +6 -6
- data/spec/components/fingerprinters/servers/jetty_spec.rb +6 -6
- data/spec/components/fingerprinters/servers/nginx_spec.rb +6 -4
- data/spec/components/fingerprinters/servers/tomcat_spec.rb +15 -6
- data/spec/components/path_extractors/data_url_spec.rb +19 -0
- data/spec/components/plugins/autologin_spec.rb +23 -0
- data/spec/components/plugins/login_script_spec.rb +112 -24
- data/spec/components/plugins/restrict_to_dom_state_spec.rb +16 -0
- data/spec/components/plugins/vector_feed_spec.rb +39 -1
- data/spec/support/factories/page/dom.rb +9 -4
- data/spec/support/factories/page/dom/transition.rb +31 -9
- data/spec/support/factories/scan_report.rb +8 -6
- data/spec/support/fixtures/empty/placeholder +0 -0
- data/spec/support/fixtures/report.afr +0 -0
- data/spec/support/fixtures/reporters/manager_spec/error.rb +18 -0
- data/spec/support/servers/arachni/browser.rb +117 -11
- data/spec/support/servers/arachni/browser/javascript/dom_monitor.rb +148 -4
- data/spec/support/servers/arachni/check/auditor.rb +4 -0
- data/spec/support/servers/arachni/element/cookie/cookie_dom.rb +1 -1
- data/spec/support/servers/arachni/http/client.rb +5 -0
- data/spec/support/servers/arachni/http/client/dynamic_404_handler.rb +13 -0
- data/spec/support/servers/checks/active/code_injection_timing.rb +1 -1
- data/spec/support/servers/checks/active/file_inclusion.rb +2 -2
- data/spec/support/servers/checks/active/path_traversal.rb +2 -2
- data/spec/support/servers/checks/active/source_code_disclosure.rb +40 -33
- data/spec/support/servers/checks/active/trainer_check.rb +9 -10
- data/spec/support/servers/checks/active/unvalidated_redirect_dom.rb +7 -4
- data/spec/support/servers/checks/active/xss.rb +35 -0
- data/spec/support/servers/checks/active/xss_dom.rb +1 -1
- data/spec/support/servers/checks/active/xss_dom_inputs.rb +24 -0
- data/spec/support/servers/checks/active/xss_dom_script_context.rb +1 -1
- data/spec/support/servers/checks/passive/common_admin_interfaces.rb +6 -0
- data/spec/support/servers/plugins/autologin.rb +9 -0
- data/spec/support/servers/plugins/restrict_to_dom_state.rb +4 -0
- data/spec/support/shared/element/base.rb +42 -0
- data/spec/support/shared/element/capabilities/auditable.rb +4 -4
- data/spec/support/shared/element/capabilities/auditable/dom.rb +26 -0
- data/spec/support/shared/element/capabilities/inputtable.rb +16 -11
- data/spec/support/shared/element/capabilities/submitable.rb +7 -2
- data/spec/support/shared/fingerprinter.rb +8 -0
- data/spec/support/shared/path_extractor.rb +1 -1
- data/ui/cli/framework.rb +3 -3
- data/ui/cli/framework/option_parser.rb +9 -0
- data/ui/cli/output.rb +9 -0
- data/ui/cli/reporter.rb +5 -2
- data/ui/cli/utilities.rb +4 -2
- metadata +76 -17
- data/lib/arachni/http/proxy_server/ssl-interceptor-cert.pem +0 -34
- data/lib/arachni/http/proxy_server/ssl-interceptor-pkey.pem +0 -51
- data/spec/components/fingerprinters/languages/jsp_spec.rb +0 -56
@@ -30,18 +30,20 @@ module Browser
|
|
30
30
|
state.set_status_message :browser_cluster_startup
|
31
31
|
end
|
32
32
|
|
33
|
-
@browser_cluster ||= BrowserCluster.new
|
33
|
+
@browser_cluster ||= BrowserCluster.new(
|
34
|
+
on_pop: proc do
|
35
|
+
next if !pause?
|
36
|
+
|
37
|
+
print_debug 'Blocking browser cluster on pop.'
|
38
|
+
wait_if_paused
|
39
|
+
end
|
40
|
+
)
|
34
41
|
state.clear_status_messages
|
42
|
+
|
35
43
|
@browser_cluster
|
36
44
|
end
|
37
45
|
end
|
38
46
|
|
39
|
-
def browser
|
40
|
-
return if !use_browsers?
|
41
|
-
|
42
|
-
@browser ||= Arachni::Browser.new( store_pages: false )
|
43
|
-
end
|
44
|
-
|
45
47
|
# @return [Bool]
|
46
48
|
# `true` if the environment has a browser, `false` otherwise.
|
47
49
|
def host_has_browser?
|
@@ -58,36 +60,6 @@ module Browser
|
|
58
60
|
browser_cluster.skip_states( browser_job.id )
|
59
61
|
end
|
60
62
|
|
61
|
-
# @private
|
62
|
-
def apply_dom_metadata( page )
|
63
|
-
return false if page.dom.depth > 0 || !page.has_script? ||
|
64
|
-
!browser
|
65
|
-
|
66
|
-
# This optimization only affects Form::DOM elements, so don't bother
|
67
|
-
# if none of the checks are interested in any of them.
|
68
|
-
return false if !checks.values.find do |c|
|
69
|
-
c.check? page, [Element::Form::DOM, Element::Cookie::DOM]
|
70
|
-
end
|
71
|
-
|
72
|
-
begin
|
73
|
-
bp = browser.load( page ).to_page
|
74
|
-
rescue Selenium::WebDriver::Error::WebDriverError,
|
75
|
-
Watir::Exception::Error => e
|
76
|
-
print_debug "Could not apply metadata to '#{page.dom.url}'" <<
|
77
|
-
" because: #{e} [#{e.class}"
|
78
|
-
return
|
79
|
-
end
|
80
|
-
|
81
|
-
# Request timeout or some other failure...
|
82
|
-
return if bp.code == 0
|
83
|
-
|
84
|
-
page.import_metadata( bp, :skip_dom )
|
85
|
-
|
86
|
-
true
|
87
|
-
ensure
|
88
|
-
browser.clear_buffers if browser
|
89
|
-
end
|
90
|
-
|
91
63
|
def use_browsers?
|
92
64
|
options.browser_cluster.pool_size > 0 &&
|
93
65
|
options.scope.dom_depth_limit > 0 && host_has_browser?
|
@@ -95,13 +67,6 @@ module Browser
|
|
95
67
|
|
96
68
|
private
|
97
69
|
|
98
|
-
def shutdown_browser
|
99
|
-
return if !@browser
|
100
|
-
|
101
|
-
@browser.shutdown
|
102
|
-
@browser = nil
|
103
|
-
end
|
104
|
-
|
105
70
|
def shutdown_browser_cluster
|
106
71
|
return if !@browser_cluster
|
107
72
|
|
@@ -125,11 +90,6 @@ module Browser
|
|
125
90
|
synchronize do
|
126
91
|
return if !push_to_page_queue page
|
127
92
|
|
128
|
-
pushed_paths = nil
|
129
|
-
if crawl?
|
130
|
-
pushed_paths = push_paths_from_page( page ).size
|
131
|
-
end
|
132
|
-
|
133
93
|
print_status "Got new page from the browser-cluster: #{page.dom.url}"
|
134
94
|
print_info "DOM depth: #{page.dom.depth} (Limit: #{options.scope.dom_depth_limit})"
|
135
95
|
|
@@ -137,10 +97,6 @@ module Browser
|
|
137
97
|
print_info ' Transitions:'
|
138
98
|
page.dom.print_transitions( method(:print_info), ' ' )
|
139
99
|
end
|
140
|
-
|
141
|
-
if pushed_paths
|
142
|
-
print_info " -- Analysis resulted in #{pushed_paths} usable paths."
|
143
|
-
end
|
144
100
|
end
|
145
101
|
end
|
146
102
|
|
@@ -154,13 +110,52 @@ module Browser
|
|
154
110
|
Options.scope.dom_depth_limit.to_i < page.dom.depth + 1 ||
|
155
111
|
!page.has_script?
|
156
112
|
|
157
|
-
|
158
|
-
|
113
|
+
# We need to schedule a separate job for applying metadata because it
|
114
|
+
# needs to have a clean state.
|
115
|
+
schedule_dom_metadata_application( page )
|
116
|
+
|
117
|
+
browser_cluster.queue( browser_job.forward( resource: page ) ) do |result|
|
118
|
+
handle_browser_page result.page
|
159
119
|
end
|
160
120
|
|
161
121
|
true
|
162
122
|
end
|
163
123
|
|
124
|
+
def schedule_dom_metadata_application( page )
|
125
|
+
return if page.dom.depth > 0
|
126
|
+
return if page.metadata.map { |_, data| data['skip_dom'].values }.
|
127
|
+
flatten.compact.any?
|
128
|
+
|
129
|
+
# This optimization only affects Form & Cookie DOM elements,
|
130
|
+
# so don't bother if none of the checks are interested in them.
|
131
|
+
return if !checks.values.
|
132
|
+
find { |c| c.check? page, [Element::Form::DOM, Element::Cookie::DOM], true }
|
133
|
+
|
134
|
+
page.clear_cache
|
135
|
+
|
136
|
+
browser_cluster.with_browser do |browser|
|
137
|
+
apply_dom_metadata( browser, page )
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def apply_dom_metadata( browser, page )
|
142
|
+
bp = nil
|
143
|
+
|
144
|
+
begin
|
145
|
+
bp = browser.load( page ).to_page
|
146
|
+
rescue Selenium::WebDriver::Error::WebDriverError,
|
147
|
+
Watir::Exception::Error => e
|
148
|
+
print_debug "Could not apply metadata to '#{page.dom.url}'" <<
|
149
|
+
" because: #{e} [#{e.class}"
|
150
|
+
return
|
151
|
+
end
|
152
|
+
|
153
|
+
# Request timeout or some other failure...
|
154
|
+
return if bp.code == 0
|
155
|
+
|
156
|
+
handle_browser_page bp
|
157
|
+
end
|
158
|
+
|
164
159
|
def browser_job
|
165
160
|
# We'll recycle the same job since all of them will have the same
|
166
161
|
# callback. This will force the BrowserCluster to use the same block
|
@@ -67,11 +67,12 @@ module Check
|
|
67
67
|
# Check to run.
|
68
68
|
# @param [Page] page
|
69
69
|
def check_page( check, page )
|
70
|
+
ps = page.platforms.to_a
|
71
|
+
|
70
72
|
# If we've been given platforms which the check doesn't support don't
|
71
73
|
# even bother running it.
|
72
|
-
if !check.supports_platforms?(
|
73
|
-
print_info "Check #{check.shortname} does not support: "
|
74
|
-
Options.platforms.join( ' + ' )
|
74
|
+
if !check.supports_platforms?( ps )
|
75
|
+
print_info "Check #{check.shortname} does not support: #{ps.join( ' + ' )}"
|
75
76
|
return false
|
76
77
|
end
|
77
78
|
|
@@ -27,8 +27,9 @@ module Data
|
|
27
27
|
# `true` if push was successful, `false` if the `page` matched any
|
28
28
|
# exclusion criteria or has already been seen.
|
29
29
|
def push_to_page_queue( page, force = false )
|
30
|
-
return false if !force && (!accepts_more_pages? ||
|
31
|
-
|
30
|
+
return false if !force && (!accepts_more_pages? ||
|
31
|
+
state.page_seen?( page ) || page.scope.out? ||
|
32
|
+
page.scope.redundant?( true ))
|
32
33
|
|
33
34
|
# We want to update from the already loaded page cache (if there is one)
|
34
35
|
# as we have to store the page anyways (needs to go through Browser analysis)
|
@@ -40,6 +41,7 @@ module Data
|
|
40
41
|
# some other component; however, it wouldn't be the end of the world if
|
41
42
|
# that were to happen.
|
42
43
|
ElementFilter.update_from_page_cache page
|
44
|
+
page.clear_cache
|
43
45
|
|
44
46
|
data.push_to_page_queue page
|
45
47
|
state.page_seen page
|
@@ -99,12 +101,24 @@ module Data
|
|
99
101
|
!url_queue.empty? || !page_queue.empty?
|
100
102
|
end
|
101
103
|
|
104
|
+
# @return [Page, nil]
|
105
|
+
# A page if the queues aren't empty, `nil` otherwise.
|
106
|
+
def pop_page
|
107
|
+
pop_page_from_queue || pop_page_from_url_queue
|
108
|
+
end
|
109
|
+
|
110
|
+
# @return [Page, nil]
|
111
|
+
# A page if the queue wasn't empty, `nil` otherwise.
|
102
112
|
def pop_page_from_url_queue( &block )
|
103
113
|
return if url_queue.empty?
|
104
114
|
|
105
115
|
grabbed_page = nil
|
106
|
-
Page.from_url(
|
107
|
-
|
116
|
+
Page.from_url(
|
117
|
+
url_queue.pop,
|
118
|
+
http: {
|
119
|
+
update_cookies: true,
|
120
|
+
performer: self
|
121
|
+
}
|
108
122
|
) do |page|
|
109
123
|
@retries[page.url.hash] ||= 0
|
110
124
|
|
@@ -125,7 +139,8 @@ module Data
|
|
125
139
|
@failures << page.url
|
126
140
|
|
127
141
|
print_error "Giving up trying to audit: #{page.url}"
|
128
|
-
print_error "Couldn't get a response after #{AUDIT_PAGE_MAX_TRIES}
|
142
|
+
print_error "Couldn't get a response after #{AUDIT_PAGE_MAX_TRIES}" +
|
143
|
+
" tries: #{page.response.return_message}."
|
129
144
|
else
|
130
145
|
print_bad "Retrying for: #{page.url} [#{page.response.return_message}]"
|
131
146
|
@retries[page.url.hash] += 1
|
@@ -135,16 +150,36 @@ module Data
|
|
135
150
|
grabbed_page = nil
|
136
151
|
block.call grabbed_page if block_given?
|
137
152
|
end
|
153
|
+
|
138
154
|
http.run if !block_given?
|
139
155
|
grabbed_page
|
140
156
|
end
|
141
157
|
|
142
|
-
# @return [Page]
|
158
|
+
# @return [Page, nil]
|
159
|
+
# A page if the queue wasn't empty, `nil` otherwise.
|
143
160
|
def pop_page_from_queue
|
144
161
|
return if page_queue.empty?
|
145
162
|
page_queue.pop
|
146
163
|
end
|
147
164
|
|
165
|
+
def replenish_page_queue_from_url_queue
|
166
|
+
return if !page_queue.empty?
|
167
|
+
|
168
|
+
# Number pulled out of my ass, low enough to not add any noticeable
|
169
|
+
# stress, hopefully high enough to grab us at least one page that has
|
170
|
+
# some workload which will result in HTTP requests which will mask the
|
171
|
+
# next replenishing operation.
|
172
|
+
[10, page_queue.free_buffer_size].min.times do
|
173
|
+
return if url_queue.empty?
|
174
|
+
|
175
|
+
# We push directly to the queue instead of using #push_to_page_queue
|
176
|
+
# because it's too early to deduplicate.
|
177
|
+
pop_page_from_url_queue { |p| page_queue << p }
|
178
|
+
end
|
179
|
+
|
180
|
+
!url_queue.empty?
|
181
|
+
end
|
182
|
+
|
148
183
|
def add_to_sitemap( page )
|
149
184
|
data.add_page_to_sitemap( page )
|
150
185
|
end
|
@@ -65,6 +65,14 @@ module State
|
|
65
65
|
def initialize
|
66
66
|
super
|
67
67
|
|
68
|
+
Element::Capabilities::Auditable.skip_like do |element|
|
69
|
+
if pause?
|
70
|
+
print_debug "Blocking on element audit: #{element.audit_id}"
|
71
|
+
end
|
72
|
+
|
73
|
+
wait_if_paused
|
74
|
+
end
|
75
|
+
|
68
76
|
state.status = :ready
|
69
77
|
end
|
70
78
|
|
@@ -96,13 +104,14 @@ module State
|
|
96
104
|
return if @cleaned_up
|
97
105
|
@cleaned_up = true
|
98
106
|
|
107
|
+
state.force_resume
|
108
|
+
|
99
109
|
state.status = :cleanup
|
100
110
|
|
101
111
|
sitemap.merge!( browser_sitemap )
|
102
112
|
|
103
113
|
if shutdown_browsers
|
104
114
|
state.set_status_message :browser_cluster_shutdown
|
105
|
-
shutdown_browser
|
106
115
|
shutdown_browser_cluster
|
107
116
|
end
|
108
117
|
|
@@ -128,6 +137,11 @@ module State
|
|
128
137
|
true
|
129
138
|
end
|
130
139
|
|
140
|
+
# @private
|
141
|
+
def reset_trainer
|
142
|
+
@trainer = Trainer.new( self )
|
143
|
+
end
|
144
|
+
|
131
145
|
# @note Prefer this from {.reset} if you already have an instance.
|
132
146
|
# @note You should first reset {Arachni::Options}.
|
133
147
|
#
|
@@ -319,11 +333,6 @@ module State
|
|
319
333
|
state.suspended?
|
320
334
|
end
|
321
335
|
|
322
|
-
# @private
|
323
|
-
def reset_trainer
|
324
|
-
@trainer = Trainer.new( self )
|
325
|
-
end
|
326
|
-
|
327
336
|
private
|
328
337
|
|
329
338
|
# @note Must be called before calling any audit methods.
|
@@ -370,7 +379,7 @@ module State
|
|
370
379
|
new_element = true
|
371
380
|
end
|
372
381
|
|
373
|
-
if e.respond_to?( :dom ) && e.dom
|
382
|
+
if page.dom.depth > 0 && e.respond_to?( :dom ) && e.dom
|
374
383
|
if !state.element_checked?( e.dom )
|
375
384
|
state.element_checked e.dom
|
376
385
|
new_element = true
|
data/lib/arachni/http/client.rb
CHANGED
@@ -140,7 +140,11 @@ class Client
|
|
140
140
|
headers.merge!( Options.http.request_headers )
|
141
141
|
|
142
142
|
cookie_jar.load( Options.http.cookie_jar_filepath ) if Options.http.cookie_jar_filepath
|
143
|
-
|
143
|
+
|
144
|
+
Options.http.cookies.each do |name, value|
|
145
|
+
update_cookies( name => value )
|
146
|
+
end
|
147
|
+
|
144
148
|
update_cookies( Options.http.cookie_string ) if Options.http.cookie_string
|
145
149
|
|
146
150
|
reset_burst_info
|
@@ -465,14 +469,30 @@ class Client
|
|
465
469
|
@running = true
|
466
470
|
|
467
471
|
reset_burst_info
|
472
|
+
|
473
|
+
# Lots of new objects are about to be generated, make sure that old ones
|
474
|
+
# have been collected to prevent RAM spikes.
|
475
|
+
gc
|
476
|
+
|
468
477
|
client_run
|
469
478
|
|
479
|
+
# Collect the new objects as well.
|
480
|
+
gc
|
481
|
+
|
470
482
|
@queue_size = 0
|
471
483
|
@running = false
|
472
484
|
|
473
485
|
@burst_runtime += Time.now - @burst_runtime_start
|
474
486
|
@total_runtime += @burst_runtime
|
475
487
|
end
|
488
|
+
|
489
|
+
def gc
|
490
|
+
# Don't GC after every little run, only do it when we're past the
|
491
|
+
# maximum queue size.
|
492
|
+
return if @queue_size < Options.http.request_queue_size
|
493
|
+
|
494
|
+
GC.start
|
495
|
+
end
|
476
496
|
|
477
497
|
def reset_burst_info
|
478
498
|
@burst_response_time_sum = 0
|
@@ -501,46 +521,12 @@ class Client
|
|
501
521
|
print_debug_level_3 "Headers: #{request.headers}"
|
502
522
|
print_debug_level_3 "Cookies: #{request.cookies}"
|
503
523
|
print_debug_level_3 "Train?: #{request.train?}"
|
524
|
+
print_debug_level_3 "Fingerprint?: #{request.fingerprint?}"
|
504
525
|
print_debug_level_3 '------------'
|
505
526
|
end
|
506
527
|
|
507
528
|
if add_callbacks
|
508
|
-
request.on_complete
|
509
|
-
synchronize do
|
510
|
-
@response_count += 1
|
511
|
-
@burst_response_count += 1
|
512
|
-
@burst_response_time_sum += response.time
|
513
|
-
@total_response_time_sum += response.time
|
514
|
-
|
515
|
-
if Platform::Manager.fingerprint?( response )
|
516
|
-
# Force a fingerprint by converting the Response to a Page object.
|
517
|
-
response.to_page
|
518
|
-
end
|
519
|
-
|
520
|
-
notify_on_complete( response )
|
521
|
-
|
522
|
-
parse_and_set_cookies( response ) if request.update_cookies?
|
523
|
-
|
524
|
-
if debug_level_3?
|
525
|
-
print_debug_level_3 '------------'
|
526
|
-
print_debug_level_3 "Got response for request ID#: #{response.request.id}"
|
527
|
-
print_debug_level_3 "Performer: #{response.request.performer.inspect}"
|
528
|
-
print_debug_level_3 "Status: #{response.code}"
|
529
|
-
print_debug_level_3 "Code: #{response.return_code}"
|
530
|
-
print_debug_level_3 "Message: #{response.return_message}"
|
531
|
-
print_debug_level_3 "URL: #{response.url}"
|
532
|
-
print_debug_level_3 "Headers:\n#{response.headers_string}"
|
533
|
-
print_debug_level_3 "Parsed headers: #{response.headers}"
|
534
|
-
end
|
535
|
-
|
536
|
-
if response.timed_out?
|
537
|
-
print_debug_level_3 "Request timed-out! -- ID# #{response.request.id}"
|
538
|
-
@time_out_count += 1
|
539
|
-
end
|
540
|
-
|
541
|
-
print_debug_level_3 '------------'
|
542
|
-
end
|
543
|
-
end
|
529
|
+
request.on_complete( &method(:global_on_complete) )
|
544
530
|
end
|
545
531
|
|
546
532
|
synchronize { @request_count += 1 }
|
@@ -559,6 +545,47 @@ class Client
|
|
559
545
|
request
|
560
546
|
end
|
561
547
|
|
548
|
+
def global_on_complete( response )
|
549
|
+
request = response.request
|
550
|
+
|
551
|
+
synchronize do
|
552
|
+
@response_count += 1
|
553
|
+
@burst_response_count += 1
|
554
|
+
@burst_response_time_sum += response.time
|
555
|
+
@total_response_time_sum += response.time
|
556
|
+
|
557
|
+
if response.request.fingerprint? &&
|
558
|
+
Platform::Manager.fingerprint?( response )
|
559
|
+
|
560
|
+
# Force a fingerprint by converting the Response to a Page object.
|
561
|
+
response.to_page
|
562
|
+
end
|
563
|
+
|
564
|
+
notify_on_complete( response )
|
565
|
+
|
566
|
+
parse_and_set_cookies( response ) if request.update_cookies?
|
567
|
+
|
568
|
+
if debug_level_3?
|
569
|
+
print_debug_level_3 '------------'
|
570
|
+
print_debug_level_3 "Got response for request ID#: #{response.request.id}\n#{response.request}"
|
571
|
+
print_debug_level_3 "Performer: #{response.request.performer.inspect}"
|
572
|
+
print_debug_level_3 "Status: #{response.code}"
|
573
|
+
print_debug_level_3 "Code: #{response.return_code}"
|
574
|
+
print_debug_level_3 "Message: #{response.return_message}"
|
575
|
+
print_debug_level_3 "URL: #{response.url}"
|
576
|
+
print_debug_level_3 "Headers:\n#{response.headers_string}"
|
577
|
+
print_debug_level_3 "Parsed headers: #{response.headers}"
|
578
|
+
end
|
579
|
+
|
580
|
+
if response.timed_out?
|
581
|
+
print_debug_level_3 "Request timed-out! -- ID# #{response.request.id}"
|
582
|
+
@time_out_count += 1
|
583
|
+
end
|
584
|
+
|
585
|
+
print_debug_level_3 '------------'
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
562
589
|
def client_initialize
|
563
590
|
@hydra = Typhoeus::Hydra.new(
|
564
591
|
max_concurrency: Options.http.request_concurrency || MAX_CONCURRENCY
|
@@ -566,7 +593,8 @@ class Client
|
|
566
593
|
end
|
567
594
|
|
568
595
|
def client_run
|
569
|
-
|
596
|
+
# Can get Ethon select errors.
|
597
|
+
exception_jail( false ) { @hydra.run }
|
570
598
|
end
|
571
599
|
|
572
600
|
def client_abort
|