Class: Arachni::BrowserCluster::Worker

Inherits:
Arachni::Browser show all
Defined in:
lib/arachni/browser_cluster/worker.rb

Overview

Overrides some Arachni::Browser methods to make multiple browsers play well with each other when they're part of a Arachni::BrowserCluster.

Author:

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

Constant Summary collapse

TRIES =

How many times to retry timed-out jobs.

6

Constants inherited from Arachni::Browser

Arachni::Browser::ASSET_EXTENSIONS, Arachni::Browser::ASSET_EXTRACTORS, Arachni::Browser::BROWSER_SPAWN_TIMEOUT, Arachni::Browser::ELEMENT_APPEARANCE_TIMEOUT, Arachni::Browser::INPUT_EVENTS, Arachni::Browser::INPUT_EVENTS_TO_FORCE

Instance Attribute Summary collapse

Attributes inherited from Arachni::Browser

#javascript, #last_url, #page_snapshots_with_sinks, #preloads, #proxy, #selenium, #transitions, #watir

Instance Method Summary collapse

Methods inherited from Arachni::Browser

add_asset_domain, asset_domains, #capture?, #capture_snapshot, #captured_pages, #clear_buffers, #cookies, #dom_url, #each_element_with_events, executable, #explore_and_flush, #fire_event, #fire_event_js, #flush_page_snapshots_with_sinks, #flush_pages, #goto, has_executable?, #load, #load_delay, #on_fire_event, #on_new_page, #on_new_page_with_sink, #on_response, #page_snapshots, #preload, reset, #response, #skip_path?, #source, #source_with_line_numbers, #start_capture, #state, #stop_capture, #to_page, #trigger_event, #trigger_events, #update_cookies, #url, #wait_for_timers, #wait_till_ready

Methods included from Support::Mixins::Observable

included

Methods included from Utilities

#available_port, available_port_mutex, #bytes_to_kilobytes, #bytes_to_megabytes, #caller_name, #caller_path, #cookie_decode, #cookie_encode, #cookies_from_file, #cookies_from_parser, #cookies_from_response, #exception_jail, #exclude_path?, #follow_protocol?, #form_decode, #form_encode, #forms_from_parser, #forms_from_response, #full_and_absolute_url?, #generate_token, #get_path, #hms_to_seconds, #html_decode, #html_encode, #include_path?, #links_from_parser, #links_from_response, #normalize_url, #page_from_response, #page_from_url, #parse_set_cookie, #path_in_domain?, #path_too_deep?, #port_available?, #rand_port, #random_seed, #redundant_path?, #regexp_array_match, #remove_constants, #request_parse_body, #seconds_to_hms, #skip_page?, #skip_path?, #skip_resource?, #skip_response?, #to_absolute, #uri_decode, #uri_encode, #uri_parse, #uri_parse_query, #uri_parser, #uri_rewrite

Methods included from UI::Output

#debug?, #debug_level_1?, #debug_level_2?, #debug_level_3?, #debug_level_4?, #debug_off, #debug_on, #disable_only_positives, #included, #mute, #muted?, #only_positives, #only_positives?, #print_bad, #print_debug, #print_debug_backtrace, #print_debug_level_1, #print_debug_level_2, #print_debug_level_3, #print_debug_level_4, #print_error, #print_error_backtrace, #print_exception, #print_info, #print_line, #print_ok, #print_status, #print_verbose, #reroute_to_file, #reroute_to_file?, reset_output_options, #unmute, #verbose?, #verbose_on

Constructor Details

#initialize(options = {}) ⇒ Worker

Returns a new instance of Worker.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/arachni/browser_cluster/worker.rb', line 40

def initialize( options = {} )
    @master           = options.delete( :master )

    @max_time_to_live = options.delete( :max_time_to_live ) ||
        Options.browser_cluster.worker_time_to_live
    @time_to_live     = @max_time_to_live

    @done_signal = Queue.new

    # Don't store pages if there's a master, we'll be sending them to him
    # as soon as they're logged.
    super options.merge( store_pages: false )

    start_capture

    return if !@master
    start
end

Instance Attribute Details

#jobJob (readonly)

Returns Currently assigned job.

Returns:

  • (Job)

    Currently assigned job.



31
32
33
# File 'lib/arachni/browser_cluster/worker.rb', line 31

def job
  @job
end

#masterBrowserCluster (readonly)

Returns:



27
28
29
# File 'lib/arachni/browser_cluster/worker.rb', line 27

def master
  @master
