# module WpItems::Detectable

### Attributes

item_xpath[R]
vulns_file[R]

### Public Instance Methods

aggressive_detection(wp_target, options = {}) click to toggle source

@param [ WpTarget ] wp_target @param [ Hash ] options @option options [ Boolean ] :show_progression Whether or not output the progress bar @option options [ Boolean ] :only_vulnerable Only check for vulnerable items @option options [ String ] :exclude_content

@return [ WpItems ]

    # File lib/common/collections/wp_items/detectable.rb, line 14
    def aggressive_detection(wp_target, options = {})
      browser          = Browser.instance
      hydra            = browser.hydra
      targets          = targets_items(wp_target, options)
      progress_bar     = progress_bar(targets.size, options)
      exist_options    = {
        error_404_hash:  wp_target.error_404_hash,
        homepage_hash:   wp_target.homepage_hash,
        exclude_content: options[:exclude_content] ? %r{#{options[:exclude_content]}} : nil
      }

      # If we only want the vulnerable ones, the passive detection is ignored

      # Otherwise, a passive detection is performed, and results will be merged

      results = options[:only_vulnerable] ? new : passive_detection(wp_target, options)

      targets.each do |target_item|
        request = browser.forge_request(target_item.url, request_params)

        request.on_complete do |response|
          progress_bar.progress += 1 if options[:show_progression]

          if target_item.exists?(exist_options, response)
            if !results.include?(target_item)
              results << target_item
            end
          end
        end

        hydra.queue(request)
      end

      hydra.run
      results.sort!
      results # can't just return results.sort because the #sort returns an array, and we want a WpItems

    end

passive_detection(wp_target, options = {}) click to toggle source

@param [ WpTarget ] wp_target @param [ Hash ] options

@return [ WpItems ]

    # File lib/common/collections/wp_items/detectable.rb, line 70
    def passive_detection(wp_target, options = {})
      results      = new
      item_class   = self.item_class
      type         = self.to_s.gsub(/Wp/, '').downcase
      response     = Browser.get(wp_target.url)
      item_options = {
        wp_content_dir: wp_target.wp_content_dir,
        wp_plugins_dir: wp_target.wp_plugins_dir,
        vulns_file:     self.vulns_file
      }

      regex1 = %r{(?:[^=:]+)\s?(?:=|:)\s?(?:"|')[^"']+\?/}
      regex2 = %r{\?/}
      regex3 = %r{\?/([^/\"']+)\?(?:/|"|')}

      names = response.body.scan(/#{regex1}#{Regexp.escape(wp_target.wp_content_dir)}#{regex2}#{Regexp.escape(type)}#{regex3}/)

      names.flatten.uniq.each do |name|
        results << item_class.new(wp_target.uri, item_options.merge(name: name))
      end

      results.sort!
      results
    end

progress_bar(targets_size, options) click to toggle source

@param [ Integer ] targets_size @param [ Hash ] options

@return [ ProgressBar ] :nocov:

    # File lib/common/collections/wp_items/detectable.rb, line 55
    def progress_bar(targets_size, options)
      if options[:show_progression]
        ProgressBar.create(
          format: '%t %a <%B> (%c / %C) %P%% %e',
          title: '  ', # Used to craete a left margin

          total: targets_size
        )
      end
    end

### Protected Instance Methods

create_item(klass, name, wp_target, vulns_file = nil) click to toggle source

@param [ Class ] klass @param [ String ] name @param [ WpTarget ] wp_target @option [ String ] #vulns_file

@return [ WpItem ]

    # File lib/common/collections/wp_items/detectable.rb, line 152
    def create_item(klass, name, wp_target, vulns_file = nil)
      klass.new(
        wp_target.uri,
        name:           name,
        vulns_file:     vulns_file,
        wp_content_dir: wp_target.wp_content_dir,
        wp_plugins_dir: wp_target.wp_plugins_dir
      )
    end

item_class() click to toggle source

@return [ Class ]

    # File lib/common/collections/wp_items/detectable.rb, line 185
    def item_class
      Object.const_get(self.to_s.gsub(/.$/, ''))
    end

request_params() click to toggle source

The default request parameters

@return [ Hash ]

    # File lib/common/collections/wp_items/detectable.rb, line 100
    def request_params; { cache_ttl: 0, followlocation: true } end

targets_items(wp_target, options = {}) click to toggle source

@param [ WpTarget ] wp_target @param [ options ] options @option options [ Boolean ] :only_vulnerable @option options [ String ] :file The path to the file containing the targets

@return [ Array<WpItem> ]

    # File lib/common/collections/wp_items/detectable.rb, line 108
    def targets_items(wp_target, options = {})
      item_class = self.item_class
      vulns_file = self.vulns_file

      targets = vulnerable_targets_items(wp_target, item_class, vulns_file)

      unless options[:only_vulnerable]
        unless options[:file]
          raise 'A file must be supplied'
        end

        targets += targets_items_from_file(options[:file], wp_target, item_class, vulns_file)
      end

      targets.uniq! { |t| t.name }
      targets.sort_by { rand }
    end

targets_items_from_file(file, wp_target, item_class, vulns_file) click to toggle source

@param [ String ] file @param [ WpTarget ] wp_target @param [ Class ] #item_class @param [ String ] #vulns_file

@return [ WpItem ]

    # File lib/common/collections/wp_items/detectable.rb, line 168
    def targets_items_from_file(file, wp_target, item_class, vulns_file)
      targets = []

      File.open(file, 'r') do |f|
        f.readlines.collect do |item_name|
          targets << create_item(
            item_class,
            item_name.strip,
            wp_target,
            vulns_file
          )
        end
      end
      targets
    end

vulnerable_targets_items(wp_target, item_class, vulns_file) click to toggle source

@param [ WpTarget ] wp_target @param [ Class ] #item_class @param [ String ] #vulns_file

@return [ Array<WpItem> ]

    # File lib/common/collections/wp_items/detectable.rb, line 131
    def vulnerable_targets_items(wp_target, item_class, vulns_file)
      targets = []
      xml     = xml(vulns_file)

      xml.xpath(item_xpath).each do |node|
        targets << create_item(
          item_class,
          node.attribute('name').text,
          wp_target,
          vulns_file
        )
      end
      targets
    end