class Arachni::State::Framework

State information for {Arachni::Framework}.

@author Tasos “Zapotek” Laskos <tasos.laskos@arachni-scanner.com>

Attributes

audited_page_count[RW]

@return [Integer]

browser_skip_states[R]

@return [Set]

element_pre_check_filter[R]

@return [Support::LookUp::HashSet]

page_queue_filter[R]

@return [Support::LookUp::HashSet]

pause_signals[R]

@return [Array]

rpc[RW]

@return [RPC]

running[RW]

@return [Bool]

status[RW]

@return [Symbol]

status_messages[R]

@return [Array<String>]

url_queue_filter[R]

@return [Support::LookUp::HashSet]

Public Class Methods

load( directory ) click to toggle source
# File lib/arachni/state/framework.rb, line 420
def self.load( directory )
    framework = new

    framework.rpc = RPC.load( "#{directory}/rpc/" )

    %w(element_pre_check_filter page_queue_filter url_queue_filter
        browser_skip_states).each do |attribute|
        path = "#{directory}/#{attribute}"
        next if !File.exist?( path )

        framework.send(attribute).merge Marshal.load( IO.binread( path ) )
    end

    framework.audited_page_count = Marshal.load( IO.binread( "#{directory}/audited_page_count" ) )
    framework
end
new() click to toggle source
# File lib/arachni/state/framework.rb, line 68
def initialize
    @rpc = RPC.new
    @audited_page_count = 0

    @browser_skip_states = Support::LookUp::HashSet.new( hasher: :persistent_hash )

    @page_queue_filter = Support::LookUp::HashSet.new( hasher: :persistent_hash )
    @url_queue_filter  = Support::LookUp::HashSet.new( hasher: :persistent_hash )

    @element_pre_check_filter = Support::LookUp::HashSet.new( hasher: :coverage_hash )

    @running = false
    @pre_pause_status = nil

    @pause_signals    = Set.new
    @paused_signal    = Queue.new
    @suspended_signal = Queue.new
    @aborted_signal   = Queue.new

    @status_messages = []
end

Public Instance Methods

abort( block = true ) click to toggle source

@param [Bool] block

`true` if the method should block until an abortion has completed,
`false` otherwise.

@return [Bool]

