Packages

Multi-surface application runtime for Elixir. One TEA module renders to terminal, browser (LiveView), SSH, and MCP (agents). 30+ widgets, flexbox + CSS grid, AI agent runtime, distributed swarm with CRDTs, time-travel debugging, session recording, sandboxed REPL, and agentic commerce.

Current section

Files

Jump to
raxol lib raxol audit integration.ex
Raw

lib/raxol/audit/integration.ex

defmodule Raxol.Audit.Integration do
@moduledoc """
Integration module for hooking audit logging into terminal operations.
This module provides convenience functions for integrating audit logging
throughout the Raxol terminal framework.
"""
alias Raxol.Audit.Logger
@doc """
Audits a terminal command execution.
"""
def audit_command(terminal_id, user_id, command, result) do
Logger.log_terminal_operation(user_id, terminal_id, :command_executed,
command: command,
exit_code: result.exit_code,
duration_ms: result.duration_ms,
output_size: byte_size(result.output || "")
)
# Check for dangerous commands
log_dangerous_command_if_needed(
dangerous_command?(command),
user_id,
terminal_id,
command
)
# Check for privilege escalation
log_privilege_escalation_if_needed(
privilege_escalation?(command),
user_id,
terminal_id,
command
)
end
@doc """
Audits terminal session lifecycle events.
"""
def audit_session_start(terminal_id, user_id, opts \\ []) do
Logger.log_terminal_operation(
user_id,
terminal_id,
:session_started,
Keyword.merge(opts,
timestamp: System.system_time(:millisecond),
environment: System.get_env()
)
)
end
def audit_session_end(terminal_id, user_id, opts \\ []) do
Logger.log_terminal_operation(
user_id,
terminal_id,
:session_ended,
Keyword.merge(opts,
timestamp: System.system_time(:millisecond),
duration_ms: Keyword.get(opts, :duration_ms, 0)
)
)
end
@doc """
Audits file operations within terminal.
"""
def audit_file_operation(
terminal_id,
user_id,
operation,
file_path,
opts \\ []
) do
Logger.log_data_access(
user_id,
operation,
"file",
Keyword.merge(opts,
resource_id: file_path,
terminal_id: terminal_id,
data_classification: classify_file(file_path)
)
)
# Extra logging for sensitive files
log_sensitive_file_access_if_needed(
sensitive_file?(file_path),
user_id,
terminal_id,
operation,
file_path
)
end
@doc """
Audits authentication attempts for terminal access.
"""
def audit_authentication(username, method, outcome, opts \\ []) do
Logger.log_authentication(username, method, outcome, opts)
# Track failed attempts for account lockout
handle_authentication_outcome(outcome, username, opts)
end
@doc """
Audits authorization checks for terminal operations.
"""
def audit_authorization(user_id, resource, action, outcome, opts \\ []) do
Logger.log_authorization(user_id, resource, action, outcome, opts)
# Alert on critical resource denial
log_critical_resource_denial_if_needed(
outcome == :denied and critical_resource?(resource),
user_id,
resource,
action
)
end
@doc """
Audits configuration changes in the terminal.
"""
def audit_config_change(
user_id,
component,
setting,
old_value,
new_value,
opts \\ []
) do
Logger.log_configuration_change(
user_id,
component,
setting,
old_value,
new_value,
opts
)
# Alert on security-relevant changes
log_security_config_change_if_needed(
security_relevant_setting?(setting),
user_id,
component,
setting,
old_value,
new_value
)
end
@doc """
Audits clipboard operations for data loss prevention.
"""
def audit_clipboard_operation(
terminal_id,
user_id,
operation,
content_summary,
opts \\ []
) do
Logger.log_data_access(
user_id,
operation,
"clipboard",
Keyword.merge(opts,
terminal_id: terminal_id,
content_size: byte_size(content_summary || ""),
data_classification: classify_content(content_summary)
)
)
# Check for potential data exfiltration
log_potential_data_exfiltration_if_needed(
large_clipboard_content?(content_summary),
user_id,
terminal_id,
operation,
content_summary
)
end
@doc """
Audits network connections from terminal.
"""
def audit_network_connection(
terminal_id,
user_id,
host,
port,
direction,
opts \\ []
) do
Logger.log_terminal_operation(
user_id,
terminal_id,
:network_connection,
Keyword.merge(opts,
host: host,
port: port,
direction: direction
)
)
# Check for suspicious connections
log_suspicious_connection_if_needed(
suspicious_host?(host) or suspicious_port?(port),
user_id,
terminal_id,
host,
port,
direction
)
end
## Pattern Matching Helper Functions for Security Logging
defp log_dangerous_command_if_needed(false, _user_id, _terminal_id, _command),
do: :ok
defp log_dangerous_command_if_needed(true, user_id, terminal_id, command) do
Logger.log_security_event(
:dangerous_command,
:high,
"Dangerous command executed: #{command}",
user_id: user_id,
terminal_id: terminal_id,
command: command
)
end
defp log_privilege_escalation_if_needed(
false,
_user_id,
_terminal_id,
_command
),
do: :ok
defp log_privilege_escalation_if_needed(true, user_id, terminal_id, command) do
Logger.log_terminal_operation(user_id, terminal_id, :privilege_escalation,
command: command,
elevation_type: detect_elevation_type(command)
)
end
defp log_sensitive_file_access_if_needed(
false,
_user_id,
_terminal_id,
_operation,
_file_path
),
do: :ok
defp log_sensitive_file_access_if_needed(
true,
user_id,
terminal_id,
operation,
file_path
) do
Logger.log_security_event(
:sensitive_file_access,
:medium,
"Access to sensitive file: #{file_path}",
user_id: user_id,
terminal_id: terminal_id,
operation: operation,
file_path: file_path
)
end
defp handle_authentication_outcome(:failure, username, opts),
do: track_failed_attempt(username, opts)
defp handle_authentication_outcome(_outcome, _username, _opts), do: :ok
defp log_critical_resource_denial_if_needed(
false,
_user_id,
_resource,
_action
),
do: :ok
defp log_critical_resource_denial_if_needed(true, user_id, resource, action) do
Logger.log_security_event(
:critical_resource_denied,
:high,
"Access denied to critical resource",
user_id: user_id,
resource: resource,
action: action
)
end
defp log_security_config_change_if_needed(
false,
_user_id,
_component,
_setting,
_old_value,
_new_value
),
do: :ok
defp log_security_config_change_if_needed(
true,
user_id,
component,
setting,
old_value,
new_value
) do
Logger.log_security_event(
:security_config_changed,
:medium,
"Security configuration modified: #{setting}",
user_id: user_id,
component: component,
old_value: sanitize_value(old_value),
new_value: sanitize_value(new_value)
)
end
defp log_potential_data_exfiltration_if_needed(
false,
_user_id,
_terminal_id,
_operation,
_content_summary
),
do: :ok
defp log_potential_data_exfiltration_if_needed(
true,
user_id,
terminal_id,
operation,
content_summary
) do
Logger.log_security_event(
:potential_data_exfiltration,
:medium,
"Large clipboard operation detected",
user_id: user_id,
terminal_id: terminal_id,
operation: operation,
size: byte_size(content_summary || "")
)
end
defp log_suspicious_connection_if_needed(
false,
_user_id,
_terminal_id,
_host,
_port,
_direction
),
do: :ok
defp log_suspicious_connection_if_needed(
true,
user_id,
terminal_id,
host,
port,
direction
) do
Logger.log_security_event(
:suspicious_connection,
:high,
"Suspicious network connection detected",
user_id: user_id,
terminal_id: terminal_id,
host: host,
port: port,
direction: direction
)
end
## Helper Functions
defp dangerous_command?(command) do
dangerous_patterns = [
~r/rm\s+-rf\s+\//,
~r/dd\s+.*of=\/dev\//,
~r/mkfs/,
~r/>\s*\/dev\/null\s*2>&1/,
~r/curl.*\|\s*(bash|sh)/,
~r/wget.*\|\s*(bash|sh)/
]
Enum.any?(dangerous_patterns, &Regex.match?(&1, command))
end
defp privilege_escalation?(command) do
escalation_patterns = [
~r/^sudo\s+/,
~r/^su\s+/,
~r/^doas\s+/,
~r/chmod\s+[u\+s]/,
~r/setuid/
]
Enum.any?(escalation_patterns, &Regex.match?(&1, command))
end
# Helper functions for pattern matching refactoring
defp detect_elevation_type("sudo" <> _rest), do: :sudo
defp detect_elevation_type("su" <> _rest), do: :su
defp detect_elevation_type("doas" <> _rest), do: :doas
defp detect_elevation_type(_command), do: :other
defp sensitive_file?(path) do
sensitive_patterns = [
~r/\.ssh\//,
~r/\.gnupg\//,
~r/\.aws\//,
~r/\.env$/,
~r/passwd$/,
~r/shadow$/,
~r/\.key$/,
~r/\.pem$/,
~r/\.crt$/
]
Enum.any?(sensitive_patterns, &Regex.match?(&1, path))
end
defp classify_file(path) do
case {sensitive_file?(path), String.contains?(path, "config"),
String.contains?(path, "log")} do
{true, _, _} -> :restricted
{false, true, _} -> :confidential
{false, false, true} -> :internal
{false, false, false} -> :public
end
end
defp classify_content(nil), do: :public
defp classify_content(content) when is_binary(content) do
case {String.contains?(content, "password"),
String.contains?(content, "token"),
String.contains?(content, "key")} do
{true, _, _} -> :restricted
{false, true, _} -> :restricted
{false, false, true} -> :confidential
{false, false, false} -> :public
end
end
defp classify_content(_content), do: :public
defp critical_resource?(%{type: type}) do
type in ["admin", "system", "security", "audit"]
end
defp critical_resource?(_), do: false
defp security_relevant_setting?(setting) do
security_settings = [
"authentication",
"authorization",
"encryption",
"mfa",
"password_policy",
"session_timeout",
"audit_level"
]
Enum.any?(security_settings, &String.contains?(to_string(setting), &1))
end
defp sanitize_value(value) when is_binary(value) do
value
|> String.replace(~r/password=\S+/, "password=***")
|> String.replace(~r/token=\S+/, "token=***")
|> String.replace(~r/key=\S+/, "key=***")
end
defp sanitize_value(value), do: value
defp large_clipboard_content?(content) when is_binary(content) do
# 10KB threshold
byte_size(content) > 10_000
end
defp large_clipboard_content?(_), do: false
defp suspicious_host?(host) do
suspicious_domains = [
"malware.com",
"phishing.net",
"botnet.org"
]
Enum.any?(suspicious_domains, &String.contains?(host, &1))
end
defp suspicious_port?(port) do
# Common malware/backdoor ports
suspicious_ports = [31_337, 12_345, 4444, 5555, 6666, 7777]
port in suspicious_ports
end
defp track_failed_attempt(username, opts) do
# This integrates with the existing account lockout mechanism in Raxol.Auth
ip = Keyword.get(opts, :ip_address)
Logger.debug("Tracking failed attempt for #{username} from #{ip}")
# Log security event for audit trail
Logger.log_security_event(
:failed_authentication,
:medium,
"Failed authentication attempt",
username: username,
ip_address: ip,
user_agent: Keyword.get(opts, :user_agent),
timestamp: DateTime.utc_now()
)
# Note: Account lockout logic is handled automatically in Raxol.Auth.authenticate_user_password/2
# which updates failed_login_attempts and sets locked_until when attempts >= 5
:ok
end
@doc """
Example usage in a terminal handler.
"""
def example_usage do
# Audit session start
audit_session_start("term001", "user123",
ip_address: "192.168.1.100",
client: "iTerm2"
)
# Audit command execution
command_result = %{
exit_code: 0,
duration_ms: 125,
output: "Command output here..."
}
audit_command("term001", "user123", "ls -la /etc", command_result)
# Audit file operation
audit_file_operation("term001", "user123", :read, "/etc/passwd",
bytes_read: 2048
)
# Audit configuration change
audit_config_change(
"admin",
"terminal",
"colors.background",
"#000000",
"#FFFFFF",
approved_by: "superadmin"
)
# Audit session end
audit_session_end("term001", "user123",
duration_ms: 3_600_000,
commands_executed: 42
)
end
end