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, #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, #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?, #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, #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



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

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.



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/arachni/plugin/manager.rb', line 157

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.



221
222
223
# File 'lib/arachni/plugin/manager.rb', line 221

def busy?
    @jobs.any?
end

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



152
153
154
# File 'lib/arachni/plugin/manager.rb', line 152

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

#dataObject



260
261
262
# File 'lib/arachni/plugin/manager.rb', line 260

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.



227
228
229
# File 'lib/arachni/plugin/manager.rb', line 227

def job_names
    @jobs.keys
end

#jobsHash{String=>Thread}

Returns All the running threads.

Returns:

  • (Hash{String=>Thread})

    All the running threads.



233
234
235
# File 'lib/arachni/plugin/manager.rb', line 233

def jobs
    @jobs
end

#kill(name) ⇒ Object

Kills a plug-in by ‘name`.

Parameters:



240
241
242
243
244
245
246
# File 'lib/arachni/plugin/manager.rb', line 240

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

#killallObject



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

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



273
274
275
276
277
# File 'lib/arachni/plugin/manager.rb', line 273

def reset
    killall
    clear
    self.class.reset
end

#restoreObject



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/arachni/plugin/manager.rb', line 189

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



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

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

def run
    schedule.each do |name, options|
        @jobs[name] = Thread.new do
            exception_jail( false ) do
                Thread.current[:instance] = create( name, options )
                Thread.current[:instance].prepare
                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
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.



137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/arachni/plugin/manager.rb', line 137

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:



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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/arachni/plugin/manager.rb', line 89

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



256
257
258
# File 'lib/arachni/plugin/manager.rb', line 256

def state
    State.plugins
end

#suspendObject



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/arachni/plugin/manager.rb', line 173

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