class Msf::Analyze
  def initialize(framework)
    @framework = framework
  end
  def host(eval_host, payloads: nil)
    unless eval_host.vulns
      return {}
    end
    
    vuln_families = group_vulns(eval_host.vulns)
    
    suggested_modules = suggest_modules_for_vulns(eval_host, vuln_families, payloads: payloads)
    {results: suggested_modules}
  end
  private
  def group_vulns(vulns)
    return [] if vulns.empty?
    vulns = vulns.map do |vuln|
      [vuln, Set.new(vuln.refs.map {|r| r.name.upcase})]
    end
    grouped_vulns = Hash.new
    vulns.each_index do |ii|
      vuln, refs = vulns[ii]
      grouped_vulns[vuln] ||= [Set.new, Set.new]
      grouped_vulns[vuln][0] << vuln
      grouped_vulns[vuln][1].merge(refs)
      vulns[(ii+1)..-1].each do |candidate_match, candidate_refs|
        
        
        if candidate_refs.intersect? refs
          if grouped_vulns[candidate_match]
            
            
            
            
            grouped_vulns[candidate_match][0] = grouped_vulns[candidate_match][0].merge(grouped_vulns[vuln][0])
            grouped_vulns[candidate_match][1] = grouped_vulns[candidate_match][1].merge(grouped_vulns[vuln][1])
            grouped_vulns[vuln][0] = grouped_vulns[candidate_match][0]
            grouped_vulns[vuln][1] = grouped_vulns[candidate_match][1]
          else
            
            grouped_vulns[candidate_match] = grouped_vulns[vuln]
          end
        end
      end
    end
    vuln_families = grouped_vulns.values
    vuln_families.uniq!
    vuln_families
  end
  def suggest_modules_for_vulns(eval_host, vuln_families, payloads: nil)
    mrefs, _mports, _mservs = Msf::Modules::Metadata::Cache.instance.all_exploit_maps
    suggested_modules = []
    evaluated_module_targets = Set.new
    to_evaluate_with_defaults = Array.new
    vuln_families.each do |vulns, refs|
      found_modules = mrefs.values_at(*refs).compact.reduce(:+)
      next unless found_modules
      services = vulns.map(&:service).compact
      found_modules.each do |fnd_mod|
        if services.any?
          services.each do |svc|
            port = svc.port
            next if evaluated_module_targets.include?([fnd_mod, port])
            creds = @framework.db.creds(svcs: [svc.name], workspace: eval_host.workspace)
            r = Result.new(mod: fnd_mod, host: eval_host, datastore: {'rport': port},
                           available_creds: creds, payloads: payloads, framework: @framework)
            if r.match?
              suggested_modules << r.evaluate
            end
            evaluated_module_targets << [fnd_mod, port]
          end
        else
          
          
          
          port = fnd_mod.rport
          to_evaluate_with_defaults << [fnd_mod, port]
        end
      end
    end
    to_evaluate_with_defaults.each do |fnd_mod, port|
      next if evaluated_module_targets.include?([fnd_mod, port])
      creds = @framework.db.creds(port: port, workspace: eval_host.workspace) if port
      r = Result.new(mod: fnd_mod, host: eval_host, datastore: {'rport': port},
                     available_creds: creds, payloads: payloads, framework: @framework)
      if r.match?
        suggested_modules << r.evaluate
      end
      evaluated_module_targets << [fnd_mod, port]
    end
    suggested_modules
  end
end