Files
Wiktor 8071a21d71 feat(api): add secure command execution API and HTTP client fallback
- Introduce /api/command/exec endpoint in webui-server.sh for executing a limited set of safe shell commands with JSON response.
- Implement security checks to restrict allowed commands and return appropriate HTTP status codes.
- Add HTTPClient in webroot/js/api.js to communicate with backend API, including execCommand method.
- Enhance ksu mock object to try HTTPClient execCommand first, then fallback to mock implementation.
- Add accessibility improvements in CSS with skip-link styles.

This enables secure remote command execution via the web UI and improves development mode API simulation.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
2025-07-21 23:39:12 +00:00

272 lines
7.8 KiB
Bash
Executable File

#!/system/bin/sh
# KernelSU Anti-Bootloop & Backup Module WebUIX Server Script
MODDIR=${0%/*}
MODDIR=${MODDIR%/*}
CONFIG_DIR="$MODDIR/config"
WEBROOT_DIR="$MODDIR/webroot"
# Log function for debugging
log_message() {
echo "$(date +"%Y-%m-%d %H:%M:%S") - $1" >> "$CONFIG_DIR/webui.log"
}
log_message "WebUIX server script started"
# Check if WebUI is enabled
check_webui_enabled() {
ENABLED=$(grep "webui_enabled" "$CONFIG_DIR/main.conf" | cut -d= -f2 || echo "true")
if [ "$ENABLED" == "true" ]; then
log_message "WebUI is enabled"
return 0
else
log_message "WebUI is disabled"
return 1
fi
}
# Get configured port
get_webui_port() {
PORT=$(grep "webui_port" "$CONFIG_DIR/main.conf" | cut -d= -f2 || echo "8080")
log_message "Using port: $PORT"
echo "$PORT"
}
# Check if authentication is required
check_auth_required() {
AUTH_REQUIRED=$(grep "webui_auth" "$CONFIG_DIR/main.conf" | cut -d= -f2 || echo "true")
if [ "$AUTH_REQUIRED" == "true" ]; then
log_message "Authentication is required"
return 0
else
log_message "Authentication is disabled"
return 1
fi
}
# Start the WebUI server
start_webui_server() {
PORT=$(get_webui_port)
log_message "Starting WebUIX server on port $PORT"
# Start busybox httpd server
if command -v busybox >/dev/null 2>&1; then
# Create httpd configuration
cat > "$CONFIG_DIR/httpd.conf" <<EOF
*.cgi:/system/bin/sh
/api/*:application/json
EOF
# Start busybox httpd
busybox httpd -p "$PORT" -h "$WEBROOT_DIR" -c "$CONFIG_DIR/httpd.conf" -f &
HTTPD_PID=$!
echo "$HTTPD_PID" > "$CONFIG_DIR/httpd.pid"
log_message "Busybox httpd started with PID: $HTTPD_PID"
else
# Fallback to netcat-based simple server
start_netcat_server "$PORT" &
NC_PID=$!
echo "$NC_PID" > "$CONFIG_DIR/httpd.pid"
log_message "Netcat server started with PID: $NC_PID"
fi
log_message "WebUIX server started successfully"
}
# Netcat-based simple HTTP server fallback
start_netcat_server() {
local port="$1"
while true; do
{
echo "HTTP/1.1 200 OK"
echo "Content-Type: text/html"
echo ""
cat "$WEBROOT_DIR/index.html"
} | nc -l -p "$port"
done
}
# API Handler Functions
get_system_info() {
echo "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{"
echo "\"device_model\":\"$(getprop ro.product.model)\","
echo "\"android_version\":\"$(getprop ro.build.version.release)\","
echo "\"kernel_version\":\"$(uname -r)\","
echo "\"kernelsu_version\":\"$(getprop ro.kernelsu.version)\","
echo "\"module_version\":\"1.0.0\""
echo "}"
}
list_backups() {
echo "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{"
echo "\"backups\":["
local first=true
for backup in "$CONFIG_DIR/backups"/*; do
if [ -d "$backup" ]; then
[ "$first" = false ] && echo ","
echo " {\"id\":\"$(basename "$backup")\",\"created\":\"$(stat -c %Y "$backup")\"}"
first=false
fi
done
echo "]}"
}
create_backup_api() {
local profile="${REQUEST_BODY:-default}"
"$MODDIR/scripts/backup-engine.sh" create_backup "$profile"
echo "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{"
echo "\"status\":\"success\","
echo "\"message\":\"Backup created successfully\""
echo "}"
}
restore_backup_api() {
local backup_id="${REQUEST_BODY}"
"$MODDIR/scripts/backup-engine.sh" restore_from_backup "$backup_id"
echo "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{"
echo "\"status\":\"success\","
echo "\"message\":\"Backup restored successfully\""
echo "}"
}
get_safety_status() {
local boot_count=$(cat "$CONFIG_DIR/boot_counter" 2>/dev/null || echo "0")
local safe_mode=$([ -f "/data/local/tmp/ksu_safe_mode" ] && echo "true" || echo "false")
echo "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{"
echo "\"boot_count\":$boot_count,"
echo "\"safe_mode\":$safe_mode,"
echo "\"last_boot\":\"$(date)\""
echo "}"
}
get_settings() {
echo "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{"
echo "\"webui_port\":8080,"
echo "\"auth_required\":true,"
echo "\"backup_encryption\":$(grep 'backup_encryption' "$CONFIG_DIR/main.conf" | cut -d= -f2 || echo 'false'),"
echo "\"auto_backup\":false"
echo "}"
}
exec_command_api() {
# Read POST body and extract command
local command=""
if [ -n "$REQUEST_BODY" ]; then
# Simple JSON parsing - extract command value
command=$(echo "$REQUEST_BODY" | sed -n 's/.*"command":\s*"\([^"]*\)".*/\1/p')
fi
if [ -z "$command" ]; then
echo "HTTP/1.1 400 Bad Request\r\nContent-Type: application/json\r\n\r\n{\"error\":\"Missing command parameter\"}"
return
fi
# Security check - only allow safe commands
case "$command" in
"getprop "*|"cat /proc/uptime"|"uname -r"|"su -v"|"id"|"whoami"|"date"|"uptime")
# Execute safe command and capture output
local output
output=$(eval "$command" 2>&1)
local exit_code=$?
# Return JSON response
echo "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{"
echo "\"status\":\"success\","
echo "\"output\":\"$(echo "$output" | sed 's/\\/\\\\/g; s/"/\\"/g')\","
echo "\"exit_code\":$exit_code"
echo "}"
;;
*)
echo "HTTP/1.1 403 Forbidden\r\nContent-Type: application/json\r\n\r\n{\"error\":\"Command not allowed for security reasons\"}"
;;
esac
log_message "Command execution API called: $command"
}
# Handle API requests
handle_api_request() {
REQUEST="$1"
log_message "Handling API request: $REQUEST"
# Parse API request and route to appropriate handler
case "$REQUEST" in
"/api/system/info")
get_system_info
;;
"/api/backups/list")
list_backups
;;
"/api/backups/create")
create_backup_api
;;
"/api/backups/restore")
restore_backup_api
;;
"/api/safety/status")
get_safety_status
;;
"/api/settings")
get_settings
;;
"/api/command/exec")
exec_command_api
;;
*)
echo "HTTP/1.1 404 Not Found\r\nContent-Type: application/json\r\n\r\n{\"error\":\"Endpoint not found\"}"
;;
esac
log_message "API request handled (placeholder)"
}
# Set up authentication
setup_authentication() {
log_message "Setting up authentication"
# Generate random session token
SESSION_TOKEN=$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 32)
echo "$SESSION_TOKEN" > "$CONFIG_DIR/session_token"
# Create basic auth file if it doesn't exist
if [ ! -f "$CONFIG_DIR/auth_users" ]; then
# Default credentials: admin/kernelsu
echo "admin:$(echo -n 'kernelsu' | sha256sum | cut -d' ' -f1)" > "$CONFIG_DIR/auth_users"
fi
log_message "Authentication configured with session token"
log_message "Authentication setup completed (placeholder)"
}
# Main function
main() {
log_message "WebUIX main function started"
# Check if WebUI is enabled
if check_webui_enabled; then
# Check if authentication is required
if check_auth_required; then
setup_authentication
fi
# Start the WebUI server
start_webui_server
else
log_message "WebUI is disabled, exiting"
exit 0
fi
}
# Execute main function
main