feat(release): Loxide 1.0 RC

- Added comprehensive documentation:
  - System Design Requirements (SDR)
  - System Design Specification (SDS)
  - API Reference
  - User Stories & Use Cases
- Fixed minor UI issues related to double encoding
This commit is contained in:
2025-12-22 10:45:36 -05:00
parent e1e0cb7ac7
commit 423e9e8208
8 changed files with 3416 additions and 22 deletions
+1 -1
View File
@@ -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
+20 -20
View File
@@ -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"
)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+476
View File
@@ -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": "<pattern>" }`
**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": "<id>" }`
**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": <code> }`
**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": "<id>", "groupid": "<target_group>" }`
**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": "<id>" }`
**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": "<id>", "auditmode": "<mode>" }`
**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": "<id>" }`
**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": "<id>" }`
**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": "<source>", "targetgroupid": "<target>" }`
### 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": "<id>", "path": ["<path1>", "<path2>"] }`
### 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": "<id>", "publisher": ["<pub1>", "<pub2>"] }`
---
## 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": "<id>",
"duration": "<minutes>",
"purpose": "<description>"
}
```
**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": "<id>" }`
### 4.7 otp_revoke(otpid: str)
Revoke an active OTP session.
**Endpoint:** `POST /v1/otp/revoke`
**Payload:** `{ "otpid": "<id>" }`
**Returns:** dict with operation result
### 4.8 otp_validate(otpcode: str)
Validate an OTP code.
**Endpoint:** `POST /v1/otp/validate`
**Payload:** `{ "otpcode": "<code>" }`
**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": "<id>" }`
**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": "<YYYY-MM-DD>",
"dateto": "<YYYY-MM-DD>",
"hostname": "<name>"
}
```
**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": "<checkpoint_id>",
"policy": ["<policy_name>"]
}
```
**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": "<last_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": "<id>" }`
**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": "<id>" }`
**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": "<id>" }`
**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": "<id>", "hashes": ["<sha256>", ...] }`
**Returns:** dict with operation result
### 7.8 hash_query(hashes: List[str])
Query information about specific hashes.
**Endpoint:** `POST /v1/hash/query`
**Payload:** `{ "hashes": ["<sha256>", ...] }`
**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
+510
View File
@@ -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
+1 -1
View File
@@ -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"