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:

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

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, #matches_glob?, #matches_globs?, #name_to_path, #parse, #path_to_name, #paths, #prepare_options

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

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



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

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.



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# 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?Bool

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

Returns:

  • (Bool)

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



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

def busy?
    @jobs.any?
end

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



157
158
159
# File 'lib/arachni/plugin/manager.rb', line 157

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

#dataObject



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

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.



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

def job_names
    @jobs.keys
end

#jobsHash{String=>Thread}

Returns All the running threads.

Returns:

  • (Hash{String=>Thread})

    All the running threads.



238
239
240
# File 'lib/arachni/plugin/manager.rb', line 238

def jobs
    @jobs
end

#kill(name) ⇒ Object

Kills a plug-in by `name`.

Parameters:



245
246
247
248
249
250
251
# 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

#killallObject



253
254
255
256
257
258
259
# File 'lib/arachni/plugin/manager.rb', line 253

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



278
279
280
281
282
# File 'lib/arachni/plugin/manager.rb', line 278

def reset
    killall
    clear
    self.class.reset
end

#restoreObject



194
195
196
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
# 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

#resultsObject



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

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
# 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) ⇒ 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.



142
143
144
145
146
147
148
149
150
151
152
153
154
155
# 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

#scheduleHash

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

Returns:

  • (Hash)

    Sorted plugins (by priority) with their prepared options.

Raises:



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
132
133
134
135
136
# 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

#stateObject



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

def state
    State.plugins
end

#suspendObject



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# 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