diff --git a/config.template b/config.template index 2dde9e1..acde289 100644 --- a/config.template +++ b/config.template @@ -285,6 +285,9 @@ news_file_path = news.txt news_random_line = False # enable the use of exernal shell commands, this enables some data in `sysinfo` enable_runShellCmd = False +# if runShellCmd and you think it is safe to allow the x: command to run +# direct shell command handler the x: command in DMs +allowXcmd = False [smtp] # enable or disable the SMTP module diff --git a/mesh_bot.py b/mesh_bot.py index cac0ad8..a02725d 100755 --- a/mesh_bot.py +++ b/mesh_bot.py @@ -96,6 +96,7 @@ def auto_response(message, snr, rssi, hop, pkiStatus, message_from_id, channel_n "wx": lambda: handle_wxc(message_from_id, deviceID, 'wx'), "wxa": lambda: handle_wxalert(message_from_id, deviceID, message), "wxalert": lambda: handle_wxalert(message_from_id, deviceID, message), + "x:": lambda: handleShellCmd(message, message_from_id, channel_number, isDM, deviceID), "wxc": lambda: handle_wxc(message_from_id, deviceID, 'wxc'), "📍": lambda: handle_whoami(message_from_id, deviceID, hop, snr, rssi, pkiStatus), "🔔": lambda: handle_alertBell(message_from_id, deviceID, message), @@ -1518,6 +1519,8 @@ async def start_rx(): logger.debug(f"System: File Monitor Enabled for {file_monitor_file_path}, broadcasting to channels: {file_monitor_broadcastCh}") if enable_runShellCmd: logger.debug(f"System: Shell Command monitor enabled") + if allowXcmd and enable_runShellCmd: + logger.warning(f"System: File Monitor shell XCMD Enabled") if read_news_enabled: logger.debug(f"System: File Monitor News Reader Enabled for {news_file_path}") if bee_enabled: diff --git a/modules/filemon.py b/modules/filemon.py index 9ea1914..ecc1ed3 100644 --- a/modules/filemon.py +++ b/modules/filemon.py @@ -82,4 +82,29 @@ def call_external_script(message, script="script/runShell.sh"): except Exception as e: logger.warning(f"FileMon: Error calling external script: {e}") return None - \ No newline at end of file + +def handleShellCmd(message, message_from_id, channel_number, isDM, deviceID): + if not allowXcmd: + return "x: command is disabled" + + if str(message_from_id) not in bbs_admin_list: + logger.warning(f"FileMon: Unauthorized x: command attempt from {message_from_id}") + return "x: command not authorized" + + if not isDM: + return "x: command not authorized in group chat" + + if enable_runShellCmd: + command = message.removeprefix("x: ").strip() + try: + logger.info(f"FileMon: Running shell command from {message_from_id}: {command}") + output = os.popen(command).read().encode('utf-8').decode('utf-8') + if output: + return output + else: + return "x: command returned no output" + except Exception as e: + logger.warning(f"FileMon: Error running shell command: {e}") + return "x: command error" + else: + return "x: command is disabled" \ No newline at end of file diff --git a/modules/settings.py b/modules/settings.py index fa72579..e7825eb 100644 --- a/modules/settings.py +++ b/modules/settings.py @@ -357,6 +357,7 @@ try: news_file_path = config['fileMon'].get('news_file_path', 'news.txt') # default news.txt news_random_line_only = config['fileMon'].getboolean('news_random_line', False) # default False enable_runShellCmd = config['fileMon'].getboolean('enable_runShellCmd', False) # default False + allowXcmd = config['fileMon'].getboolean('allowXcmd', False) # default False # games game_hop_limit = config['messagingSettings'].getint('game_hop_limit', 5) # default 3 hops diff --git a/modules/system.py b/modules/system.py index 2f6a65f..88b4b66 100644 --- a/modules/system.py +++ b/modules/system.py @@ -260,6 +260,10 @@ if file_monitor_enabled or read_news_enabled or bee_enabled: # Bee Configuration uses file monitor module if bee_enabled: trap_list = trap_list + ("🐝",) + # x: command for shell access + if enable_runShellCmd and allowXcmd: + trap_list = trap_list + ("x:",) + help_message = help_message + ", x:" # clean up the help message help_message = help_message.split(", ")