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)
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)
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()
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)
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()
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)
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)
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)
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")