class Arachni::Platform::Manager

Represents a collection of platform {List lists}.

It also holds a DB of all fingerprints per URI as a class variable and provides helper method for accessing and manipulating it.

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

Constants

DB
FRAMEWORKS

WebApp frameworks.

LANGUAGES
OS
PLATFORM_CACHE_SIZE
PLATFORM_NAMES
SERVERS
TYPES

Public Class Methods

[]( uri ) click to toggle source

@param [String, URI] uri

@return [Manager]

Platform for the given `uri`
# File lib/arachni/platform/manager.rb, line 284
def self.[]( uri )
    # If fingerprinting is disabled there's no point in filling the cache
    # with the same object over and over, create an identical one for all
    # URLs and return that always.
    if !Options.fingerprint?
        return @default ||= new_from_options
    end

    return new_from_options if !(key = make_key( uri ))
    synchronize { @platforms.fetch(key) { new_from_options } }
end
[]=( uri, platforms ) click to toggle source

Sets platform manager for the given ‘uri`.

@param [String, URI] uri @param [Enumerable] platforms

@return [Manager]

@raise [Error::Invalid]

On {#invalid?} platforms.
# File lib/arachni/platform/manager.rb, line 305
def self.[]=( uri, platforms )
    # For some reason we failed to make a key, try to salvage the situation.
    if !(key = make_key( uri ))
        return new_from_options( platforms )
    end

    synchronize do
        @platforms[key] =
            platforms.is_a?( self ) ?
                platforms :
                new_from_options( platforms )
    end
end
any?() click to toggle source

@return [Boolean]

`true` if there are platforms fingerprints, `false` otherwise.
# File lib/arachni/platform/manager.rb, line 352
def self.any?
    !empty?
end
clear() click to toggle source

Clears global platforms DB.

# File lib/arachni/platform/manager.rb, line 220
def self.clear
    @platforms.clear
end
empty?() click to toggle source

@return [Boolean]

`true` if there are no platforms fingerprints, `false` otherwise.
# File lib/arachni/platform/manager.rb, line 346
def self.empty?
    @platforms.empty?
end
find_type( platform ) click to toggle source
# File lib/arachni/platform/manager.rb, line 184
def self.find_type( platform )
    @find_type ||= {}

    if @find_type.empty?
        TYPES.keys.each do |type|

            platforms = const_get( type.to_s.upcase.to_sym )
            platforms = platforms.find_symbol_keys_recursively if platforms.is_a?( Hash )

            platforms.each do |p|
                @find_type[p] = type
            end
        end
    end

    @find_type[platform]
end
fingerprint( page ) click to toggle source

Runs all fingerprinters against the given ‘page`.

@param [Page] page

Page to fingerprint.

@return [Manager]

Updated `self`.
# File lib/arachni/platform/manager.rb, line 259
def self.fingerprint( page )
    synchronize do
        return page if !fingerprint? page

        fingerprinters.available.each do |name|
            exception_jail( false ) do
                fingerprinters[name].new( page ).run
            end
        end

        # We do this to flag the resource as checked even if no platforms
        # were identified. We don't want to keep checking a resource that
        # yields nothing over and over.
        update( page.url, [] )
    end

    # Fingerprinting will have resulted in element parsing, clear the element
    # caches to keep RAM consumption down.
    page.clear_cache
end
fingerprint?( resource ) click to toggle source

@param [HTTP::Response, Page] resource

@return [Bool]

`true` if the resource should be fingerprinted, `false` otherwise.
# File lib/arachni/platform/manager.rb, line 247
def self.fingerprint?( resource )
    !(!Options.fingerprint? || resource.code != 200 || !resource.text? ||
        include?( resource.url ) || resource.scope.out?)
end
fingerprinters() click to toggle source
# File lib/arachni/platform/manager.rb, line 236
def self.fingerprinters
    @manager ||=
        Component::Manager.new( Options.paths.fingerprinters,
                                Platform::Fingerprinters )
end
include?( uri ) click to toggle source

@param [String, URI] uri

# File lib/arachni/platform/manager.rb, line 324
def self.include?( uri )
    @platforms.include?( make_key( uri ) )
end
make_key( uri ) click to toggle source
# File lib/arachni/platform/manager.rb, line 356
def self.make_key( uri )
    return if !(parsed = Arachni::URI( uri ))
    parsed.without_query
end
new( platforms = [] ) click to toggle source

@param [Array<String, Symbol>] platforms

Platforms with which to initialize the lists.
# File lib/arachni/platform/manager.rb, line 367
def initialize( platforms = [] )
    @platforms = {}
    TYPES.keys.each do |type|
        @platforms[type] =
            List.new( self.class.const_get( type.to_s.upcase.to_sym ) )
    end

    update platforms
end
new_from_options( platforms = [] ) click to toggle source
# File lib/arachni/platform/manager.rb, line 361
def self.new_from_options( platforms = [] )
    new( platforms | Options.platforms )
end
reset() click to toggle source

Empties the global platform fingerprints.

# File lib/arachni/platform/manager.rb, line 225
def self.reset
    set Hash.new
    @manager.clear if @manager
    @manager = nil

    @mutex  = Monitor.new

    self
end
set( platforms ) click to toggle source

Sets global platforms fingerprints @private

# File lib/arachni/platform/manager.rb, line 213
def self.set( platforms )
    @platforms = Support::Cache::LeastRecentlyPushed.new( PLATFORM_CACHE_SIZE )
    platforms.each { |k, v| @platforms[k] = v }
    @platforms
end
size() click to toggle source
# File lib/arachni/platform/manager.rb, line 319
def self.size
    @platforms.size
end
synchronize( &block ) click to toggle source
# File lib/arachni/platform/manager.rb, line 180
def self.synchronize( &block )
    @mutex.synchronize( &block )
