Ejemplo n.º 1
0
def process_command(command, plugin_id, custom_plugins_folder, dont_run,
                    summary, output_file, show_output, ignore_info):
    plugins_manager = PluginsManager(custom_plugins_folder,
                                     ignore_info=ignore_info)
    analyzer = CommandAnalyzer(plugins_manager)
    if plugin_id:
        plugin = plugins_manager.get_plugin(plugin_id)
        if not plugin:
            click.echo(click.style(f"Invalid Plugin: {plugin_id}", fg="red"),
                       err=True)
            return
    else:
        plugin = analyzer.get_plugin(command)
        if not plugin:
            click.echo(click.style(f"Failed to detect command: {command}",
                                   fg="red"),
                       err=True)
            return
    current_path = os.path.abspath(os.getcwd())
    modified_command = plugin.processCommandString(getpass.getuser(),
                                                   current_path, command)
    if modified_command:
        command = modified_command
    if dont_run:
        color_message = click.style("Command: ", fg="green")
        click.echo(f"{color_message} {command}")
    else:
        p = subprocess.Popen(shlex.split(command),
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        output = io.StringIO()
        while True:
            retcode = p.poll()
            line = p.stdout.readline().decode('utf-8')
            if show_output:
                sys.stdout.write(line)
            output.write(line)
            if retcode is not None:
                extra_lines = map(lambda x: x.decode('utf-8'),
                                  p.stdout.readlines())
                if show_output:
                    sys.stdout.writelines(line)
                output.writelines(extra_lines)
                break
        output_value = output.getvalue()
        if retcode == 0:
            plugin.processOutput(output_value)
            if summary:
                click.echo(json.dumps(plugin.get_summary(), indent=4))
            else:
                if output_file:
                    with open(output_file, "w") as f:
                        json.dump(plugin.get_data(), f)
                else:
                    click.echo(json.dumps(plugin.get_data(), indent=4))
        else:
            click.echo(click.style("Command execution error!!", fg="red"),
                       err=True)
Ejemplo n.º 2
0
def detect_command(command, custom_plugins_folder):
    plugins_manager = PluginsManager(custom_plugins_folder)
    analyzer = CommandAnalyzer(plugins_manager)
    plugin = analyzer.get_plugin(command)
    if plugin:
        click.echo(click.style(f"Faraday Plugin: {plugin.id}", fg="cyan"))
    else:
        click.echo(click.style(f"Failed to detect command: {command}",
                               fg="red"),
                   err=True)
Ejemplo n.º 3
0
 def __init__(self, plugin_repo_path, pending_actions=None):
     self._controllers = {}
     self._plugin_modules = {}
     self._plugin_instances = {}
     self._plugin_settings = {}
     self.pending_actions = pending_actions
     self._plugins_manager = PluginsManager(CONF.getCustomPluginsPath())
     self.commands_analyzer = CommandAnalyzer(self._plugins_manager)
     self.report_analyzer = ReportAnalyzer(self._plugins_manager)
     self._loadSettings()
Ejemplo n.º 4
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self.TABLE_PRETTY_FORMAT = "psql"
     # hide unwanted settings
     settings_to_hide = ["debug"]
     for setting_name in settings_to_hide:
         self.remove_settable(setting_name)
     f = Figlet(font="slant")
     intro = []
     intro.append(style(f.renderText("Faraday Cli"), fg="red"))
     if active_config.faraday_url and active_config.token:
         intro.append(
             style(f"Server: {active_config.faraday_url}", fg="green"))
         self.api_client = FaradayApi(
             active_config.faraday_url,
             ignore_ssl=active_config.ignore_ssl,
             token=active_config.token,
         )
     else:
         self.api_client = FaradayApi()
         intro.append(
             style(f"Missing faraday server, run 'auth'", fg="yellow"))
     self.intro = "\n".join(intro)
     self.data_queue = queue.Queue()
     self.custom_plugins_path = None
     self.update_prompt()
     self.add_settable(
         Settable(
             "custom_plugins_path",
             str,
             "Path of custom plugins",
             onchange_cb=self._onchange_custom_plugins_path,
         ))
     self.plugins_manager = PluginsManager(
         active_config.custom_plugins_folder)
     self.report_analyzer = ReportAnalyzer(self.plugins_manager)
     self.command_analyzer = CommandAnalyzer(self.plugins_manager)
Ejemplo n.º 5
0
import json
import pytest
from faraday_plugins.plugins.manager import PluginsManager, CommandAnalyzer
from faraday_plugins.plugins.plugin import PluginBase

plugins_manager = PluginsManager()
analyzer = CommandAnalyzer(plugins_manager)

COMMANDS_FILE = './tests/commands.json'


def list_commands():
    with open(COMMANDS_FILE) as f:
        commands_dict = json.load(f)
    for command_data in commands_dict["commands"]:
        yield command_data


@pytest.mark.parametrize("command_data", list_commands())
def test_autodetected_on_commands(command_data):
    plugin_id = command_data["plugin_id"]
    command_string = command_data["command"]
    command_result = command_data["command_result"]

    plugin: PluginBase = analyzer.get_plugin(command_string)
    assert plugin, command_string
    assert plugin.id.lower() == plugin_id.lower()
    assert plugin.command.lower() == command_result.lower()
Ejemplo n.º 6
0
class FaradayShell(Cmd):
    prompt = "Faraday> "
    intro = "Welcome to Faraday Cli! Type ? to list commands"
    doc_header = "Available Commands (type help <command>)"
    ruler = "-"

    emojis = {
        "cross": "\U0000274c",
        "check": "\U00002714",
        "arrow_up": "\U00002b06",
        "page": "\U0001f4c4",
        "laptop": "\U0001f4bb",
    }
    delattr(Cmd, "do_run_pyscript")
    delattr(Cmd, "do_run_script")
    delattr(Cmd, "do_edit")
    delattr(Cmd, "do_shell")
    delattr(Cmd, "do_shortcuts")
    delattr(Cmd, "do_py")

    def __init__(self, *args, **kwargs):
        super().__init__(persistent_history_file="~/.faraday-cli_history",
                         *args,
                         **kwargs)
        self.hidden_commands += ["EOF", "cd", "alias", "macro"]
        self.shell_mode = False
        self.TABLE_PRETTY_FORMAT = "psql"
        # hide unwanted settings
        settings_to_hide = ["debug"]
        for setting_name in settings_to_hide:
            self.remove_settable(setting_name)
        intro = [style(f"{logo}\nv:{__version__}", fg=COLORS.CYAN)]
        (
            self.update_available,
            self.latest_version,
        ) = self.check_update_available()
        if self.update_available:
            intro.append(
                style(
                    f"A new version of faraday-cli ({self.latest_version}) is available!!",
                    fg=COLORS.RED,
                ))
        if active_config.faraday_url and active_config.token:
            intro.append(
                style(f"Server: {active_config.faraday_url}", fg=COLORS.GREEN))
            self.api_client = FaradayApi(
                active_config.faraday_url,
                ignore_ssl=active_config.ignore_ssl,
                token=active_config.token,
            )
        else:
            self.api_client = FaradayApi()
            intro.append(
                style("Missing faraday server, run 'auth'", fg=COLORS.YELLOW))
        self.custom_plugins_path = active_config.custom_plugins_path
        self.ignore_info_severity = active_config.ignore_info_severity
        self.auto_command_detection = active_config.auto_command_detection
        self.intro = "\n".join(intro)
        self.data_queue = queue.Queue()
        self.update_prompt()
        self.add_settable(
            Settable(
                "custom_plugins_path",
                str,
                "Path of custom plugins folder",
                onchange_cb=self._onchange_custom_plugins_path,
                settable_object=self,
            ))
        self.add_settable(
            Settable(
                "ignore_info_severity",
                bool,
                "Ignore Informational vulnerabilities "
                "from reports and commands",
                onchange_cb=self._onchange_ignore_info_severity,
                settable_object=self,
            ))
        self.add_settable(
            Settable(
                "auto_command_detection",
                bool,
                "Enable/disable automatic command detection",
                onchange_cb=self._onchange_auto_command_detection,
                settable_object=self,
            ))
        self._create_plugin_manager()

    @staticmethod
    def check_update_available():
        try:
            latest_version = luddite.get_version_pypi("faraday-cli")
            update_available = version.parse(latest_version) > version.parse(
                __version__)
            return update_available, latest_version
        except:  # noqa: E722
            return False, __version__

    def do_version(self, _):
        """Faraday cli version"""
        self.poutput(f"faraday-cli: {__version__}")

    def _create_plugin_manager(self):
        self.plugins_manager = PluginsManager(
            self.custom_plugins_path, ignore_info=self.ignore_info_severity)
        self.report_analyzer = ReportAnalyzer(self.plugins_manager)
        self.command_analyzer = CommandAnalyzer(self.plugins_manager)

    def _onchange_custom_plugins_path(self, param_name, old, new):
        custom_plugins_path = Path(new)
        if custom_plugins_path.is_dir():
            active_config.custom_plugins_path = new
            active_config.save()
            self.custom_plugins_path = new
            self._create_plugin_manager()
        else:
            self.perror(f"Invalid Path: {new}")
            self.custom_plugins_path = old

    def _onchange_ignore_info_severity(self, param_name, old, new):
        active_config.ignore_info_severity = new
        active_config.save()
        self.ignore_info_severity = new
        self._create_plugin_manager()

    def _onchange_auto_command_detection(self, param_name, old, new):
        active_config.auto_command_detection = new
        active_config.save()
        self.auto_command_detection = new

    # Auth
    auth_parser = argparse.ArgumentParser()
    auth_parser.add_argument(
        "-f",
        "--faraday-url",
        type=str,
        help="Faraday server URL",
        required=False,
    )
    auth_parser.add_argument(
        "-i",
        "--ignore-ssl",
        action="store_true",
        help="Ignore SSL verification",
    )
    auth_parser.add_argument("-u",
                             "--user",
                             type=str,
                             help="Faraday user",
                             required=False)
    auth_parser.add_argument("-p",
                             "--password",
                             type=str,
                             help="Faraday password",
                             required=False)

    @with_argparser(auth_parser)
    def do_auth(self, args):
        """Authenticate with a faraday server"""
        faraday_url = args.faraday_url
        user = args.user
        password = args.password
        ignore_ssl = args.ignore_ssl
        if not faraday_url:
            faraday_url = utils.validate_url(
                click.prompt("\nFaraday url",
                             default=active_config.faraday_url))
            url_data = urlparse(faraday_url)
            if url_data.scheme == "https":
                ignore_ssl = (click.prompt(
                    f"\nValidate SSL certificate for [{faraday_url}]",
                    type=click.Choice(choices=["Y", "N"],
                                      case_sensitive=False),
                    default="Y",
                ) == "N")
        else:
            faraday_url = utils.validate_url(faraday_url)
        if not user:
            user = click.prompt("\nUser")
        if not password:
            password = click.prompt("\nPassword", hide_input=True)
        try:
            api_client = FaradayApi(faraday_url, ignore_ssl=ignore_ssl)
            login_ok = api_client.login(user, password)
            if login_ok is None or login_ok is True:
                if login_ok is None:
                    # 2FA Required
                    second_factor = click.prompt("2FA")
                else:
                    second_factor = None
                token = api_client.get_token(second_factor)
                active_config.faraday_url = faraday_url
                active_config.ignore_ssl = args.ignore_ssl
                active_config.token = token
                active_config.workspace = None
                active_config.save()
                self.api_client = FaradayApi(faraday_url,
                                             ignore_ssl=ignore_ssl,
                                             token=token)
                self.poutput(style("Saving config", fg=COLORS.GREEN))
                self.poutput(
                    style(
                        f"{self.emojis['check']} Authenticated with faraday: {faraday_url}",  # noqa: E501
                        fg=COLORS.GREEN,
                    ))
                self.update_prompt()
            else:
                self.perror("Invalid credentials")
                if self.shell_mode:
                    sys.exit(1)
        except Invalid2FA:
            self.perror("Invalid 2FA")
            if self.shell_mode:
                sys.exit(1)
        except InvalidCredentials:
            self.perror("Invalid credentials")
            if self.shell_mode:
                sys.exit(1)
        except ClientError:
            self.perror("Invalid credentials")
            if self.shell_mode:
                sys.exit(1)
        except ClientConnectionError as e:
            self.perror(f"Connection refused: {e} (check your Faraday server)")
            if self.shell_mode:
                sys.exit(1)
        except Exception as e:
            self.perror(f"{e}")
            if self.shell_mode:
                sys.exit(1)

    def do_exit(self, inp):
        """Exit shell"""
        self.poutput(style("Bye", fg=COLORS.GREEN))
        return True

    def help_exit(self):
        print("exit the application. Shorthand: Ctrl-D.")

    def postcmd(self, stop, line):
        def send_to_faraday(ws, data):
            spinner = Halo(text="Sending", text_color="green", spinner="dots")
            spinner.start()
            try:
                self.api_client.bulk_create(ws, data)
            except Exception as e:
                spinner.stop()
                return str(e)
            else:
                spinner.stop()
                return None

        while not self.data_queue.empty():
            data = self.data_queue.get()
            message = f"{self.emojis['arrow_up']} Sending data to workspace: {data['workspace']}"  # noqa: E501
            self.poutput(style(message, fg=COLORS.GREEN))
            upload_error = send_to_faraday(data["workspace"],
                                           data["json_data"])
            if upload_error is not None:
                self.poutput(
                    style(
                        f"\n{self.emojis['cross']} {upload_error}",
                        fg=COLORS.RED,
                    ))
            else:
                self.poutput(
                    style(f"\n{self.emojis['check']} Done", fg=COLORS.GREEN))
        return Cmd.postcmd(self, stop, line)

    def update_prompt(self) -> None:
        self.prompt = self.get_prompt()

    @staticmethod
    def get_prompt() -> str:
        if active_config.workspace:
            return style(f"[ws:{active_config.workspace}]> ", fg=COLORS.BLUE)
        else:
            return style("Faraday> ", fg=COLORS.BLUE)

    # Status
    status_parser = argparse.ArgumentParser()
    status_parser.add_argument("-p",
                               "--pretty",
                               action="store_true",
                               help="Pretty Tables")

    def print_output(self, message):
        if os.getenv("KAKER_MODE", "0") == "1":
            Sneakers(message).decrypt()
        else:
            self.poutput(message)

    @with_argparser(status_parser)
    def do_status(self, args):
        """Show Cli status"""
        valid_token = self.api_client.is_token_valid()
        user = None
        version = "-"
        if valid_token:
            version_data = self.api_client.get_version()
            version = f"{version_data['product']}-{version_data['version']}"
            user = self.api_client.get_user()
        data = [{
            "FARADAY SERVER":
            active_config.faraday_url,
            "IGNORE SSL":
            active_config.ignore_ssl,
            "SERVER VERSION":
            version,
            "USER":
            user if user else "-",
            "VALID TOKEN":
            "\U00002714" if valid_token else "\U0000274c",
            "WORKSPACE":
            active_config.workspace,
            "CLI VERSION":
            __version__ if not self.update_available else style(
                f"{__version__} (latest: {self.latest_version})",
                fg=COLORS.RED,
            ),
        }]
        self.print_output(
            style(
                tabulate(
                    data,
                    headers="keys",
                    tablefmt=self.TABLE_PRETTY_FORMAT
                    if args.pretty else "simple",
                ),
                fg=COLORS.GREEN,
            ))

    # Workspace
    workspace_parser = argparse.ArgumentParser()
    workspace_subparsers = workspace_parser.add_subparsers(title="action")

    @with_argparser(workspace_parser)
    @with_category("Objects")
    def do_workspace(self, ns: argparse.Namespace):
        """workspace actions"""
        handler = ns.cmd2_handler.get()
        if handler is not None:
            handler(ns)
        else:
            self.do_help("workspace")

    # Host
    host_parser = argparse.ArgumentParser()
    host_subparsers = host_parser.add_subparsers(title="action")

    @with_argparser(host_parser)
    @with_category("Objects")
    def do_host(self, ns: argparse.Namespace):
        """host actions"""
        handler = ns.cmd2_handler.get()
        if handler is not None:
            handler(ns)
        else:
            self.do_help("host")

    # Service
    service_parser = argparse.ArgumentParser()
    service_subparsers = service_parser.add_subparsers(title="action")

    @with_argparser(service_parser)
    @with_category("Objects")
    def do_service(self, ns: argparse.Namespace):
        """service actions"""
        handler = ns.cmd2_handler.get()
        if handler is not None:
            handler(ns)
        else:
            self.do_help("service")

    # Vulnerability
    vulnerability_parser = argparse.ArgumentParser()
    vulnerability_subparsers = vulnerability_parser.add_subparsers(
        title="action")

    @with_argparser(vulnerability_parser)
    @with_category("Objects")
    def do_vuln(self, ns: argparse.Namespace):
        """vulnerabilities actions"""
        handler = ns.cmd2_handler.get()
        if handler is not None:
            handler(ns)
        else:
            self.do_help("vuln")

    # Agent
    agent_parser = argparse.ArgumentParser()
    agent_subparsers = agent_parser.add_subparsers(title="action")

    @with_argparser(agent_parser)
    @with_category("Objects")
    def do_agent(self, ns: argparse.Namespace):
        """agent actions"""
        handler = ns.cmd2_handler.get()
        if handler is not None:
            handler(ns)
        else:
            self.do_help("agent")

    # Executive Report
    executive_report_parser = argparse.ArgumentParser()
    executive_report_subparsers = executive_report_parser.add_subparsers(
        title="action")

    @with_argparser(executive_report_parser)
    @with_category("Objects")
    def do_executive_report(self, ns: argparse.Namespace):
        """executive_report actions"""
        handler = ns.cmd2_handler.get()
        if handler is not None:
            handler(ns)
        else:
            self.do_help("executive_report")

    # Tool
    tool_report_parser = argparse.ArgumentParser()
    tool_report_subparsers = tool_report_parser.add_subparsers(title="action")

    @with_argparser(tool_report_parser)
    @with_category("Objects")
    def do_tool(self, ns: argparse.Namespace):
        """tool actions"""
        handler = ns.cmd2_handler.get()
        if handler is not None:
            handler(ns)
        else:
            self.do_help("tool")

    # Run Command
    def run_command(self, plugin, user, command):
        current_path = os.path.abspath(os.getcwd())
        modified_command = plugin.processCommandString(getpass.getuser(),
                                                       current_path, command)
        if modified_command:
            command = modified_command
        p = subprocess.Popen(
            shlex.split(command),
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )
        output = io.StringIO()
        while True:
            retcode = p.poll()
            line = p.stdout.readline().decode("utf-8")
            sys.stdout.write(line)
            output.write(line)
            if retcode is not None:
                extra_lines = map(lambda x: x.decode("utf-8"),
                                  p.stdout.readlines())
                sys.stdout.writelines(line)
                output.writelines(extra_lines)
                break
        output_value = output.getvalue()
        if retcode == 0:
            plugin.processOutput(output_value)
            return plugin.get_data()
        else:
            return None

    def do_cd(self, path):
        """Change directory"""
        if not path:
            path = "~"
        cd_path = os.path.expanduser(path)
        if os.path.isdir(cd_path):
            os.chdir(cd_path)
        else:
            self.poutput(f"cd: no such file or directory: {path}")

    def default(self, statement: Statement):
        if self.shell_mode and not self.auto_command_detection:
            os.system(statement.raw)
        else:
            plugin = self.command_analyzer.get_plugin(statement.raw)
            if plugin:
                if not active_config.workspace:
                    self.perror("No active Workspace")
                    os.system(statement.raw)
                else:
                    click.secho(
                        f"{self.emojis['laptop']} Processing {plugin.id} command",
                        fg="green",
                    )
                    command_json = utils.run_tool(plugin, getpass.getuser(),
                                                  statement.raw)
                    if not command_json:
                        click.secho(
                            f"{self.emojis['cross']} Command execution error!!",
                            fg="red",
                        )
                    else:
                        self.data_queue.put({
                            "workspace": active_config.workspace,
                            "json_data": command_json,
                        })
            else:
                os.system(statement.raw)
Ejemplo n.º 7
0
 def _create_plugin_manager(self):
     self.plugins_manager = PluginsManager(
         self.custom_plugins_path, ignore_info=self.ignore_info_severity)
     self.report_analyzer = ReportAnalyzer(self.plugins_manager)
     self.command_analyzer = CommandAnalyzer(self.plugins_manager)
Ejemplo n.º 8
0
class FaradayShell(Cmd):
    __hiden_methods = ("do_EOF", "do_cd")
    prompt = "Faraday> "
    intro = "Welcome to Faraday Cli! Type ? to list commands"
    doc_header = "Available Commands (type help <command>)"
    ruler = "-"

    emojis = {
        "cross": "\U0000274c",
        "check": "\U00002714",
        "arrow_up": "\U00002b06",
        "page": "\U0001f4c4",
        "laptop": "\U0001f4bb",
    }
    delattr(Cmd, "do_run_pyscript")
    delattr(Cmd, "do_run_script")

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.TABLE_PRETTY_FORMAT = "psql"
        # hide unwanted settings
        settings_to_hide = ["debug"]
        for setting_name in settings_to_hide:
            self.remove_settable(setting_name)
        f = Figlet(font="slant")
        intro = []
        intro.append(style(f.renderText("Faraday Cli"), fg="red"))
        if active_config.faraday_url and active_config.token:
            intro.append(
                style(f"Server: {active_config.faraday_url}", fg="green"))
            self.api_client = FaradayApi(
                active_config.faraday_url,
                ignore_ssl=active_config.ignore_ssl,
                token=active_config.token,
            )
        else:
            self.api_client = FaradayApi()
            intro.append(
                style(f"Missing faraday server, run 'auth'", fg="yellow"))
        self.intro = "\n".join(intro)
        self.data_queue = queue.Queue()
        self.custom_plugins_path = None
        self.update_prompt()
        self.add_settable(
            Settable(
                "custom_plugins_path",
                str,
                "Path of custom plugins",
                onchange_cb=self._onchange_custom_plugins_path,
            ))
        self.plugins_manager = PluginsManager(
            active_config.custom_plugins_folder)
        self.report_analyzer = ReportAnalyzer(self.plugins_manager)
        self.command_analyzer = CommandAnalyzer(self.plugins_manager)

    def _onchange_custom_plugins_path(self, param_name, old, new):
        custom_plugins_path = Path(new)
        if custom_plugins_path.is_dir():
            active_config.custom_plugins_path = new
            active_config.save()

    auth_parser = argparse.ArgumentParser()
    auth_parser.add_argument(
        "-f",
        "--faraday-url",
        type=str,
        help="Faraday server URL",
        required=False,
    )
    auth_parser.add_argument(
        "-i",
        "--ignore-ssl",
        action="store_true",
        help="Ignore SSL verification",
    )
    auth_parser.add_argument("-u",
                             "--user",
                             type=str,
                             help="Faraday user",
                             required=False)
    auth_parser.add_argument("-p",
                             "--password",
                             type=str,
                             help="Faraday password",
                             required=False)

    @with_argparser(auth_parser)
    def do_auth(self, args):
        """Authenticate with a faraday server"""
        faraday_url = args.faraday_url
        user = args.user
        password = args.password
        ignore_ssl = args.ignore_ssl
        if not faraday_url:
            faraday_url = utils.validate_url(
                click.prompt("Faraday url", default=active_config.faraday_url))
            url_data = urlparse(faraday_url)
            if url_data.scheme == "https":
                ignore_ssl = (click.prompt(
                    f"Validate SSL certificate for [{faraday_url}]",
                    type=click.Choice(choices=["Y", "N"],
                                      case_sensitive=False),
                    default="Y",
                ) == "N")
        else:
            faraday_url = utils.validate_url(faraday_url)
        if not user:
            user = click.prompt("User")
        if not password:
            password = click.prompt("Password", hide_input=True)
        try:
            api_client = FaradayApi(faraday_url, ignore_ssl=ignore_ssl)
            login_ok = api_client.login(user, password)
            if login_ok is None or login_ok is True:
                if login_ok is None:
                    # 2FA Required
                    second_factor = click.prompt("2FA")
                else:
                    second_factor = None
                token = api_client.get_token(user, password, second_factor)
                active_config.faraday_url = faraday_url
                active_config.ignore_ssl = args.ignore_ssl
                active_config.token = token
                active_config.workspace = None
                active_config.save()
                self.api_client = FaradayApi(faraday_url,
                                             ignore_ssl=ignore_ssl,
                                             token=token)
                self.poutput(style("Saving config", fg="green"))
                self.poutput(
                    style(
                        f"{self.emojis['check']} Authenticated with faraday: {faraday_url}",
                        fg="green",
                    ))
                self.update_prompt()
            else:
                self.perror("Invalid credentials")
        except Invalid2FA:
            self.perror("Invalid 2FA")
        except InvalidCredentials:
            self.perror("Invalid credentials")
        except ClientError:
            self.perror("Invalid credentials")
        except ClientConnectionError as e:
            self.perror(f"Connection refused: {e}")
        except Exception as e:
            self.perror(f"{e}")

    def do_exit(self, inp):
        """Exit shell"""
        self.poutput(style("Bye", fg="green"))
        return True

    def help_exit(self):
        print("exit the application. Shorthand: Ctrl-D.")

    def postcmd(self, stop, line):
        @Halo(text="Sending", text_color="green", spinner="dots")
        def send_to_faraday(ws, data):
            self.api_client.bulk_create(ws, data)

        while not self.data_queue.empty():
            data = self.data_queue.get()
            message = f"{self.emojis['arrow_up']} Sending data to workspace: {data['workspace']}"
            self.poutput(style(message, fg="green"))
            send_to_faraday(data["workspace"], data["json_data"])
            self.poutput(style(f"{self.emojis['check']} Done", fg="green"))

        return Cmd.postcmd(self, stop, line)

    def update_prompt(self) -> None:
        self.prompt = self.get_prompt()

    def get_prompt(self) -> str:
        if active_config.workspace:
            return style(f"[ws:{active_config.workspace}]> ", fg="blue")
        else:
            return style("Faraday> ", fg="blue")

    status_parser = argparse.ArgumentParser()
    status_parser.add_argument("-p",
                               "--pretty",
                               action="store_true",
                               help="Pretty Tables")

    @with_argparser(status_parser)
    def do_status(self, args):
        """Show Cli status"""
        valid_token = self.api_client.is_token_valid()
        version = "-"
        if valid_token:
            version_data = self.api_client.get_version()
            version = f"{version_data['product']}-{version_data['version']}"
        data = [{
            "FARADAY SERVER": active_config.faraday_url,
            "IGNORE SSL": active_config.ignore_ssl,
            "VERSION": version,
            "VALID TOKEN": "\U00002714" if valid_token else "\U0000274c",
            "WORKSPACE": active_config.workspace,
        }]
        self.poutput(
            style(
                tabulate(
                    data,
                    headers="keys",
                    tablefmt=self.TABLE_PRETTY_FORMAT
                    if args.pretty else "simple",
                ),
                fg="green",
            ))

    def run_command(self, plugin, user, command):
        current_path = os.path.abspath(os.getcwd())
        modified_command = plugin.processCommandString(getpass.getuser(),
                                                       current_path, command)
        if modified_command:
            command = modified_command
        p = subprocess.Popen(
            shlex.split(command),
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )
        output = io.StringIO()
        while True:
            retcode = p.poll()
            line = p.stdout.readline().decode("utf-8")
            sys.stdout.write(line)
            output.write(line)
            if retcode is not None:
                extra_lines = map(lambda x: x.decode("utf-8"),
                                  p.stdout.readlines())
                sys.stdout.writelines(line)
                output.writelines(extra_lines)
                break
        output_value = output.getvalue()
        if retcode == 0:
            plugin.processOutput(output_value)
            return plugin.get_data()
        else:
            return None

    def do_cd(self, path):
        """Change directory"""
        if not path:
            path = "~"
        cd_path = os.path.expanduser(path)
        if os.path.isdir(cd_path):
            os.chdir(cd_path)
        else:
            self.poutput(f"cd: no such file or directory: {path}")

    def default(self, statement: Statement):
        if not active_config.workspace:
            os.system(statement.raw)
        else:
            plugin = self.command_analyzer.get_plugin(statement.raw)
            if plugin:
                click.secho(
                    f"{self.emojis['laptop']} Processing {plugin.id} command",
                    fg="green",
                )
                command_json = self.run_command(plugin, getpass.getuser(),
                                                statement.raw)
                if not command_json:
                    click.secho(
                        f"{self.emojis['cross']} Command execution error!!",
                        fg="red",
                    )
                else:
                    self.data_queue.put({
                        "workspace": active_config.workspace,
                        "json_data": command_json,
                    })
            else:
                os.system(statement.raw)
Ejemplo n.º 9
0
def command(api_client, custom_plugins_folder, plugin_id, workspace,
            hide_output, command):

    if workspace:
        if not api_client.is_workspace_valid(workspace):
            click.secho(f"Invalid workspace: {workspace}", fg="red")
            return
        else:
            destination_workspace = workspace
    else:
        if not active_config.workspace:
            click.secho(f"Missing default workspace", fg="red")
            return
        else:
            destination_workspace = active_config.workspace
    plugins_manager = PluginsManager(custom_plugins_folder)
    analyzer = CommandAnalyzer(plugins_manager)
    if plugin_id:
        plugin = plugins_manager.get_plugin(plugin_id)
        if not plugin:
            click.echo(click.style(f"Invalid Plugin: {plugin_id}", fg="red"),
                       err=True)
            return
    else:
        plugin = analyzer.get_plugin(command)
        if not plugin:
            click.echo(click.style(f"Failed to detect command: {command}",
                                   fg="red"),
                       err=True)
            return
    current_path = os.path.abspath(os.getcwd())
    modified_command = plugin.processCommandString(getpass.getuser(),
                                                   current_path, command)
    if modified_command:
        command = modified_command
    p = subprocess.Popen(shlex.split(command),
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    output = io.StringIO()
    while True:
        retcode = p.poll()
        line = p.stdout.readline().decode('utf-8')
        if not hide_output:
            sys.stdout.write(line)
        output.write(line)
        if retcode is not None:
            extra_lines = map(lambda x: x.decode('utf-8'),
                              p.stdout.readlines())
            if not hide_output:
                sys.stdout.writelines(line)
            output.writelines(extra_lines)
            break
    output_value = output.getvalue()
    if retcode == 0:
        plugin.processOutput(output_value)
        click.secho(
            f"Sending data from command {command} to {destination_workspace}",
            fg="green")
        api_client.bulk_create(destination_workspace, plugin.get_data())
    else:
        click.secho("Command execution error!!", fg="red")