module Arachni::Framework::Parts::State
Provides access to {Arachni::State::Framework} and helpers.
@author Tasos “Zapotek” Laskos <tasos.laskos@arachni-scanner.com>
Public Class Methods
# File lib/arachni/framework/parts/state.rb, line 18 def self.included( base ) base.extend ClassMethods end
# File lib/arachni/framework/parts/state.rb, line 65 def initialize super Element::Capabilities::Auditable.skip_like do |element| if pause? print_debug "Blocking on element audit: #{element.audit_id}" end wait_if_paused end state.status = :ready end
Public Instance Methods
Aborts the framework {#run} on a best effort basis.
@param [Bool] wait
Wait until the system has been aborted.
# File lib/arachni/framework/parts/state.rb, line 292 def abort( wait = true ) state.abort wait end
@return [Bool]
`true` if the framework has been instructed to abort (i.e. is in the process of being aborted or has been aborted), `false` otherwise.
# File lib/arachni/framework/parts/state.rb, line 278 def abort? state.abort? end
@return [Bool]
`true` if the framework {#run} has been aborted, `false` otherwise.
# File lib/arachni/framework/parts/state.rb, line 271 def aborted? state.aborted? end
@return [Bool]
`true` if the framework is in the process of aborting, `false` otherwise.
# File lib/arachni/framework/parts/state.rb, line 284 def aborting? state.aborting? end
Cleans up the framework; should be called after running the audit or after canceling a running scan.
It stops the clock and waits for the plugins to finish up.
# File lib/arachni/framework/parts/state.rb, line 103 def clean_up( shutdown_browsers = true ) return if @cleaned_up @cleaned_up = true state.force_resume state.status = :cleanup if shutdown_browsers state.set_status_message :browser_cluster_shutdown shutdown_browser_cluster end state.set_status_message :clearing_queues page_queue.clear url_queue.clear @finish_datetime = Time.now @start_datetime ||= Time.now # Make sure this is disabled or it'll break reporter output. disable_only_positives state.running = false state.set_status_message :waiting_for_plugins @plugins.block # Plugins may need the session right till the very end so save it for last. @session.clean_up @session = nil true end
@return (see Arachni::State::Framework#done?
)
# File lib/arachni/framework/parts/state.rb, line 248 def done? state.done? end
@note Each call from a unique caller is counted as a pause request
and in order for the system to resume **all** pause callers need to {#resume} it.
Pauses the framework on a best effort basis.
@param [Bool] wait
Wait until the system has been paused.
@return [Integer]
ID identifying this pause request.
# File lib/arachni/framework/parts/state.rb, line 263 def pause( wait = true ) id = generate_token.hash state.pause id, wait id end
@return [Bool]
`true` if the framework has been instructed to pause (i.e. is in the process of being paused or has been paused), `false` otherwise.
# File lib/arachni/framework/parts/state.rb, line 237 def pause? state.pause? end
@return [Bool]
`true` if the framework is paused, `false` otherwise.
# File lib/arachni/framework/parts/state.rb, line 230 def paused? state.paused? end
@return [Bool]
`true` if the framework is in the process of pausing, `false` otherwise.
# File lib/arachni/framework/parts/state.rb, line 243 def pausing? state.pausing? end
@note Prefer this from {.reset} if you already have an instance. @note You should first reset {Arachni::Options}.
Resets everything and allows the framework to be re-used.
# File lib/arachni/framework/parts/state.rb, line 147 def reset @cleaned_up = false @browser_job = nil @failures.clear @retries.clear # This needs to happen before resetting the other components so they # will be able to put in their hooks. self.class.reset clear_observers reset_trainer reset_session @checks.clear @reporters.clear @plugins.clear end
@private
# File lib/arachni/framework/parts/state.rb, line 139 def reset_trainer @trainer = Trainer.new( self ) end
@param [String] afs
Path to an `.afs.` (Arachni Framework Snapshot) file created by {#suspend}.
@return [Framework]
Restored instance.
# File lib/arachni/framework/parts/state.rb, line 177 def restore( afs ) Snapshot.load afs browser_job_update_skip_states state.browser_skip_states checks.load Options.checks plugins.load Options.plugins.keys nil end
@note Each call from a unique caller is counted as a pause request
and in order for the system to resume **all** pause callers need to {#resume} it.
Removes a {#pause} request for the current caller.
@param [Integer] id
ID of the {#pause} request.
# File lib/arachni/framework/parts/state.rb, line 304 def resume( id ) state.resume id end
@return [Bool]
`true` if the framework is running, `false` otherwise. This is `true` even if the scan is {#paused?}.
# File lib/arachni/framework/parts/state.rb, line 218 def running? state.running? end
@return [Bool]
`true` if the system is scanning, `false` otherwise.
# File lib/arachni/framework/parts/state.rb, line 224 def scanning? state.scanning? end
@return [String]
Provisioned {#suspend} dump file for this instance.
# File lib/arachni/framework/parts/state.rb, line 81 def snapshot_path return @state_archive if @state_archive default_filename = "#{URI(options.url).host} #{Time.now.to_s.gsub( ':', '_' )} " << "#{generate_token}.afs" location = options.snapshot.save_path if !location location = default_filename elsif File.directory? location location += "/#{default_filename}" end @state_archive ||= File.expand_path( location ) end
@return [State::Framework]
# File lib/arachni/framework/parts/state.rb, line 168 def state Arachni::State.framework end
@return [Symbol]
Status of the instance, possible values are (in order): * `:ready` -- {#initialize Initialised} and waiting for instructions. * `:preparing` -- Getting ready to start (i.e. initializing plugins etc.). * `:scanning` -- The instance is currently {#run auditing} the webapp. * `:pausing` -- The instance is being {#pause paused} (if applicable). * `:paused` -- The instance has been {#pause paused} (if applicable). * `:suspending` -- The instance is being {#suspend suspended} (if applicable). * `:suspended` -- The instance has being {#suspend suspended} (if applicable). * `:cleanup` -- The scan has completed and the instance is {Framework::Parts::State#clean_up cleaning up} after itself (i.e. waiting for plugins to finish etc.). * `:aborted` -- The scan has been {Framework::Parts::State#abort}, you can grab the report and shutdown. * `:done` -- The scan has completed, you can grab the report and shutdown.
# File lib/arachni/framework/parts/state.rb, line 211 def status state.status end
@return [Array<String>]
Messages providing more information about the current {#status} of the framework.
# File lib/arachni/framework/parts/state.rb, line 191 def status_messages state.status_messages end
Writes a {Snapshot.dump} to disk and aborts the scan.
@param [Bool] wait
Wait for the system to write it state to disk.
@return [String,nil]
Path to the state file `wait` is `true`, `nil` otherwise.
# File lib/arachni/framework/parts/state.rb, line 315 def suspend( wait = true ) state.suspend( wait ) return snapshot_path if wait nil end
@return [Bool]
`true` if the system is in the process of being suspended, `false` otherwise.
# File lib/arachni/framework/parts/state.rb, line 324 def suspend? state.suspend? end
@return [Bool]
`true` if the system has been suspended, `false` otherwise.
# File lib/arachni/framework/parts/state.rb, line 330 def suspended? state.suspended? end
Private Instance Methods
# File lib/arachni/framework/parts/state.rb, line 412 def abort_if_signaled return if !abort? clean_up state.aborted end
# File lib/arachni/framework/parts/state.rb, line 401 def handle_signals wait_if_paused abort_if_signaled suspend_if_signaled end
Small but (sometimes) important optimization:
Keep track of page elements which have already been passed to checks, in order to filter them out and hopefully even avoid running checks against pages with no new elements.
It’s not like there were going to be redundant audits anyways, because each layer of the audit performs its own redundancy checks, but those redundancy checks can introduce significant latencies when dealing with pages with lots of elements.
# File lib/arachni/framework/parts/state.rb, line 366 def pre_audit_element_filter( page ) unique_elements = {} page.elements.each do |e| next if !Options.audit.element?( e.type ) next if e.is_a?( Cookie ) || e.is_a?( Header ) new_element = false unique_elements[e.type] ||= [] if !state.element_checked?( e ) state.element_checked e new_element = true end if page.dom.depth > 0 && e.respond_to?( :dom ) && e.dom if !state.element_checked?( e.dom ) state.element_checked e.dom new_element = true end end next if !new_element unique_elements[e.type] << e end # Remove redundant elements from the page cache, if there are thousands # of them then just skipping them during the audit will introduce latency. unique_elements.each do |type, elements| page.send( "#{type}s=", elements ) end page end
@note Must be called before calling any audit methods.
Prepares the framework for the audit.
-
Sets the status to ‘:preparing`.
-
Starts the clock.
-
Runs the plugins.
# File lib/arachni/framework/parts/state.rb, line 343 def prepare state.status = :preparing state.running = true @start_datetime = Time.now Snapshot.restored? ? @plugins.restore : @plugins.run end
# File lib/arachni/framework/parts/state.rb, line 351 def reset_session @session.clean_up if @session @session = Session.new end
# File lib/arachni/framework/parts/state.rb, line 418 def suspend_if_signaled return if !suspend? suspend_to_disk end
# File lib/arachni/framework/parts/state.rb, line 423 def suspend_to_disk while wait_for_browser_cluster? last_pending_jobs ||= 0 pending_jobs = browser_cluster.pending_job_counter if pending_jobs != last_pending_jobs state.set_status_message :waiting_for_browser_cluster_jobs, pending_jobs print_info "Suspending: #{status_messages.first}" end last_pending_jobs = pending_jobs sleep 0.1 end # Make sure the component options are up to date with what's actually # happening. options.checks = checks.loaded options.plugins = plugins.loaded. inject({}) { |h, name| h[name.to_s] = Options.plugins[name.to_s] || {}; h } if browser_cluster_job_skip_states state.browser_skip_states.merge browser_cluster_job_skip_states end state.set_status_message :suspending_plugins @plugins.suspend state.set_status_message :saving_snapshot, snapshot_path Snapshot.dump( snapshot_path ) state.clear_status_messages clean_up state.set_status_message :snapshot_location, snapshot_path print_info status_messages.first state.suspended end
# File lib/arachni/framework/parts/state.rb, line 407 def wait_if_paused state.paused if pause? sleep 0.2 while pause? && !abort? end