Class: Arachni::Plugin::Manager

Inherits:
Component::Manager show all
Includes:
MonitorMixin
Defined in:
lib/arachni/plugin/manager.rb

Overview

Holds and manages the Arachni::Plugins.

Author:

Direct Known Subclasses

RPC::Server::Plugin::Manager

Constant Summary collapse

NAMESPACE =

Namespace under which all plugins reside.

Arachni::Plugins
DEFAULT =

Expressions matching default plugins.

%w(defaults/*)

Constants inherited from Component::Manager

Component::Manager::EXCLUDE, Component::Manager::WILDCARD

Instance Attribute Summary

Attributes inherited from Component::Manager

#lib, #namespace

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Component::Manager

#[], #available, #clear, #delete, #include?, #load, #load_all, #load_by_tags, #loaded, #name_to_path, #parse, #path_to_name, #paths, #prepare_options

Methods included from Utilities

#available_port, #bytes_to_kilobytes, #bytes_to_megabytes, #caller_name, #caller_path, #cookie_decode, #cookie_encode, #cookies_from_document, #cookies_from_file, #cookies_from_response, #exception_jail, #exclude_path?, #follow_protocol?, #form_decode, #form_encode, #forms_from_document, #forms_from_response, #full_and_absolute_url?, #generate_token, #get_path, #hms_to_seconds, #html_decode, #html_encode, #include_path?, #links_from_document, #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_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_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

Methods inherited from Hash

#apply_recursively, #downcase, #find_symbol_keys_recursively, #my_stringify, #my_stringify_keys, #my_symbolize_keys, #recode, #recode!, #stringify_recursively_and_freeze

Constructor Details

#initialize(framework) ⇒ Manager

Returns a new instance of Manager.

Parameters:



36
37
38
39
40
41
# File 'lib/arachni/plugin/manager.rb', line 36

def initialize( framework )
    super( framework.options.paths.plugins, NAMESPACE )
    @framework = framework

    @jobs = {}
end

Class Method Details

.resetObject



276
277
278
279
280
# File 'lib/arachni/plugin/manager.rb', line 276

def self.reset
    State.plugins.clear
    Data.plugins.clear
    remove_constants( NAMESPACE )
end

Instance Method Details

#blockObject

Blocks until all plug-ins have finished executing.



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/arachni/plugin/manager.rb', line 165

def block
    while busy?
        print_debug
        print_debug "Waiting on #{@jobs.size} plugins to finish:"
        print_debug job_names.join( ', ' )
        print_debug

        synchronize do
            @jobs.select! { |_ ,j| j.alive? }
        end

        sleep 0.1
    end
    nil
end

#busy?Bool

Returns ‘false` if all plug-ins have finished executing, `true` otherwise.

Returns:

  • (Bool)

    ‘false` if all plug-ins have finished executing, `true` otherwise.



229
230
231
# File 'lib/arachni/plugin/manager.rb', line 229

def busy?
    @jobs.any?
end

#create(name, options = {}) ⇒ Object



160
161
162
# File 'lib/arachni/plugin/manager.rb', line 160

def create( name, options = {} )
    self[name].new( @framework, options )
end

#dataObject



268
269
270
# File 'lib/arachni/plugin/manager.rb', line 268

def data
    Data.plugins
end

#defaultArray<String> Also known as: defaults

Returns Components to load, by name.

Returns:



53
54
55
# File 'lib/arachni/plugin/manager.rb', line 53

def default
    parse DEFAULT
end

#job_namesArray

Returns Names of all running plug-ins.

Returns:

  • (Array)

    Names of all running plug-ins.



235
236
237
# File 'lib/arachni/plugin/manager.rb', line 235

def job_names
    @jobs.keys
end

#jobsHash{String=>Thread}

Returns All the running threads.

Returns:

  • (Hash{String=>Thread})

    All the running threads.



241
242
243
# File 'lib/arachni/plugin/manager.rb', line 241

def jobs
    @jobs
end

#kill(name) ⇒ Object

Kills a plug-in by ‘name`.

Parameters:



248
249
250
251
252
253
254
# File 'lib/arachni/plugin/manager.rb', line 248

def kill( name )
    synchronize do
        job = @jobs.delete( name.to_sym )
        return true if job && job.kill
    end
    false
end

#killallObject



256
257
258
259
260
261
262
# File 'lib/arachni/plugin/manager.rb', line 256

def killall
    synchronize do
        @jobs.values.each(&:kill)
        @jobs.clear
        true
    end
end

#load_defaultObject Also known as: load_defaults

Loads the default plugins.

See Also:



46
47
48
# File 'lib/arachni/plugin/manager.rb', line 46

def load_default
    load DEFAULT
end

#resetObject



281
282
283
284
285
# File 'lib/arachni/plugin/manager.rb', line 281

def reset
    killall
    clear
    self.class.reset
end

#restoreObject



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/arachni/plugin/manager.rb', line 197

def restore
    schedule.each do |name, options|
        @jobs[name] = Thread.new do
            exception_jail( false ) do
                if state.include? name
                    Thread.current[:instance] = create( name, state[name][:options] )
                    Thread.current[:instance].restore state[name][:data]
                else
                    Thread.current[:instance] = create( name, options )
                    Thread.current[:instance].prepare
                end

                Thread.current[:instance].run
                Thread.current[:instance].clean_up

                synchronize do
                    @jobs.delete name
                end
            end
        end
    end

    return if @jobs.empty?

    print_status 'Waiting for plugins to settle...'
    sleep 1

    nil
end

#resultsObject



272
273
274
# File 'lib/arachni/plugin/manager.rb', line 272

def results
    data.results
end

#runObject

Runs each plug-in in its own thread.

Raises:



62
63
64
65
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
# File 'lib/arachni/plugin/manager.rb', line 62

def run
    prepared_signals = Queue.new

    schedule.each do |name, options|
        @jobs[name] = Thread.new do
            exception_jail( false ) do
                begin
                    Thread.current[:instance] = create( name, options )
                    Thread.current[:instance].prepare
                ensure
                    prepared_signals << nil
                end

                Thread.current[:instance].run
                Thread.current[:instance].clean_up

                synchronize do
                    @jobs.delete name
                end
            end
        end
    end

    return if @jobs.empty?

    print_status 'Preparing plugins...'
    self.size.times { prepared_signals.pop }
    print_status '... done.'
end

#sane_env?(plugin) ⇒ TrueClass, Hash

Checks whether or not the environment satisfies all plugin dependencies.

Returns:

  • (TrueClass, Hash)

    ‘true` if the environment is sane, a hash with errors otherwise.



145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/arachni/plugin/manager.rb', line 145

def sane_env?( plugin )
    gem_errors = []

    plugin.gems.each do |gem|
        begin
            require gem
        rescue LoadError
            gem_errors << gem
        end
    end

    return { gem_errors: gem_errors } if !gem_errors.empty?
    true
end

#scheduleHash

Returns Sorted plugins (by priority) with their prepared options.

Returns:

  • (Hash)

    Sorted plugins (by priority) with their prepared options.

Raises:



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/arachni/plugin/manager.rb', line 97

def schedule
    ordered   = []
    unordered = []

    loaded.each do |name|
        ph = { name => self[name] }

        if (order = self[name].info[:priority])
            ordered[order] ||= []
            ordered[order] << ph
        else
            unordered << ph
        end
    end

    ordered << unordered
    ordered.flatten!

    ordered.inject({}) do |h, ph|
        name   = ph.keys.first
        plugin = ph.values.first

        if (ret = sane_env?( plugin )) != true
            deps = ''
            if !ret[:gem_errors].empty?
                print_bad "[#{name}] The following plug-in dependencies aren't satisfied:"
                ret[:gem_errors].each { |gem| print_bad "\t* #{gem}" }

                deps = ret[:gem_errors].join( ' ' )
                print_bad 'Try installing them by running:'
                print_bad "\tgem install #{deps}"
            end

            fail Error::UnsatisfiedDependency,
                 "Plug-in dependencies not met: #{name} -- #{deps}"
        end

        h[name.to_sym] = prepare_options(
            name, plugin, @framework.options.plugins[name]
        )
        h
    end
end

#stateObject



264
265
266
# File 'lib/arachni/plugin/manager.rb', line 264

def state
    State.plugins
end

#suspendObject



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/arachni/plugin/manager.rb', line 181

def suspend
    @jobs.dup.each do |name, job|
        next if !job.alive?
        plugin = job[:instance]

        state.store( name,
            data:    plugin.suspend,
            options: plugin.options
        )

        kill name
    end

    nil
end