Codebase list python-faraday / upstream/1.0.18 managers / reports_managers.py
upstream/1.0.18

Tree @upstream/1.0.18 (Download .tar.gz)

reports_managers.py @upstream/1.0.18raw · history · blame

#!/usr/bin/env python
'''
Faraday Penetration Test IDE
Copyright (C) 2013  Infobyte LLC (http://www.infobytesec.com/)
See the file 'doc/LICENSE' for the license information

'''

import os
import model.api
import threading
import time
import traceback
import re

try:
    import xml.etree.cElementTree as ET

except ImportError:
    print "cElementTree could not be imported. Using ElementTree instead"
    import xml.etree.ElementTree as ET
from apis.rest.client import PluginControllerAPIClient

from config.configuration import getInstanceConfiguration
CONF = getInstanceConfiguration()


class ReportProcessor():
    def __init__(self):
        host = CONF.getApiConInfoHost()
        port_rest = int(CONF.getApiRestfulConInfoPort())

        self.client = PluginControllerAPIClient(host, port_rest)

    def processReport(self, filename):
        """
        Process one Report
        """
        model.api.log("Report file is %s" % filename)

        parser = ReportParser(filename)
        if (parser.report_type is not None):
            model.api.log(
                "The file is %s, %s" % (filename, parser.report_type))

            command_string = "./%s %s" % (parser.report_type.lower(),
                                          filename)
            model.api.log("Executing %s" % (command_string))

            new_cmd, output_file = self.client.send_cmd(command_string)
            self.client.send_output(command_string, filename)
            return True
        return False

    def onlinePlugin(self, cmd):
        new_cmd, output_file = self.client.send_cmd(cmd)
        self.client.send_output(cmd)


class ReportManager(threading.Thread):
    def __init__(self, timer, ws_name):
        threading.Thread.__init__(self)
        self.setDaemon(True)
        self.timer = timer
        self._stop = False
        self._report_path = os.path.join(CONF.getReportPath(), ws_name)
        self._report_ppath = os.path.join(self._report_path, "process")
        self.processor = ReportProcessor()

        if not os.path.exists(self._report_path):
            os.mkdir(self._report_path)

        if not os.path.exists(self._report_ppath):
            os.mkdir(self._report_ppath)

    def run(self):
        tmp_timer = 0
        while not self._stop:

            time.sleep(1)
            tmp_timer += 1
            if tmp_timer == self.timer:
                try:
                    self.syncReports()
                except Exception:
                    model.api.log("An exception was captured while saving reports\n%s" % traceback.format_exc())
                finally:
                    tmp_timer = 0

    def stop(self):
        self._stop = True

    def syncReports(self):
        """
        Synchronize report directory using the DataManager and Plugins online
        We first make sure that all shared reports were added to the repo
        """

        for root, dirs, files in os.walk(self._report_path, False):

            if root == self._report_path:
                for name in files:
                    filename = os.path.join(root, name)

                    self.processor.processReport(filename)

                    os.rename(filename, os.path.join(self._report_ppath, name))

        self.onlinePlugins()

    def onlinePlugins(self):
        """
        Process online plugins
        """
        pluginsOn = {"MetasploitOn": "./metasploiton online"}
        pluginsOn.update({"Beef": "./beef online"})
        psettings = CONF.getPluginSettings()

        for name, cmd in pluginsOn.iteritems():
            if name in psettings:
                if psettings[name]['settings']['Enable'] == "1":
                    self.processor.onlinePlugin(cmd)


class ReportParser(object):

    """
    Class that handles reports files.

    :param filepath: report file.

    :class:`.LoadReport`
    """

    def __init__(self, report_path):
        self.report_type = ""
        root_tag, output = self.getRootTag(report_path)

        if root_tag:
            self.report_type = self.rType(root_tag, output)

    def open_file(self, file_path):
        """
        This method uses file signatures to recognize file types

        :param file_path: report file.
        """

        """
        If you need add support to a new report type
        add the file signature here
        and add the code in self.getRootTag() for get the root tag.
        """

        f = result = None

        signatures = {
         "\x50\x4B" : "zip" ,
         "\x3C\x3F\x78\x6D\x6C" : "xml"
        }

        try:
            f = open(file_path, 'rb')
            file_signature = f.read(10)
            f.seek(0)

            for key in signatures:
                if file_signature.find(key) == 0:

                    result = signatures[key]
                    model.api.log("Report type detected: %s" %result)
                    break

        except IOError, err:
            self.report_type = None
            model.api.log("Error while opening file.\n%s. %s" % (err, file_path))

        return f, result

    def getRootTag(self, file_path):

        report_type = result = f = None

        f, report_type = self.open_file(file_path)

        #Check error in open_file()
        if f == None and report_type == None:
            self.report_type = None
            return None, None

        #Find root tag based in report_type
        if report_type == "zip":
            result = "maltego"

        else:

            try:
                for event, elem in ET.iterparse(f, ('start', )):
                    result = elem.tag
                    break

            except SyntaxError, err:
                self.report_type = None
                model.api.log("Not an xml file.\n %s" % (err))

        f.seek(0)
        output = f.read()
        if f: f.close()

        return result, output

    def rType(self, tag, output):
        """Compares report root tag with known root tags.

        :param root_tag
        :rtype
        """
        if "nmaprun" == tag:
            return "nmap"
        elif "w3af-run" == tag:
            return "w3af"
        elif "NessusClientData_v2" == tag:
            return "nessus"
        elif "report" == tag:
            if re.search("https://raw.githubusercontent.com/Arachni/arachni/", output) != None:
                return "arachni_faraday"
            elif re.search("OpenVAS", output) != None or re.search('<omp><version>', output) != None:
                return "openvas"
            else:
                return "zap"
        elif "niktoscan" == tag:
            return "nikto"
        elif "MetasploitV4" == tag:
            return "metasploit"
        elif "MetasploitV5" == tag:
            return "metasploit"
        elif "issues" == tag:
            return "burp"
        elif "OWASPZAPReport" == tag:
            return "zap"
        elif "ScanGroup" == tag:
            return "acunetix"
        elif "session" == tag:
            return "x1"
        elif "landscapePolicy" == tag:
            return "x1"
        elif "entities" == tag:
            return "impact"
        elif "NeXposeSimpleXML" == tag:
            return "nexpose"
        elif "NexposeReport" == tag:
            return "nexpose-full"
        elif "ASSET_DATA_REPORT" == tag:
            return "qualysguard"
        elif "scanJob" == tag:
            return "retina"
        elif "netsparker" == tag:
            return "netsparker"
        elif "maltego" == tag:
            return "maltego_faraday"
        else:
            return None