Class: Arachni::Component::Manager

Inherits:
Hash show all
Extended by:
Utilities
Includes:
UI::Output, Utilities
Defined in:
lib/arachni/component/manager.rb

Overview

Handles checks, reports, path extractor checks, plug-ins, pretty much every modular aspect of the framework.

It is usually extended to fill-in for system specific functionality.

Examples:


# create a namespace for our components
module Components
end

LIB       = "#{File.dirname( __FILE__ )}/lib/"
NAMESPACE = Components

# $ ls LIB
#   component1.rb  component2.rb
#
# $ cat LIB/component1.rb
#   class Components::Component1
#   end
#
# $ cat LIB/component2.rb
#   class Components::Component2
#   end

p components = Arachni::Component::Manager.new( LIB, NAMESPACE )
#=> {}

p components.available
#=> ["component2", "component1"]

p components.load_all
#=> ["component2", "component1"]

p components
#=> {"component2"=>Components::Component2, "component1"=>Components::Component1}

p components.clear
#=> {}

p components.load :component1
#=> ["component1"]

p components
#=> {"component1"=>Components::Component1}

p components.clear
#=> {}

p components[:component2]
#=> Components::Component2

Author:

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

Constant Summary collapse

WILDCARD =
'*'
EXCLUDE =
'-'

Instance Attribute Summary collapse

Instance Method Summary collapse

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(lib, namespace) ⇒ Manager

Returns a new instance of Manager.

Parameters:

  • lib (String)

    The path to the component library/directory.

  • namespace (Module, Class)

    Namespace under which all components are directly defined.



104
105
106
107
108
109
110
111
# File 'lib/arachni/component/manager.rb', line 104

def initialize( lib, namespace )
    @lib       = lib
    @namespace = namespace

    @helper_check_cache = {}
    @name_to_path_cache = {}
    @path_to_name_cache = {}
end

Instance Attribute Details

#libString (readonly)

Returns The path to the component library/directory.

Returns:

  • (String)

    The path to the component library/directory.



94
95
96
# File 'lib/arachni/component/manager.rb', line 94

def lib
  @lib
end

#namespaceModule (readonly)

Returns Namespace under which all components are directly defined.

Returns:

  • (Module)

    Namespace under which all components are directly defined.



98
99
100
# File 'lib/arachni/component/manager.rb', line 98

def namespace
  @namespace
end

Instance Method Details

#[](name) ⇒ Component::Base

Fetches a component's class by name, loading it on the fly if need be.

Parameters:

  • name (String, Symbol)

    Component name.

Returns:



285
286
287
288
289
# File 'lib/arachni/component/manager.rb', line 285

def []( name )
    name = name.to_s
    return fetch( name ) if include?( name )
    self[name] = load_from_path( name_to_path( name ) )
end

#availableArray

Returns Names of available components.

Returns:

  • (Array)

    Names of available components.



322
323
324
# File 'lib/arachni/component/manager.rb', line 322

def available
    paths.map { |path| path_to_name( path ) }
end

#clearObject Also known as: unload_all

Unloads all loaded components.



297
298
299
# File 'lib/arachni/component/manager.rb', line 297

def clear
    keys.each { |l| delete( l ) }
end

#delete(name) ⇒ Object Also known as: unload

Unloads a component by name.

Parameters:

  • name (String, Symbol)

    Component name.



306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/arachni/component/manager.rb', line 306

def delete( name )
    name = name.to_s
    begin
        @namespace.send(
            :remove_const,
            fetch( name ).to_s.split( ':' ).last.to_sym
        )
    rescue
    end

    super( name )
end

#include?(k) ⇒ Boolean Also known as: loaded?

Returns:

  • (Boolean)


291
292
293
# File 'lib/arachni/component/manager.rb', line 291

def include?( k )
    super( k.to_s )
end

#load(*components) ⇒ Array

Loads components.

Parameters:

  • components (Array<String,Symbol>)

    Components to load.

Returns:

  • (Array)

    Names of loaded components.



120
121
122
# File 'lib/arachni/component/manager.rb', line 120

def load( *components )
    parse( [components].flatten ).each { |component| self.[]( component ) }
end

#load_allArray

Loads all components, equivalent of `load '*'`.

Returns:

  • (Array)

    Names of loaded components.



128
129
130
# File 'lib/arachni/component/manager.rb', line 128

def load_all
    load '*'
end

#load_by_tags(tags) ⇒ Array

Loads components by the tags found in the `Hash` returned by their `.info` method (tags should be in either: `:tags` or `:issue`).

Parameters:

  • tags (Array)

    Tags to look for in components.

Returns:

  • (Array)

    Components loaded.



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/arachni/component/manager.rb', line 140

