class Arachni::Plugin::Manager

Holds and manages the {Plugins}.

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

Constants

DEFAULT

Expressions matching default plugins.

NAMESPACE

The namespace under which all plugins exist.

Public Class Methods

new( framework ) click to toggle source

@param [Arachni::Framework] framework

Framework instance.
Calls superclass method Arachni::Component::Manager::new
# File lib/arachni/plugin/manager.rb, line 36
def initialize( framework )
    super( framework.options.paths.plugins, NAMESPACE )
    @framework = framework

    @jobs = {}
end
reset() click to toggle source
# File lib/arachni/plugin/manager.rb, line 273
def self.reset
    State.plugins.clear
    Data.plugins.clear
    remove_constants( NAMESPACE )
end

Public Instance Methods

block() click to toggle source

Blocks until all plug-ins have finished executing.

# File lib/arachni/plugin/manager.rb, line 162
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?() click to toggle source

@return [Bool]

`false` if all plug-ins have finished executing, `true` otherwise.
# File lib/arachni/plugin/manager.rb, line 226
def busy?
    @jobs.any?
end
create( name, options = {} ) click to toggle source
# File lib/arachni/plugin/manager.rb, line 157
def create( name, options = {} )
    self[name].new( @framework, options )
end
data() click to toggle source
# File lib/arachni/plugin/manager.rb, line 265
def data
    Data.plugins
end
default() click to toggle source

@return [Array<String>]

Components to load, by name.
# File lib/arachni/plugin/manager.rb, line 53
def default
    parse DEFAULT
end
Also aliased as: defaults
defaults()
Alias for: default
job_names() click to toggle source

@return [Array]

Names of all running plug-ins.
# File lib/arachni/plugin/manager.rb, line 232
def job_names
    @jobs.keys
end
jobs() click to toggle source

@return [Hash{String=>Thread}]

All the running threads.
# File lib/arachni/plugin/manager.rb, line 238
def jobs
    @jobs
end
kill( name ) click to toggle source

Kills a plug-in by ‘name`.

@param [String] name

# File lib/arachni/plugin/manager.rb, line 245
def kill( name )
    synchronize do
        job = @jobs.delete( name.to_sym )
        return true if job && job.kill
    end
    false
end
killall() click to toggle source
# File lib/arachni/plugin/manager.rb, line 253
def killall
    synchronize do
        @jobs.values.each(&:kill)
        @jobs.clear
        true
    end
end
load_default() click to toggle source

Loads the default plugins.

@see DEFAULT

# File lib/arachni/plugin/manager.rb, line 46
def load_default
    load DEFAULT
end
Also aliased as: load_defaults
load_defaults()
Alias for: load_default
reset() click to toggle source
# File lib/arachni/plugin/manager.rb, line 278
def reset
    killall
    clear
    self.class.reset
end
restore() click to toggle source
# File lib/arachni/plugin/manager.rb, line 194
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
results() click to toggle source
# File lib/arachni/plugin/manager.rb, line 269
def results
    data.results
end
run() click to toggle source

Runs each plug-in in its own thread.

@raise [Error::UnsatisfiedDependency]

If the environment is {#sane_env? not sane}.
# File lib/arachni/plugin/manager.rb, line 62
def run
    print_status 'Preparing plugins...'

    schedule.each do |name, options|
        instance = create( name, options )

        exception_jail do
            instance.prepare
        end rescue next

        @jobs[name] = Thread.new do
            exception_jail( false ) do
                Thread.current[:instance] = instance
                Thread.current[:instance].run
                Thread.current[:instance].clean_up
            end

            synchronize do
                @jobs.delete name
            end
        end
    end

    print_status '... done.'
end
sane_env?( plugin ) click to toggle source

Checks whether or not the environment satisfies all plugin dependencies.

@return [TrueClass, Hash]

`true` if the environment is sane, a hash with errors otherwise.
# File lib/arachni/plugin/manager.rb, line 142
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
schedule() click to toggle source

@return [Hash]

Sorted plugins (by priority) with their prepared options.

@raise [Error::UnsatisfiedDependency]

If the environment is not {#sane_env? sane}.
# File lib/arachni/plugin/manager.rb, line 93
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.compact!

    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
state() click to toggle source
# File lib/arachni/plugin/manager.rb, line 261
def state
    State.plugins
end
suspend() click to toggle source
# File lib/arachni/plugin/manager.rb, line 178
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