end

#max_time_to_liveInteger

Returns:

  • (Integer)


34
35
36
# File 'lib/arachni/browser_cluster/worker.rb', line 34

def max_time_to_live
  @max_time_to_live
end

#time_to_liveInteger (readonly)

Returns Remaining time-to-live measured in jobs.

Returns:

  • (Integer)

    Remaining time-to-live measured in jobs.



38
39
40
# File 'lib/arachni/browser_cluster/worker.rb', line 38

def time_to_live
  @time_to_live
end

Instance Method Details

#browser_shutdownObject



139
# File 'lib/arachni/browser_cluster/worker.rb', line 139

alias :browser_shutdown :shutdown

#distribute_event(resource, element, event) ⇒ Object

Direct the distribution to the master and let it take it from there.

See Also:



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/arachni/browser_cluster/worker.rb', line 122

def distribute_event( resource, element, event )
    master.queue(
        @job.forward_as(
            @job.class::EventTrigger,
            resource: resource,
            element:  element,
            event:    event
        ),
        master.callback_for( @job )
    )
    true
# Job may have been marked as done or the cluster may have been shut down.
rescue BrowserCluster::Job::Error::AlreadyDone,
    BrowserCluster::Error::AlreadyShutdown
    false
end

#inspectObject



183
184
185
186
187
188
189
190
# File 'lib/arachni/browser_cluster/worker.rb', line 183

def inspect
    s = "#<#{self.class} "
    s << "pid=#{@lifeline_pid} "
    s << "job=#{@job.inspect} "
    s << "last-url=#{@last_url.inspect} "
    s << "transitions=#{@transitions.size}"
    s << '>'
end

#run_job(job) ⇒ Array<Page>

Returns Pages which resulted from firing events, clicking JavaScript links and capturing AJAX requests.

Parameters:

Returns:

  • (Array<Page>)

    Pages which resulted from firing events, clicking JavaScript links and capturing AJAX requests.

See Also:



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/arachni/browser_cluster/worker.rb', line 66

def run_job( job )
    @job = job
    print_debug "Started: #{@job}"

    # PhantomJS may have crashed (it happens sometimes) so make sure that
    # we've got a live one before running the job.
    # If we can't respawn, then bail out.
    return if browser_respawn_if_necessary.nil?

    time = Time.now
    begin

        Timeout.timeout Options.browser_cluster.job_timeout do
            @job.configure_and_run( self )
        end

        @job.time = Time.now - time

    rescue Selenium::WebDriver::Error::WebDriverError,
        Watir::Exception::Error => e

        print_debug "WebDriver error while processing job: #{@job}"
        print_debug_exception e

    # This can be thrown by a Selenium call somewhere down the line,
    # catch it here and retry the entire job.
    rescue Timeout::Error => e

        @job.timed_out!( Time.now - time )

        print_bad "Job timed-out: #{@job}"
        print_debug_exception e

        master.increment_time_out_count
    end

    decrease_time_to_live

# Something went horribly wrong, cleanup.
rescue => e
    print_error "Error while processing job: #{@job}"
    print_exception e

    browser_respawn
ensure
    print_debug "Finished: #{@job}"
    @job = nil

    reset
    master.job_done job
end

#shutdown(wait = true) ⇒ Object

Note:

If there is a running job it will wait for it to finish.

Shuts down the worker.



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/arachni/browser_cluster/worker.rb', line 143

def shutdown( wait = true )
    return if @shutdown
    @shutdown = true

    print_debug "Shutting down (wait: #{wait}) ..."

    # Keep checking to see if any of the 'done' criteria are true.
    kill_check = Thread.new do
        while wait && @job
            print_debug_level_2 "Waiting for job to complete: #{job}"
            sleep 0.1
        end

        print_debug_level_2 'Signaling done.'
        @done_signal << nil
    end

    print_debug_level_2 'Waiting for done signal...'
    # If we've got a job running wait for it to finish before closing the
    # browser otherwise we'll get Selenium errors and zombie processes.
    @done_signal.pop
    print_debug_level_2 '...done.'

    print_debug_level_2 'Waiting for kill check...'
    kill_check.join
    print_debug_level_2 '...done.'

    if @consumer
        print_debug_level_2 'Killing consumer thread...'
        @consumer.kill
        print_debug_level_2 '...done.'
    end

    print_debug_level_2 'Calling parent shutdown...'
    browser_shutdown
    print_debug_level_2 '...done.'

    print_debug '...shutdown complete.'
end