def load_by_tags( tags )
    return [] if !tags

    tags = [tags].flatten.compact.map( &:to_s )
    return [] if tags.empty?

    load_all
    map do |k, v|
        component_tags  = [v.info[:tags]]
        component_tags |= [v.info[:issue][:tags]] if v.info[:issue]
        component_tags  = [component_tags].flatten.uniq.compact

        if !component_tags.includes_tags?( tags )
            delete( k )
            next
        end
        k
    end.compact
end

#loadedArray

Returns Names of loaded components.

Returns:

  • (Array)

    Names of loaded components.



328
329
330
# File 'lib/arachni/component/manager.rb', line 328

def loaded
    keys
end

#matches_glob?(path, glob) ⇒ Boolean

Returns:

  • (Boolean)


367
368
369
370
371
372
373
374
375
# File 'lib/arachni/component/manager.rb', line 367

def matches_glob?( path, glob )
    relative_path = File.dirname( path.gsub( @lib, '' ) )
    relative_path << '/' if !relative_path.end_with?( '/' )

    name = path_to_name( path )

    Support::Glob.new( glob ) =~ name ||
        Support::Glob.new( glob ) =~ relative_path
end

#matches_globs?(path, globs) ⇒ Boolean

Returns:

  • (Boolean)


363
364
365
# File 'lib/arachni/component/manager.rb', line 363

def matches_globs?( path, globs )
    !![globs].flatten.compact.find { |glob| matches_glob?( path, glob ) }
end

#name_to_path(name) ⇒ String

Converts the name of a component to a its file's path.

Parameters:

  • name (String)

    Name of the component.

Returns:

  • (String)

    Path to component file.



339
340
341
342
# File 'lib/arachni/component/manager.rb', line 339

def name_to_path( name )
    @name_to_path_cache[name] ||=
        paths.find { |path| name.to_s == path_to_name( path ) }
end

#parse(components) ⇒ Array

It parses the component array making sure that its structure is valid and takes into consideration wildcard and exclusion modifiers.

Parameters:

  • components (Array<String,Symbol>)

    Component names.

Returns:

  • (Array)

    Components to load.



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/arachni/component/manager.rb', line 228

def parse( components )
    unload = []
    load   = []

    components = [components].flatten.map( &:to_s )

    return load if components[0] == EXCLUDE

    components = components.deep_clone

    components.each do |component|
        if component[0] == EXCLUDE
            component[0] = ''

            if component[WILDCARD]
                unload |= glob_to_names( component )
            else
                unload << component
            end

        end
    end

    if !components.include?( WILDCARD )

        avail_components  = available(  )

        components.each do |component|

            if component.include?( WILDCARD )
                load |= glob_to_names( component )
            else

                if avail_components.include?( component )
                    load << component
                else
                    fail Error::NotFound,
                         "Component '#{component}' could not be found."
                end
            end
        end

        load.flatten!
    else
        available.each{ |component| load << component }
    end

    load - unload
end

#path_to_name(path) ⇒ String

Converts the path of a component to a component name.

Parameters:

  • path (String)

    File-path of the component.

Returns:

  • (String)

    Component name.



351
352
353
# File 'lib/arachni/component/manager.rb', line 351

def path_to_name( path )
    @path_to_name_cache[path] ||= File.basename( path, '.rb' )
end

#pathsArray

Returns Paths of all available components (excluding helper files).

Returns:

  • (Array)

    Paths of all available components (excluding helper files).



357
358
359
360
361
# File 'lib/arachni/component/manager.rb', line 357

def paths
    @paths_cache ||=
        Dir.glob( File.join( "#{@lib}**", "*.rb" ) ).
            reject{ |path| helper?( path ) }
end

#prepare_options(component_name, component, user_opts = {}) ⇒ Hash

Validates and prepares options for a given component.

Parameters:

  • component_name (String)

    Name of the component.

  • component (Component::Base)

    Component.

  • user_opts (Hash) (defaults to: {})

    User options.

Returns:

  • (Hash)

    Prepared options to be passed to the component.

Raises:



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
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/component/manager.rb', line 174

def prepare_options( component_name, component, user_opts = {} )
    info = component.info
    return {} if !info.include?( :options ) || info[:options].empty?

    user_opts ||= {}
    user_opts   = user_opts.my_symbolize_keys(false)

    options     = {}
    errors      = {}
    info[:options].each do |option|
        option.value = user_opts[option.name]

        if option.missing_value?
            errors[option.name] = {
                option: option,
                value:  option.value,
                type:   :missing_value
            }

            break
        end

        next if option.effective_value.nil?

        if !option.valid?
            errors[option.name] = {
                option: option,
                value:  option.value,
                type:   :invalid
            }

            break
        end

        options.merge! option.for_component
    end

    if !errors.empty?
        fail Component::Options::Error::Invalid,
             format_error_string( component_name, errors )
    end

    options.my_symbolize_keys( false )
end