RustImplementation #28

Merged
mysticmomba merged 37 commits from RustImplementation into master 2025-11-18 14:51:42 -05:00
3 changed files with 75 additions and 39 deletions
Showing only changes of commit eb1d710d07 - Show all commits
+3 -14
View File
@@ -23,14 +23,13 @@
import logging import logging
import os import os
import tempfile
import dotenv
import urllib3 import urllib3
from services.API import AirlockAPIWrapper from services.API import AirlockAPIWrapper
from services.security import getAPI from services.security import getAPI
from TUI.TUI import run_Loxide from TUI.TUI import run_Loxide
from utils.configmanager import get_system_value
from utils.setup import get_base_directory, setup from utils.setup import get_base_directory, setup
from utils.utils import irtang from utils.utils import irtang
@@ -38,24 +37,14 @@ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def main(): def main():
if "NUITKA_ONEFILE_PARENT" in os.environ:
splash_filename = os.path.join(
tempfile.gettempdir(),
f"onefile_{int(os.environ['NUITKA_ONEFILE_PARENT'])}_splash_feedback.tmp",
)
if os.path.exists(splash_filename):
os.unlink(splash_filename)
irtang() irtang()
# Determine working directory, setup directory, configure logging, sent env, get API and URL if not already stored # Determine working directory, setup directory, configure logging, sent env, get API and URL if not already stored
setup() setup()
base_dir = get_base_directory() base_dir = get_base_directory()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
dotenv.load_dotenv(dotenv_path=base_dir / ".env")
try: try:
url = os.getenv("URL") url = get_system_value("URL")
username = os.getenv("USERNAME") username = os.getenv("USERNAME")
if not url: if not url:
@@ -75,7 +64,7 @@ def main():
raise ValueError("API key for Loxide is missing.") raise ValueError("API key for Loxide is missing.")
api = AirlockAPIWrapper( api = AirlockAPIWrapper(
base_url=str(os.getenv("URL")), base_url=str(url),
api_key=api_key, api_key=api_key,
) )
run_Loxide(api) run_Loxide(api)
+59 -15
View File
@@ -32,7 +32,7 @@ class AllowlistSelectionWidget(Static):
layout: vertical; layout: vertical;
} }
#allowlist_main { #allowlist_main {
height: 100%; height: 1fr;
width: 100%; width: 100%;
} }
#left_panel { #left_panel {
@@ -61,7 +61,8 @@ class AllowlistSelectionWidget(Static):
margin: 1 0; margin: 1 0;
} }
#action_buttons { #action_buttons {
height: 10%; height: auto;
min-height: 3;
padding: 1; padding: 1;
content-align: center middle; content-align: center middle;
} }
@@ -149,7 +150,7 @@ class AllowlistSelectionWidget(Static):
# Action buttons at bottom # Action buttons at bottom
with Horizontal(id="action_buttons"): with Horizontal(id="action_buttons"):
self.back_btn = Button(" Back", id="back_btn") self.back_btn = Button(" Back", id="back_btn")
self.add_btn = Button(" Add to Allowlist", id="add_to_allowlist_btn") self.add_btn = Button(" Add to Allowlist", id="add_to_allowlist_btn")
self.back_btn.styles.width = "50%" self.back_btn.styles.width = "50%"
@@ -176,7 +177,8 @@ class AllowlistSelectionWidget(Static):
# First, try to get the host's policy if hostname is provided # First, try to get the host's policy if hostname is provided
host_policy_allowlists = [] host_policy_allowlists = []
host_policy_ids = set() host_policy_ids = set()
policy_name = None policy_name = "Unknown Policy" # Default value
group_id = None
if self.hostname: if self.hostname:
try: try:
@@ -185,8 +187,23 @@ class AllowlistSelectionWidget(Static):
if not agents_df.empty: if not agents_df.empty:
# Get the policy group ID for this host # Get the policy group ID for this host
group_id = agents_df.iloc[0].get("groupid") group_id = agents_df.iloc[0].get("groupid")
policy_name = agents_df.iloc[0].get(
"groupname", "Unknown Policy" # Look up the policy name from app's cached policies
if (
group_id
and hasattr(self.app, "policies")
and self.app.policies
):
for policy in self.app.policies:
if policy.groupid == group_id:
policy_name = policy.name
logger.info(
f"Found policy name: '{policy_name}' for group_id: {group_id}"
)
break
logger.info(
f"Found host '{self.hostname}' in policy '{policy_name}' (group_id: {group_id})"
) )
if group_id: if group_id:
@@ -208,6 +225,33 @@ class AllowlistSelectionWidget(Static):
except Exception as e: except Exception as e:
logger.warning(f"Could not get host's policy allowlists: {e}") logger.warning(f"Could not get host's policy allowlists: {e}")
# If we still don't have a policy name, try to get it from the first allowlist or use a default
if not policy_name:
# Get all policies and try to find which one has allowlists
try:
all_policies_df = self.api.policy_find_all()
if not all_policies_df.empty:
# If we have a group_id from somewhere, use it
if group_id:
policy_row = all_policies_df[
all_policies_df["groupid"] == group_id
]
if not policy_row.empty:
policy_name = policy_row.iloc[0].get(
"groupname", "Unknown Policy"
)
else:
# Use the first policy as fallback
policy_name = all_policies_df.iloc[0].get(
"groupname", "Default Policy"
)
logger.info(f"Using first available policy: {policy_name}")
else:
policy_name = "Unknown Policy"
except Exception as e:
logger.warning(f"Could not fetch policies: {e}")
policy_name = "Unknown Policy"
# Get all allowlists # Get all allowlists
all_allowlists_df = self.api.allowlist_find_all() all_allowlists_df = self.api.allowlist_find_all()
@@ -243,7 +287,7 @@ class AllowlistSelectionWidget(Static):
# Add policy-associated allowlists if any # Add policy-associated allowlists if any
if host_policy_allowlists: if host_policy_allowlists:
# Add section header # Add section header
header_text = f"━━━ Policy: {policy_name or 'Host Policy'} ━━━" header_text = f"=== Policy: {policy_name or 'Host Policy'} ==="
self.allowlist_table.add_row(header_text, "", "", key="header_policy") self.allowlist_table.add_row(header_text, "", "", key="header_policy")
current_row += 1 current_row += 1
@@ -270,7 +314,7 @@ class AllowlistSelectionWidget(Static):
current_row += 1 current_row += 1
self.allowlist_table.add_row( self.allowlist_table.add_row(
"━━━ Other Available Allowlists ━━━", "", "", key="header_other" "=== Other Available Allowlists ===", "", "", key="header_other"
) )
current_row += 1 current_row += 1
@@ -336,9 +380,9 @@ class AllowlistSelectionWidget(Static):
if found_col: if found_col:
self.hash_column = found_col self.hash_column = found_col
preview_lines.append(f" Found hash column: **{found_col}**\n") preview_lines.append(f"✓ Found hash column: **{found_col}**\n")
else: else:
preview_lines.append("⚠️ **No hash column found**\n") preview_lines.append("⚠️ **No hash column found**\n")
preview_lines.append("Available columns:\n") preview_lines.append("Available columns:\n")
for col in self.selected_data.columns: for col in self.selected_data.columns:
if col != "_row_id": if col != "_row_id":
@@ -420,7 +464,7 @@ class AllowlistSelectionWidget(Static):
self.selected_allowlist = self.allowlists[actual_allowlist_index] self.selected_allowlist = self.allowlists[actual_allowlist_index]
self.add_btn.disabled = False self.add_btn.disabled = False
self.add_btn.label = ( self.add_btn.label = (
f" Add to '{self.selected_allowlist.get('name', 'Unknown')}'" f"➕ Add to '{self.selected_allowlist.get('name', 'Unknown')}'"
) )
# Update preview with selection # Update preview with selection
@@ -523,7 +567,7 @@ class AllowlistSelectionWidget(Static):
# Success notification # Success notification
self.app.notify( self.app.notify(
f" Successfully added {len(self.hashes_to_add)} hashes to '{allowlist_name}'", f"✅ Successfully added {len(self.hashes_to_add)} hashes to '{allowlist_name}'",
title="Success", title="Success",
severity="information", severity="information",
timeout=5, timeout=5,
@@ -531,7 +575,7 @@ class AllowlistSelectionWidget(Static):
# Update preview to show success # Update preview to show success
self.preview_area.text = ( self.preview_area.text = (
f"## SUCCESS\n\n" f"## ✅ SUCCESS\n\n"
f"Added **{len(self.hashes_to_add)} hashes** to allowlist:\n" f"Added **{len(self.hashes_to_add)} hashes** to allowlist:\n"
f"**{allowlist_name}** (ID: {app_id})\n\n" f"**{allowlist_name}** (ID: {app_id})\n\n"
f"### Operation Details:\n" f"### Operation Details:\n"
@@ -551,7 +595,7 @@ class AllowlistSelectionWidget(Static):
except Exception as exc: except Exception as exc:
logger.exception(f"Failed to add hashes to allowlist: {exc}") logger.exception(f"Failed to add hashes to allowlist: {exc}")
self.app.notify( self.app.notify(
f" Failed to add hashes: {str(exc)}", f"❌ Failed to add hashes: {str(exc)}",
title="Error", title="Error",
severity="error", severity="error",
timeout=10, timeout=10,
@@ -559,7 +603,7 @@ class AllowlistSelectionWidget(Static):
# Re-enable button # Re-enable button
self.add_btn.disabled = False self.add_btn.disabled = False
self.add_btn.label = " Retry Add to Allowlist" self.add_btn.label = "➕ Retry Add to Allowlist"
class AllowlistSelectionScreen(Screen): class AllowlistSelectionScreen(Screen):
+13 -10
View File
@@ -56,12 +56,13 @@ class OTPActivitiesWidget(Static):
layout: vertical; layout: vertical;
} }
#activity_preview_container { #activity_preview_container {
height: 75%; height: 1fr;
border: none; border: none;
padding: 1 1; padding: 1 1;
} }
#activity_buttons { #activity_buttons {
height: 25%; height: auto;
min-height: 3;
padding: 1 1; padding: 1 1;
content-align: center middle; content-align: center middle;
} }
@@ -481,16 +482,18 @@ class ActivityDetailWidget(Static):
layout: vertical; layout: vertical;
} }
#detail_table_container { #detail_table_container {
height: 75%; height: 1fr;
padding: 1 1; padding: 1 1;
} }
#selection_buttons { #selection_buttons {
height: 10%; height: auto;
min-height: 3;
padding: 1 1; padding: 1 1;
content-align: center middle; content-align: center middle;
} }
#detail_buttons { #detail_buttons {
height: 15%; height: auto;
min-height: 3;
padding: 1 1; padding: 1 1;
content-align: center middle; content-align: center middle;
} }
@@ -649,7 +652,7 @@ class ActivityDetailWidget(Static):
logger.exception("Failed to sort by column %s: %s", column_key, exc) logger.exception("Failed to sort by column %s: %s", column_key, exc)
return return
# Only refresh rows, not columns # ✅ Only refresh rows, not columns
await self._build_table(rebuild=False) await self._build_table(rebuild=False)
async def on_button_pressed(self, event) -> None: async def on_button_pressed(self, event) -> None:
@@ -728,13 +731,13 @@ class ActivityDetailWidget(Static):
if self.activities_df is None or self.activities_df.empty: if self.activities_df is None or self.activities_df.empty:
logger.info("No activities to export.") logger.info("No activities to export.")
await self.mount( await self.mount(
Static(" No activities to export.", classes="notification") Static("❌ No activities to export.", classes="notification")
) )
return return
if not self.selected_row_ids: if not self.selected_row_ids:
logger.info("No rows selected for export.") logger.info("No rows selected for export.")
await self.mount( await self.mount(
Static(" No rows selected for export.", classes="notification") Static("❌ No rows selected for export.", classes="notification")
) )
return return
try: try:
@@ -755,12 +758,12 @@ class ActivityDetailWidget(Static):
logger.exception("Failed to export detail activities: %s", exc) logger.exception("Failed to export detail activities: %s", exc)
await self.mount( await self.mount(
Static( Static(
" Failed to export activities; check logs.", "❌ Failed to export activities; check logs.",
classes="notification", classes="notification",
) )
) )
# Helper methods # ✅ Helper methods
def get_selected_data(self) -> pd.DataFrame: def get_selected_data(self) -> pd.DataFrame:
"""Return a DataFrame of the selected rows.""" """Return a DataFrame of the selected rows."""
if not self.selected_row_ids: if not self.selected_row_ids: