diff --git a/faraday_client/gui/gtk/application.py b/faraday_client/gui/gtk/application.py index 4a8bb5d7..a58b63c9 100644 --- a/faraday_client/gui/gtk/application.py +++ b/faraday_client/gui/gtk/application.py @@ -20,16 +20,16 @@ try: import gi except ImportError as e: - print ("You are missing Gobject Instrospection. Please install " - "version 3.14 or above (recommended) or 3.12") + print("You are missing Gobject Instrospection. Please install " + "version 3.14 or above (recommended) or 3.12") sys.exit(1) try: gi.require_version('Gtk', '3.0') except ValueError: - print ("WARNING: You don't seem to have installed the recommended version" - " of GTK. You can still use the program, but we recommend you" - " check your install of GTK+3") + print("WARNING: You don't seem to have installed the recommended version" + " of GTK. You can still use the program, but we recommend you" + " check your install of GTK+3") try: gi.require_version('Vte', '2.91') @@ -41,8 +41,8 @@ # modules. this just checks for every dependence when starting the app from gi.repository import Gio, Gtk, GdkPixbuf, Vte, GLib, GObject, Gdk except ImportError as e: - print ("You are missing some of the required dependencies. " - "Check that you have GTK+3 and Vte installed.") + print("You are missing some of the required dependencies. " + "Check that you have GTK+3 and Vte installed.") sys.exit(1) import faraday_client.model.guiapi @@ -87,13 +87,12 @@ from faraday_client.plugins import fplugin_utils - CONF = getInstanceConfiguration() logger = logging.getLogger(__name__) -def checkSSL(uri): +def check_ssl(uri): """ This method checks SSL validation It only returns True if the certificate is valid @@ -201,8 +200,8 @@ def remove_workspace(self, button, ws_name): except Exception as ex: traceback_str = traceback.format_exc() faraday_client.model.api.log("An exception was captured while deleting " - "workspace %s\n%s" % (ws_name, traceback_str), - "ERROR") + "workspace %s\n%s" % (ws_name, traceback_str), + "ERROR") available_workspaces = self.serverIO.get_workspaces_names() if available_workspaces: @@ -311,7 +310,8 @@ def exit(*args, **kwargs): logger.info("Exit because there are no workspaces found, and user is not admin") GObject.idle_add(self.window.destroy) GObject.idle_add(self.on_quit) - error_message = "You don't have permissions or no workspace is available.\n"\ + + error_message = "You don't have permissions or no workspace is available.\n" \ "Please contact faraday admin to create a workspace or to assign permissions." dialog = errorDialog(self.window, error_message) dialog.connect("destroy", exit) @@ -377,7 +377,7 @@ def certs_ok(server_uri): with https but didn't pass the checkSSL test. """ if server_uri.startswith("https://"): - return checkSSL(server_uri) + return check_ssl(server_uri) else: return True @@ -391,7 +391,7 @@ def certs_ok(server_uri): "password are still valid.")) success = False elif server_url.startswith("https://"): - if not checkSSL(server_url): + if not check_ssl(server_url): errorDialog(self.window, "The SSL certificate validation has failed") success = False @@ -768,8 +768,8 @@ def do_startup(self): appmenu.insert_submenu(1, "Faraday Plugin", fmenu) - helpMenu = builder.get_object('Help') - self.set_menubar(helpMenu) + help_menu = builder.get_object('Help') + self.set_menubar(help_menu) def do_activate(self): """If there's no window, create one and present it (show it to user). @@ -792,14 +792,14 @@ def do_activate(self): self.window.set_icon(self.icon) self.window.present() - self.loghandler = GUIHandler() + self.log_handler = GUIHandler() if CONF.getDebugStatus(): - self.loghandler.setLevel(logging.DEBUG) + self.log_handler.setLevel(logging.DEBUG) else: - self.loghandler.setLevel(logging.INFO) + self.log_handler.setLevel(logging.INFO) faraday_client.model.guiapi.setMainApp(self) - add_handler(self.loghandler) - self.loghandler.registerGUIOutput(self.window) + add_handler(self.log_handler) + self.log_handler.registerGUIOutput(self.window) notifier = faraday_client.model.log.getNotifier() notifier.widget = self.window @@ -813,11 +813,17 @@ def do_activate(self): if not should_login: return - - if not is_authenticated(CONF.getServerURI(), CONF.getFaradaySessionCookies()): - loginDialog = ForceLoginDialog(self.window, - self.exit_faraday_without_confirm) - loginDialog.run(3, CONF.getServerURI(), self.window) + server_url = CONF.getServerURI() + need_login = False + if not server_url: + need_login = True + elif not is_authenticated(server_url, CONF.getFaradaySessionCookies()): + need_login = True + + if need_login: + login_dialog = ForceLoginDialog(self.window, + self.exit_faraday_without_confirm) + login_dialog.run(3, self.window) self.reload_workspaces() workspace_argument_set = self.open_workspace_from_args() @@ -829,9 +835,9 @@ def on_quit(self, action=None, param=None): def on_plugin_options(self, action, param): """Defines what happens when you press "Plugins" on the menu""" - pluginsOption_window = PluginOptionsDialog(self.plugin_manager, - self.window) - pluginsOption_window.show_all() + plugins_option_window = PluginOptionsDialog(self.plugin_manager, + self.window) + plugins_option_window.show_all() def on_faraday_plugin(self, action, param): """Defines what happens when you press "Faraday Plugin..." on the menu""" @@ -842,11 +848,10 @@ def on_faraday_plugin(self, action, param): else: name = "" - - pluginsOption_window = FaradayPluginsDialog(self.window.get_current_focused_terminal(), + plugin_option_window = FaradayPluginsDialog(self.window.get_current_focused_terminal(), name, self.window) - pluginsOption_window.show_all() + plugin_option_window.show_all() def on_new_button(self, action=None, params=None, title=None): """Defines what happens when you press the 'new' button on the toolbar @@ -969,7 +974,7 @@ def on_preferences(self, action=None, param=None): changes her Couch URL, the sidebar will reload reflecting the new workspaces available""" - #preference_window = PreferenceWindowDialog(self.reload_workspaces, + # preference_window = PreferenceWindowDialog(self.reload_workspaces, # self.connect_to_couch, # self.window) preference_window = AuthDialog(self.reload_workspaces, self.window) @@ -1011,5 +1016,4 @@ def type_faraday_plugin_command(self, action, param=None): fd = terminal.get_pty().get_fd() os.write(fd, command.encode()) - # I'm Py3 diff --git a/faraday_client/gui/gtk/dialogs.py b/faraday_client/gui/gtk/dialogs.py index abd2b9c9..f878bff7 100644 --- a/faraday_client/gui/gtk/dialogs.py +++ b/faraday_client/gui/gtk/dialogs.py @@ -11,15 +11,7 @@ from faraday_client.persistence.server.exceptions import Required2FAError from past.builtins import basestring - -import webbrowser -import gi # pylint: disable=import-error -import os -from faraday_client.start_client import FARADAY_CLIENT_BASE -gi.require_version('Gtk', '3.0') - from faraday_client.persistence.server.server import ResourceDoesNotExist -from gi.repository import Gtk, GdkPixbuf, Gdk # pylint: disable=import-error from faraday_client.config.configuration import getInstanceConfiguration from faraday_client.persistence.server.server import ( is_authenticated, @@ -33,10 +25,20 @@ from faraday_client.gui.gtk.compatibility import CompatibleScrolledWindow as GtkScrolledWindow from faraday_client.plugins import fplugin_utils -CONF = getInstanceConfiguration() +import webbrowser +import gi # pylint: disable=import-error +import os +from faraday_client.start_client import FARADAY_CLIENT_BASE + +from gi.repository import Gtk, GdkPixbuf, Gdk # pylint: disable=import-error + +gi.require_version('Gtk', '3.0') + +user_config = getInstanceConfiguration() logger = logging.getLogger(__name__) + class PreferenceWindowDialog(Gtk.Dialog): """Sets up a preference dialog with basically nothing more than a label, a text entry to input your Faraday server IP and a couple of buttons. @@ -63,7 +65,7 @@ def __init__(self, reload_ws_callback, connect_to_couch, parent): ip_label.set_text("Faraday Server IP or URL") main_box.pack_start(ip_label, True, False, 10) - couch_uri = CONF.getServerURI() + couch_uri = user_config.getServerURI() self.ip_entry = Gtk.Entry() text = couch_uri if couch_uri else "http://127.0.0.1:5050" self.ip_entry.set_text(text) @@ -72,9 +74,9 @@ def __init__(self, reload_ws_callback, connect_to_couch, parent): button_box = Gtk.Box(spacing=6) main_box.pack_end(button_box, False, True, 10) - OK_button = Gtk.Button.new_with_label("OK") - OK_button.connect("clicked", self.on_click_ok) - button_box.pack_start(OK_button, False, True, 10) + ok_button = Gtk.Button.new_with_label("OK") + ok_button.connect("clicked", self.on_click_ok) + button_box.pack_start(ok_button, False, True, 10) cancel_button = Gtk.Button.new_with_label("Cancel") self.connect("key_press_event", key_reactions) @@ -87,20 +89,19 @@ def on_click_ok(self, button=None): """Button is useless, only there because GTK likes it. Takes the repourl (Couch IP) from self.ip_entry and connect to it if possible. """ - repourl = self.ip_entry.get_text() + repo_url = self.ip_entry.get_text() - if not check_server_url(repourl): + if not check_server_url(repo_url): errorDialog(self, "Could not connect to Faraday Server.", - ("Are you sure it is running and that the URL is correct?")) + "Are you sure it is running and that the URL is correct?") return False - - credentials_ok = self.credentialsOK(repourl) - couch_connection_ok = self.connectCouchCallback(repourl, parent=self) + credentials_ok = self.credentials_ok(repo_url) + couch_connection_ok = self.connectCouchCallback(repo_url, parent=self) if credentials_ok and couch_connection_ok: self.destroy() - def credentialsOK(self, repourl): + def credentials_ok(self, repo_url): """Pops up a dialog (if necessary) to set up Faraday credentials. Dialog is a LoginDialog which emits a signal marked by 42 when the user clicks its button. The run method returns 42 @@ -108,12 +109,12 @@ def credentialsOK(self, repourl): It's a boolean function, return True if auth ok, False if not. Number 42 was chosen for obvious reasons :) """ - if is_authenticated(repourl, CONF.getFaradaySessionCookies()): + if is_authenticated(repo_url, user_config.getFaradaySessionCookies()): return True # if that didn't work... - loginDialog = LoginDialog(self) - return loginDialog.run(3, repourl, self) + login_dialog = LoginDialog(self) + return login_dialog.run(3, repo_url) def on_click_cancel(self, button=None): self.destroy() @@ -149,16 +150,16 @@ def on_click_cancel(self, button=None): class LoginDialog(Gtk.Dialog): """A simple login dialog with a user and password""" + def __init__(self, parent): Gtk.Dialog.__init__(self, + title=f"Faraday login", flags=Gtk.DialogFlags.MODAL, buttons=("OK", Gtk.ResponseType.OK, "Cancel", Gtk.ResponseType.CANCEL)) self.set_default_response(Gtk.ResponseType.OK) - self.set_keep_above(True) - self.set_transient_for(parent) content_area = self.get_content_area() @@ -168,76 +169,97 @@ def __init__(self, parent): instructions.set_max_width_chars(38) content_area.pack_start(instructions, True, True, 10) - userBox = Gtk.Box() + """ + Add extra Entry to let user add custom URL + If url is valid, we set it as text else let it be empty + """ + url_box = Gtk.Box() + url_label = Gtk.Label() + url_label.set_text("URL:") + self.url_entry = Gtk.Entry() + self.url_entry.set_width_chars(24) + self.url_entry.set_activates_default(True) + server_url = user_config.getServerURI() + if server_url: + self.url_entry.set_text(server_url) + else: + self.url_entry.set_text("http://localhost:5985") + url_box.pack_start(url_label, True, True, 3) + url_box.pack_start(self.url_entry, False, False, 5) + content_area.pack_start(url_box, True, True, 10) + + user_box = Gtk.Box() user_label = Gtk.Label() user_label.set_text("User:") self.user_entry = Gtk.Entry() self.user_entry.set_width_chars(24) self.user_entry.set_activates_default(True) - userBox.pack_start(user_label, True, True, 3) - userBox.pack_start(self.user_entry, False, False, 5) - content_area.pack_start(userBox, True, True, 10) + user_box.pack_start(user_label, True, True, 3) + user_box.pack_start(self.user_entry, False, False, 5) + content_area.pack_start(user_box, True, True, 10) - passwordBox = Gtk.Box() + passwd_box = Gtk.Box() password_label = Gtk.Label() password_label.set_text("Password:") self.password_entry = Gtk.Entry() self.password_entry.set_visibility(False) self.password_entry.set_width_chars(24) self.password_entry.set_activates_default(True) - passwordBox.pack_start(password_label, True, True, 3) - passwordBox.pack_start(self.password_entry, False, False, 5) - content_area.pack_start(passwordBox, True, True, 10) + passwd_box.pack_start(password_label, True, True, 3) + passwd_box.pack_start(self.password_entry, False, False, 5) + content_area.pack_start(passwd_box, True, True, 10) self.show_all() - def getUser(self): + def entry_get_url(self): + if self.url_entry.get_text() is not None: + res = self.url_entry.get_text() + else: + res = "" + return res + + def entry_get_user(self): if self.user_entry.get_text() is not None: res = self.user_entry.get_text() else: res = "" return res - def getPassword(self): + def entry_get_password(self): if self.password_entry.get_text() is not None: res = self.password_entry.get_text() else: res = "" return res - def run(self, attempts, url, parent): + def run(self, attempts, parent): for attempt in range(attempts): run = Gtk.Dialog.run(self) if run == Gtk.ResponseType.OK: - newUser = self.getUser() - newPass = self.getPassword() - session_cookie = login_user(url, newUser, newPass) + txt_user = self.entry_get_user() + txt_pass = self.entry_get_password() + txt_url = self.entry_get_url() + session_cookie = login_user(txt_url, txt_user, txt_pass) if not session_cookie: - if attempt != attempts-1: - errorDialog(self, ("Invalid credentials!. You " - "have " + - str(attempts-1-attempt) + - " attempt(s) left.")) + if attempt != attempts - 1: + errorDialog(self, f"Invalid credentials!. You have {attempts - 1 - attempt} attempt(s) left.") else: - - CONF.setDBUser(newUser) - CONF.setFaradaySessionCookies(session_cookie) - + user_config.setDBUser(txt_user) + user_config.setFaradaySessionCookies(session_cookie) user_info = get_user_info() try: if 'client' in user_info['userCtx']['roles']: - errorDialog(self, ("You can't login as a client. " - "You have " + - str(attempts - 1 - attempt) + - " attempt(s) left.")) + errorDialog(self, f"You can't login as a client. You have {attempts - 1 - attempt}\ + attempt(s) left.") continue except (TypeError, KeyError): pass self.destroy() - + user_config.setAPIUrl(txt_url) + user_config.saveConfig() return True if run in [Gtk.ResponseType.CANCEL, -4]: @@ -245,9 +267,7 @@ def run(self, attempts, url, parent): self.exit() return False else: - errorDialog(self, ("Invalid credentials after " + - str(attempts) + " tries. " + - "Check your credentials and try again.")) + errorDialog(self, f"Invalid credentials after {attempts} tries. Check your credentials and try again.") self.exit() return False @@ -257,11 +277,10 @@ def exit(self): class ForceLoginDialog(LoginDialog): """A simple login dialog with a user and password""" + def __init__(self, parent, exit_faraday_callback): LoginDialog.__init__(self, parent) - self.set_deletable(False) - self.exit_faraday = exit_faraday_callback def exit(self, button=None): @@ -270,11 +289,14 @@ def exit(self, button=None): class AuthDialog(Gtk.Dialog): - """Faraday auth dialog""" def __init__(self, reload_ws_callback, parent): - super().__init__(title=f"Faraday login for {CONF.getAPIUrl()}", flags=Gtk.DialogFlags.MODAL) + server_url = user_config.getAPIUrl() + if not server_url: + super().__init__(title=f"Faraday login", flags=Gtk.DialogFlags.MODAL) + else: + super().__init__(title=f"Faraday login for {server_url}", flags=Gtk.DialogFlags.MODAL) self.set_default_response(Gtk.ResponseType.OK) self.set_keep_above(True) self.set_transient_for(parent) @@ -289,36 +311,54 @@ def __init__(self, reload_ws_callback, parent): instructions.set_max_width_chars(38) content_area.pack_start(instructions, True, True, 10) - userBox = Gtk.Box() + """ + Add extra Entry to let user add custom URL + If url is valid, we set it as text else let it be empty + """ + url_box = Gtk.Box() + url_label = Gtk.Label() + url_label.set_text("URL:") + self.url_entry = Gtk.Entry() + self.url_entry.set_width_chars(24) + self.url_entry.set_activates_default(True) + if server_url: + self.url_entry.set_text(server_url) + else: + self.url_entry.set_text("http://localhost:5985") + url_box.pack_start(url_label, True, True, 3) + url_box.pack_start(self.url_entry, False, False, 5) + content_area.pack_start(url_box, True, True, 10) + + user_box = Gtk.Box() user_label = Gtk.Label() user_label.set_text("User:") self.user_entry = Gtk.Entry() self.user_entry.set_width_chars(24) self.user_entry.set_activates_default(True) - userBox.pack_start(user_label, True, True, 3) - userBox.pack_start(self.user_entry, False, False, 5) - content_area.pack_start(userBox, True, True, 10) + user_box.pack_start(user_label, True, True, 3) + user_box.pack_start(self.user_entry, False, False, 5) + content_area.pack_start(user_box, True, True, 10) - passwordBox = Gtk.Box() + password_box = Gtk.Box() password_label = Gtk.Label() password_label.set_text("Password:") self.password_entry = Gtk.Entry() self.password_entry.set_visibility(False) self.password_entry.set_width_chars(24) self.password_entry.set_activates_default(True) - passwordBox.pack_start(password_label, True, True, 3) - passwordBox.pack_start(self.password_entry, False, False, 5) - content_area.pack_start(passwordBox, True, True, 10) + password_box.pack_start(password_label, True, True, 3) + password_box.pack_start(self.password_entry, False, False, 5) + content_area.pack_start(password_box, True, True, 10) - u2FABox = Gtk.Box() - u2FA_label = Gtk.Label() - u2FA_label.set_text("2FA Token:") + u_2fa_box = Gtk.Box() + u_2fa_label = Gtk.Label() + u_2fa_label.set_text("2FA Token:") self.u2FA_entry = Gtk.Entry() self.u2FA_entry.set_width_chars(24) self.u2FA_entry.set_activates_default(True) - u2FABox.pack_start(u2FA_label, True, True, 3) - u2FABox.pack_start(self.u2FA_entry, False, False, 5) - content_area.pack_start(u2FABox, True, True, 10) + u_2fa_box.pack_start(u_2fa_label, True, True, 3) + u_2fa_box.pack_start(self.u2FA_entry, False, False, 5) + content_area.pack_start(u_2fa_box, True, True, 10) button_box = Gtk.Box(spacing=6) content_area.pack_end(button_box, False, True, 10) @@ -331,21 +371,28 @@ def __init__(self, reload_ws_callback, parent): button_box.pack_end(cancel_button, False, True, 10) self.show_all() - def getUser(self): + def get_url(self): + if self.url_entry.get_text() is not None: + res = self.url_entry.get_text() + else: + res = "" + return res + + def get_user(self): if self.user_entry.get_text() is not None: res = self.user_entry.get_text() else: res = "" return res - def getPassword(self): + def get_password(self): if self.password_entry.get_text() is not None: res = self.password_entry.get_text() else: res = "" return res - def get2FAToken(self): + def get_2fa_token(self): if self.u2FA_entry.get_text() is not None: res = self.u2FA_entry.get_text() else: @@ -361,53 +408,54 @@ def exit(self): self.destroy() def on_click_ok(self, buttons=None): - newUser = self.getUser() - newPass = self.getPassword() - new2FAToken = self.get2FAToken() + new_url = self.get_url() + new_user = self.get_user() + new_pass = self.get_password() + new_2fa_token = self.get_2fa_token() if self.attempts_counter < self.max_attempts: try: - session_cookie = login_user(CONF.getAPIUrl(), newUser, newPass, new2FAToken) + session_cookie = login_user(new_url, new_user, new_pass, new_2fa_token) except Required2FAError: error_message = f"2FA Token Required" - errorDialog(self, (error_message)) + errorDialog(self, error_message) else: if not session_cookie: logger.warning("Faraday Auth invalid") - self.clear() self.attempts_counter += 1 error_message = f"Invalid credentials!. You have {self.max_attempts - self.attempts_counter} attempt(s) left." - errorDialog(self, (error_message)) + errorDialog(self, error_message) else: - old_cookies = CONF.getFaradaySessionCookies() - CONF.setFaradaySessionCookies(session_cookie) + old_cookies = user_config.getFaradaySessionCookies() + user_config.setFaradaySessionCookies(session_cookie) user_info = get_user_info() if 'client' in user_info['roles']: - CONF.setFaradaySessionCookies(old_cookies) + user_config.setFaradaySessionCookies(old_cookies) self.clear() self.attempts_counter += 1 error_message = f"You can't login as a client. You have {self.max_attempts - self.attempts_counter} attempt(s) left." - errorDialog(self, (error_message)) + errorDialog(self, error_message) self.show_all() else: logger.info("Faraday Auth Successful") - CONF.saveConfig() + user_config.saveConfig() self.reload_ws_callback() MessageDialog(self, "Faraday Authentication Successful") self.destroy() else: - error_message= "Max login attempts reached" - errorDialog(self, (error_message)) + error_message = "Max login attempts reached" + errorDialog(self, error_message) def on_click_cancel(self, button=None): """Override on_click_cancel to make it exit Faraday.""" self.destroy() + class NewWorkspaceDialog(Gtk.Window): """Sets up the New Workspace Dialog, where the user can set a name, a description and a type for a new workspace. Also checks that the those attributes don't correspond to an existing workspace""" - def __init__(self, create_ws_callback, workspace_manager, sidebar, parent, + def __init__(self, create_ws_callback, workspace_manager, sidebar, parent, title=None): Gtk.Window.__init__(self, title="Create New Workspace") @@ -568,7 +616,7 @@ def __init__(self, plugin_manager, parent): for i in range(3)] self.nameEntry, self.versionEntry, self.pluginVersionEntry = [ - Gtk.Label() for i in range(3)] + Gtk.Label() for i in range(3)] nameLabel.set_text("Name: ") versionLabel.set_text("Version: ") @@ -866,6 +914,7 @@ class HostInfoDialog(Gtk.Window): strings and ints) and the object per se, which are in the model folder and are totally alien to GTK. """ + def __init__(self, parent, active_ws_name, host): """Creates a window with the information about a given hosts. The parent is needed so the window can set transient for @@ -883,7 +932,7 @@ def __init__(self, parent, active_ws_name, host): host_info = self.model[0] host_id = self.model[0][0] - couch_url = CONF.getServerURI() + couch_url = user_config.getServerURI() base_url = couch_url + "/_ui/#/host/ws/" self.edit_url = base_url + active_ws_name + "/hid/" + host_id @@ -891,13 +940,13 @@ def __init__(self, parent, active_ws_name, host): self.specific_info = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.specific_info_frame = self.create_scroll_frame( - self.specific_info, - "Service Information") + self.specific_info, + "Service Information") self.vuln_info = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.vuln_info_frame = self.create_scroll_frame( - self.vuln_info, - "Vulnerability Information") + self.vuln_info, + "Vulnerability Information") main_tree = self.create_main_tree_view(self.model) vuln_list = self.create_vuln_list() @@ -1150,6 +1199,7 @@ def create_vuln_model(self, obj): """Return the model for the vulnerabilities of the obj object. It will be sorted alphabetically. """ + def params_to_string(params): # XXX """Converts params to a string, in case it gets here as a list. It's pretty anoyting, but needed for backwards compatibility. @@ -1246,6 +1296,7 @@ def get_object(self, selected_object, object_type): """Take a selection as selected_object and an object_type and return the actual object, not the model's selection. """ + def safely(func): def safe_wrapper(*args, **kwargs): try: @@ -1254,7 +1305,8 @@ def safe_wrapper(*args, **kwargs): dialog = errorDialog(self, ("There has been a problem. " "The object you clicked on " "does not exist anymore.")) - self.destroy() # exit + self.destroy() # exit + return safe_wrapper object_id = selected_object[0] @@ -1411,8 +1463,8 @@ def save(self, button, keeper): dialog.run() dialog.destroy() - except ResourceDoesNotExist: # TODO: revert this hack to prevent exception when - # fixing conflict of non existent object + except ResourceDoesNotExist: # TODO: revert this hack to prevent exception when + # fixing conflict of non existent object dialog = Gtk.MessageDialog(self, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, @@ -1851,8 +1903,8 @@ class aboutDialog(Gtk.AboutDialog): """The simple about dialog displayed when the user clicks on "about" ont the menu. Could be in application.py, but for consistency reasons its here""" - def __init__(self, main_window): + def __init__(self, main_window): Gtk.AboutDialog.__init__(self, transient_for=main_window, modal=True) icons = os.path.join(FARADAY_CLIENT_BASE, "data", "images", "icons") faraday_icon = GdkPixbuf.Pixbuf.new_from_file( @@ -1860,8 +1912,8 @@ def __init__(self, main_window): self.set_logo(faraday_icon) self.set_program_name("Faraday") - app_name = str(CONF.getAppname()) - version = str(CONF.getVersion()) + app_name = str(user_config.getAppname()) + version = str(user_config.getVersion()) self.set_comments(app_name + " " + version + " " + "\n FaradaySec LLC. All rights reserved." @@ -1871,6 +1923,7 @@ def __init__(self, main_window): self.set_website(faraday_website) self.set_website_label("Learn more about Faraday") + class errorDialog(Gtk.MessageDialog): """A simple error dialog to show the user where things went wrong. Takes the parent window, (Gtk.Window or Gtk.Dialog, most probably) @@ -1950,5 +2003,4 @@ def strict_key_reactions(window, event): else: return False - # I'm Py3 diff --git a/faraday_client/model/application.py b/faraday_client/model/application.py index f5289d90..85927b22 100644 --- a/faraday_client/model/application.py +++ b/faraday_client/model/application.py @@ -138,7 +138,7 @@ def __exit(self, exit_code=0): faraday_client.model.api.stopAPIServer() restapi.stopServer() self._model_controller.stop() - if self._model_controller.isAlive(): + if self._model_controller.is_alive(): # runs only if thread has started, i.e. self._model_controller.start() is run first self._model_controller.join() faraday_client.model.api.devlog("Waiting for controller threads to end...") diff --git a/faraday_client/persistence/server/server.py b/faraday_client/persistence/server/server.py index 834368c4..7aa4f1a3 100644 --- a/faraday_client/persistence/server/server.py +++ b/faraday_client/persistence/server/server.py @@ -39,10 +39,10 @@ from faraday_client import __version__ as f_version from faraday_client.persistence.server.utils import force_unique from faraday_client.persistence.server.server_io_exceptions import (WrongObjectSignature, - CantCommunicateWithServerError, - ConflictInDatabase, - ResourceDoesNotExist, - Unauthorized) + CantCommunicateWithServerError, + ConflictInDatabase, + ResourceDoesNotExist, + Unauthorized) from faraday_client.persistence.server.changes_stream import ( WebsocketsChangesStream @@ -71,7 +71,6 @@ } - def _conf(): from faraday_client.config.configuration import getInstanceConfiguration # pylint:disable=import-outside-toplevel CONF = getInstanceConfiguration() @@ -79,7 +78,6 @@ def _conf(): def _get_base_server_url(): - # Faraday server is running, and this module is used by upload_reports... if FARADAY_UPLOAD_REPORTS_OVERWRITE_SERVER_URL: server_url = FARADAY_UPLOAD_REPORTS_OVERWRITE_SERVER_URL @@ -87,12 +85,20 @@ def _get_base_server_url(): server_url = _conf().getAPIUrl() else: server_url = SERVER_URL - return server_url.rstrip('/') + try: + return server_url.rstrip('/') + except AttributeError: + # Sometime program crashes (NonType object has no attribute rstrip + # Could be logic bug? + # Try to fix it + return SERVER_URL.rstrip('/') + def _create_server_api_url(): """Return the server's api url.""" return "{0}/_api/v2".format(_get_base_server_url()) + def _create_server_get_url(workspace_name, object_name=None, object_id=None, **params): """Creates a url to get from the server. Takes the workspace name as a string, an object_name paramter which is the object you want to @@ -136,6 +142,7 @@ def _create_server_put_url(workspace_name, obj_type, obj_id, command_id): def _create_server_delete_url(workspace_name, obj_type, object_id, command_id=None): return _create_server_put_url(workspace_name, obj_type, object_id, command_id) + # XXX: COUCH IT! def _create_couch_get_url(workspace_name, object_id): server_url = _get_base_server_url() @@ -164,6 +171,7 @@ def _add_session_cookies(func): """A decorator which wrapps a function dealing with I/O with the server and adds authentication to the parameters. """ + def wrapper(*args, **kwargs): if FARADAY_UPLOAD_REPORTS_WEB_COOKIE: kwargs['cookies'] = FARADAY_UPLOAD_REPORTS_WEB_COOKIE @@ -171,6 +179,7 @@ def wrapper(*args, **kwargs): kwargs['cookies'] = _conf().getFaradaySessionCookies() response = func(*args, **kwargs) return response + return wrapper if FARADAY_UP else func @@ -205,6 +214,7 @@ def _unsafe_io_with_server(server_io_function, server_expected_responses, raise CantCommunicateWithServerError(server_io_function, server_url, payload, answer) return answer + def _parse_json(response_object): """Takes a response object and return its response as a dictionary.""" try: @@ -227,6 +237,7 @@ def _get(request_url, **params): request_url, params=params)) + def _put(post_url, expected_response=201, **params): """Put to the post_url. If update is True, try to get the object revision first so as to update the object in Couch. You can @@ -261,7 +272,7 @@ def _delete(delete_url, database=False): last_rev = _get(delete_url)['_rev'] params = {'rev': last_rev} return _parse_json(_unsafe_io_with_server(requests.delete, - [200,204], + [200, 204], delete_url, params=params)) @@ -313,6 +324,7 @@ def _get_raw_workspace_summary(workspace_name): request_url = _create_server_get_url(workspace_name) return _get(request_url) + def _save_to_server(workspace_name, **params): """ @@ -323,14 +335,17 @@ def _save_to_server(workspace_name, **params): post_url = _create_server_post_url(workspace_name, params['type'], params.get('command_id', None)) return _post(post_url, update=False, expected_response=201, **params) + def _get_raw_report_count_vulns(workspace_name, **params): request_url = _create_server_get_url(workspace_name, 'report/countVulns') return _get(request_url, **params) + def _update_in_server(workspace_name, faraday_object_id, **params): put_url = _create_server_put_url(workspace_name, params['type'], faraday_object_id, params.get('command_id', None)) return _put(put_url, expected_response=200, **params) + def _save_db_to_server(db_name, **params): post_url = _create_server_db_url(db_name) return _post(post_url, expected_response=201, **params) @@ -433,6 +448,7 @@ def get_web_vulns(workspace_name, **params): """ return get_all_vulns(workspace_name, type="VulnerabilityWeb", **params) + def get_interfaces(workspace_name, **params): """Get interfaces from the server. @@ -446,6 +462,7 @@ def get_interfaces(workspace_name, **params): return _get_faraday_ready_dictionaries(workspace_name, 'interfaces', 'interfaces', **params) + def get_services(workspace_name, **params): """Get services from the server. @@ -459,6 +476,7 @@ def get_services(workspace_name, **params): return _get_faraday_ready_dictionaries(workspace_name, 'services', 'services', **params) + def get_credentials(workspace_name, **params): """Get credentials from the server. @@ -472,6 +490,7 @@ def get_credentials(workspace_name, **params): return _get_faraday_ready_dictionaries(workspace_name, 'credentials', 'rows', **params) + def get_notes(workspace_name, **params): """Get notes from the server. @@ -485,6 +504,7 @@ def get_notes(workspace_name, **params): return _get_faraday_ready_dictionaries(workspace_name, 'notes', 'rows', **params) + def get_commands(workspace_name, **params): """Get commands from the server. @@ -509,6 +529,7 @@ def get_report(workspace_name, object_id): request_url = _create_server_post_url(workspace_name, object_id) return _get(request_url) + def get_report_docx(workspace_name, report_object_id): """Get an report docx. @@ -533,6 +554,7 @@ def get_report_docx(workspace_name, report_object_id): return _unsafe_io_with_server(requests.get, 200, request_url) + def get_report_count_vulns(workspace_name, confirmed=False, tags=[]): """ Get vulnerabilities count from the server. @@ -549,7 +571,7 @@ def get_report_count_vulns(workspace_name, confirmed=False, tags=[]): """ return _get_raw_report_count_vulns( - workspace_name, confirmed="true" if confirmed else "false", tags=",".join(tags)) + workspace_name, confirmed="true" if confirmed else "false", tags=",".join(tags)) def get_objects(workspace_name, object_signature, **params): @@ -594,13 +616,15 @@ def get_changes_stream(workspace_name, heartbeat='1000', stream_provider=_websoc stream_provider: A function that returns an instance of a Stream provider """ return stream_provider(workspace_name, feed='continuous', - heartbeat=heartbeat, **extra_params) + heartbeat=heartbeat, **extra_params) + def get_workspaces_names(): """Returns: A dictionary with a list with the workspaces names.""" return _get("{0}/ws".format(_create_server_api_url())) + # XXX: COUCH IT! def _clean_up_stupid_couch_response(response_string): """Couch likes to give invalid jsons as a response :). So nice.""" @@ -610,6 +634,7 @@ def _clean_up_stupid_couch_response(response_string): hopefully_valid_json = "{{{0}}}".format(ok_yeah) return json.loads(hopefully_valid_json) + # XXX: COUCH IT! # COUCH IT LEVEL: REVOLUTIONS def get_object_before_last_revision(workspace_name, object_id): @@ -666,6 +691,7 @@ def get_object(workspace_name, object_signature, object_id): objects = get_objects(workspace_name, object_signature, couchid=object_id) return force_unique(objects) + def get_host(workspace_name, host_id): """Get an unique host. @@ -683,6 +709,7 @@ def get_host(workspace_name, host_id): """ return force_unique(get_hosts(workspace_name, couchid=host_id)) + def get_vuln(workspace_name, vuln_id): """Get an unique vuln. @@ -700,6 +727,7 @@ def get_vuln(workspace_name, vuln_id): """ return force_unique(get_vulns(workspace_name, couchid=vuln_id)) + def get_web_vuln(workspace_name, vuln_id): """Get an unique web vuln. @@ -717,6 +745,7 @@ def get_web_vuln(workspace_name, vuln_id): """ return force_unique(get_web_vulns(workspace_name, couchid=vuln_id)) + def get_interface(workspace_name, interface_id): """Get an unique interface. @@ -734,6 +763,7 @@ def get_interface(workspace_name, interface_id): """ return force_unique(get_interfaces(workspace_name, couchid=interface_id)) + def get_service(workspace_name, service_id): """Get an unique service. @@ -751,6 +781,7 @@ def get_service(workspace_name, service_id): """ return force_unique(get_services(workspace_name, couchid=service_id)) + def get_note(workspace_name, note_id): """Get an unique note. @@ -768,6 +799,7 @@ def get_note(workspace_name, note_id): """ return force_unique(get_notes(workspace_name, couchid=note_id)) + def get_credential(workspace_name, credential_id): """Get an unique credential. @@ -785,6 +817,7 @@ def get_credential(workspace_name, credential_id): """ return force_unique(get_services(workspace_name, couchid=credential_id)) + def get_command(workspace_name, command_id): """Get an unique command. @@ -802,6 +835,7 @@ def get_command(workspace_name, command_id): """ return force_unique(get_commands(workspace_name, couchid=command_id)) + def get_workspace(workspace_name, **params): """Get an unique command. @@ -820,6 +854,7 @@ def get_workspace(workspace_name, **params): request_url = _create_server_get_url(workspace_name) return _get(request_url, **params) + def get_workspace_summary(workspace_name): """Get a collection of data about the workspace. @@ -831,6 +866,7 @@ def get_workspace_summary(workspace_name): """ return _get_raw_workspace_summary(workspace_name)['stats'] + def get_workspace_numbers(workspace_name): """Get the number of hosts, interfaces, services and vulns in the workspace. @@ -843,6 +879,7 @@ def get_workspace_numbers(workspace_name): stats = _get_raw_workspace_summary(workspace_name)['stats'] return stats['hosts'], stats['services'], stats['total_vulns'] + def get_hosts_number(workspace_name, **params): """ Args: @@ -854,6 +891,7 @@ def get_hosts_number(workspace_name, **params): """ return int(get_workspace_summary(workspace_name)['hosts']) + def get_services_number(workspace_name, **params): """ Args: @@ -865,6 +903,7 @@ def get_services_number(workspace_name, **params): """ return int(get_workspace_summary(workspace_name)['services']) + def get_interfaces_number(workspace_name, **params): """ Args: @@ -876,6 +915,7 @@ def get_interfaces_number(workspace_name, **params): """ return int(get_workspace_summary(workspace_name)['interfaces']) + def get_vulns_number(workspace_name, **params): """ Args: @@ -887,6 +927,7 @@ def get_vulns_number(workspace_name, **params): """ return int(get_workspace_summary(workspace_name)['total_vulns']) + def get_notes_number(workspace_name, **params): """ Args: @@ -898,6 +939,7 @@ def get_notes_number(workspace_name, **params): """ return int(get_workspace_summary(workspace_name)['notes']) + def get_credentials_number(workspace_name, **params): """ Args: @@ -909,6 +951,7 @@ def get_credentials_number(workspace_name, **params): """ return int(_get_raw_credentials(workspace_name, **params)) + def get_commands_number(workspace_name, **params): """ Args: @@ -920,6 +963,7 @@ def get_commands_number(workspace_name, **params): """ return int(_get_raw_commands(workspace_name, **params)) + def create_host(workspace_name, command_id, ip, os, default_gateway=None, description="", metadata=None, owned=False, owner="", parent=None, hostnames=None, mac=None): @@ -955,6 +999,7 @@ def create_host(workspace_name, command_id, ip, os, default_gateway=None, mac=mac, type="Host") + def update_host(workspace_name, command_id, id, ip, os, default_gateway="", description="", metadata=None, owned=False, owner="", parent=None, hostnames=None, mac=None): @@ -1028,6 +1073,7 @@ def create_service(workspace_name, command_id, name, description, ports, parent, type="Service", metadata=metadata) + def update_service(workspace_name, command_id, id, name, description, ports, parent, owned=False, owner="", protocol="", status="", version="", metadata=None): @@ -1113,7 +1159,8 @@ def create_vuln(workspace_name, command_id, name, description, parent, parent_ty status=status, metadata=metadata, policyviolations=policyviolations, - external_id=external_id) + external_id=external_id) + def update_vuln(workspace_name, command_id, id, name, description, parent, parent_type, owned=None, owner="", confirmed=False, data="", @@ -1163,7 +1210,8 @@ def update_vuln(workspace_name, command_id, id, name, description, parent, status=status, metadata=metadata, policyviolations=policyviolations, - external_id=external_id) + external_id=external_id) + def create_vuln_web(workspace_name, command_id, name, description, parent, parent_type, owned=None, owner="", confirmed=False, @@ -1233,6 +1281,7 @@ def create_vuln_web(workspace_name, command_id, name, description, parent, policyviolations=policyviolations, tags=tags, external_id=external_id) + def update_vuln_web(workspace_name, command_id, id, name, description, parent, parent_type, owned=None, owner="", confirmed=False, data="", refs=None, severity="info", resolution="", @@ -1303,6 +1352,7 @@ def update_vuln_web(workspace_name, command_id, id, name, description, tags=tags, external_id=external_id) + def create_note(workspace_name, command_id, object_type, object_id, name, text, owned=None, owner="", description="", metadata=None): """Creates a note. @@ -1333,6 +1383,7 @@ def create_note(workspace_name, command_id, object_type, object_id, name, text, type="Note", metadata=metadata) + def update_note(workspace_name, command_id, id, name, text, object_type, object_id, owned=None, owner="", description="", metadata=None): @@ -1399,6 +1450,7 @@ def create_credential(workspace_name, command_id, name, username, password, password=password, type="Cred") + def update_credential(workspace_name, command_id, id, name, username, password, parent, parent_type, owned=None, owner="", description="", metadata=None): @@ -1433,6 +1485,7 @@ def update_credential(workspace_name, command_id, id, name, username, password, password=password, type="Cred") + def create_command(workspace_name, command, tool, import_source, duration=None, hostname=None, ip=None, itime=None, params=None, user=None): """Creates a command. @@ -1463,8 +1516,9 @@ def create_command(workspace_name, command, tool, import_source, duration=None, workspace=workspace_name, type="CommandRunInformation") + def update_command(workspace_name, command_id, command, tool, import_source -,duration=None, hostname=None, + , duration=None, hostname=None, ip=None, itime=None, params=None, user=None): """Updates a command. @@ -1498,7 +1552,8 @@ def update_command(workspace_name, command_id, command, tool, import_source def create_executive_report(workspace_name, id, name, tags=[], title="", enterprise="", scope="", objectives="", - summary="", confirmed=False, template_name="", conclusions="", recommendations="", date=None, + summary="", confirmed=False, template_name="", conclusions="", recommendations="", + date=None, owner="", grouped=False): """Creates a Executive report. @@ -1608,6 +1663,7 @@ def delete_workspace(workspace_name): db_url = _create_server_db_url(workspace_name) return _delete(db_url, database=True) + def server_info(): """Return server info if we can stablish a connection with the server, None otherwise. @@ -1617,6 +1673,7 @@ def server_info(): except: return None + def login_user(uri, uname, upass, u2fa_token=None): auth = {"email": uname, "password": upass} headers = {'User-Agent': f'faraday-client/{f_version}'} @@ -1678,9 +1735,11 @@ def check_server_url(url_to_test): test_okey = False return test_okey + def get_user_info(): try: - resp = requests.get(urlparse.urljoin(_get_base_server_url(), "/_api/session"), cookies=_conf().getFaradaySessionCookies(), timeout=1) + resp = requests.get(urlparse.urljoin(_get_base_server_url(), "/_api/session"), + cookies=_conf().getFaradaySessionCookies(), timeout=1) if (resp.status_code != 401) and (resp.status_code != 403): return resp.json() else: @@ -1690,5 +1749,4 @@ def get_user_info(): except requests.adapters.ReadTimeout: return False - # I'm Py3 diff --git a/faraday_client/start_client.py b/faraday_client/start_client.py index 5a4a152a..ad137e89 100755 --- a/faraday_client/start_client.py +++ b/faraday_client/start_client.py @@ -19,7 +19,6 @@ import requests.exceptions import logging - from faraday_client.config.configuration import getInstanceConfiguration from faraday_client.config.constant import ( CONST_USER_HOME, @@ -39,11 +38,12 @@ CONST_FARADAY_HOME_PATH = os.path.expanduser('~/.faraday') from faraday_client import __version__ -from faraday_client.persistence.server.server import login_user, get_user_info, is_authenticated +from faraday_client.persistence.server.server import login_user, get_user_info import faraday_client from colorama import init, Fore, Back, Style + init(autoreset=True) from urllib.parse import urlparse, urljoin @@ -77,7 +77,7 @@ logger = logging.getLogger(__name__) -def getParserArgs(): +def get_parser_args(): """ Parser setup for faraday launcher arguments. """ @@ -134,7 +134,7 @@ def getParserArgs(): dest="gui", default="gtk", help="Select interface to start faraday. Supported values are " - "gtk and 'no' (no GUI at all). Defaults to GTK") + "gtk and 'no' (no GUI at all). Defaults to GTK") parser.add_argument('--cli', action="store_true", @@ -162,7 +162,6 @@ def getParserArgs(): default=False, help="Enables debug mode. Default = disabled") - parser.add_argument('--nodeps', action="store_true", help='Skip dependency check') @@ -254,7 +253,7 @@ def setupZSH(): dst.write(src.read()) -def setupXMLConfig(): +def setup_xml_config(): """ Checks user configuration file status. @@ -268,7 +267,7 @@ def setupXMLConfig(): logger.info("Using custom user configuration.") -def checkConfiguration(gui_type): +def check_configuration(gui_type): """ Checks if the environment is ready to run Faraday. @@ -280,20 +279,20 @@ def checkConfiguration(gui_type): logger.info("Setting up ZSH integration.") setupZSH() logger.info("Setting up user configuration.") - setupXMLConfig() + setup_xml_config() -def setupFolders(folderlist): +def setup_folders(folder_list): """ Checks if a list of folders exists and creates them otherwise. """ - for folder in folderlist: + for folder in folder_list: fp_folder = os.path.join(FARADAY_USER_HOME, folder) - checkFolder(fp_folder) + check_folder(fp_folder) -def checkFolder(folder): +def check_folder(folder): """ Checks whether a folder exists and creates it if it doesn't. """ @@ -304,11 +303,11 @@ def checkFolder(folder): os.makedirs(folder) -def printBanner(): +def print_banner(): """ Prints Faraday's ascii banner. """ - print (Fore.RED + """ + print(Fore.RED + """ _____ .___ _/ ____\_____ ____________ __| _/_____ ___.__. \ __\ \__ \ \_ __ \__ \ / __ | \__ \ < | | @@ -323,12 +322,12 @@ def printBanner(): logger.info("Starting Faraday IDE.") - def try_login_user(server_uri, api_username, api_password, u2fa_token=None): try: session_cookie = login_user(server_uri, api_username, api_password, u2fa_token) except requests.exceptions.SSLError: - print("SSL certificate validation failed.\nYou can use the --cert option in Faraday to set the path of the cert") + print( + "SSL certificate validation failed.\nYou can use the --cert option in Faraday to set the path of the cert") sys.exit(-1) except requests.exceptions.MissingSchema: print("The Faraday Server URL is incorrect, please try again.") @@ -341,23 +340,22 @@ def try_login_user(server_uri, api_username, api_password, u2fa_token=None): return session_cookie -def login(ask_for_credentials, cert_path): +def login(cert_path): """ Sets the username and passwords from the command line. If --login flag is set then username and password is set """ - CONF = getInstanceConfiguration() - server_url = CONF.getAPIUrl() + user_config = getInstanceConfiguration() + server_url = user_config.getAPIUrl() try: if not server_url: server_url = input("\nPlease enter the Faraday Server URL (Press enter for http://localhost:5985): ") \ or "http://localhost:5985" else: - if ask_for_credentials: - server_url = input(f"\nPlease enter the Faraday Server URL (Press enter for last used: {server_url}): ") \ - or server_url + server_url = input(f"\nPlease enter the Faraday Server URL (Press enter for last used: {server_url}): ") \ + or server_url parsed_url = urlparse(server_url) - if not all([parsed_url.scheme, parsed_url.netloc]): + if not all([parsed_url.scheme, parsed_url.netloc]): logger.error("Invalid URL: %s", server_url) sys.exit(1) try: @@ -375,20 +373,10 @@ def login(ask_for_credentials, cert_path): except requests.exceptions.ConnectionError as e: logger.error("Connection to Faraday server FAILED: %s - use --login to set a new server", server_url) sys.exit(1) - CONF.setAPIUrl(server_url) - if not ask_for_credentials: - session_cookies = CONF.getFaradaySessionCookies() - if session_cookies and server_url: - if is_authenticated(server_url, session_cookies): - logger.debug("Valid Previous session cookie found") - if parsed_url.scheme == "https" and cert_path: - CONF.setCerPath(cert_path) - else: - CONF.setCerPath(None) - return True + user_config.setAPIUrl(server_url) print(f"""\nPlease provide your valid Faraday credentials for {server_url}\nYou have 3 attempts.""") - MAX_ATTEMPTS = 3 - for attempt in range(1, MAX_ATTEMPTS + 1): + max_attempts = 3 + for attempt in range(1, max_attempts + 1): api_username = input("Username (press enter for faraday): ") or "faraday" api_password = getpass.getpass('Password: ') try: @@ -400,40 +388,41 @@ def login(ask_for_credentials, cert_path): u2fa_token = input("2FA Token: ") session_cookie = try_login_user(server_url, api_username, api_password, u2fa_token) if session_cookie: - CONF.setFaradaySessionCookies(session_cookie) + user_config.setFaradaySessionCookies(session_cookie) if parsed_url.scheme == "https" and cert_path: - CONF.setCerPath(cert_path) + user_config.setCerPath(cert_path) else: - CONF.setCerPath(None) + user_config.setCerPath(None) user_info = get_user_info() if not user_info: continue else: if 'roles' in user_info: if 'client' in user_info['roles']: - print(f"You can't login as a client. You have {MAX_ATTEMPTS - attempt} attempt(s) left.") + print(f"You can't login as a client. You have {max_attempts - attempt} attempt(s) left.") continue logger.info('Login successful: {0}'.format(api_username)) - CONF.saveConfig() + user_config.saveConfig() break - print(f'Login failed, please try again. You have {MAX_ATTEMPTS - attempt} more attempts') + print(f'Login failed, please try again. You have {max_attempts - attempt} more attempts') else: - logger.fatal(f'Invalid credentials, {MAX_ATTEMPTS} attempts failed. Quitting Faraday...') + logger.fatal(f'Invalid credentials, {max_attempts} attempts failed. Quitting Faraday...') sys.exit(-1) except KeyboardInterrupt: sys.exit(0) + def main(): """ Main function for launcher. """ global args - args = getParserArgs() - setupFolders(CONST_FARADAY_FOLDER_LIST) - printBanner() + args = get_parser_args() + setup_folders(CONST_FARADAY_FOLDER_LIST) + print_banner() logger.info("Dependencies met.") - checkConfiguration(args.gui) + check_configuration(args.gui) setConf() CONF = getInstanceConfiguration() cert_path = CONF.getCertPath() @@ -444,7 +433,10 @@ def main(): cert_path = os.path.abspath(args.cert_path) if cert_path: os.environ[REQUESTS_CA_BUNDLE_VAR] = cert_path - login(args.login, cert_path) + + if args.login: + # We only call terminal login when user provides login flag + login(cert_path) start_faraday_client() diff --git a/faraday_client/utils/error_report.py b/faraday_client/utils/error_report.py index f5deeea8..c0e4f800 100644 --- a/faraday_client/utils/error_report.py +++ b/faraday_client/utils/error_report.py @@ -14,8 +14,6 @@ import traceback import threading import requests -import hashlib -import platform import faraday_client.model.guiapi from io import StringIO from faraday_client.gui.customevents import ShowExceptionCustomEvent @@ -34,11 +32,12 @@ def get_crash_log(): pass + def get_system_info(): pass -def exception_handler(type, value, tb): +def exception_handler(obj_type, value, tb): """ This is a custom exception handler to replace the python original one. The idea is to show the user a dialog with the information and let him/her @@ -47,29 +46,28 @@ def exception_handler(type, value, tb): Since this handler may be called from threads, the dialog must be created using gtk idle_add or signals to avoid issues. """ + import hashlib + import platform + import distro text = StringIO() - traceback.print_exception(type, value, tb, file=text) + traceback.print_exception(obj_type, value, tb, file=text) error_name = text.getvalue().split('\n')[-2] - excepts = """ - Traceback: %s - """ % (text.getvalue() ) - + excepts = f"Traceback: {text.getvalue()}".encode('utf-8') exception_hash = hashlib.sha256(excepts).hexdigest() - os_dist = " ".join(platform.dist()) + os_dist = " ".join(distro.linux_distribution()) python_version = platform.python_version() faraday_version = CONF.getVersion() modules_info = "" try: - modules_info = ",".join([ "%s=%s" % (x.key, x.version) - for x in pip.get_installed_distributions()]) + modules_info = ",".join(["%s=%s" % (x.key, x.version) + for x in pip.get_installed_distributions()]) except (ImportError, AttributeError): pass - - python_dist = "Python %s \n Modules: [ %s ]" % (python_version, modules_info) + python_dist = f"Python {python_version} \n Modules: [ {modules_info} ]" description = """ Exception: %s @@ -79,8 +77,6 @@ def exception_handler(type, value, tb): Python Versions: %s """ % (excepts, exception_hash, os_dist, faraday_version, python_dist) - - event = ShowExceptionCustomEvent(description, reportToDevelopers, error_name) faraday_client.model.guiapi.postCustomEvent(event) text.seek(0) @@ -103,8 +99,8 @@ def reportToDevelopers(name=None, *description): params['summary'] = 'autoreport %s' % time.time() resp = requests.post(uri, - headers = headers, - data = params, timeout = 1, verify=True) + headers=headers, + data=params, timeout=1, verify=True) model.api.devlog("Report sent to faraday server") @@ -112,6 +108,7 @@ def reportToDevelopers(name=None, *description): model.api.devlog("Error reporting to developers:") model.api.devlog(e) + def installThreadExcepthook(): """ Workaround for sys.excepthook thread bug from @@ -122,9 +119,11 @@ def installThreadExcepthook(): since this replaces a new-style class method. """ init_old = threading.Thread.__init__ + def init(self, *args, **kwargs): init_old(self, *args, **kwargs) run_old = self.run + def run_with_except_hook(*args, **kw): try: run_old(*args, **kw) @@ -132,8 +131,9 @@ def run_with_except_hook(*args, **kw): if isinstance(e, (KeyboardInterrupt, SystemExit)): raise sys.excepthook(*sys.exc_info()) + self.run = run_with_except_hook - threading.Thread.__init__ = init + threading.Thread.__init__ = init # I'm Py3