class Arachni::Component::Manager
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.
@example
# 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>
Constants
- EXCLUDE
- WILDCARD
Attributes
@return [String]
The path to the component library/directory.
@return [Module]
Namespace under which all components are directly defined.
Public Class Methods
@param [String] lib
The path to the component library/directory.
@param [Module,Class] namespace
Namespace under which all components are directly defined.
# 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
Public Instance Methods
Fetches a component’s class by name, loading it on the fly if need be.
@param [String, Symbol] name
Component name.
@return [Component::Base]
Component.
# 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
@return [Array]
Names of available components.
# File lib/arachni/component/manager.rb, line 322 def available paths.map { |path| path_to_name( path ) } end
Unloads all loaded components.
# File lib/arachni/component/manager.rb, line 297 def clear keys.each { |l| delete( l ) } end
Unloads a component by name.
@param [String, Symbol] name
Component name.
# 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
# File lib/arachni/component/manager.rb, line 291 def include?( k ) super( k.to_s ) end
Loads components.
@param [Array<String,Symbol>] components
Components to load.
@return [Array]
Names of loaded components.
# File lib/arachni/component/manager.rb, line 120 def load( *components ) parse( [components].flatten ).each { |component| self.[]( component ) } end
Loads all components, equivalent of ‘load ’*‘`.
@return [Array]
Names of loaded components.
# File lib/arachni/component/manager.rb, line 128 def load_all load '*' end
@return [Array]
Names of loaded components.
# File lib/arachni/component/manager.rb, line 328 def loaded keys end
# 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
# File lib/arachni/component/manager.rb, line 363 def matches_globs?( path, globs ) !![globs].flatten.compact.find { |glob| matches_glob?( path, glob ) } end
Converts the name of a component to a its file’s path.
@param [String] name
Name of the component.
@return [String]
Path to component file.
# 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
It parses the component array making sure that its structure is valid and takes into consideration {WILDCARD wildcard} and {EXCLUDE exclusion} modifiers.
@param [Array<String,Symbol>] components
Component names.
@return [Array]
Components to load.
# 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
Converts the path of a component to a component name.
@param [String] path
File-path of the component.
@return [String]
Component name.
# File lib/arachni/component/manager.rb, line 351 def path_to_name( path ) @path_to_name_cache[path] ||= File.basename( path, '.rb' ) end
@return [Array]
Paths of all available components (excluding helper files).
# File lib/arachni/component/manager.rb, line 357 def paths @paths_cache ||= Dir.glob( File.join( "#{@lib}**", "*.rb" ) ). reject{ |path| helper?( path ) } end
Validates and prepares options for a given component.
@param [String] component_name
Name of the component.
@param [Component::Base] component
Component.
@param [Hash] user_opts
User options.
@return [Hash]
Prepared options to be passed to the component.
@raise [Component::Options::Error::Invalid]
If given options are invalid.
# 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
Private Instance Methods
# File lib/arachni/component/manager.rb, line 413 def classes @namespace.constants.reject{ |c| !get_obj( c ).is_a?( Class ) } end
# File lib/arachni/component/manager.rb, line 389 def format_error_string( name, errors ) "Invalid options for component: #{name}\n" + errors.map do |optname, error| val = error[:value].nil? ? '<empty>' : error[:value] msg = (error[:type] == :invalid) ? 'Invalid type' : 'Missing value' " * #{msg}: #{optname} => '#{val}'\n" + " * Expected type: #{error[:option].type}" end.join( "\n\n" ) end
# File lib/arachni/component/manager.rb, line 417 def get_obj( sym ) @namespace.const_get( sym ) end
# File lib/arachni/component/manager.rb, line 379 def glob_to_names( glob ) if glob[WILDCARD] paths.map do |path| next if !matches_glob?( path, glob ) path_to_name( path ) end.compact end end
# File lib/arachni/component/manager.rb, line 421 def helper?( path ) @helper_check_cache[path] ||= File.exist?( File.dirname( path ) + '.rb' ) end
# File lib/arachni/component/manager.rb, line 400 def load_from_path( path ) pre = classes ::Kernel::load( path ) post = classes return if pre == post get_obj( (post - pre).first ).tap do |component| next if !component.respond_to?( :shortname= ) component.shortname = path_to_name( path ) end end