`true` if the abort request was successful, `false` if the system is
already {#suspended?} or is {#suspending?}.

@raise [StateNotAbortable]

When not {#running?}.
# File lib/arachni/state/framework.rb, line 225
def abort( block = true )
    return false if aborting? || aborted?

    if !running?
        fail Error::StateNotAbortable, 'Cannot abort an idle state.'
    end

    set_status_message :aborting
    @status = :aborting
    @abort = true

    @aborted_signal.pop if block
    true
end
abort?() click to toggle source

@return [Bool]

`true` if a {#abort} signal is in place , `false` otherwise.
# File lib/arachni/state/framework.rb, line 242
def abort?
    !!@abort
end
aborted() click to toggle source

Signals a completed abort operation.

# File lib/arachni/state/framework.rb, line 247
def aborted
    @abort = false
    @status = :aborted
    @aborted_signal << true
    nil
end
aborted?() click to toggle source

@return [Bool]

`true` if the system has been aborted, `false` otherwise.
# File lib/arachni/state/framework.rb, line 256
def aborted?
    @status == :aborted
end
aborting?() click to toggle source

@return [Bool]

`true` if the system is being aborted, `false` otherwise.
# File lib/arachni/state/framework.rb, line 262
def aborting?
    @status == :aborting
end
add_status_message( message, *sprintf ) click to toggle source

Pushes a message to {#status_messages}.

@param [String, Symbol] message

Status message. If `Symbol`, it will be grabbed from
{#available_status_messages}.

@param [String, Numeric] sprintf

`sprintf` arguments.
# File lib/arachni/state/framework.rb, line 131
def add_status_message( message, *sprintf )
    if message.is_a? Symbol
        if !available_status_messages.include?( message )
            fail Error::InvalidStatusMessage,
                 "Could not find status message for: '#{message}'"
        end

        message = available_status_messages[message] % sprintf
    end

    @status_messages << message.to_s
end
available_status_messages() click to toggle source

@return [Hash{Symbol=>String}]

All possible {#status_messages} by type.
# File lib/arachni/state/framework.rb, line 100
def available_status_messages
    {
        suspending:                       'Will suspend as soon as the current page is audited.',
        waiting_for_browser_cluster_jobs: 'Waiting for %i browser cluster jobs to finish.',
        suspending_plugins:               'Suspending plugins.',
        saving_snapshot:                  'Saving snapshot at: %s',
        snapshot_location:                'Snapshot location: %s',
        browser_cluster_startup:          'Initialising the browser cluster.',
        browser_cluster_shutdown:         'Shutting down the browser cluster.',
        clearing_queues:                  'Clearing the audit queues.',
        waiting_for_plugins:              'Waiting for the plugins to finish.',
        aborting:                         'Aborting the scan.'
    }
end
clear() click to toggle source
# File lib/arachni/state/framework.rb, line 437
def clear
    rpc.clear

    @element_pre_check_filter.clear

    @page_queue_filter.clear
    @url_queue_filter.clear

    @pause_signals.clear
    @paused_signal.clear
    @suspended_signal.clear

    @running = false
    @pre_pause_status = nil

    @browser_skip_states.clear
    @audited_page_count = 0
end
clear_status_messages() click to toggle source

Clears {#status_messages}.

# File lib/arachni/state/framework.rb, line 145
def clear_status_messages
    @status_messages.clear
end
done?() click to toggle source

@return [Bool]

`true` if the system has completed successfully, `false` otherwise.
# File lib/arachni/state/framework.rb, line 268
def done?
    @status == :done
end
dump( directory ) click to toggle source
# File lib/arachni/state/framework.rb, line 409
def dump( directory )
    FileUtils.mkdir_p( directory )

    rpc.dump( "#{directory}/rpc/" )

    %w(element_pre_check_filter page_queue_filter url_queue_filter
        browser_skip_states audited_page_count).each do |attribute|
        IO.binwrite( "#{directory}/#{attribute}", Marshal.dump( send(attribute) ) )
    end
end
element_checked( e ) click to toggle source

@param [Page] e

Element to mark as seen.

@see element_checked?

# File lib/arachni/state/framework.rb, line 207
def element_checked( e )
    @element_pre_check_filter << e
end
element_checked?( e ) click to toggle source

@param [#coverage_hash] e

@return [Bool]

`true` if the element has already been seen (based on the
{#element_pre_check_filter}), `false` otherwise.

@see element_checked

# File lib/arachni/state/framework.rb, line 199
def element_checked?( e )
    @element_pre_check_filter.include? e
end
force_resume() click to toggle source
# File lib/arachni/state/framework.rb, line 403
def force_resume
    @pause_signals.to_a.each do |ref|
        resume ref
    end
end
page_seen( page ) click to toggle source

@param [Page] page

Page to mark as seen.

@see page_seen?

# File lib/arachni/state/framework.rb, line 164
def page_seen( page )
    @page_queue_filter << page
end
page_seen?( page ) click to toggle source

@param [Page] page

@return [Bool]

`true` if the `page` has already been seen (based on the
{#page_queue_filter}), `false` otherwise.

@see page_seen

# File lib/arachni/state/framework.rb, line 156
def page_seen?( page )
    @page_queue_filter.include? page
end
pause( caller, block = true ) click to toggle source

@param [Object] caller

Identification for the caller which issued the pause signal.

@param [Bool] block

`true` if the method should block until the pause has completed,
`false` otherwise.

@return [TrueClass]

Pauses the framework on a best effort basis, might take a while to take
effect.
# File lib/arachni/state/framework.rb, line 342
def pause( caller, block = true )
    @pre_pause_status ||= @status if !paused? && !pausing?

    if !paused?
        @status = :pausing
    end

    @pause_signals << caller

    paused if !running?

    @paused_signal.pop if block && !paused?
    true
end
pause?() click to toggle source

@return [Bool]

`true` if the framework should pause, `false` otherwise.
# File lib/arachni/state/framework.rb, line 378
def pause?
    @pause_signals.any?
end
paused() click to toggle source

Signals that the system has been paused..

# File lib/arachni/state/framework.rb, line 358
def paused
    clear_status_messages
    @status = :paused
    @paused_signal << nil
end
paused?() click to toggle source

@return [Bool]

`true` if the framework is paused.
# File lib/arachni/state/framework.rb, line 366
def paused?
    @status == :paused
end
pausing?() click to toggle source

@return [Bool]

`true` if the system is being paused, `false` otherwise.
# File lib/arachni/state/framework.rb, line 372
def pausing?
    @status == :pausing
end
resume( caller ) click to toggle source

Resumes a paused system

@param [Object] caller

Identification for the caller whose {#pause} signal to remove. The
system is resumed once there are no more {#pause} signals left.

@return [Bool]

`true` if the system is resumed, `false` if there are more {#pause}
signals pending.
# File lib/arachni/state/framework.rb, line 391
def resume( caller )
    @pause_signals.delete( caller )

    if @pause_signals.empty?
        @status = @pre_pause_status
        @pre_pause_status = nil
        return true
    end

    false
end
running?() click to toggle source
# File lib/arachni/state/framework.rb, line 211
def running?
    !!@running
end
scanning?() click to toggle source

@return [Bool]

`true` if the system is scanning, `false` otherwise.
# File lib/arachni/state/framework.rb, line 329
def scanning?
    @status == :scanning
end
set_status_message( *args ) click to toggle source

Sets a message as {#status_messages}.

@param (see add_status_message) @return (see add_status_message)

# File lib/arachni/state/framework.rb, line 119
def set_status_message( *args )
    clear_status_messages
    add_status_message( *args )
end
statistics() click to toggle source
# File lib/arachni/state/framework.rb, line 90
def statistics
    {
        rpc:                @rpc.statistics,
        audited_page_count: @audited_page_count,
        browser_states:     @browser_skip_states.size
    }
end
suspend( block = true ) click to toggle source

@param [Bool] block

`true` if the method should block until a suspend has completed,
`false` otherwise.

@return [Bool]

`true` if the suspend request was successful, `false` if the system is
already {#suspended?} or is {#suspending?}.

@raise [StateNotSuspendable]

When {#paused?} or {#pausing?}.
# File lib/arachni/state/framework.rb, line 282
def suspend( block = true )
    return false if suspending? || suspended?

    if paused? || pausing?
        fail Error::StateNotSuspendable, 'Cannot suspend a paused state.'
    end

    if !running?
        fail Error::StateNotSuspendable, 'Cannot suspend an idle state.'
    end

    set_status_message :suspending
    @status = :suspending
    @suspend = true

    @suspended_signal.pop if block
    true
end
suspend?() click to toggle source

@return [Bool]

`true` if an {#abort} signal is in place , `false` otherwise.
# File lib/arachni/state/framework.rb, line 303
def suspend?
    !!@suspend
end
suspended() click to toggle source

Signals a completed suspension.

# File lib/arachni/state/framework.rb, line 308
def suspended
    @suspend = false
    @status = :suspended
    @suspended_signal << true
    nil
end
suspended?() click to toggle source

@return [Bool]

`true` if the system has been suspended, `false` otherwise.
# File lib/arachni/state/framework.rb, line 317
def suspended?
    @status == :suspended
end
suspending?() click to toggle source

@return [Bool]

`true` if the system is being suspended, `false` otherwise.
# File lib/arachni/state/framework.rb, line 323
def suspending?
    @status == :suspending
end
update_browser_skip_states( states ) click to toggle source

@param [Support::LookUp::HashSet] states

# File lib/arachni/state/framework.rb, line 188
def update_browser_skip_states( states )
    @browser_skip_states.merge states
end
url_seen( url ) click to toggle source

@param [Page] url

URL to mark as seen.

@see url_seen?

# File lib/arachni/state/framework.rb, line 183
def url_seen( url )
    @url_queue_filter << url
end
url_seen?( url ) click to toggle source

@param [String] url

@return [Bool]

`true` if the `url` has already been seen (based on the
{#url_queue_filter}), `false` otherwise.

@see url_seen

# File lib/arachni/state/framework.rb, line 175
def url_seen?( url )
    @url_queue_filter.include? url
end