end
update( uri, platforms ) click to toggle source

Updates the ‘platforms` for the given `uri`.

@param [String, URI] uri @param [Manager] platforms

@return [Manager]

Updated manager.

@raise [Error::Invalid]

On {#invalid?} platforms.
# File lib/arachni/platform/manager.rb, line 338
def self.update( uri, platforms )
    synchronize do
        self[uri].update platforms
    end
end
valid() click to toggle source
# File lib/arachni/platform/manager.rb, line 202
def self.valid
    @valid ||= Set.new( PLATFORM_NAMES.keys )
end
valid?( platforms ) click to toggle source
# File lib/arachni/platform/manager.rb, line 206
def self.valid?( platforms )
    platforms = [platforms].flatten.compact
    (valid & platforms).to_a == platforms
end

Public Instance Methods

<<( platform ) click to toggle source

@param [Symbol, String] platform

Platform to add to the appropriate list.

@return [Manager]

`self`

@raise [Error::Invalid]

On {#invalid?} platforms.
# File lib/arachni/platform/manager.rb, line 550
def <<( platform )
    find_list( platform ) << platform
    self
end
any?() click to toggle source

@return [Boolean]

`true` if there are applicable platforms, `false` otherwise.
# File lib/arachni/platform/manager.rb, line 521
def any?
    !empty?
end
clear() click to toggle source
# File lib/arachni/platform/manager.rb, line 525
def clear
    @platforms.clear
end
each( &block ) click to toggle source

@param [Block] block

Block to be passed each platform.

@return [Enumerator, Manager]

`Enumerator` if no `block` is given, `self` otherwise.
# File lib/arachni/platform/manager.rb, line 495
def each( &block )
    return enum_for( __method__ ) if !block_given?
    @platforms.map { |_, p| p.to_a }.flatten.each( &block )
    self
end
empty?() click to toggle source

@return [Boolean]

`true` if there are no applicable platforms, `false` otherwise.
# File lib/arachni/platform/manager.rb, line 515
def empty?
    !@platforms.map { |_, p| p.empty? }.include?( false )
end
find_list( platform ) click to toggle source

@param [String, Symbol] platform

Platform whose list to find.

@return [List]

Platform list.
# File lib/arachni/platform/manager.rb, line 568
def find_list( platform )
    @platforms[find_type( normalize( platform ) )]
end
find_type( platform ) click to toggle source

@param [String, Symbol] platform

Platform whose type to find

@return [Symbol] Platform type.

# File lib/arachni/platform/manager.rb, line 559
def find_type( platform )
    self.class.find_type( platform )
end
fullname( platform ) click to toggle source

Converts a platform shortname to a full name.

@param [String, Symbol] platform

Platform shortname.

@return [String]

Full name.

@raise [Error::Invalid]

On {#invalid?} platforms.
# File lib/arachni/platform/manager.rb, line 423
def fullname( platform )
    PLATFORM_NAMES[normalize( platform )]
end
include?( platform ) click to toggle source

@param [Symbol, String] platform

Platform to check.

@return [Boolean]

`true` if one of the lists contains the `platform`, `false` otherwise.

@raise [Error::Invalid]

On {#invalid?} `platforms`.
# File lib/arachni/platform/manager.rb, line 509
def include?( platform )
    find_list( platform ).include?( platform )
end
invalid?( platform ) click to toggle source

@param [Symbol, String] platform

Platform to check.

@return [Boolean]

`true` if platform is invalid (i.e. not in {#valid}), `false` otherwise.

@see invalid?

# File lib/arachni/platform/manager.rb, line 486
def invalid?( platform )
    !valid?( platform )
end
pick( data_per_platform ) click to toggle source

Selects appropriate data, depending on the applicable platforms, from ‘data_per_platform`.

@param [Hash{<Symbol, String> => Object}] data_per_platform

Hash with platform names as keys and arbitrary data as values.

@return [Hash]

`data_per_platform` with non-applicable entries (for non-empty platform
lists) removed. Data for platforms whose list is empty will not be removed.

@raise [Error::Invalid]

On {#invalid?} platforms.
# File lib/arachni/platform/manager.rb, line 439
def pick( data_per_platform )
    data_per_list = {}
    data_per_platform.each do |platform, value|
        list = find_list( platform )
        data_per_list[list]           ||= {}
        data_per_list[list][platform]   = value
    end

    picked = {}
    data_per_list.each do |list, data|
        # If a platform list is empty pass the given data without picking...
        if list.empty?
            picked.merge! data
            next
        end

        # ...otherwise enforce its platform restrictions.
        picked.merge! list.pick( data )
    end

    picked
end
update( enum ) click to toggle source

@param [Enumerable] enum

Enumerable object containing platforms.

@return [Manager]

Updated `self`.

@raise [Error::Invalid]

On {#invalid?} platforms.
# File lib/arachni/platform/manager.rb, line 537
def update( enum )
    enum.each { |p| self << p }
    self
end
valid() click to toggle source

@return [Set<Symbol>]

List of valid platforms.
# File lib/arachni/platform/manager.rb, line 464
def valid
    self.class.valid
end
valid?( platform ) click to toggle source

@param [Symbol, String] platform

Platform to check.

@return [Boolean]

`true` if platform is valid (i.e. in {#valid}), `false` otherwise.

@see invalid?

# File lib/arachni/platform/manager.rb, line 475
def valid?( platform )
    valid.include? platform
end

Private Instance Methods

normalize( platform ) click to toggle source
# File lib/arachni/platform/manager.rb, line 574
def normalize( platform )
    platform = List.normalize( platform )
    fail Error::Invalid, "Invalid platform: #{platform}" if invalid?( platform )
    platform
end