diff --git a/Loxide.py b/Loxide.py index 07d1ec6..da2da0a 100644 --- a/Loxide.py +++ b/Loxide.py @@ -51,6 +51,7 @@ import airlock_libs from models.agent import Agent from models.policy import Policy from services.API import AirlockAPIWrapper +from services.security import getAPI from TUI.Screens.executionhistoryscreen import ExecutionHistoryScreen from TUI.Screens.moveagentworkflowscreen import MoveAgentWorkflowScreen from TUI.Screens.otpactivityscreen import OTPActivitiesScreen @@ -70,7 +71,6 @@ from utils.configmanager import ( load_env, save_user_config, ) -from utils.security import getAPI from utils.setup import get_base_directory, setup from utils.utils import irtang, open_directory diff --git a/TUI/Widgets/agentmoveoperations.py b/TUI/Widgets/agentmoveoperations.py index 412a200..00e465b 100644 --- a/TUI/Widgets/agentmoveoperations.py +++ b/TUI/Widgets/agentmoveoperations.py @@ -273,11 +273,11 @@ class AgentMoveOperations(Widget): results_lines.append(" (none)") results_lines.append("") - results_lines.append(f"❌ Failed ({len(unsuccessful)}):") + results_lines.append(f"❌ Failed ({len(unsuccessful)}):") if unsuccessful: for agent, error in unsuccessful: - results_lines.append(f" ❌ {agent.hostname}: {error}") + results_lines.append(f" ❌ {agent.hostname}: {error}") else: results_lines.append(" (none)") @@ -312,9 +312,9 @@ class AgentMoveOperations(Widget): - Operations panel: 1/3 width - Results area: Initially hidden, shown after operation completion """ - yield Header(show_clock=True, icon="âš™❗") + yield Header(show_clock=True, icon="⚙️") title_text = Static( - f"🖥❗ Agent Operations - {len(self.agents)} device(s) selected", + f"🖥️ Agent Operations - {len(self.agents)} device(s) selected", id="move_ops_title", ) title_text.styles.margin = (0, 0, 1, 0) @@ -349,39 +349,39 @@ class AgentMoveOperations(Widget): yield operations_label # Operation buttons - export_csv_btn = Button("📄 Export CSV", id="export_csv_btn") + export_csv_btn = Button("📄 Export CSV", id="export_csv_btn") export_csv_btn.styles.width = "100%" export_csv_btn.styles.margin = (0, 0, 1, 0) yield export_csv_btn local_approval_btn = Button( - "✔❗ Local Approval Mode", id="local_approval_btn" + "✔️ Local Approval Mode", id="local_approval_btn" ) local_approval_btn.styles.width = "100%" local_approval_btn.styles.margin = (0, 0, 1, 0) yield local_approval_btn - otp_gen_btn = Button("🎫 Generate One Time Passes", id="otp_gen_btn") + otp_gen_btn = Button("🎫 Generate One Time Passes", id="otp_gen_btn") otp_gen_btn.styles.width = "100%" otp_gen_btn.styles.margin = (0, 0, 1, 0) yield otp_gen_btn toggle_enforcement_btn = Button( - "🔄 Toggle Audit/Enforcement", id="toggle_enforcement_btn" + "🔄 Toggle Audit/Enforcement", id="toggle_enforcement_btn" ) toggle_enforcement_btn.styles.width = "100%" toggle_enforcement_btn.styles.margin = (0, 0, 1, 0) yield toggle_enforcement_btn other_policy_btn = Button( - "🔀 Move to Other Policy", id="other_policy_btn" + "🔀 Move to Other Policy", id="other_policy_btn" ) other_policy_btn.styles.width = "100%" other_policy_btn.styles.margin = (0, 0, 1, 0) yield other_policy_btn exec_history_btn = Button( - "📊 View Execution History", id="exec_history_btn" + "📊 View Execution History", id="exec_history_btn" ) exec_history_btn.styles.width = "100%" exec_history_btn.styles.margin = (0, 0, 1, 0) @@ -448,17 +448,17 @@ class AgentMoveOperations(Widget): pyperclip.copy(results_text.text) self.app.notify( - "📋✅ Results copied to clipboard!", + "📋✅ Results copied to clipboard!", severity="information", timeout=2, ) except ImportError: self.app.notify( - "❌ pyperclip not installed. Run: pip install pyperclip", + "❌ pyperclip not installed. Run: pip install pyperclip", severity="warning", ) except Exception as e: - self.app.notify(f"❌ Failed to copy: {str(e)}", severity="error") + self.app.notify(f"❌ Failed to copy: {str(e)}", severity="error") event.stop() elif btn_id == "export_csv_btn": self._start_export_csv_operation() @@ -509,7 +509,7 @@ class AgentMoveOperations(Widget): self.operation_in_progress = True status_label = self.query_one("#status_label", Static) - status_label.update("✔❗ Moving agents to local approval...") + status_label.update("✔️ Moving agents to local approval...") # Get API from app api = self.app.api @@ -542,7 +542,7 @@ class AgentMoveOperations(Widget): except Exception as e: logger.error(f"Error during local approval operation: {e}") - status_label.update(f"❌ Error: {str(e)}") + status_label.update(f"❌ Error: {str(e)}") self.operation_in_progress = False return @@ -592,7 +592,7 @@ class AgentMoveOperations(Widget): successful.append(file_path) status_label.update(f"✅ Exported to {file_path}") except Exception: - status_label.update("❌ Failed") + status_label.update("❌ Failed") self.operation_in_progress = False @@ -636,7 +636,7 @@ class AgentMoveOperations(Widget): self.operation_in_progress = True status_label = self.query_one("#status_label", Static) - status_label.update("🔄 Toggling enforcement mode...") + status_label.update("🔄 Toggling enforcement mode...") # Get API from app api = self.app.api @@ -669,7 +669,7 @@ class AgentMoveOperations(Widget): except Exception as e: logger.error(f"Error during toggle enforcement operation: {e}") - status_label.update(f"❌ Error: {str(e)}") + status_label.update(f"❌ Error: {str(e)}") self.operation_in_progress = False return @@ -739,7 +739,7 @@ class AgentMoveOperations(Widget): except Exception as e: logger.error(f"Error loading policies: {e}") - status_label.update(f"❌ Error: {str(e)}") + status_label.update(f"❌ Error: {str(e)}") self.operation_in_progress = False self.selected_operation = "" self.app.notify(f"Failed to load policies: {str(e)}", severity="error") @@ -774,7 +774,7 @@ class AgentMoveOperations(Widget): ) except Exception as e: logger.error(f"Failed to open execution history viewer: {e}") - status_label.update(f"❌ Error: {str(e)}") + status_label.update(f"❌ Error: {str(e)}") self.app.notify( f"Failed to open execution history: {str(e)}", severity="error" ) diff --git a/docs/01_Software_Requirements_Specification.md b/docs/01_Software_Requirements_Specification.md new file mode 100644 index 0000000..90e64c2 --- /dev/null +++ b/docs/01_Software_Requirements_Specification.md @@ -0,0 +1,1109 @@ +# Loxide +## Software Requirements Specification + +**Document Version:** 1.0 +**Date:** December 2025 +**Authors:** Brandon Wickline (Lead Python Developer), James Brotosky (Lead Rust Developer) +**Status:** Approved + +--- + +## Document Control + +### Revision History + +| Version | Date | Author | Description | +|---------|------|--------|-------------| +| 0.1 | Jul 2025 | B. Wickline | Initial draft | +| 0.2 | Oct 2025 | J. Brotosky | Added Rust integration requirements | +| 0.3 | Nov 2025 | B. Wickline | Added OTP management, execution history | +| 1.0 | Dec 2025 | B. Wickline | First release candidate | + +### Document Approval + +| Role | Name | Signature | Date | +|------|------|-----------|------| +| Lead Developer | Brandon Wickline | | | +| Lead Developer | James Brotosky | | | +| Project Sponsor | | | | + +### Distribution List + +This document is distributed to all project stakeholders including development team members, security administrators, and operations staff. + +--- + +## Table of Contents + +1. Introduction +2. Overall Description +3. Specific Requirements +4. External Interface Requirements +5. System Features +6. Non-Functional Requirements +7. Data Requirements +8. Security Requirements +9. Phase 2: LEMON Integration +10. Appendices + +--- + +## 1. Introduction + +### 1.1 Purpose + +This Software Requirements Specification (SRS) document provides a comprehensive description of the functional and non-functional requirements for Loxide, a terminal-based user interface (TUI) application for managing Airlock endpoint security infrastructure. This document is intended for: + +- Development team members responsible for implementation +- Quality assurance personnel responsible for testing +- System administrators who will deploy and operate the system +- Security personnel who will use the system for endpoint management +- Project stakeholders requiring visibility into system capabilities + +This document serves as the authoritative source for system requirements and provides the foundation for design, implementation, and testing activities. + +### 1.2 Scope + +Loxide is a Python-based TUI application that provides an enhanced management interface for Airlock endpoint security infrastructure. The system enables security administrators to perform bulk operations on endpoints, manage security policies, generate and monitor One-Time Passwords (OTPs), and analyze execution history data. + +#### 1.2.1 In Scope + +The following capabilities are within the scope of this specification: + +- Multi-agent selection and bulk operations on endpoints +- Policy lifecycle management including enforcement preparation +- One-Time Password (OTP) generation, monitoring, and revocation +- Execution history analysis with configurable date ranges +- Server activity log monitoring and analysis +- Quiet agent detection for enforcement readiness assessment +- Allowlist management and hash approval workflows +- Secure credential storage using platform-native mechanisms +- Configuration management with system and user-level settings +- Data export capabilities (CSV format) +- Visual analytics through terminal-based charts + +#### 1.2.2 Out of Scope + +The following items are explicitly excluded from this specification: + +- Direct modification of Airlock server configuration +- Agent installation or removal from endpoints +- Network-level security controls +- Integration with third-party SIEM systems (planned for Phase 2) +- Mobile or web-based interfaces +- Real-time alerting and notification systems + +### 1.3 Definitions, Acronyms, and Abbreviations + +#### 1.3.1 Definitions + +| Term | Definition | +|------|------------| +| Agent | Software component installed on endpoints that communicates with the Airlock server and enforces security policies | +| Allowlist | A collection of approved application hashes that are permitted to execute under a security policy | +| Audit Mode | Policy state where unauthorized application executions are logged but not blocked, allowing observation without enforcement | +| Baseline | A predefined set of known-good application hashes used as a reference for policy configuration | +| Blocklist | A collection of prohibited application hashes that are blocked from execution regardless of other policy settings | +| Enforcement Mode | Policy state where unauthorized application executions are actively blocked according to policy rules | +| Execution History | Log of application execution events recorded by agents, including metadata such as filename, hash, publisher, and timestamp | +| Hash | A cryptographic fingerprint (SHA-256) uniquely identifying an application binary | +| One-Time Password (OTP) | A temporary credential that allows bypassing policy restrictions for a specified duration | +| Policy | A set of rules defining which applications can execute on endpoints assigned to a policy group | +| Policy Group | A logical container for security policy configuration that can have multiple agents assigned | +| Publisher | The digital signature certificate holder that signed an application binary | +| Quiet Agent | An endpoint that has not reported any execution activity within a specified time period | +| VirusTotal (VT) Score | A reputation score from VirusTotal indicating how many antivirus engines flagged a hash as malicious | + +#### 1.3.2 Acronyms + +| Acronym | Expansion | +|---------|-----------| +| API | Application Programming Interface | +| CSV | Comma-Separated Values | +| GUI | Graphical User Interface | +| HTTPS | Hypertext Transfer Protocol Secure | +| JSON | JavaScript Object Notation | +| KDF | Key Derivation Function | +| OTP | One-Time Password | +| PBKDF2 | Password-Based Key Derivation Function 2 | +| TUI | Text User Interface | +| VT | VirusTotal | +| XML | Extensible Markup Language | + +### 1.4 References + +| Reference | Description | +|-----------|-------------| +| Airlock API Documentation | Official API reference for Airlock server endpoints | +| Textual Framework Documentation | Reference documentation for Textual TUI framework (v6.5.0) | +| Python 3.10+ Language Reference | Official Python programming language specification | +| NIST SP 800-132 | Recommendation for Password-Based Key Derivation | +| airlock_libs Documentation | Internal documentation for private Airlock library package | + +### 1.5 Overview + +The remainder of this document is organized as follows: + +- **Section 2** provides an overall description of the product including its context, functions, user characteristics, constraints, and assumptions +- **Section 3** details specific functional requirements organized by feature area +- **Section 4** describes external interface requirements including user, hardware, software, and communication interfaces +- **Section 5** provides detailed system feature specifications +- **Section 6** specifies non-functional requirements including performance, security, and quality attributes +- **Section 7** details data requirements including data models and persistence +- **Section 8** covers security requirements in depth +- **Section 9** describes planned Phase 2 LEMON integration +- **Section 10** contains appendices with supplementary information + +--- + +## 2. Overall Description + +### 2.1 Product Perspective + +Loxide operates as a client application that interfaces with an existing Airlock server infrastructure. It provides an enhanced management interface for administrators who require bulk operations, advanced analytics, and streamlined workflows not available in the standard Airlock management console. + +#### 2.1.1 System Context + +``` + +-------------------+ + | Airlock Server | + | | + | - Policy Engine | + | - Agent Manager | + | - Hash Database | + | - VT Integration | + +--------+----------+ + | + | HTTPS/REST API + | ++------------------+ +--------+----------+ +| Endpoints | | Loxide | +| | | | +| +------+ +----+ | | +---------------+ | +| |Agent | |Agent| |<------------->| | TUI Client | | +| +------+ +----+ | Policies | +---------------+ | +| ... | | | | ++------------------+ | +------+------+ | + | | API Wrapper | | + | +-------------+ | + | | | + | +------+------+ | + | | Rust Libs | | + | +-------------+ | + +------------------+ + | + +--------+----------+ + | Local Storage | + | | + | - Encrypted Keys | + | - Config Files | + | - Log Files | + +-------------------+ +``` + +#### 2.1.2 System Interfaces + +Loxide interfaces with the following external systems: + +1. **Airlock Server API** - RESTful API providing access to agent management, policy configuration, OTP operations, and execution history data. VirusTotal reputation checking is performed through Airlock's built-in VT integration. +2. **Platform Keyring** - Operating system credential storage for secure API key management +3. **File System** - Local storage for configuration files, logs, and exported data +4. **Private PyPI Server** - Package repository for the airlock_libs dependency + +#### 2.1.3 Relationship to Other Products + +Loxide complements but does not replace the standard Airlock management console. It is designed for power users requiring: + +- Bulk operations across many endpoints +- Scripted or repeatable workflows +- Terminal-based access without GUI dependencies +- Advanced filtering and analysis capabilities +- Integration with terminal-based operational workflows + +### 2.2 Product Functions + +Loxide provides the following major functional areas: + +#### 2.2.1 Multi-Agent Operations + +The system enables bulk operations on multiple endpoints simultaneously: + +- Selection of agents by hostname, wildcard pattern, or file import +- Movement of agents between policy groups +- Toggling between audit and enforcement modes +- Generation of OTPs for multiple agents +- Viewing execution history for selected agents + +#### 2.2.2 Policy Preparation Workflow + +A guided workflow for preparing policies for enforcement: + +- Selection of source policies for analysis +- Configuration of analysis parameters (history days, allowlists) +- Fetching and categorization of execution history data +- Review of unapproved applications by category +- Addition of approved hashes to allowlists +- Export of analysis results for documentation + +#### 2.2.3 Quiet Agent Detection + +Identification of inactive endpoints ready for enforcement: + +- Configuration of quiet day threshold +- High-performance analysis using Rust libraries +- Review of quiet agent list with policy information +- Bulk movement of quiet agents to enforcement + +#### 2.2.4 OTP Management + +Complete One-Time Password lifecycle management: + +- Generation of OTPs with configurable duration and purpose +- Monitoring of active OTP sessions +- Viewing of applications executed during OTP sessions +- Revocation of active OTPs +- Historical OTP activity analysis + +#### 2.2.5 Execution History Analysis + +Detailed analysis of application execution events: + +- Date range selection for focused analysis +- Filtering by agent, policy, filename, publisher, or hash +- Categorization of executions by approval status +- Export of results for external analysis +- VirusTotal reputation data display (retrieved through Airlock's VT integration) + +#### 2.2.6 Server Activity Monitoring + +Real-time visibility into Airlock server operations: + +- Display of server activity logs +- Configurable time window (default 72 hours) +- Automatic refresh capability +- Filtering and search functionality + +### 2.3 User Classes and Characteristics + +Loxide is designed for three primary user classes with varying levels of expertise and access requirements: + +#### 2.3.1 Administrator + +**Description:** Primary users responsible for managing endpoint security policies across the organization. Administrators have full access to all Loxide functionality. + +**Characteristics:** +- Advanced technical expertise in endpoint security +- Familiarity with Airlock concepts and configuration +- Comfortable with terminal-based interfaces +- Responsible for policy lifecycle management +- May manage hundreds to thousands of endpoints + +**Primary Activities:** +- Policy preparation and enforcement rollout +- Bulk agent operations +- Security posture assessment +- Configuration management + +#### 2.3.2 Analyst + +**Description:** Security operations personnel who use Loxide for monitoring, investigation, and reporting purposes. Analysts primarily consume data rather than making configuration changes. + +**Characteristics:** +- Intermediate technical expertise +- Focus on monitoring and investigation +- May not have full Airlock administrative privileges +- Responsible for security event analysis + +**Primary Activities:** +- Execution history analysis +- OTP activity monitoring +- Server log review +- Report generation and export + +#### 2.3.3 Support + +**Description:** IT support personnel who use Loxide for operational tasks such as OTP generation for end users experiencing application blocking issues. + +**Characteristics:** +- Basic to intermediate technical expertise +- Ticket-driven workflow +- Need for quick, targeted operations +- Limited scope of access + +**Primary Activities:** +- OTP generation for end users +- Basic agent status lookup +- Escalation of complex issues to administrators + +### 2.4 Operating Environment + +#### 2.4.1 Hardware Requirements + +| Component | Minimum | Recommended | +|-----------|---------|-------------| +| Processor | 64-bit x86 processor | Multi-core processor | +| Memory | 4 GB RAM | 8 GB RAM | +| Storage | 500 MB available | 1 GB available | +| Display | 80x24 terminal | 120x40 terminal | + +#### 2.4.2 Software Requirements + +| Component | Requirement | +|-----------|-------------| +| Operating System | Windows 10/11, Linux (Ubuntu 24+) | +| Python Runtime | 3.10 or higher (for development/pip install) | +| Terminal | Modern terminal with Unicode and ANSI escape code support | +| Network | HTTPS access to Airlock server | + +#### 2.4.3 Network Requirements + +- Outbound HTTPS (port 443) to Airlock server +- Outbound HTTPS to Gitea instance (development only) +- No inbound connections required +- Proxy support via standard environment variables + +### 2.5 Design and Implementation Constraints + +#### 2.5.1 Mandatory Requirements + +The following requirements are mandatory and non-negotiable: + +1. **Terminal-Based Interface** - The application must operate as a TUI without requiring a graphical desktop environment +2. **Secure Credential Storage** - API keys must be stored using platform-native secure storage mechanisms with encryption at rest +3. **HTTPS Communication** - All communication with the Airlock server must use HTTPS +4. **Private Dependency** - The application depends on airlock_libs package hosted on Gitea +5. **Windows Executable Distribution** - The application must be distributable as a standalone Windows executable + +#### 2.5.2 Implementation Choices + +The following are implementation choices that may be revisited: + +| Choice | Rationale | Flexibility | +|--------|-----------|-------------| +| Textual Framework | Python async support, modern styling, active development | May consider alternatives if significant limitations discovered | +| GNU AGPL v3.0 License | Open source with copyleft | License may change based on business requirements | +| Nuitka Compilation | Produces standalone executable, good performance | May consider PyInstaller or other tools | +| PBKDF2-HMAC-SHA256 (200,000 iterations) | Industry standard, configurable security | Parameters may be updated for security improvements | + +### 2.6 Assumptions and Dependencies + +#### 2.6.1 Assumptions + +1. Users have valid Airlock API credentials with appropriate permissions for their role +2. Network connectivity to the Airlock server is available and reliable +3. The terminal emulator supports Unicode characters and ANSI escape sequences +4. Users have basic familiarity with terminal applications +5. The Airlock server API version is compatible with the API wrapper implementation +6. Sufficient disk space is available for log files and exported data +7. VirusTotal integration is configured on the Airlock server for reputation data + +#### 2.6.2 Dependencies + +| Dependency | Version | Purpose | +|------------|---------|---------| +| textual | 6.5.0 | TUI framework | +| pandas | 2.3.3 | Data manipulation and analysis | +| numpy | 2.3.4 | Numerical operations (pandas dependency) | +| requests | 2.32.5 | HTTP client for API communication | +| pymongo | 4.15.3 | MongoDB client (telemetry, future features) | +| cryptography | 46.0.3 | Encryption primitives | +| keyring | 25.6.0 | Platform keyring integration | +| python-dotenv | 1.2.1 | Environment variable management | +| urllib3 | 2.5.0 | HTTP library (requests dependency) | +| plotext | 5.3.2 | Terminal-based charts | +| pyperclip | 1.11.0 | Clipboard integration | +| airlock_libs | 6.1.1 | Private Airlock integration library (Gitea) | + +--- + +## 3. Specific Requirements + +### 3.1 External Interface Requirements + +#### 3.1.1 User Interfaces + +##### 3.1.1.1 General UI Requirements + +| ID | Requirement | Priority | +|----|-------------|----------| +| UI-001 | The system shall provide a terminal-based user interface using the Textual framework | Must | +| UI-002 | The interface shall support keyboard-only navigation for all functions | Must | +| UI-003 | The interface shall display a header showing application name and current context | Must | +| UI-004 | The interface shall display a footer showing available keyboard shortcuts | Must | +| UI-005 | The interface shall support multiple color themes selectable by the user | Should | +| UI-006 | The interface shall display progress indicators for long-running operations | Must | +| UI-007 | The interface shall display error messages as toast notifications | Must | +| UI-008 | The interface shall support a minimum terminal size of 80 columns by 24 rows | Must | +| UI-009 | The interface shall gracefully handle terminal resize events | Should | + +##### 3.1.1.2 Main Screen Requirements + +| ID | Requirement | Priority | +|----|-------------|----------| +| UI-010 | The main screen shall provide tabbed navigation for major functional areas | Must | +| UI-011 | The main screen shall display summary statistics for the connected environment | Should | +| UI-012 | The main screen shall indicate the currently connected Airlock server | Must | +| UI-013 | The main screen shall provide access to application settings | Must | + +##### 3.1.1.3 Data Display Requirements + +| ID | Requirement | Priority | +|----|-------------|----------| +| UI-020 | Data tables shall support column sorting where applicable | Should | +| UI-021 | Data tables shall support row selection for bulk operations | Must | +| UI-022 | Data tables shall display scrollbars when content exceeds visible area | Must | +| UI-023 | Data tables shall support keyboard navigation between rows and columns | Must | +| UI-024 | Charts shall render using ASCII/Unicode characters within the terminal | Must | + +#### 3.1.2 Hardware Interfaces + +The system does not have direct hardware interfaces. All hardware interaction is mediated through the operating system. + +#### 3.1.3 Software Interfaces + +##### 3.1.3.1 Airlock Server API + +| ID | Requirement | Priority | +|----|-------------|----------| +| SW-001 | The system shall communicate with the Airlock server via its REST API | Must | +| SW-002 | The system shall authenticate to the API using an API key in the X-APIKey header | Must | +| SW-003 | The system shall handle API error responses and display appropriate error messages | Must | +| SW-004 | The system shall support configurable API endpoint URLs | Must | +| SW-005 | The system shall implement request timeout handling with configurable timeouts | Should | +| SW-006 | The system shall support SSL certificate verification bypass for self-signed certificates | Must | +| SW-007 | The system shall retrieve VirusTotal reputation data through Airlock's VT integration | Must | + +##### 3.1.3.2 Platform Keyring + +| ID | Requirement | Priority | +|----|-------------|----------| +| SW-010 | On Windows, the system shall use Windows Credential Manager for credential storage | Must | +| SW-011 | On Linux, the system shall use Secret Service (KWallet/GNOME Keyring) for credential storage | Must | +| SW-012 | The system shall encrypt API keys before storing in the keyring | Must | +| SW-013 | The system shall support credential update and deletion operations | Should | + +##### 3.1.3.3 File System + +| ID | Requirement | Priority | +|----|-------------|----------| +| SW-020 | The system shall store configuration files in platform-appropriate locations | Must | +| SW-021 | On Windows, configuration shall be stored in %APPDATA%\Loxide | Must | +| SW-022 | On Linux, configuration shall be stored in ~/.local/share/Loxide | Must | +| SW-023 | The system shall create configuration directories if they do not exist | Must | +| SW-024 | The system shall write log files to a logs subdirectory | Must | +| SW-025 | The system shall support exporting data to user-specified file locations | Must | + +#### 3.1.4 Communications Interfaces + +| ID | Requirement | Priority | +|----|-------------|----------| +| CI-001 | All API communication shall use HTTPS (TLS 1.2 or higher) | Must | +| CI-002 | The system shall use JSON format for API request and response payloads | Must | +| CI-003 | The system shall support HTTP proxy configuration via environment variables | Should | +| CI-004 | The system shall implement connection retry logic for transient failures | Should | + +### 3.2 Functional Requirements + +#### 3.2.1 Multi-Agent Selection (FR-001) + +##### 3.2.1.1 Description + +The system shall provide a multi-agent selector widget that enables users to select multiple agents for bulk operations using various selection methods. + +##### 3.2.1.2 Requirements + +| ID | Requirement | Priority | +|----|-------------|----------| +| FR-001-01 | The system shall accept device names via paste input supporting 700+ lines | Must | +| FR-001-02 | The system shall support wildcard patterns using * (any characters) and ? (single character) | Must | +| FR-001-03 | The system shall provide file import functionality for device lists | Must | +| FR-001-04 | The system shall offer exact and fuzzy matching modes | Should | +| FR-001-05 | The system shall display unmatched entries for user review | Must | +| FR-001-06 | The system shall allow selection/deselection of all matched agents | Must | +| FR-001-07 | The system shall display agent count and selection status | Must | +| FR-001-08 | The system shall support search filtering of the agent list | Should | + +##### 3.2.1.3 Inputs + +- Text input containing device names (one per line or comma-separated) +- File path for device list import +- Wildcard pattern string +- Match mode selection (exact/fuzzy) + +##### 3.2.1.4 Processing + +1. Parse input text to extract individual device identifiers +2. For each identifier, query the Airlock API for matching agents +3. Apply wildcard expansion for pattern inputs +4. Compile matched and unmatched lists +5. Present results to user for selection confirmation + +##### 3.2.1.5 Outputs + +- List of selected agent objects with full metadata +- List of unmatched device names +- Selection count summary + +#### 3.2.2 Policy Preparation Workflow (FR-002) + +##### 3.2.2.1 Description + +The system shall provide a multi-step wizard for preparing security policies for enforcement by analyzing execution history and managing allowlists. + +##### 3.2.2.2 Requirements + +| ID | Requirement | Priority | +|----|-------------|----------| +| FR-002-01 | The system shall support selection of multiple source policies for analysis | Must | +| FR-002-02 | The system shall allow configuration of history days (1-365 days) | Must | +| FR-002-03 | The system shall allow selection of destination allowlists for hash approval | Must | +| FR-002-04 | The system shall fetch execution history for all agents in selected policies | Must | +| FR-002-05 | The system shall categorize executions by approval status (approved, unapproved, blocked) | Must | +| FR-002-06 | The system shall display unapproved executions with VT scores from Airlock's VT integration | Must | +| FR-002-07 | The system shall support adding selected hashes to allowlists | Must | +| FR-002-08 | The system shall export analysis results to CSV | Must | +| FR-002-09 | The system shall display progress during data fetch operations | Must | +| FR-002-10 | The system shall handle large execution datasets without memory exhaustion | Must | + +##### 3.2.2.3 Workflow Steps + +1. **Policy Selection** - User selects one or more source policies +2. **Configuration** - User configures history days and selects allowlists +3. **Data Fetch** - System retrieves execution history from Airlock +4. **Analysis** - System categorizes and summarizes execution data +5. **Review** - User reviews unapproved executions by category +6. **Approval** - User selects hashes to add to allowlist +7. **Export** - User exports results for documentation + +#### 3.2.3 Quiet Agent Detection (FR-003) + +##### 3.2.3.1 Description + +The system shall identify agents with no recent execution activity that may be ready for enforcement mode transition. + +##### 3.2.3.2 Requirements + +| ID | Requirement | Priority | +|----|-------------|----------| +| FR-003-01 | The system shall allow configuration of quiet day threshold | Must | +| FR-003-02 | The system shall analyze agent activity using high-performance Rust code | Must | +| FR-003-03 | The system shall display analysis progress with progress bar | Must | +| FR-003-04 | The system shall list quiet agents with hostname, policy, and last activity date | Must | +| FR-003-05 | The system shall support bulk selection of quiet agents for enforcement | Must | +| FR-003-06 | The system shall suspend the TUI during Rust analysis to show console progress | Must | + +#### 3.2.4 OTP Management (FR-004) + +##### 3.2.4.1 Description + +The system shall provide complete One-Time Password lifecycle management including generation, monitoring, and revocation. + +##### 3.2.4.2 Requirements + +| ID | Requirement | Priority | +|----|-------------|----------| +| FR-004-01 | The system shall generate OTPs for selected agents | Must | +| FR-004-02 | The system shall support configurable OTP duration | Must | +| FR-004-03 | The system shall require purpose/ticket number for OTP generation | Must | +| FR-004-04 | The system shall display generated OTP codes with copy-to-clipboard support | Must | +| FR-004-05 | The system shall list all active OTP sessions | Must | +| FR-004-06 | The system shall display OTP session details including agent, duration, and status | Must | +| FR-004-07 | The system shall allow revocation of active OTPs | Must | +| FR-004-08 | The system shall display applications executed during OTP sessions | Must | +| FR-004-09 | The system shall support filtering OTPs by status (active, awaiting, enforced, revoked) | Should | + +#### 3.2.5 Execution History Analysis (FR-005) + +##### 3.2.5.1 Description + +The system shall provide detailed analysis capabilities for application execution events recorded by agents. + +##### 3.2.5.2 Requirements + +| ID | Requirement | Priority | +|----|-------------|----------| +| FR-005-01 | The system shall support date range selection for history queries | Must | +| FR-005-02 | The system shall display execution events in a sortable data table | Must | +| FR-005-03 | The system shall show filename, publisher, hash, timestamp, and category | Must | +| FR-005-04 | The system shall display VT reputation scores obtained through Airlock's VT integration | Must | +| FR-005-05 | The system shall support export of execution history to CSV | Must | +| FR-005-06 | The system shall support filtering by various criteria | Should | +| FR-005-07 | The system shall handle large result sets with pagination or virtual scrolling | Should | + +#### 3.2.6 Server Activity Monitoring (FR-006) + +##### 3.2.6.1 Description + +The system shall provide visibility into Airlock server activity logs for operational monitoring. + +##### 3.2.6.2 Requirements + +| ID | Requirement | Priority | +|----|-------------|----------| +| FR-006-01 | The system shall display server activity logs | Must | +| FR-006-02 | The system shall support configurable time window (default 72 hours) | Should | +| FR-006-03 | The system shall provide refresh capability (manual and automatic) | Must | +| FR-006-04 | The system shall display timestamp, event type, user, and details | Must | +| FR-006-05 | The system shall format timestamps consistently (YYYY-MM-DD HH:MM:SS) | Must | + +#### 3.2.7 Configuration Management (FR-007) + +##### 3.2.7.1 Description + +The system shall support two-tier configuration with system-level (immutable) and user-level (mutable) settings. + +##### 3.2.7.2 Requirements + +| ID | Requirement | Priority | +|----|-------------|----------| +| FR-007-01 | The system shall load system configuration from bundled system_config.json | Must | +| FR-007-02 | The system shall load user configuration from user_config.json in config directory | Must | +| FR-007-03 | The system shall prevent modification of system configuration keys | Must | +| FR-007-04 | The system shall allow modification of user configuration keys | Must | +| FR-007-05 | The system shall create default user configuration if not present | Must | +| FR-007-06 | The system shall support UI theme selection as user preference | Should | + +##### 3.2.7.3 System Configuration Keys + +The following keys are system-controlled and cannot be modified by users: + +- URL - Airlock server base URL +- APPNAME - Application name +- LOG_LEVEL - Logging verbosity +- BAD_PATH_PARTS - Paths to flag in analysis +- BAD_PUBLISHERS - Publishers to flag in analysis +- PUPS - Potentially Unwanted Programs list +- PATH_EXCLUSION_CONST - Path exclusion threshold +- MIN_FILES_FOR_PATH - Minimum files for path suggestion +- VT_THREAT_TOLERANCE - VirusTotal score threshold (used with Airlock's VT data) +- POLICY_MAP_ENF_AUD - Policy enforcement/audit mappings + +##### 3.2.7.4 User Configuration Keys + +The following keys can be modified by users: + +- TELEMETRY - Opt-in/out for telemetry +- TELEM_URL - Telemetry endpoint URL +- TEXTUAL_THEME - UI theme preference +- EXTRAS - Feature flags + +--- + +## 4. External Interface Requirements + +### 4.1 Airlock API Interface + +#### 4.1.1 API Overview + +Loxide communicates with the Airlock server through its REST API. All operations require authentication via API key. VirusTotal reputation data is obtained through Airlock's built-in VT integration rather than direct VT API calls. + +#### 4.1.2 Authentication + +``` +Header: X-APIKey: +Content-Type: application/json +``` + +#### 4.1.3 API Endpoints Used + +| Endpoint | Method | Purpose | +|----------|--------|---------| +| /v1/agent/find | POST | Search for agents | +| /v1/agent/move | POST | Move agent to policy group | +| /v1/group | POST | List policy groups | +| /v1/group/agents | POST | List agents in group | +| /v1/group/policies | POST | List allowlists for group | +| /v1/group/settings/auditmode | POST | Set audit/enforcement mode | +| /v1/application | POST | List allowlists | +| /v1/hash/application/add | POST | Add hashes to allowlist | +| /v1/hash/query | POST | Query hash information (includes VT data from Airlock) | +| /v1/otp/retrieve | POST | Generate OTP | +| /v1/otp/usage | POST | Query OTP usage | +| /v1/otp/revoke | POST | Revoke OTP | +| /v1/otp/activities | POST | Get OTP activities | +| /v1/getexechistory | POST | Get execution history | +| /v1/logging/svractivities | POST | Get server logs | +| /v1/baseline | POST | List baselines | +| /v1/blocklist | POST | List blocklists | + +#### 4.1.4 Error Handling + +The system shall handle the following API error conditions: + +| HTTP Status | Handling | +|-------------|----------| +| 400 Bad Request | Display validation error message | +| 401 Unauthorized | Prompt for credential re-entry | +| 403 Forbidden | Display permission denied message | +| 404 Not Found | Display resource not found message | +| 500 Internal Server Error | Display server error with retry option | +| Network Error | Display connectivity error with retry option | + +### 4.2 Data Export Interface + +#### 4.2.1 CSV Export Format + +All CSV exports shall: + +- Use UTF-8 encoding with BOM for Excel compatibility +- Use comma as field delimiter +- Quote fields containing commas, quotes, or newlines +- Include header row with column names +- Use ISO 8601 format for timestamps + +#### 4.2.2 Export File Naming + +Default export file naming convention: +``` +{export_type}_{date}_{time}.csv +Example: execution_history_2025-12-20_143022.csv +``` + +--- + +## 5. System Features + +### 5.1 Agent Move Operations + +#### 5.1.1 Description + +Bulk agent management operations including movement between policy groups and mode toggling. + +#### 5.1.2 Feature Details + +**Move Agents to Policy** +- Select target policy from policy tree +- Confirm selection with agent count +- Execute move with progress indication +- Display results with success/failure counts + +**Toggle Enforcement Mode** +- Display current mode for selected agents +- Confirm toggle action +- Execute toggle with progress indication +- Display results + +**Local Approval** +- Approve agents locally for testing +- Log approval with purpose/ticket + +#### 5.1.3 Dependencies + +- Multi-Agent Selector widget +- Airlock API agent_move endpoint +- Airlock API policy_set_auditmode endpoint + +### 5.2 Analytics Dashboard + +#### 5.2.1 Description + +Visual representation of environment status using terminal-based charts. + +#### 5.2.2 Feature Details + +- Policy distribution charts (agents per policy) +- Enforcement status breakdown +- Execution category breakdown +- OTP activity trends + +#### 5.2.3 Dependencies + +- plotext library +- Airlock API various endpoints + +### 5.3 Allowlist Management + +#### 5.3.1 Description + +Management of application allowlists for policy configuration. + +#### 5.3.2 Feature Details + +- List available allowlists +- Add hashes to allowlist +- Export allowlist to XML +- View allowlist contents + +--- + +## 6. Non-Functional Requirements + +### 6.1 Performance Requirements + +| ID | Requirement | Target | +|----|-------------|--------| +| PF-001 | Application startup time | < 5 seconds | +| PF-002 | Agent list load time (1000 agents) | < 10 seconds | +| PF-003 | Execution history load (30 days, single agent) | < 15 seconds | +| PF-004 | UI response to user input | < 100 milliseconds | +| PF-005 | Memory usage (idle) | < 200 MB | +| PF-006 | Memory usage (large dataset) | < 1 GB | +| PF-007 | CSV export (10,000 rows) | < 30 seconds | + +### 6.2 Reliability Requirements + +| ID | Requirement | +|----|-------------| +| RL-001 | The system shall handle API timeouts gracefully without crashing | +| RL-002 | The system shall recover from network interruptions | +| RL-003 | The system shall preserve unsaved user input during recoverable errors | +| RL-004 | The system shall log all errors for troubleshooting | +| RL-005 | The system shall not corrupt configuration files on abnormal termination | + +### 6.3 Availability Requirements + +| ID | Requirement | +|----|-------------| +| AV-001 | The system shall be available whenever the user's workstation is operational | +| AV-002 | The system shall operate in offline mode for cached data review (future) | +| AV-003 | The system shall clearly indicate when Airlock server is unreachable | + +### 6.4 Maintainability Requirements + +| ID | Requirement | +|----|-------------| +| MT-001 | The system shall use modular architecture with clear separation of concerns | +| MT-002 | The system shall include comprehensive logging for debugging | +| MT-003 | The system shall follow Python PEP 8 style guidelines | +| MT-004 | The system shall include type hints for public interfaces | +| MT-005 | The system shall document all public APIs with docstrings | + +### 6.5 Portability Requirements + +| ID | Requirement | +|----|-------------| +| PT-001 | The system shall run on Windows 10 and Windows 11 | +| PT-002 | The system shall run on Linux (Ubuntu 24+) | +| PT-003 | The system shall use platform-agnostic APIs where possible | +| PT-004 | Platform-specific code shall be isolated in dedicated modules | + +### 6.6 Usability Requirements + +| ID | Requirement | +|----|-------------| +| US-001 | All primary functions shall be accessible via keyboard shortcuts | +| US-002 | Keyboard shortcuts shall be displayed in the footer | +| US-003 | Error messages shall be actionable and suggest resolution | +| US-004 | Long-running operations shall display progress indicators | +| US-005 | The system shall provide confirmation dialogs for destructive operations | +| US-006 | The system shall remember user preferences across sessions | + +--- + +## 7. Data Requirements + +### 7.1 Data Models + +#### 7.1.1 Agent + +| Field | Type | Description | +|-------|------|-------------| +| agentid | string | Unique identifier | +| hostname | string | Device hostname | +| groupid | string | Assigned policy group | +| status | integer | Connection status (0=Offline, 1=Online, 3=Safemode) | +| username | string | Logged-in user | +| osversion | string | Operating system version | +| agentversion | string | Agent software version | +| lastcheckin | datetime | Last communication timestamp | + +#### 7.1.2 Policy + +| Field | Type | Description | +|-------|------|-------------| +| groupid | string | Unique identifier | +| groupname | string | Display name | +| parentid | string | Parent group ID | +| auditmode | integer | Mode (0=Enforcement, 1=Audit) | +| agentcount | integer | Number of assigned agents | + +#### 7.1.3 Execution Event + +| Field | Type | Description | +|-------|------|-------------| +| id | string | Unique identifier | +| hostname | string | Agent hostname | +| filename | string | Executed file name | +| filepath | string | Full file path | +| sha256 | string | File hash | +| publisher | string | Code signing publisher | +| category | integer | Execution category | +| timestamp | datetime | Execution timestamp | +| username | string | Executing user | +| commandline | string | Command line arguments | +| policyname | string | Active policy name | +| vtscore | integer | VirusTotal detection count (from Airlock VT integration) | + +#### 7.1.4 OTP + +| Field | Type | Description | +|-------|------|-------------| +| otpid | string | Unique identifier | +| otpcode | string | One-time password value | +| agentid | string | Associated agent | +| duration | integer | Duration in hours | +| purpose | string | Reason/ticket number | +| status | integer | Status (0=Awaiting, 1=Active, 2=Enforced, 3=Revoked) | +| createtime | datetime | Creation timestamp | +| activetime | datetime | Activation timestamp | +| expiretime | datetime | Expiration timestamp | + +### 7.2 Data Persistence + +#### 7.2.1 Configuration Storage + +- System configuration: Bundled JSON file (read-only) +- User configuration: JSON file in config directory (read-write) + +#### 7.2.2 Credential Storage + +- API keys: Encrypted in platform keyring +- Encryption: AES-256-GCM +- Key derivation: PBKDF2-HMAC-SHA256 + +#### 7.2.3 Log Storage + +- Application logs: Text files in logs directory +- Log rotation: By size or date (configurable) +- Log retention: Configurable days + +--- + +## 8. Security Requirements + +### 8.1 Authentication and Authorization + +| ID | Requirement | +|----|-------------| +| SE-001 | The system shall require API key authentication for all Airlock operations | +| SE-002 | The system shall support password-protected API key storage | +| SE-003 | The system shall enforce password complexity requirements | +| SE-004 | The system shall lock out after 3 failed password attempts | +| SE-005 | The system shall not display API keys in logs or UI | + +### 8.2 Credential Storage Security + +| ID | Requirement | +|----|-------------| +| SE-010 | API keys shall be encrypted at rest using AES-256-GCM | +| SE-011 | Encryption keys shall be derived using PBKDF2-HMAC-SHA256 | +| SE-012 | Key derivation shall use minimum 200,000 iterations | +| SE-013 | Each credential shall use a unique random salt (128-bit) | +| SE-014 | Encrypted credentials shall be stored in platform keyring | + +### 8.3 Password Requirements + +| ID | Requirement | +|----|-------------| +| SE-020 | Passwords shall be minimum 12 characters | +| SE-021 | Passwords shall contain at least one uppercase letter | +| SE-022 | Passwords shall contain at least one lowercase letter | +| SE-023 | Passwords shall contain at least one digit | +| SE-024 | Passwords shall contain at least one special character | + +### 8.4 Communication Security + +| ID | Requirement | +|----|-------------| +| SE-030 | All API communication shall use HTTPS | +| SE-031 | The system shall support TLS 1.2 and TLS 1.3 | +| SE-032 | Certificate validation shall be configurable (for self-signed certs) | +| SE-033 | API keys shall be transmitted in headers, not URLs | + +### 8.5 Data Protection + +| ID | Requirement | +|----|-------------| +| SE-040 | Sensitive data shall not be written to log files | +| SE-041 | Exported data shall not contain API credentials | +| SE-042 | Memory containing credentials shall be cleared after use | +| SE-043 | Configuration files shall have restricted permissions | + +### 8.6 Audit and Logging + +| ID | Requirement | +|----|-------------| +| SE-050 | Security-relevant events shall be logged | +| SE-051 | Logs shall include timestamp, event type, and outcome | +| SE-052 | Authentication failures shall be logged | +| SE-053 | Administrative actions shall be logged | + +--- + +## 9. Phase 2: LEMON Integration + +### 9.1 Overview + +LEMON (Loxide Execution MONitoring) is a planned backend service that will integrate with Loxide to provide automated hash-based local approval sessions. This capability is planned for Phase 2 development after the core Loxide functionality is stable. + +### 9.2 Capability Summary + +LEMON will enable: + +- **Local Approval Sessions** - Time-limited windows where an endpoint runs in audit mode with automatic hash capture +- **Automatic Hash Approval** - Clean hashes (based on VirusTotal reputation from Airlock's VT integration and publisher rules) automatically added to allowlists +- **Manual Review Queue** - Borderline hashes queued for administrator review in Loxide +- **Device State Management** - Automatic return to enforcement mode when session expires + +### 9.3 Key Differentiators from Current OTP + +| Current OTP (Phase 1) | LEMON Local Approval (Phase 2) | +|-----------------------|--------------------------------| +| Bypasses policy entirely | Captures hashes for permanent approval | +| Time-limited bypass only | Results in allowlist additions | +| No record of what ran | Full hash capture during window | +| Must re-request for same apps | One-time approval, permanent trust | +| Single user operation | Multi-user coordination with RBAC | + +### 9.4 Security Requirements + +Given LEMON's capability to modify security controls: + +- Mutual TLS authentication with internal CA certificates +- All requests cryptographically signed for non-repudiation +- Immutable, hash-chained audit log +- Role-based access control with separate LEMON credentials + +### 9.5 Documentation Reference + +Complete LEMON specifications are maintained in separate documents: + +- LEMON_01_Software_Requirements_Specification.md +- LEMON_02_Software_Design_Document.md +- LEMON_03_User_Stories.md + +--- + +## 10. Appendices + +### Appendix A: Glossary + +See Section 1.3 for definitions and acronyms. + +### Appendix B: Analysis Models + +Reserved for data flow diagrams and entity relationship diagrams. + +### Appendix C: Requirements Traceability + +Requirements traceability matrix mapping requirements to design elements, test cases, and user stories is maintained separately. + +### Appendix D: Supporting Information + +Additional supporting materials including UI mockups and workflow diagrams are maintained in the project documentation repository. + +--- + +## Document Approval + +This Software Requirements Specification has been reviewed and approved for implementation. + +| Role | Name | Date | +|------|------|------| +| Author | Brandon Wickline | | +| Technical Reviewer | James Brotosky | | +| Approver | | | + +--- + +*End of Document* diff --git a/docs/02_Software_Design_Document.md b/docs/02_Software_Design_Document.md new file mode 100644 index 0000000..37b058a --- /dev/null +++ b/docs/02_Software_Design_Document.md @@ -0,0 +1,1299 @@ +# Loxide +## Software Design Document + +**Document Version:** 1.0 +**Date:** December 2025 +**Authors:** Brandon Wickline (Lead Python Developer), James Brotosky (Lead Rust Developer) +**Status:** Approved + +--- + +## Document Control + +### Revision History + +| Version | Date | Author | Description | +|---------|------|--------|-------------| +| 0.1 | Jul 2025 | B. Wickline | Initial architecture draft | +| 0.2 | Oct 2025 | J. Brotosky | Added Rust integration design | +| 0.3 | Oct 2025 | B. Wickline | Added security and configuration design | +| 1.0 | Dec 2025 | B. Wickline | First release candidate | + +### Document Approval + +| Role | Name | Signature | Date | +|------|------|-----------|------| +| Lead Developer | Brandon Wickline | | | +| Lead Developer | James Brotosky | | | +| Technical Reviewer | | | | + +--- + +## Table of Contents + +1. Introduction +2. System Overview +3. Architecture Design +4. Component Design +5. Data Design +6. Security Design +7. User Interface Design +8. External Interface Design +9. Distribution and Deployment +10. Error Handling +11. Phase 2: LEMON Integration +12. Appendices + +--- + +## 1. Introduction + +### 1.1 Purpose + +This Software Design Document (SDD) provides a comprehensive description of the design and architecture of Loxide, a terminal-based user interface application for Airlock endpoint security management. This document translates the requirements specified in the Software Requirements Specification (SRS) into a detailed design that can be implemented by the development team. + +### 1.2 Scope + +This document covers: + +- System architecture and component design +- Data models and storage design +- Security implementation details +- User interface design patterns +- External interface specifications +- Distribution and deployment design +- Error handling strategies + +### 1.3 Intended Audience + +This document is intended for: + +- Software developers implementing system components +- System architects reviewing design decisions +- Quality assurance engineers developing test plans +- Operations personnel preparing deployment procedures +- Technical stakeholders requiring design visibility + +### 1.4 Design Goals + +The design prioritizes the following goals in order of importance: + +1. **Security** - Protect credentials and sensitive operations +2. **Reliability** - Handle errors gracefully without data loss +3. **Usability** - Provide intuitive keyboard-driven interface +4. **Performance** - Respond quickly to user actions +5. **Maintainability** - Enable easy modification and extension + +### 1.5 References + +| Reference | Description | +|-----------|-------------| +| Loxide SRS | Software Requirements Specification | +| Textual Documentation | TUI framework reference | +| Airlock API Specification | External API documentation | +| Python Style Guide (PEP 8) | Code style reference | + +--- + +## 2. System Overview + +### 2.1 System Context + +Loxide operates as a client application connecting to an Airlock server for endpoint security management. The system provides a terminal-based interface enabling bulk operations that are cumbersome in the standard web console. + +### 2.2 Design Constraints + +The following constraints influenced the design: + +| Constraint | Impact on Design | +|------------|------------------| +| Terminal-based UI | All interaction via keyboard; no mouse-dependent features | +| Cross-platform (Windows/Linux) | Platform-specific code isolated; keyring abstraction | +| Standalone executable | Nuitka compilation; bundled dependencies | +| Private dependency | airlock_libs requires Gitea access | +| Secure credential storage | Platform keyring with encryption layer | + +### 2.3 System Decomposition + +The system is decomposed into the following major subsystems: + +``` ++------------------------------------------------------------------+ +| LOXIDE | +| | +| +------------------------+ +---------------------------+ | +| | User Interface | | Business Logic | | +| | | | | | +| | +------------------+ | | +---------------------+ | | +| | | Main App | | | | Policy Operations | | | +| | | (Loxide.py) | | | +---------------------+ | | +| | +------------------+ | | +---------------------+ | | +| | +------------------+ | | | Agent Operations | | | +| | | Screens | | | +---------------------+ | | +| | +------------------+ | | +---------------------+ | | +| | +------------------+ | | | OTP Operations | | | +| | | Widgets | | | +---------------------+ | | +| | +------------------+ | | +---------------------+ | | +| +------------------------+ | | Analysis Engine | | | +| | +---------------------+ | | +| +------------------------+ +---------------------------+ | +| | Data Access | | +| | | +---------------------------+ | +| | +------------------+ | | Infrastructure | | +| | | API Wrapper | | | | | +| | | (API.py) | | | +---------------------+ | | +| | +------------------+ | | | Configuration | | | +| | +------------------+ | | +---------------------+ | | +| | | Data Models | | | +---------------------+ | | +| | +------------------+ | | | Security | | | +| +------------------------+ | +---------------------+ | | +| | +---------------------+ | | +| +------------------------+ | | Logging | | | +| | External Libraries | | +---------------------+ | | +| | | +---------------------------+ | +| | +------------------+ | | +| | | airlock_libs | | | +| | | (Rust) | | | +| | +------------------+ | | +| +------------------------+ | ++------------------------------------------------------------------+ +``` + +--- + +## 3. Architecture Design + +### 3.1 Architectural Style + +Loxide employs a layered architecture with clear separation between: + +1. **Presentation Layer** - TUI screens and widgets +2. **Business Logic Layer** - Operations and workflows +3. **Data Access Layer** - API communication and data models +4. **Infrastructure Layer** - Configuration, security, logging + +### 3.2 Module Organization + +``` +loxide/ +├── Loxide.py # Main application entry point +├── models/ +│ ├── agent.py # Agent data model +│ ├── policy.py # Policy data model +│ └── execution.py # Execution event model +├── services/ +│ ├── API.py # Airlock API wrapper +│ └── security.py # Credential management +├── TUI/ +│ ├── Screens/ +│ │ ├── policyprepworkflowscreen.py +│ │ ├── quietagentworkflowscreen.py +│ │ ├── executionhistoryscreen.py +│ │ ├── otpactivityscreen.py +│ │ ├── otprevokescreen.py +│ │ ├── otpworkflowscreen.py +│ │ └── moveagentworkflowscreen.py +│ ├── Widgets/ +│ │ ├── multiagentselector.py +│ │ ├── policyselector.py +│ │ ├── policytreewidget.py +│ │ ├── agentmoveoperations.py +│ │ ├── serverlogwidget.py +│ │ ├── resultsdisplay.py +│ │ └── themeselector.py +│ └── Themes/ +│ ├── theme_amber_terminal.py +│ └── theme_retro_terminal.py +├── utils/ +│ ├── configmanager.py # Configuration management +│ ├── setup.py # Initialization and logging +│ └── utils.py # Utility functions +└── system_config.json # System configuration (bundled) +``` + +### 3.3 Component Interactions + +#### 3.3.1 Startup Sequence + +``` +1. main() called + │ + ├─> setup() initializes logging and configuration + │ ├─> load_system_config() reads system_config.json + │ ├─> load_user_config() reads/creates user_config.json + │ └─> configure logging based on LOG_LEVEL + │ + ├─> getAPI() retrieves credentials + │ ├─> check keyring for stored credential + │ ├─> if found: prompt for password, decrypt + │ └─> if not found: prompt for API key, encrypt, store + │ + ├─> AirlockAPIWrapper() created with base_url and api_key + │ + └─> LoxideApp().run() starts the TUI + ├─> compose() builds initial UI + └─> on_mount() loads initial data +``` + +#### 3.3.2 Screen Navigation Flow + +``` +MainScreen (Tabs) +│ +├── Dashboard Tab +│ └── Summary statistics and charts +│ +├── Agent Operations Tab +│ ├── MultiAgentSelector (widget) +│ └── AgentMoveOperations (widget) +│ ├── Move to Policy → PolicySelector (screen) +│ ├── Toggle Enforcement +│ ├── Generate OTP → OTPWorkflowScreen +│ └── View History → ExecutionHistoryScreen +│ +├── Policy Prep Tab +│ └── PolicyPrepWorkflowScreen +│ ├── Step 1: Select Policies +│ ├── Step 2: Configure +│ ├── Step 3: Fetch Data +│ ├── Step 4: Review Results +│ └── Step 5: Approve Hashes +│ +├── Quiet Agents Tab +│ └── QuietAgentWorkflowScreen +│ ├── Configure thresholds +│ ├── Run analysis (Rust) +│ └── Review results +│ +├── OTP Management Tab +│ ├── Active OTPs list +│ ├── OTPActivitiesScreen +│ └── OTPRevokeScreen +│ +└── Server Logs Tab + └── ServerLogWidget +``` + +### 3.4 Data Flow + +#### 3.4.1 Agent Selection and Operation + +``` +User Input (hostnames) + │ + ▼ +MultiAgentSelector + │ + ├── Parse input (split by newline/comma) + │ + ├── For each hostname: + │ └── API.agent_find_by_hostname() + │ + ├── Compile matches/unmatches + │ + ▼ +User Selection (checkboxes) + │ + ▼ +AgentMoveOperations + │ + ├── Move: API.agent_move() + ├── Toggle: API.policy_set_auditmode() + ├── OTP: API.otp_generate() + └── History: API.history_execution() + │ + ▼ +ResultsDisplay (success/failure counts) +``` + +#### 3.4.2 Policy Preparation Workflow + +``` +Policy Selection + │ + ▼ +Configuration (days, allowlists) + │ + ▼ +Data Fetch + │ + ├── For each policy: + │ └── API.policy_list_agents() + │ + ├── For each agent: + │ └── API.history_execution() + │ + ▼ +Categorization + │ + ├── Check against allowlists + ├── Check against baselines + ├── Apply VT_THREAT_TOLERANCE (using Airlock VT data) + ├── Apply BAD_PUBLISHERS filter + │ + ▼ +Results Display + │ + ├── Approved hashes + ├── Unapproved hashes (by category) + └── Blocked hashes + │ + ▼ +Hash Approval + │ + └── API.hash_add_to_allowlist() +``` + +--- + +## 4. Component Design + +### 4.1 Main Application (Loxide.py) + +#### 4.1.1 Class: LoxideApp + +```python +class LoxideApp(App): + """Main Textual application class.""" + + # Reactive properties + api: reactive[AirlockAPIWrapper] + current_tab: reactive[str] + + # Key bindings + BINDINGS = [ + ("q", "quit", "Quit"), + ("t", "toggle_theme", "Theme"), + ("r", "refresh", "Refresh"), + ("?", "help", "Help"), + ] + + def compose(self) -> ComposeResult: + """Build the main UI layout.""" + yield Header() + yield Tabs(...) + yield ContentSwitcher(...) + yield Footer() + + def on_mount(self) -> None: + """Initialize on application start.""" + self.load_initial_data() + + async def action_quit(self) -> None: + """Handle quit action with confirmation.""" + ... +``` + +#### 4.1.2 Responsibilities + +- Application lifecycle management +- Global keyboard shortcut handling +- Tab navigation coordination +- Theme management +- Error handling and notification display + +### 4.2 API Wrapper (API.py) + +#### 4.2.1 Class: AirlockAPIWrapper + +```python +class AirlockAPIWrapper: + """Wrapper for Airlock REST API communication.""" + + def __init__(self, base_url: str, api_key: str): + self.base_url = base_url.rstrip("/") + self.api_key = api_key + self.headers = {"X-APIKey": self.api_key} + + def _post(self, endpoint: str, payload: dict = None) -> dict: + """Send POST request and return JSON response.""" + ... + + # Agent operations + def agent_find_all(self) -> pd.DataFrame: ... + def agent_find_by_hostname(self, hostname: str) -> pd.DataFrame: ... + def agent_move(self, agentid: str, groupid: str) -> dict: ... + + # Policy operations + def policy_find_all(self) -> pd.DataFrame: ... + def policy_set_auditmode(self, groupid: str, auditmode: str) -> dict: ... + + # OTP operations + def otp_generate(self, agentid: str, duration: int, purpose: str) -> str: ... + def otp_find_active(self) -> pd.DataFrame: ... + def otp_revoke(self, otpid: str) -> dict: ... + + # Hash operations (includes VT data from Airlock) + def hash_query(self, hashes: List[str]) -> pd.DataFrame: ... + def hash_add_to_allowlist(self, applicationid: str, hashes: List[str]) -> dict: ... + + # History operations + def history_execution(self, today: str, date_selected: str, agent_name: str) -> List[Dict]: ... +``` + +#### 4.2.2 Error Handling + +All API methods implement consistent error handling: + +```python +def _post(self, endpoint: str, payload: dict = None) -> dict: + url = f"{self.base_url}{endpoint}" + try: + response = requests.post( + url, + headers=self.headers, + data=json.dumps(payload or {}), + verify=False, # Configurable for self-signed certs + timeout=30 + ) + response.raise_for_status() + return response.json() + except requests.exceptions.Timeout: + logger.error(f"Request timeout: {endpoint}") + raise APITimeoutError(f"Request to {endpoint} timed out") + except requests.exceptions.ConnectionError: + logger.error(f"Connection error: {endpoint}") + raise APIConnectionError(f"Cannot connect to {self.base_url}") + except requests.exceptions.HTTPError as e: + logger.error(f"HTTP error {e.response.status_code}: {endpoint}") + raise APIError(f"API error: {e.response.status_code}") +``` + +### 4.3 Multi-Agent Selector (multiagentselector.py) + +#### 4.3.1 Class: MultiAgentSelector + +```python +class MultiAgentSelector(Widget): + """Widget for selecting multiple agents via various input methods.""" + + # Signals + class AgentsSelected(Message): + def __init__(self, agents: List[Agent]): ... + + def compose(self) -> ComposeResult: + yield Input(placeholder="Paste hostnames or wildcards...") + yield Button("Load from File") + yield DataTable(id="agent-table") + yield Static(id="status") + + async def on_input_submitted(self, event: Input.Submitted) -> None: + """Process pasted input.""" + lines = event.value.strip().split("\n") + await self.process_hostnames(lines) + + async def process_hostnames(self, hostnames: List[str]) -> None: + """Query API for each hostname and populate table.""" + matched = [] + unmatched = [] + for hostname in hostnames: + if "*" in hostname or "?" in hostname: + # Wildcard handling + agents = await self.expand_wildcard(hostname) + else: + agents = self.api.agent_find_by_hostname(hostname) + ... + + def get_selected_agents(self) -> List[Agent]: + """Return list of currently selected agents.""" + ... +``` + +#### 4.3.2 Wildcard Expansion + +```python +async def expand_wildcard(self, pattern: str) -> List[Agent]: + """Expand wildcard pattern to matching agents.""" + import fnmatch + all_agents = self.api.agent_find_all() + matches = [] + for _, agent in all_agents.iterrows(): + if fnmatch.fnmatch(agent["hostname"].lower(), pattern.lower()): + matches.append(agent) + return matches +``` + +### 4.4 Policy Selector (policyselector.py) + +#### 4.4.1 Class: PolicySelector + +```python +class PolicySelector(Widget): + """Reusable widget for policy/group selection.""" + + # Configuration + multi_select: bool = True + show_agent_count: bool = True + + def compose(self) -> ComposeResult: + yield Input(placeholder="Filter policies...") + yield PolicyTreeWidget(id="policy-tree") + + def on_mount(self) -> None: + self.load_policies() + + def load_policies(self) -> None: + """Load policy tree from API.""" + policies = self.api.policy_find_all() + self.query_one(PolicyTreeWidget).build_tree(policies) + + def get_selected_policies(self) -> List[Policy]: + """Return selected policy objects.""" + ... +``` + +### 4.5 Policy Tree Widget (policytreewidget.py) + +#### 4.5.1 Class: PolicyTreeWidget + +```python +class PolicyTreeWidget(Tree): + """Tree view of policy hierarchy with checkboxes.""" + + def build_tree(self, policies: pd.DataFrame) -> None: + """Build tree structure from flat policy list.""" + # Build parent-child relationships + root_policies = policies[policies["parentid"].isna()] + for _, policy in root_policies.iterrows(): + node = self.root.add(policy["groupname"], data=policy) + self._add_children(node, policy["groupid"], policies) + + def _add_children(self, parent_node, parent_id: str, policies: pd.DataFrame) -> None: + """Recursively add child policies.""" + children = policies[policies["parentid"] == parent_id] + for _, child in children.iterrows(): + node = parent_node.add(child["groupname"], data=child) + self._add_children(node, child["groupid"], policies) +``` + +### 4.6 Configuration Manager (configmanager.py) + +#### 4.6.1 Architecture + +```python +# Two-tier configuration architecture + +# System config (immutable) - bundled in executable +SYSTEM_CONFIG_KEYS = [ + "URL", # Airlock server URL + "APPNAME", # Application name + "LOG_LEVEL", # Logging level + "BAD_PATH_PARTS", # Paths to flag + "BAD_PUBLISHERS", # Publishers to flag + "PUPS", # Potentially unwanted programs + "PATH_EXCLUSION_CONST", # Path exclusion threshold + "MIN_FILES_FOR_PATH", # Minimum files for path suggestion + "VT_THREAT_TOLERANCE", # VT score threshold (Airlock VT data) + "POLICY_MAP_ENF_AUD", # Policy mappings +] + +# User config (mutable) - in user's config directory +USER_CONFIG_KEYS = [ + "TELEMETRY", # Telemetry opt-in + "TELEM_URL", # Telemetry endpoint + "TEXTUAL_THEME", # UI theme + "EXTRAS", # Feature flags +] +``` + +#### 4.6.2 Configuration Loading + +```python +def load_system_config() -> dict: + """Load system configuration from bundled file.""" + # Check for bundled location (Nuitka) + bundled_path = Path(getattr(sys, "_MEIPASS", "")) / "system_config.json" + if bundled_path.exists(): + return json.load(open(bundled_path)) + # Fallback to development location + return json.load(open(Path(__file__).parent.parent / "system_config.json")) + +def load_user_config(config_dir: Path) -> dict: + """Load or create user configuration.""" + user_config_path = config_dir / "user_config.json" + if not user_config_path.exists(): + # Create with defaults + default_config = { + "TELEMETRY": False, + "TELEM_URL": "", + "TEXTUAL_THEME": "gruvbox", + "EXTRAS": "NOTTODAY", + } + user_config_path.parent.mkdir(parents=True, exist_ok=True) + json.dump(default_config, open(user_config_path, "w"), indent=4) + return default_config + return json.load(open(user_config_path)) +``` + +--- + +## 5. Data Design + +### 5.1 Data Models + +#### 5.1.1 Agent Model + +```python +@dataclass +class Agent: + """Represents an Airlock agent/endpoint.""" + agentid: str + hostname: str + groupid: str + groupname: str + status: int # 0=Offline, 1=Online, 3=Safemode + username: str + osversion: str + agentversion: str + lastcheckin: datetime + + @property + def is_online(self) -> bool: + return self.status == 1 + + @property + def status_display(self) -> str: + return {0: "Offline", 1: "Online", 3: "Safemode"}.get(self.status, "Unknown") +``` + +#### 5.1.2 Policy Model + +```python +@dataclass +class Policy: + """Represents an Airlock policy/group.""" + groupid: str + groupname: str + parentid: Optional[str] + auditmode: int # 0=Enforcement, 1=Audit + agentcount: int + + @property + def is_audit(self) -> bool: + return self.auditmode == 1 + + @property + def mode_display(self) -> str: + return "Audit" if self.is_audit else "Enforcement" +``` + +#### 5.1.3 Execution Model + +```python +@dataclass +class Execution: + """Represents an application execution event.""" + id: str + hostname: str + filename: str + filepath: str + sha256: str + publisher: str + category: int + timestamp: datetime + username: str + commandline: str + policyname: str + vtscore: Optional[int] = None # From Airlock VT integration + + @property + def category_display(self) -> str: + categories = { + 0: "Approved", + 1: "Unapproved", + 2: "Blocked", + 3: "OTP Bypass", + } + return categories.get(self.category, f"Unknown ({self.category})") +``` + +### 5.2 Data Storage + +#### 5.2.1 Configuration Files + +**System Configuration (system_config.json)** + +```json +{ + "URL": "https://airlock.example.com/api", + "APPNAME": "Loxide", + "LOG_LEVEL": "INFO", + "BAD_PATH_PARTS": ["temp", "tmp", "cache"], + "BAD_PUBLISHERS": ["Unknown Publisher", "Self-signed"], + "PUPS": ["toolbars", "adware"], + "PATH_EXCLUSION_CONST": 4, + "MIN_FILES_FOR_PATH": 4, + "VT_THREAT_TOLERANCE": 4, + "POLICY_MAP_ENF_AUD": {} +} +``` + +**User Configuration (user_config.json)** + +```json +{ + "TELEMETRY": false, + "TELEM_URL": "", + "TEXTUAL_THEME": "gruvbox", + "EXTRAS": "NOTTODAY" +} +``` + +#### 5.2.2 File Locations + +| Platform | Base Directory | Subdirectories | +|----------|----------------|----------------| +| Windows | %APPDATA%\Loxide | config/, logs/, cache/, data/ | +| Linux | ~/.local/share/Loxide | config/, logs/, cache/, data/ | + +--- + +## 6. Security Design + +### 6.1 Credential Storage Architecture + +``` ++-------------------+ +-------------------+ +-------------------+ +| User Password | --> | Key Derivation | --> | Encryption Key | ++-------------------+ | (PBKDF2) | | (256-bit) | + +-------------------+ +-------------------+ + | + v ++-------------------+ +-------------------+ +-------------------+ +| API Key | --> | AES-256-GCM | --> | Encrypted Blob | +| (plaintext) | | Encryption | | (salt+nonce+ct) | ++-------------------+ +-------------------+ +-------------------+ + | + v + +-------------------+ + | Platform Keyring | + | (Base64 encoded) | + +-------------------+ +``` + +### 6.2 Key Derivation Implementation + +```python +# security.py + +KDF_ITERATIONS = 200_000 +SALT_SIZE = 16 # 128-bit +NONCE_SIZE = 12 # AES-GCM standard +KEY_SIZE = 32 # AES-256 + +def _derive_key(password: bytes, salt: bytes) -> bytes: + """Derive encryption key from password using PBKDF2.""" + kdf = PBKDF2HMAC( + algorithm=hashes.SHA256(), + length=KEY_SIZE, + salt=salt, + iterations=KDF_ITERATIONS, + ) + return kdf.derive(password) +``` + +### 6.3 Credential Encryption + +```python +def store_api_key(service: str, username: str, api_key: str, password: str): + """Encrypt and store API key in platform keyring.""" + configure_keyring_backend() + + # Generate random salt + salt = os.urandom(SALT_SIZE) + + # Derive encryption key + key = _derive_key(password.encode(), salt) + + # Encrypt API key + aesgcm = AESGCM(key) + nonce = os.urandom(NONCE_SIZE) + ciphertext = aesgcm.encrypt(nonce, api_key.encode(), associated_data=None) + + # Combine: salt || nonce || ciphertext + blob = salt + nonce + ciphertext + + # Store base64-encoded in keyring + keyring.set_password(service, username, base64.b64encode(blob).decode()) +``` + +### 6.4 Credential Retrieval + +```python +def retrieve_api_key(service: str, username: str, password: str) -> str: + """Retrieve and decrypt API key from platform keyring.""" + configure_keyring_backend() + + # Get from keyring + b64_blob = keyring.get_password(service, username) + if b64_blob is None: + raise ValueError("No stored credential") + + # Decode + blob = base64.b64decode(b64_blob) + + # Extract components + salt = blob[:SALT_SIZE] + nonce = blob[SALT_SIZE:SALT_SIZE + NONCE_SIZE] + ciphertext = blob[SALT_SIZE + NONCE_SIZE:] + + # Derive key and decrypt + key = _derive_key(password.encode(), salt) + aesgcm = AESGCM(key) + plaintext = aesgcm.decrypt(nonce, ciphertext, associated_data=None) + + return plaintext.decode() +``` + +### 6.5 Platform Keyring Configuration + +```python +def configure_keyring_backend(): + """Configure appropriate keyring backend for current platform.""" + system = platform.system() + if system == "Windows": + import keyring.backends.Windows + keyring.set_keyring(keyring.backends.Windows.WinVaultKeyring()) + elif system == "Linux": + import keyring.backends.kwallet + keyring.set_keyring(keyring.backends.kwallet.DBusKeyring()) + else: + raise EnvironmentError(f"Unsupported platform: {system}") +``` + +### 6.6 Password Complexity Validation + +```python +def check_password_complexity(password: str) -> bool: + """Validate password meets complexity requirements.""" + if len(password) < 12: + return False + if not re.search(r"[A-Z]", password): # Uppercase + return False + if not re.search(r"[a-z]", password): # Lowercase + return False + if not re.search(r"[0-9]", password): # Digit + return False + if not re.search(r"[^A-Za-z0-9]", password): # Special + return False + return True +``` + +--- + +## 7. User Interface Design + +### 7.1 Screen Layout Standards + +#### 7.1.1 Screen Structure + +All screens follow a consistent layout: + +``` ++------------------------------------------------------------------+ +| Header: Application Name | Current Screen | Connection Status | ++------------------------------------------------------------------+ +| | +| Content Area | +| | +| (Varies by screen - tables, forms, wizards, etc.) | +| | ++------------------------------------------------------------------+ +| Footer: [Q]uit [R]efresh [Esc]Back [?]Help [Tab]Navigate | ++------------------------------------------------------------------+ +``` + +#### 7.1.2 Widget vs Screen Pattern + +| Type | Has Header/Footer | Navigation | Usage | +|------|-------------------|------------|-------| +| Screen | Yes | push_screen() | Full-page workflows | +| Widget | No | Embedded | Reusable components | + +### 7.2 Keyboard Navigation + +#### 7.2.1 Global Bindings + +| Key | Action | Context | +|-----|--------|---------| +| q | Quit application | Global | +| Esc | Back/Cancel | Screens | +| Tab | Next widget | Navigation | +| Shift+Tab | Previous widget | Navigation | +| r | Refresh data | Data views | +| e | Export to CSV | Data tables | +| ? | Show help | Global | + +#### 7.2.2 Data Table Navigation + +| Key | Action | +|-----|--------| +| Up/Down | Move row cursor | +| Page Up/Down | Scroll page | +| Home/End | First/last row | +| Space | Toggle selection | +| Enter | Select/activate | +| a | Select all | +| n | Deselect all | + +### 7.3 Theme System + +#### 7.3.1 Theme Architecture + +```python +# Themes are Python functions returning CSS strings +def get_amber_terminal_theme() -> str: + return """ + Screen { + background: #1a1a1a; + } + Header { + background: #ff8c00; + color: #000000; + } + DataTable > .datatable--cursor { + background: #ff8c00 30%; + } + Button { + background: #ff8c00; + color: #000000; + } + ... + """ +``` + +#### 7.3.2 Available Themes + +| Theme | Description | +|-------|-------------| +| gruvbox | Default warm retro theme | +| amber_terminal | Classic amber CRT look | +| retro_terminal | Green phosphor terminal | + +--- + +## 8. External Interface Design + +### 8.1 Airlock API Integration + +#### 8.1.1 Request Format + +All API requests use POST with JSON payload: + +```python +def _post(self, endpoint: str, payload: dict = None) -> dict: + url = f"{self.base_url}{endpoint}" + headers = { + "X-APIKey": self.api_key, + "Content-Type": "application/json" + } + response = requests.post( + url, + headers=headers, + data=json.dumps(payload or {}), + verify=False, + timeout=30 + ) + return response.json() +``` + +#### 8.1.2 Response Handling + +```python +# Standard response structure +{ + "status": "success" | "error", + "response": { + # Endpoint-specific data + }, + "message": "Optional error message" +} + +# Extraction pattern +def agent_find_all(self) -> pd.DataFrame: + result = self._post("/v1/agent/find", {}) + return pd.DataFrame(result["response"]["agents"]) +``` + +### 8.2 Data Export + +#### 8.2.1 CSV Export Implementation + +```python +def export_to_csv(data: pd.DataFrame, filepath: str) -> None: + """Export DataFrame to CSV with Excel-compatible encoding.""" + data.to_csv( + filepath, + index=False, + encoding="utf-8-sig", # BOM for Excel + quoting=csv.QUOTE_NONNUMERIC + ) +``` + +#### 8.2.2 Timestamp Formatting + +```python +def format_timestamp(dt: datetime) -> str: + """Format timestamp for display and export.""" + return dt.strftime("%Y-%m-%d %H:%M:%S") +``` + +--- + +## 9. Distribution and Deployment + +### 9.1 Windows Executable (Nuitka) + +#### 9.1.1 Build Configuration + +```powershell +# Set inputs +$icon = '.\Loxide_Icon.ico' +$config = '.\system_config.json' + +# Clean prior outputs +Remove-Item -Recurse -Force ` + '.\Loxide.build', + '.\Loxide.dist', + '.\Loxide.onefile-build' ` + -ErrorAction SilentlyContinue + +# Build arguments +$args = @( + '--onefile', + '--follow-imports', + '--msvc=latest', + + # Keyring + pywin32 modules (ensure lazy imports are included) + '--include-module=keyring.backends.Windows', + '--include-module=win32cred', + '--include-module=pywintypes', + '--include-module=pythoncom', + '--include-module=win32api', + '--include-module=win32security', + '--include-module=win32con', + + # Product metadata and resources + '--windows-product-name=Loxide', + '--windows-file-version=0.9.0.0', + '--windows-product-version=0.9.0', + '--windows-company-name=', + "--windows-icon-from-ico=$icon", + "--include-data-file=$config=system_config.json", + + # Deployment mode (disable helper checks for production) + '--deployment', + + '.\Loxide.py' +) + +& nuitka @args +``` + +#### 9.1.2 Build Options Explained + +| Option | Purpose | +|--------|---------| +| --onefile | Single executable output | +| --follow-imports | Follow all import statements | +| --msvc=latest | Use latest MSVC compiler | +| --include-module=keyring.backends.Windows | Windows Credential Manager support | +| --include-module=win32cred | Windows credential API | +| --include-module=pywintypes | PyWin32 types | +| --include-module=pythoncom | COM support | +| --include-module=win32api | Windows API bindings | +| --include-module=win32security | Security API bindings | +| --include-module=win32con | Windows constants | +| --windows-product-name | Executable metadata | +| --windows-file-version | Version in file properties | +| --windows-company-name | Company in file properties | +| --windows-icon-from-ico | Application icon | +| --include-data-file | Bundle system_config.json | +| --deployment | Disable debug checks for production | + +#### 9.1.3 Build Requirements + +- Windows 10/11 build machine +- Python 3.10+ +- Nuitka package (`pip install nuitka`) +- MSVC compiler (Visual Studio Build Tools) +- PyWin32 package (`pip install pywin32`) +- All runtime dependencies installed +- Access to Gitea for airlock_libs + +#### 9.1.4 Build Artifacts + +| Directory | Contents | +|-----------|----------| +| Loxide.build | Intermediate build files | +| Loxide.dist | Distribution files (standalone mode) | +| Loxide.onefile-build | Onefile build cache | +| Loxide.exe | Final executable (onefile mode) | + +### 9.2 Linux Distribution + +#### 9.2.1 pip Installation + +```bash +# From Gitea PyPI registry +pip install loxide \ + --extra-index-url https:///api/packages//pypi/simple/ +``` + +#### 9.2.2 Standalone Binary + +```bash +# Nuitka on Linux +python -m nuitka \ + --standalone \ + --onefile \ + --include-package-data=textual \ + Loxide.py +``` + +### 9.3 Platform Dependencies + +| Platform | Keyring Backend | Additional Requirements | +|----------|-----------------|------------------------| +| Windows | Windows Credential Manager | None | +| Linux | Secret Service | libsecret, KWallet or GNOME Keyring | + +--- + +## 10. Error Handling + +### 10.1 Error Categories + +| Category | Examples | Handling | +|----------|----------|----------| +| API Errors | Timeout, 4xx, 5xx | Toast notification, retry option | +| Input Errors | Invalid hostname, bad date | Inline validation message | +| UI Errors | Widget crash | Graceful degradation, error screen | +| Configuration Errors | Missing config, bad JSON | Startup warning, defaults | +| Credential Errors | Wrong password, no credential | Re-prompt with retry limit | + +### 10.2 Logging Strategy + +```python +# setup.py + +class TextualNotificationHandler(logging.Handler): + """Log handler that displays toasts for important messages.""" + + def emit(self, record): + if record.levelno >= logging.WARNING: + # Show as toast notification + app = App.get_running_app() + if app: + app.notify(record.getMessage(), severity="warning") + +def setup_logging(log_level: str, log_dir: Path): + """Configure logging with file and notification handlers.""" + logger = logging.getLogger() + logger.setLevel(getattr(logging, log_level)) + + # File handler for all messages + file_handler = logging.FileHandler(log_dir / "Loxide.log") + file_handler.setFormatter(logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + )) + logger.addHandler(file_handler) + + # Notification handler for warnings and above + notification_handler = TextualNotificationHandler() + notification_handler.setLevel(logging.WARNING) + logger.addHandler(notification_handler) +``` + +### 10.3 Common Error Patterns + +#### 10.3.1 DataFrame Boolean Ambiguity + +```python +# Problem: "The truth value of a Series is ambiguous" +# Wrong: +agents[not agents["enforce_ready"]] +# Correct: +agents[~agents["enforce_ready"]] +``` + +#### 10.3.2 Empty DataFrame Access + +```python +# Problem: KeyError on empty DataFrame +# Wrong: +value = df["column"].iloc[0] +# Correct: +if not df.empty and "column" in df.columns: + value = df["column"].iloc[0] +``` + +#### 10.3.3 Duplicate Widget IDs + +```python +# Problem: "DuplicateIds" error on widget mount +# Solution: Remove existing widgets first +async def update_content(self): + await self.query("#my-widget").remove() + await self.mount(MyWidget(id="my-widget")) +``` + +--- + +## 11. Phase 2: LEMON Integration + +### 11.1 Overview + +LEMON (Loxide Execution MONitoring) is a planned backend service for automated local approval sessions. Full specifications are in separate LEMON documentation. + +### 11.2 Loxide Integration Points + +| Component | Integration | +|-----------|-------------| +| LEMONClient | New service class for LEMON API | +| LEMONSessionsScreen | View/create sessions | +| LEMONHashReviewScreen | Review pending hashes | +| Certificate Configuration | mTLS setup | + +### 11.3 Configuration Additions + +```json +{ + "LEMON_ENABLED": true, + "LEMON_URL": "https://lemon.example.com/api/v1", + "LEMON_USER_CERT": "/path/to/user.crt", + "LEMON_CA_CERT": "/path/to/internal_ca.crt" +} +``` + +--- + +## 12. Appendices + +### Appendix A: Textual Framework Patterns + +Key patterns used throughout the application. + +### Appendix B: API Response Schemas + +Complete API response schema documentation. + +### Appendix C: Configuration Schema + +JSON schema for configuration files. + +--- + +## Document Approval + +This Software Design Document has been reviewed and approved for implementation. + +| Role | Name | Date | +|------|------|------| +| Author | Brandon Wickline | | +| Technical Reviewer | James Brotosky | | +| Approver | | | + +--- + +*End of Document* diff --git a/docs/03_API_Reference.md b/docs/03_API_Reference.md new file mode 100644 index 0000000..3ad1108 --- /dev/null +++ b/docs/03_API_Reference.md @@ -0,0 +1,476 @@ +# Loxide +## API Reference Document + +**Version 1.0 | December 2025** + +--- + +## 1. Overview + +This document provides a complete reference for the AirlockAPIWrapper class and all Airlock API endpoints used by Loxide. + +### 1.1 Authentication + +All API requests require an API key passed via the X-APIKey header: + +``` +Headers: { "X-APIKey": "your-api-key-here" } +``` + +### 1.2 Base URL + +The base URL is configured in `system_config.json` and typically follows the pattern: + +``` +https://airlock.example.com/api +``` + +--- + +## 2. Agent Management + +### 2.1 agent_find_all() + +Retrieve all registered agents. + +**Endpoint:** `POST /v1/agent/find` +**Payload:** `{}` +**Returns:** DataFrame with agent records + +| Field | Type | Description | +|-------|------|-------------| +| `hostname` | str | Device hostname | +| `agentid` | str | Unique identifier | +| `clientversion` | str | Agent version | +| `groupid` | str | Policy group ID | +| `status` | int | 0=Offline, 1=Online, 2=Hidden, 3=Safemode | +| `lastcheckin` | str | Last check-in timestamp | +| `ip` | str | External IP address | +| `localip` | str | Internal IP address | +| `domain` | str | Network domain | +| `os` | str | Operating system | +| `username` | str | Logged-in user | +| `freespace` | int | Available disk space | +| `policyversion` | str | Active policy version | + +### 2.2 agent_find_by_hostname(hostname: str) + +Find agents matching a hostname pattern. + +**Endpoint:** `POST /v1/agent/find` +**Payload:** `{ "hostname": "" }` +**Returns:** DataFrame with matching agents + +### 2.3 agent_find_by_id(agentid: str) + +Find agent by unique ID. + +**Endpoint:** `POST /v1/agent/find` +**Payload:** `{ "agentid": "" }` +**Returns:** DataFrame with agent record + +### 2.4 agent_find_by_status(status: int) + +Find agents by status code. + +**Endpoint:** `POST /v1/agent/find` +**Payload:** `{ "status": }` +**Status Codes:** +- `0` - Offline +- `1` - Online +- `2` - Hidden +- `3` - Safemode + +### 2.5 agent_move(agentid: str, groupid: str) + +Move an agent to a different policy group. + +**Endpoint:** `POST /v1/agent/move` +**Payload:** `{ "agentid": "", "groupid": "" }` +**Returns:** dict with operation result + +### 2.6 agents_find_by_group(groupid: str) + +Find all agents in a policy group. + +**Endpoint:** `POST /v1/agent/find` +**Payload:** `{ "groupid": "" }` +**Returns:** DataFrame with agents + +--- + +## 3. Policy Management + +### 3.1 policy_find_all() + +Retrieve all policy groups. + +**Endpoint:** `POST /v1/group` +**Payload:** `{}` +**Returns:** DataFrame with policy records + +| Field | Type | Description | +|-------|------|-------------| +| `name` | str | Policy display name | +| `groupid` | int | Unique group identifier | +| `hidden` | bool | Visibility flag | +| `parent` | str | Parent policy name (if child) | + +### 3.2 policy_set_auditmode(groupid: str, auditmode: str) + +Toggle policy between audit and enforcement modes. + +**Endpoint:** `POST /v1/group/settings/auditmode` +**Payload:** `{ "groupid": "", "auditmode": "" }` +**Mode Values:** +- `"1"` - Audit mode (log only) +- `"0"` - Enforcement mode (block) + +### 3.3 policy_list_agents(groupid: str) + +List all agents assigned to a policy group. + +**Endpoint:** `POST /v1/group/agents` +**Payload:** `{ "groupid": "" }` +**Returns:** DataFrame with agents + +### 3.4 policy_list_allowlists(groupid: str) + +List allowlists assigned to a policy group. + +**Endpoint:** `POST /v1/group/policies` +**Payload:** `{ "groupid": "" }` +**Returns:** DataFrame with applications + +### 3.5 policy_clone(source_groupid: str, target_groupid: str) + +Clone a policy from one group to another. + +**Endpoint:** `POST /v1/group/assign` +**Payload:** `{ "groupid": "", "targetgroupid": "" }` + +### 3.6 policy_add_path_exclusions(groupid: str, paths: List[str]) + +Add path exclusions to a policy group. + +**Endpoint:** `POST /v1/group/path/add` +**Payload:** `{ "groupid": "", "path": ["", ""] }` + +### 3.7 policy_add_publishers(groupid: str, publishers: List[str]) + +Add trusted publishers to a policy group. + +**Endpoint:** `POST /v1/group/publisher/add` +**Payload:** `{ "groupid": "", "publisher": ["", ""] }` + +--- + +## 4. OTP Management + +### 4.1 otp_generate(agentid: str, duration: int, purpose: str) + +Generate a new One-Time Password for an agent. + +**Endpoint:** `POST /v1/otp/retrieve` +**Payload:** +```json +{ + "agentid": "", + "duration": "", + "purpose": "" +} +``` +**Returns:** str - The generated OTP code + +### 4.2 otp_find_active() + +Retrieve all active OTP sessions. + +**Endpoint:** `POST /v1/otp/usage` +**Payload:** `{ "status": "1" }` +**Returns:** DataFrame with OTP records + +| Field | Type | Description | +|-------|------|-------------| +| `otpid` | str | OTP session identifier | +| `agentid` | str | Associated agent ID | +| `hostname` | str | Agent hostname | +| `purpose` | str | OTP purpose description | +| `granted` | str | Grant timestamp | +| `expires` | str | Expiration timestamp | + +### 4.3 otp_find_awaiting() + +Retrieve OTPs awaiting activation. + +**Endpoint:** `POST /v1/otp/usage` +**Payload:** `{ "status": "0" }` + +### 4.4 otp_find_enforced() + +Retrieve enforced OTPs. + +**Endpoint:** `POST /v1/otp/usage` +**Payload:** `{ "status": "2" }` + +### 4.5 otp_find_revoked() + +Retrieve revoked OTPs. + +**Endpoint:** `POST /v1/otp/usage` +**Payload:** `{ "status": "3" }` + +### 4.6 otp_find_by_agent(agentid: str) + +Retrieve OTPs for a specific agent. + +**Endpoint:** `POST /v1/otp/usage` +**Payload:** `{ "agentid": "" }` + +### 4.7 otp_revoke(otpid: str) + +Revoke an active OTP session. + +**Endpoint:** `POST /v1/otp/revoke` +**Payload:** `{ "otpid": "" }` +**Returns:** dict with operation result + +### 4.8 otp_validate(otpcode: str) + +Validate an OTP code. + +**Endpoint:** `POST /v1/otp/validate` +**Payload:** `{ "otpcode": "" }` +**Returns:** dict indicating validity + +### 4.9 otp_get_activities(otpid: str) + +Retrieve activity log for a specific OTP. + +**Endpoint:** `POST /v1/otp/activities` +**Payload:** `{ "otpid": "" }` +**Returns:** DataFrame with OTP activities + +--- + +## 5. Execution History + +### 5.1 history_execution(today: str, date_selected: str, agent_name: str) + +Retrieve execution history for a specific agent. + +**Endpoint:** `POST /v1/getexechistory` +**Payload:** +```json +{ + "datefrom": "", + "dateto": "", + "hostname": "" +} +``` +**Returns:** List[Dict] with execution records + +| Field | Type | Description | +|-------|------|-------------| +| `type` | int | Execution type code | +| `hostname` | str | Device hostname | +| `username` | str | User who executed | +| `filename` | str | Executed filename | +| `sha256` | str | File hash | +| `publisher` | str | Code signer | +| `datetime` | str | Execution timestamp | +| `policyname` | str | Active policy | +| `policyver` | str | Policy version | +| `commandline` | str | Full command line | +| `pprocess` | str | Parent process | + +### 5.2 Execution Type Codes + +| Code | Description | +|------|-------------| +| 0 | Trusted Execution | +| 1 | Blocked Execution | +| 2 | Untrusted Execution [Audit] | +| 3 | Untrusted Execution [OTP] | +| 4 | Trusted Path Execution | +| 5 | Trusted Publisher Execution | +| 6 | Blocklist Execution | +| 7 | Blocklist Execution [Audit] | +| 8 | Trusted Process Execution | +| 9 | Constrained Execution | +| 10 | Trusted Metadata Execution | +| 11 | Trusted Browser Execution | +| 12 | Blocked Browser Execution | +| 13 | Untrusted Browser Execution [Audit] | +| 14 | Untrusted Browser Execution [OTP] | +| 15 | Blocklist Browser Execution [Audit] | +| 16 | Blocklist Browser Execution | +| 17 | Trusted Installer Execution | +| 18 | Trusted Browser Metadata Execution | + +### 5.3 history_logging(type: List[str], checkpoint: str, policy: Optional[List[str]]) + +Retrieve execution history logs with pagination. + +**Endpoint:** `POST /v1/logging/exechistories` +**Payload:** +```json +{ + "type": ["1", "2", "3"], + "checkpoint": "", + "policy": [""] +} +``` +**Returns:** str with execution histories + +--- + +## 6. Server Logs + +### 6.1 server_logs(checkpoint: Optional[str]) + +Retrieve server activity logs. + +**Endpoint:** `POST /v1/logging/svractivities` +**Payload:** `{}` or `{ "checkpoint": "" }` +**Returns:** str with server activities + +The checkpoint parameter enables pagination for large result sets. Pass the last checkpoint from a previous call to get subsequent records. + +--- + +## 7. Allowlist and Blocklist Management + +### 7.1 allowlist_find_all() + +Retrieve all allowlist applications. + +**Endpoint:** `POST /v1/application` +**Payload:** `{}` +**Returns:** DataFrame with application records + +### 7.2 allowlist_export(applicationid: str) + +Export allowlist as XML. + +**Endpoint:** `POST /v1/application/export` +**Payload:** `{ "applicationid": "" }` +**Returns:** bytes - XML content + +### 7.3 baseline_find_all() + +Retrieve all baselines. + +**Endpoint:** `POST /v1/baseline` +**Payload:** `{}` +**Returns:** DataFrame with baseline records + +### 7.4 baseline_export(baselineid: str) + +Export baseline as XML. + +**Endpoint:** `POST /v1/baseline/export` +**Payload:** `{ "baselineid": "" }` +**Returns:** bytes - XML content + +### 7.5 blocklist_find_all() + +Retrieve all blocklists. + +**Endpoint:** `POST /v1/blocklist` +**Payload:** `{}` +**Returns:** DataFrame with blocklist records + +### 7.6 blocklist_export(blocklistid: str) + +Export blocklist as XML. + +**Endpoint:** `POST /v1/blocklist/export` +**Payload:** `{ "blocklistid": "" }` +**Returns:** bytes - XML content + +### 7.7 hash_add_to_allowlist(applicationid: str, hashes: List[str]) + +Add hashes to an allowlist. + +**Endpoint:** `POST /v1/hash/application/add` +**Payload:** `{ "applicationid": "", "hashes": ["", ...] }` +**Returns:** dict with operation result + +### 7.8 hash_query(hashes: List[str]) + +Query information about specific hashes. + +**Endpoint:** `POST /v1/hash/query` +**Payload:** `{ "hashes": ["", ...] }` +**Returns:** DataFrame with hash records + +| Field | Type | Description | +|-------|------|-------------| +| `sha256` | str | Hash value | +| `filename` | str | Associated filename | +| `publisher` | str | Code signer (or "Not Signed") | +| `reputation` | dict | VirusTotal scan results | +| `applications` | str | Associated allowlists | +| `baselines` | str | Associated baselines | +| `blocklists` | str | Associated blocklists | + +--- + +## 8. Rust Backend (airlock_libs) + +The `airlock_libs` package provides Rust-accelerated functions for performance-critical operations. + +### 8.1 pull_policy_exec_histories(api, type, days, policy_name) + +Pull execution history for policies with optimized performance. + +| Parameter | Type | Description | +|-----------|------|-------------| +| `api` | AirlockAPIWrapper | API wrapper instance | +| `type` | str | JSON list of exec types, e.g., "[1,2,3]" | +| `days` | int | Days to look back | +| `policy_name` | Optional[str] | Specific policy or None for all | + +**Returns:** JSON string with execution history + +### 8.2 history_logging(api, exec_types, checkpoint_number, policy_names) + +Query execution logs with pagination support. + +| Parameter | Type | Description | +|-----------|------|-------------| +| `api` | AirlockAPIWrapper | API wrapper instance | +| `exec_types` | str | JSON list of exec types, e.g., "[3,5,8]" | +| `checkpoint_number` | str | Checkpoint ID for pagination | +| `policy_names` | Optional[str] | Comma-separated policy names or None | + +**Returns:** List[Dict] - Execution history records + +--- + +## 9. Error Handling + +All API methods may raise: + +- `requests.exceptions.RequestException` - Network or HTTP errors +- `ValueError` - Invalid response format +- `KeyError` - Missing expected fields in response + +Recommended pattern: + +```python +try: + result = api.agent_find_all() +except requests.exceptions.RequestException as e: + logger.error(f"API request failed: {e}") + # Handle error appropriately +``` + +--- + +## 10. License + +Copyright (C) 2025 James Brotosky, Brandon Wickline + +GNU Affero General Public License v3.0 diff --git a/docs/04_User_Stories_and_Use_Cases.md b/docs/04_User_Stories_and_Use_Cases.md new file mode 100644 index 0000000..ed61c19 --- /dev/null +++ b/docs/04_User_Stories_and_Use_Cases.md @@ -0,0 +1,510 @@ +# Loxide +## User Stories and Use Cases + +**Version 1.0 | December 2025** + +--- + +## Epic 1: Multi-Agent Operations + +**Epic Statement:** As a administrator, I need to perform bulk operations on multiple endpoints efficiently. + +--- + +### US-1.1: Agent Selection + +**User Story:** As a administrator, I want to select multiple agents using various methods so that I can perform bulk operations efficiently. + +**Acceptance Criteria:** +- ✅ Can paste a list of device names (700+ lines) +- ✅ Can use wildcards (* and ?) for pattern matching +- ✅ Can import device names from a file +- ✅ Can toggle between exact and fuzzy matching +- ✅ Unmatched entries are clearly displayed +- ✅ Can select/deselect all matched agents + +**Priority:** High +**Story Points:** 8 + +--- + +### US-1.2: Agent Policy Move + +**User Story:** As a administrator, I want to move selected agents to a different policy group so that I can organize endpoints by security requirements. + +**Acceptance Criteria:** +- ✅ Can select destination policy from list +- ✅ Move operation provides progress feedback +- ✅ Success/failure results are color-coded +- ✅ Can export results to CSV + +**Priority:** High +**Story Points:** 5 + +--- + +### US-1.3: Toggle Enforcement Mode + +**User Story:** As a administrator, I want to toggle agents between audit and enforcement mode so that I can gradually roll out policy enforcement. + +**Acceptance Criteria:** +- ✅ Clear indication of current mode +- ✅ Confirmation before mode change +- ✅ Results displayed after operation + +**Priority:** High +**Story Points:** 3 + +--- + +### US-1.4: View Agent Execution History + +**User Story:** As a administrator, I want to view execution history for selected agents so that I can understand what applications are running. + +**Acceptance Criteria:** +- ✅ Date range selector (1-365 days) +- ✅ Results displayed in DataTable +- ✅ Export to CSV functionality +- ✅ Sortable columns + +**Priority:** Medium +**Story Points:** 5 + +--- + +## Epic 2: Policy Preparation + +**Epic Statement:** As a administrator, I need to prepare policies for enforcement by analyzing execution history. + +--- + +### US-2.1: Policy Selection + +**User Story:** As a administrator, I want to select source policies for analysis so that I can review their execution history. + +**Acceptance Criteria:** +- ✅ Multi-select with checkboxes +- ✅ Filter/search capability +- ✅ Policy hierarchy visible + +**Priority:** High +**Story Points:** 5 + +--- + +### US-2.2: Configure Analysis Parameters + +**User Story:** As a administrator, I want to configure history days and allowlists so that I can customize the analysis scope. + +**Acceptance Criteria:** +- ✅ History days configurable from 1 to 365 +- ✅ Default value clearly indicated +- ✅ Allowlist selection available +- ✅ Input validation with helpful error messages + +**Priority:** High +**Story Points:** 3 + +--- + +### US-2.3: Review Analysis Results + +**User Story:** As a administrator, I want to review categorized execution history so that I can make informed enforcement decisions. + +**Acceptance Criteria:** +- ✅ Results categorized: Approved, Unapproved, Needs Review +- ✅ Hash reputation data displayed +- ✅ Publisher information shown +- ✅ Export to CSV available +- ✅ Color-coded categories + +**Priority:** High +**Story Points:** 8 + +--- + +### US-2.4: Add Hashes to Allowlist + +**User Story:** As a administrator, I want to add approved hashes to an allowlist so that they won't be blocked after enforcement. + +**Acceptance Criteria:** +- ✅ Select hashes from analysis results +- ✅ Choose target allowlist +- ✅ Confirmation before adding +- ✅ Success/failure feedback + +**Priority:** Medium +**Story Points:** 5 + +--- + +## Epic 3: Quiet Agent Detection + +**Epic Statement:** As a administrator, I need to identify inactive endpoints ready for enforcement. + +--- + +### US-3.1: Configure Quiet Threshold + +**User Story:** As a administrator, I want to set quiet day thresholds so that I can define what constitutes an inactive agent. + +**Acceptance Criteria:** +- ✅ Configurable quiet days parameter +- ✅ History days parameter +- ✅ Clear explanation of thresholds +- ✅ Input validation + +**Priority:** High +**Story Points:** 3 + +--- + +### US-3.2: Run Quiet Agent Analysis + +**User Story:** As a administrator, I want the system to analyze agent activity using high-performance Rust code so that I can quickly identify quiet agents. + +**Acceptance Criteria:** +- ✅ Progress bar during analysis +- ✅ Console output for Rust progress +- ✅ Non-blocking UI during analysis +- ✅ Clear completion notification + +**Priority:** High +**Story Points:** 8 + +--- + +### US-3.3: View Quiet Agents + +**User Story:** As a administrator, I want to see a list of quiet agents so that I can move them to enforcement. + +**Acceptance Criteria:** +- ✅ Results displayed in sortable table +- ✅ Can select agents for bulk move +- ✅ Last activity date shown +- ✅ Export to CSV + +**Priority:** High +**Story Points:** 5 + +--- + +## Epic 4: OTP Management + +**Epic Statement:** As an support technician, I need to manage temporary policy bypasses for end users. + +--- + +### US-4.1: Generate OTP + +**User Story:** As an support technician, I want to generate an OTP for an agent so that a user can temporarily bypass policy restrictions. + +**Acceptance Criteria:** +- ✅ Select agent by hostname +- ✅ Specify duration in minutes +- ✅ Enter purpose/ticket number +- ✅ OTP code displayed clearly +- ✅ Copy to clipboard functionality + +**Priority:** High +**Story Points:** 5 + +--- + +### US-4.2: View Active OTPs + +**User Story:** As a administrator, I want to view all active OTP sessions so that I can monitor temporary policy bypasses. + +**Acceptance Criteria:** +- ✅ List all active OTPs +- ✅ Show hostname, purpose, expiration +- ✅ Filter by status +- ✅ Refresh capability (r key) +- ✅ Sortable columns + +**Priority:** High +**Story Points:** 5 + +--- + +### US-4.3: Revoke OTP + +**User Story:** As a administrator, I want to revoke an active OTP so that I can end a temporary bypass immediately. + +**Acceptance Criteria:** +- ✅ Select OTPs for revocation (checkbox) +- ✅ Bulk revoke capability +- ✅ Confirmation before revocation +- ✅ Results displayed after operation +- ✅ Auto-refresh list after revocation + +**Priority:** High +**Story Points:** 5 + +--- + +### US-4.4: View OTP Activities + +**User Story:** As a administrator, I want to see what applications were executed during an OTP session so that I can audit temporary bypasses. + +**Acceptance Criteria:** +- ✅ Select OTP to view +- ✅ Display execution list +- ✅ Show file, hash, timestamp +- ✅ Export capability + +**Priority:** Medium +**Story Points:** 5 + +--- + +## Epic 5: Execution History + +**Epic Statement:** As a analyst, I need to investigate execution events on endpoints. + +--- + +### US-5.1: Query Execution History + +**User Story:** As a analyst, I want to query execution history for specific agents so that I can investigate security events. + +**Acceptance Criteria:** +- ✅ Select agent by hostname +- ✅ Configure date range (start/end) +- ✅ Filter by execution type +- ✅ Results in sortable DataTable +- ✅ Pagination for large results + +**Priority:** High +**Story Points:** 8 + +--- + +### US-5.2: Export History + +**User Story:** As a analyst, I want to export execution history to CSV so that I can perform offline analysis. + +**Acceptance Criteria:** +- ✅ Export button available (e key) +- ✅ All visible columns included +- ✅ Proper CSV formatting +- ✅ Timestamp in filename +- ✅ Notification on success + +**Priority:** Medium +**Story Points:** 3 + +--- + +### US-5.3: Hash Reputation Lookup + +**User Story:** As a analyst, I want to see reputation data for executed files so that I can assess risk. + +**Acceptance Criteria:** +- ✅ VirusTotal score displayed +- ✅ Publisher information shown +- ✅ Known allowlist membership indicated +- ✅ Risk categorization (approved/unapproved/needs_review) + +**Priority:** Medium +**Story Points:** 5 + +--- + +## Epic 6: Server Monitoring + +**Epic Statement:** As a system administrator, I need to monitor Airlock server activity. + +--- + +### US-6.1: View Server Logs + +**User Story:** As a system administrator, I want to view recent server activity so that I can monitor system health. + +**Acceptance Criteria:** +- ✅ Default view of last 72 hours +- ✅ Datetime properly formatted (YYYY-MM-DD HH:MM:SS) +- ✅ Refresh via keyboard shortcut (r) +- ✅ Escape to return to main menu +- ✅ Auto-scroll to latest entries + +**Priority:** Medium +**Story Points:** 5 + +--- + +## Epic 7: Application Configuration + +**Epic Statement:** As a power user, I need to customize the application to my preferences. + +--- + +### US-7.1: Theme Selection + +**User Story:** As a power user, I want to change the UI theme so that I can work comfortably in different lighting conditions. + +**Acceptance Criteria:** +- ✅ Multiple themes available (textual-dark, gruvbox, retro-terminal, amber-terminal) +- ✅ Theme persists across sessions +- ✅ Preview before applying +- ✅ Saved to user config + +**Priority:** Low +**Story Points:** 3 + +--- + +### US-7.2: Working Directory Access + +**User Story:** As a power user, I want to access my working directory from within the application so that I can manage exported files. + +**Acceptance Criteria:** +- ✅ Directory tree visible in main menu +- ✅ Keyboard shortcut to open in file manager (f) +- ✅ Configurable working directory +- ✅ Auto-create directory structure + +**Priority:** Low +**Story Points:** 3 + +--- + +### US-7.3: Credential Management + +**User Story:** As a user, I want my API credentials stored securely so that I don't have to enter them every time. + +**Acceptance Criteria:** +- ✅ First-time setup prompts for API key +- ✅ Master password protects credentials +- ✅ Password complexity requirements enforced +- ✅ 3 retry attempts on wrong password +- ✅ Platform-native keyring used + +**Priority:** High +**Story Points:** 8 + +--- + +## Epic 8: Statistics and Reporting + +**Epic Statement:** As a administrator, I need visibility into my environment's security posture. + +--- + +### US-8.1: View System Statistics + +**User Story:** As a administrator, I want to see an overview of agents and policies so that I can understand my environment. + +**Acceptance Criteria:** +- ✅ Total agent count +- ✅ Total policy count +- ✅ Agent status breakdown (Online/Offline/Hidden/Safemode) +- ✅ Visual charts using plotext +- ✅ Configurable time range (1/7/30 days) + +**Priority:** Medium +**Story Points:** 5 + +--- + +### US-8.2: View Execution Statistics + +**User Story:** As a administrator, I want to see execution statistics so that I can identify trends. + +**Acceptance Criteria:** +- ✅ Execution counts by type +- ✅ Top executed files +- ✅ Top blocked files +- ✅ Visual bar charts +- ✅ Refresh capability + +**Priority:** Medium +**Story Points:** 5 + + +--- + +## Phase 2: LEMON Integration + +**Project:** LEMON (Loxide Execution MONitoring) +**Previous Codename:** Overlock +**Status:** Phase 2 - Separate Project + +LEMON user stories are documented separately in `LEMON_03_User_Stories.md`. + +**Summary of Phase 2 Loxide Stories:** +- LEMON Sessions Screen - View/create/cancel sessions +- LEMON Hash Review Screen - Approve/reject pending hashes +- Certificate Setup - Configure mTLS authentication +- Audit Chain Verification - Verify log integrity + +See LEMON documentation for complete user stories. + +--- + +## Acceptance Test Scenarios + +### Scenario: Bulk Agent Move + +```gherkin +Given I am on the Multi-Agent Operations screen +When I paste 100 device names into the selector +And I click Search +Then I should see matched agents in the selection list +When I select 50 agents using checkboxes +And I choose "Production Policy" as destination +And I click "Move Selected" +Then I should see a progress indicator +And I should see 50 success results with green indicators +And I should be able to export results to CSV +``` + +### Scenario: OTP Generation and Revocation + +```gherkin +Given I am on the OTP Management screen +When I search for agent "DESKTOP-001" +And I set duration to 60 minutes +And I enter purpose "Ticket #12345 - Software installation" +And I click Generate OTP +Then I should see an 8-character OTP code +And I should be able to copy it to clipboard + +Given the OTP is active +When I navigate to OTP Revoke screen +And I select the OTP for "DESKTOP-001" +And I click "Revoke Selected" +Then I should see confirmation dialog +When I confirm revocation +Then I should see success message +And the OTP should no longer appear in active list +``` + +### Scenario: Policy Preparation Workflow + +```gherkin +Given I am on the Policy Prep screen +When I select "Audit Policy A" and "Audit Policy B" +And I set history days to 30 +And I click Next +Then I should see execution history being fetched + +When the fetch completes +Then I should see categorized results: + | Category | Count | + | Approved | 150 | + | Unapproved | 25 | + | Needs Review | 10 | + +When I click "Export to CSV" +Then I should see a file saved notification +And the CSV should contain all execution records +``` + +--- + +## License + +Copyright (C) 2025 James Brotosky, Brandon Wickline + +GNU Affero General Public License v3.0 diff --git a/utils/security.py b/services/security.py similarity index 100% rename from utils/security.py rename to services/security.py diff --git a/utils/versionchecker.py b/utils/versionchecker.py index 9b4bd24..e72ce85 100644 --- a/utils/versionchecker.py +++ b/utils/versionchecker.py @@ -34,7 +34,7 @@ import requests logger = logging.getLogger(__name__) # Current application version - UPDATE THIS ON EACH RELEASE -__version__ = "0.7.0" +__version__ = "1.0.0" # Gitea release API configuration GITEA_API_BASE = "https://git.racooncity.org/api/v1"