v0.2.0: WebUI dashboard, remove stealth, action button
- Full WebUI dashboard (localhost:8088) with status, module toggles, IOC stats, alerts, settings editor, log viewer, quick actions - Works in both KernelSU embedded mode (ksu.exec) and standalone (HTTP API) - action.sh opens WebUI when tapping module card in KernelSU manager - Removed stealth.sh (overengineered, not needed yet) - Added WEBUI_ENABLED and WEBUI_PORT config options - Bumped version to v0.2.0
This commit is contained in:
28
action.sh
Executable file
28
action.sh
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/system/bin/sh
|
||||||
|
# Vigil — Module Action Button
|
||||||
|
# This runs when the user taps the module card in KernelSU/Magisk manager
|
||||||
|
# Opens the WebUI in the default browser
|
||||||
|
|
||||||
|
VIGIL_DATA="/data/adb/vigil"
|
||||||
|
WEBUI_PORT=8088
|
||||||
|
|
||||||
|
[ -f "$VIGIL_DATA/vigil.conf" ] && . "$VIGIL_DATA/vigil.conf"
|
||||||
|
|
||||||
|
# Check if WebUI is running
|
||||||
|
WEBUI_RUNNING=0
|
||||||
|
if [ -f "$VIGIL_DATA/vigild.pid" ]; then
|
||||||
|
VIGILD_PID=$(cat "$VIGIL_DATA/vigild.pid")
|
||||||
|
kill -0 "$VIGILD_PID" 2>/dev/null && WEBUI_RUNNING=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$WEBUI_RUNNING" = "0" ]; then
|
||||||
|
echo "Starting Vigil WebUI..."
|
||||||
|
MODDIR="${0%/*}"
|
||||||
|
nohup "$MODDIR/vigil/lib/webui.sh" serve >> "$VIGIL_DATA/vigil.log" 2>&1 &
|
||||||
|
sleep 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Open WebUI in browser
|
||||||
|
am start -a android.intent.action.VIEW -d "http://localhost:${WEBUI_PORT}" 2>/dev/null
|
||||||
|
|
||||||
|
echo "Vigil WebUI: http://localhost:${WEBUI_PORT}"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
id=vigil
|
id=vigil
|
||||||
name=Vigil — Anti-Surveillance Shield
|
name=Vigil — Anti-Surveillance Shield
|
||||||
version=v0.1.0
|
version=v0.2.0
|
||||||
versionCode=1
|
versionCode=2
|
||||||
author=Setec Labs
|
author=Setec Labs
|
||||||
description=Anti-surveillance, anti-stalkerware, and anti-forensic protection for journalists, activists, and at-risk users. Detects Pegasus, stalkerware, IMSI catchers, silent SMS, forensic extraction tools, and more.
|
description=Anti-surveillance, anti-stalkerware, and anti-forensic protection for journalists, activists, and at-risk users. Detects Pegasus, stalkerware, IMSI catchers, silent SMS, forensic extraction tools, and more.
|
||||||
updateJson=
|
updateJson=
|
||||||
|
|||||||
@@ -227,6 +227,12 @@ cmd_deep_scan() {
|
|||||||
"$VIGIL_LIB/deep_scan.sh" deep
|
"$VIGIL_LIB/deep_scan.sh" deep
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd_webui() {
|
||||||
|
check_module
|
||||||
|
echo "Starting Vigil WebUI on http://localhost:${WEBUI_PORT:-8088}"
|
||||||
|
"$VIGIL_LIB/webui.sh" serve
|
||||||
|
}
|
||||||
|
|
||||||
cmd_harden() {
|
cmd_harden() {
|
||||||
check_module
|
check_module
|
||||||
local subcmd="${1:-harden}"
|
local subcmd="${1:-harden}"
|
||||||
@@ -298,6 +304,9 @@ cmd_help() {
|
|||||||
echo " duress [setup|status]"
|
echo " duress [setup|status]"
|
||||||
echo " Duress/panic trigger configuration"
|
echo " Duress/panic trigger configuration"
|
||||||
echo ""
|
echo ""
|
||||||
|
echo "${BOLD}Dashboard${NC}"
|
||||||
|
echo " webui Start WebUI dashboard (localhost:8088)"
|
||||||
|
echo ""
|
||||||
echo "${BOLD}Maintenance${NC}"
|
echo "${BOLD}Maintenance${NC}"
|
||||||
echo " update-ioc Update threat indicator database"
|
echo " update-ioc Update threat indicator database"
|
||||||
echo " version Show version"
|
echo " version Show version"
|
||||||
@@ -328,6 +337,7 @@ case "$1" in
|
|||||||
app) shift; cmd_app_honeypot "$@" ;;
|
app) shift; cmd_app_honeypot "$@" ;;
|
||||||
network) shift; cmd_network "$@" ;;
|
network) shift; cmd_network "$@" ;;
|
||||||
duress) shift; cmd_duress "$@" ;;
|
duress) shift; cmd_duress "$@" ;;
|
||||||
|
webui) cmd_webui ;;
|
||||||
log) shift; cmd_log "$@" ;;
|
log) shift; cmd_log "$@" ;;
|
||||||
version) echo "Vigil v${VERSION}" ;;
|
version) echo "Vigil v${VERSION}" ;;
|
||||||
help|--help|-h|"") cmd_help ;;
|
help|--help|-h|"") cmd_help ;;
|
||||||
|
|||||||
@@ -167,7 +167,14 @@ main() {
|
|||||||
"$VIGIL_LIB/antiforensics.sh" harden >> "$VIGIL_LOG" 2>&1
|
"$VIGIL_LIB/antiforensics.sh" harden >> "$VIGIL_LOG" 2>&1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 9. Install network blocklists
|
# 9. WebUI dashboard
|
||||||
|
if [ "${WEBUI_ENABLED:-1}" = "1" ]; then
|
||||||
|
log INFO "Starting WebUI on port ${WEBUI_PORT:-8088}..."
|
||||||
|
"$VIGIL_LIB/webui.sh" serve >> "$VIGIL_LOG" 2>&1 &
|
||||||
|
log INFO "WebUI PID: $!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 10. Install network blocklists
|
||||||
if [ "${NETWORK_BLOCK_C2:-1}" = "1" ] || [ "${NETWORK_BLOCK_TRACKERS:-1}" = "1" ]; then
|
if [ "${NETWORK_BLOCK_C2:-1}" = "1" ] || [ "${NETWORK_BLOCK_TRACKERS:-1}" = "1" ]; then
|
||||||
log INFO "Installing network blocklists..."
|
log INFO "Installing network blocklists..."
|
||||||
"$VIGIL_LIB/network_monitor.sh" install >> "$VIGIL_LOG" 2>&1
|
"$VIGIL_LIB/network_monitor.sh" install >> "$VIGIL_LOG" 2>&1
|
||||||
|
|||||||
@@ -86,3 +86,7 @@ APP_HONEYPOT_AUTO=0 # Auto-honeypot detected threats (0=manual)
|
|||||||
IOC_UPDATE_INTERVAL=86400 # Seconds between auto-updates (86400=24hr)
|
IOC_UPDATE_INTERVAL=86400 # Seconds between auto-updates (86400=24hr)
|
||||||
VIGIL_API_KEY="" # Autarch API key for backend updates
|
VIGIL_API_KEY="" # Autarch API key for backend updates
|
||||||
VIGIL_BACKEND_URL="" # Autarch backend URL
|
VIGIL_BACKEND_URL="" # Autarch backend URL
|
||||||
|
|
||||||
|
# ── WebUI Dashboard ─────────────────────────────────
|
||||||
|
WEBUI_ENABLED=1 # Start WebUI on boot
|
||||||
|
WEBUI_PORT=8088 # Port for local dashboard
|
||||||
|
|||||||
284
vigil/lib/webui.sh
Executable file
284
vigil/lib/webui.sh
Executable file
@@ -0,0 +1,284 @@
|
|||||||
|
#!/system/bin/sh
|
||||||
|
# Vigil — WebUI Server
|
||||||
|
# Serves a local web dashboard for settings, status, alerts, and scan control
|
||||||
|
# (c) Setec Labs
|
||||||
|
#
|
||||||
|
# Runs on localhost:8088 (configurable)
|
||||||
|
# Uses busybox httpd with CGI, or falls back to nc-based server
|
||||||
|
|
||||||
|
VIGIL_DATA="/data/adb/vigil"
|
||||||
|
VIGIL_LOG="$VIGIL_DATA/vigil.log"
|
||||||
|
WEBUI_PORT="${WEBUI_PORT:-8088}"
|
||||||
|
WEBUI_DIR=""
|
||||||
|
VIGIL_LIB="$(dirname "$0")"
|
||||||
|
|
||||||
|
# Find the webroot
|
||||||
|
for d in /data/adb/modules/vigil/vigil/webroot "$VIGIL_LIB/../webroot"; do
|
||||||
|
[ -d "$d" ] && WEBUI_DIR="$d" && break
|
||||||
|
done
|
||||||
|
|
||||||
|
[ -f "$VIGIL_DATA/vigil.conf" ] && . "$VIGIL_DATA/vigil.conf"
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [webui] $1" >> "$VIGIL_LOG"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── CGI API HANDLER ──
|
||||||
|
# Processes API requests and returns JSON
|
||||||
|
handle_api() {
|
||||||
|
local endpoint="$1"
|
||||||
|
local method="$2"
|
||||||
|
|
||||||
|
echo "HTTP/1.1 200 OK"
|
||||||
|
echo "Content-Type: application/json"
|
||||||
|
echo "Access-Control-Allow-Origin: *"
|
||||||
|
echo "Connection: close"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
case "$endpoint" in
|
||||||
|
/api/status)
|
||||||
|
local daemon_status="stopped"
|
||||||
|
local daemon_pid=""
|
||||||
|
if [ -f "$VIGIL_DATA/vigild.pid" ]; then
|
||||||
|
daemon_pid=$(cat "$VIGIL_DATA/vigild.pid")
|
||||||
|
kill -0 "$daemon_pid" 2>/dev/null && daemon_status="running"
|
||||||
|
fi
|
||||||
|
local lockdown=$([ -f "$VIGIL_DATA/.lockdown" ] && echo "true" || echo "false")
|
||||||
|
|
||||||
|
cat <<ENDJSON
|
||||||
|
{
|
||||||
|
"daemon": "$daemon_status",
|
||||||
|
"pid": "$daemon_pid",
|
||||||
|
"lockdown": $lockdown,
|
||||||
|
"version": "0.2.0",
|
||||||
|
"modules": {
|
||||||
|
"scanner": "${SCANNER_ENABLED:-1}",
|
||||||
|
"frostguard": "${FROSTGUARD_ENABLED:-1}",
|
||||||
|
"forensic_shield": "${FORENSIC_SHIELD_ENABLED:-1}",
|
||||||
|
"sms_shield": "${SMS_SHIELD_ENABLED:-1}",
|
||||||
|
"network_monitor": "${NETWORK_MONITOR_ENABLED:-1}",
|
||||||
|
"key_wiper": "${KEYWIPER_ENABLED:-1}",
|
||||||
|
"deep_scan": "${DEEP_SCAN_BACKGROUND:-1}",
|
||||||
|
"antiforensics": "${ANTIFORENSICS_ENABLED:-1}",
|
||||||
|
"duress": "${DURESS_ENABLED:-0}",
|
||||||
|
"sms_honeypot": "${SMS_FAKE_RESPONSE:-0}",
|
||||||
|
"app_honeypot": "${APP_HONEYPOT_AUTO:-0}",
|
||||||
|
"quarantine": "${QUARANTINE_ENABLED:-0}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ENDJSON
|
||||||
|
;;
|
||||||
|
|
||||||
|
/api/alerts)
|
||||||
|
echo "["
|
||||||
|
if [ -f "$VIGIL_DATA/alerts/history" ]; then
|
||||||
|
local first=1
|
||||||
|
tail -50 "$VIGIL_DATA/alerts/history" | while IFS='|' read -r sev ts mod msg; do
|
||||||
|
[ $first -eq 0 ] && echo ","
|
||||||
|
first=0
|
||||||
|
# Escape quotes in message
|
||||||
|
msg=$(echo "$msg" | sed 's/"/\\"/g')
|
||||||
|
echo " {\"severity\":\"$sev\",\"timestamp\":$ts,\"module\":\"$mod\",\"message\":\"$msg\"}"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
echo "]"
|
||||||
|
;;
|
||||||
|
|
||||||
|
/api/ioc-stats)
|
||||||
|
echo "{"
|
||||||
|
local first=1
|
||||||
|
for f in packages.txt certificates.txt domains.txt ips.txt hashes.txt cellebrite_hashes.txt hosts.txt; do
|
||||||
|
[ $first -eq 0 ] && echo ","
|
||||||
|
first=0
|
||||||
|
local name=$(echo "$f" | sed 's/\.txt//')
|
||||||
|
local count=0
|
||||||
|
[ -f "$VIGIL_DATA/$f" ] && count=$(wc -l < "$VIGIL_DATA/$f")
|
||||||
|
echo " \"$name\": $count"
|
||||||
|
done
|
||||||
|
echo "}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
/api/config)
|
||||||
|
if [ "$method" = "POST" ]; then
|
||||||
|
# Read POST body from stdin
|
||||||
|
read -r body
|
||||||
|
# Parse key=value pairs and update config
|
||||||
|
echo "$body" | tr '&' '\n' | while IFS='=' read -r key val; do
|
||||||
|
key=$(echo "$key" | tr -d ' ')
|
||||||
|
val=$(echo "$val" | tr -d ' ')
|
||||||
|
if grep -q "^${key}=" "$VIGIL_DATA/vigil.conf" 2>/dev/null; then
|
||||||
|
sed -i "s|^${key}=.*|${key}=${val}|" "$VIGIL_DATA/vigil.conf"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "{\"status\":\"ok\"}"
|
||||||
|
else
|
||||||
|
# Return current config as JSON
|
||||||
|
echo "{"
|
||||||
|
local first=1
|
||||||
|
grep -v '^#' "$VIGIL_DATA/vigil.conf" 2>/dev/null | grep '=' | while IFS='=' read -r key val; do
|
||||||
|
key=$(echo "$key" | tr -d ' ')
|
||||||
|
val=$(echo "$val" | sed 's/^"//' | sed 's/"$//' | sed 's/#.*//' | tr -d ' ')
|
||||||
|
[ -z "$key" ] && continue
|
||||||
|
[ $first -eq 0 ] && echo ","
|
||||||
|
first=0
|
||||||
|
echo " \"$key\": \"$val\""
|
||||||
|
done
|
||||||
|
echo "}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
/api/scan)
|
||||||
|
echo "{\"status\":\"started\"}"
|
||||||
|
# Run scan in background
|
||||||
|
"$VIGIL_LIB/scanner.sh" quick >> "$VIGIL_LOG" 2>&1 &
|
||||||
|
;;
|
||||||
|
|
||||||
|
/api/deep-scan)
|
||||||
|
echo "{\"status\":\"started\"}"
|
||||||
|
"$VIGIL_LIB/deep_scan.sh" deep >> "$VIGIL_LOG" 2>&1 &
|
||||||
|
;;
|
||||||
|
|
||||||
|
/api/lockdown)
|
||||||
|
"$VIGIL_LIB/key_wiper.sh" lockdown >> "$VIGIL_LOG" 2>&1 &
|
||||||
|
echo "{\"status\":\"lockdown_initiated\"}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
/api/harden)
|
||||||
|
"$VIGIL_LIB/antiforensics.sh" harden >> "$VIGIL_LOG" 2>&1 &
|
||||||
|
echo "{\"status\":\"hardening\"}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
/api/sanitize)
|
||||||
|
"$VIGIL_LIB/antiforensics.sh" sanitize >> "$VIGIL_LOG" 2>&1 &
|
||||||
|
echo "{\"status\":\"sanitizing\"}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
/api/update-ioc)
|
||||||
|
"$VIGIL_LIB/ioc_updater.sh" update >> "$VIGIL_LOG" 2>&1 &
|
||||||
|
echo "{\"status\":\"updating\"}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
/api/log)
|
||||||
|
echo "["
|
||||||
|
if [ -f "$VIGIL_LOG" ]; then
|
||||||
|
local first=1
|
||||||
|
tail -100 "$VIGIL_LOG" | while read -r line; do
|
||||||
|
line=$(echo "$line" | sed 's/"/\\"/g')
|
||||||
|
[ $first -eq 0 ] && echo ","
|
||||||
|
first=0
|
||||||
|
echo " \"$line\""
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
echo "]"
|
||||||
|
;;
|
||||||
|
|
||||||
|
/api/exec)
|
||||||
|
# Standalone mode: execute shell command (replaces ksu.exec)
|
||||||
|
echo "HTTP/1.1 200 OK"
|
||||||
|
echo "Content-Type: text/plain"
|
||||||
|
echo "Connection: close"
|
||||||
|
echo ""
|
||||||
|
if [ "$method" = "POST" ] && [ -n "$POST_BODY" ]; then
|
||||||
|
eval "$POST_BODY" 2>&1
|
||||||
|
fi
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "{\"error\":\"unknown endpoint\"}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── NC-BASED HTTP SERVER ──
|
||||||
|
# Simple HTTP server using netcat — no dependencies
|
||||||
|
cmd_serve() {
|
||||||
|
log "WebUI starting on port $WEBUI_PORT..."
|
||||||
|
echo "Vigil WebUI: http://localhost:$WEBUI_PORT"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
# Listen for a connection and handle it
|
||||||
|
{
|
||||||
|
# Read the HTTP request
|
||||||
|
local request=""
|
||||||
|
local method=""
|
||||||
|
local path=""
|
||||||
|
local content_length=0
|
||||||
|
|
||||||
|
while read -r line; do
|
||||||
|
line=$(echo "$line" | tr -d '\r')
|
||||||
|
[ -z "$line" ] && break
|
||||||
|
|
||||||
|
if [ -z "$request" ]; then
|
||||||
|
request="$line"
|
||||||
|
method=$(echo "$line" | awk '{print $1}')
|
||||||
|
path=$(echo "$line" | awk '{print $2}')
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$line" | grep -qi "Content-Length:"; then
|
||||||
|
content_length=$(echo "$line" | grep -oE '[0-9]+')
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Read POST body if present
|
||||||
|
local POST_BODY=""
|
||||||
|
if [ "$method" = "POST" ] && [ "$content_length" -gt 0 ] 2>/dev/null; then
|
||||||
|
POST_BODY=$(dd bs=1 count="$content_length" 2>/dev/null)
|
||||||
|
fi
|
||||||
|
export POST_BODY
|
||||||
|
|
||||||
|
# Route the request
|
||||||
|
case "$path" in
|
||||||
|
/api/*)
|
||||||
|
handle_api "$path" "$method"
|
||||||
|
;;
|
||||||
|
/|/index.html)
|
||||||
|
echo "HTTP/1.1 200 OK"
|
||||||
|
echo "Content-Type: text/html"
|
||||||
|
echo "Connection: close"
|
||||||
|
echo ""
|
||||||
|
cat "$WEBUI_DIR/index.html" 2>/dev/null || echo "<h1>WebUI files not found</h1>"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
local file="$WEBUI_DIR${path}"
|
||||||
|
if [ -f "$file" ]; then
|
||||||
|
local mime="text/plain"
|
||||||
|
case "$path" in
|
||||||
|
*.html) mime="text/html" ;;
|
||||||
|
*.css) mime="text/css" ;;
|
||||||
|
*.js) mime="application/javascript" ;;
|
||||||
|
*.json) mime="application/json" ;;
|
||||||
|
*.png) mime="image/png" ;;
|
||||||
|
*.svg) mime="image/svg+xml" ;;
|
||||||
|
esac
|
||||||
|
echo "HTTP/1.1 200 OK"
|
||||||
|
echo "Content-Type: $mime"
|
||||||
|
echo "Connection: close"
|
||||||
|
echo ""
|
||||||
|
cat "$file"
|
||||||
|
else
|
||||||
|
echo "HTTP/1.1 404 Not Found"
|
||||||
|
echo "Connection: close"
|
||||||
|
echo ""
|
||||||
|
echo "404"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
} | busybox nc -l -p "$WEBUI_PORT" 2>/dev/null || {
|
||||||
|
# Fallback: use toybox nc or /system/bin/nc
|
||||||
|
log "busybox nc not available, trying alternatives..."
|
||||||
|
break
|
||||||
|
}
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── DISPATCH ──
|
||||||
|
case "$1" in
|
||||||
|
serve) cmd_serve ;;
|
||||||
|
status) echo "WebUI port: $WEBUI_PORT" ;;
|
||||||
|
*)
|
||||||
|
echo "Vigil WebUI Server"
|
||||||
|
echo "Usage: webui.sh serve"
|
||||||
|
echo " Starts web dashboard on http://localhost:$WEBUI_PORT"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
241
vigil/webroot/index.html
Normal file
241
vigil/webroot/index.html
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Vigil — Anti-Surveillance Shield</title>
|
||||||
|
<style>
|
||||||
|
:root{--bg:#0a0a0f;--s:#12121a;--s2:#1a1a25;--b:#2a2a3a;--t:#e0e0e8;--t2:#8888a0;--a:#4a9eff;--g:#22c55e;--y:#eab308;--r:#ef4444;--o:#f97316}
|
||||||
|
*{margin:0;padding:0;box-sizing:border-box}
|
||||||
|
body{font-family:'SF Mono','Cascadia Code','Fira Code',monospace;background:var(--bg);color:var(--t);min-height:100vh;font-size:14px}
|
||||||
|
.c{max-width:900px;margin:0 auto;padding:16px}
|
||||||
|
.hdr{text-align:center;padding:24px 0 16px;border-bottom:1px solid var(--b);margin-bottom:20px}
|
||||||
|
.hdr h1{font-size:22px;font-weight:600;letter-spacing:2px}
|
||||||
|
.hdr .sub{color:var(--t2);font-size:12px;margin-top:4px}
|
||||||
|
.hdr .ver{color:var(--a);font-size:11px}
|
||||||
|
.sb{display:flex;gap:12px;margin-bottom:20px;flex-wrap:wrap}
|
||||||
|
.sc{display:flex;align-items:center;gap:6px;background:var(--s);border:1px solid var(--b);border-radius:6px;padding:8px 14px;font-size:12px;flex:1;min-width:140px}
|
||||||
|
.sd{width:8px;height:8px;border-radius:50%;flex-shrink:0}
|
||||||
|
.sd.on{background:var(--g);box-shadow:0 0 6px var(--g)}.sd.off{background:var(--r)}
|
||||||
|
.cd{background:var(--s);border:1px solid var(--b);border-radius:8px;margin-bottom:16px;overflow:hidden}
|
||||||
|
.ch{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--b);background:var(--s2);cursor:pointer;user-select:none}
|
||||||
|
.ch h2{font-size:14px;font-weight:600}
|
||||||
|
.cb{padding:16px}.cb.h{display:none}
|
||||||
|
.mg{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:10px}
|
||||||
|
.mi{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;background:var(--s2);border-radius:6px;border:1px solid var(--b)}
|
||||||
|
.mn{font-size:13px}
|
||||||
|
.tg{position:relative;width:40px;height:22px;background:#333;border-radius:11px;cursor:pointer;transition:background .2s;border:none;outline:none}
|
||||||
|
.tg.on{background:var(--g)}
|
||||||
|
.tg::after{content:'';position:absolute;top:3px;left:3px;width:16px;height:16px;background:#fff;border-radius:50%;transition:transform .2s}
|
||||||
|
.tg.on::after{transform:translateX(18px)}
|
||||||
|
.ig{display:grid;grid-template-columns:repeat(auto-fill,minmax(110px,1fr));gap:8px}
|
||||||
|
.is{text-align:center;padding:12px 8px;background:var(--s2);border-radius:6px}
|
||||||
|
.is .n{font-size:20px;font-weight:700;color:var(--a)}.is .l{font-size:10px;color:var(--t2);margin-top:4px;text-transform:uppercase}
|
||||||
|
.al{max-height:300px;overflow-y:auto}
|
||||||
|
.ai{display:flex;gap:10px;padding:8px 0;border-bottom:1px solid var(--b);font-size:12px;align-items:flex-start}
|
||||||
|
.ai:last-child{border:none}
|
||||||
|
.sv{font-size:10px;font-weight:700;padding:2px 6px;border-radius:3px;flex-shrink:0;white-space:nowrap}
|
||||||
|
.sv.CRITICAL{background:var(--r);color:#fff}.sv.HIGH{background:var(--o);color:#fff}.sv.MEDIUM{background:var(--y);color:#000}.sv.LOW{background:var(--t2);color:#fff}.sv.INFO{background:var(--a);color:#fff}
|
||||||
|
.at{color:var(--t2);white-space:nowrap;font-size:11px}.am{color:var(--t);word-break:break-word}
|
||||||
|
.br{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}
|
||||||
|
.bt{padding:8px 16px;border:1px solid var(--b);border-radius:6px;background:var(--s2);color:var(--t);font-family:inherit;font-size:12px;cursor:pointer;transition:all .15s;outline:none}
|
||||||
|
.bt:hover{background:var(--b)}.bt:active{transform:scale(.97)}
|
||||||
|
.bt.d{border-color:var(--r);color:var(--r)}.bt.d:hover{background:var(--r);color:#fff}
|
||||||
|
.bt.p{border-color:var(--a);color:var(--a)}.bt.p:hover{background:var(--a);color:#fff}
|
||||||
|
.bt.g{border-color:var(--g);color:var(--g)}.bt.g:hover{background:var(--g);color:#fff}
|
||||||
|
.bt.ld{opacity:.5;pointer-events:none}
|
||||||
|
.lv{background:#000;border-radius:6px;padding:12px;max-height:250px;overflow-y:auto;font-size:11px;line-height:1.5;color:var(--t2);white-space:pre-wrap;word-break:break-all}
|
||||||
|
.sr{display:flex;justify-content:space-between;align-items:center;padding:10px 0;border-bottom:1px solid var(--b)}
|
||||||
|
.sr:last-child{border:none}
|
||||||
|
.sl{font-size:13px}.sd2{font-size:11px;color:var(--t2)}
|
||||||
|
.si{background:var(--s2);border:1px solid var(--b);border-radius:4px;color:var(--t);padding:4px 8px;font-family:inherit;font-size:12px;width:140px;text-align:right;outline:none}
|
||||||
|
.si:focus{border-color:var(--a)}
|
||||||
|
.toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);background:var(--s2);border:1px solid var(--a);color:var(--t);padding:10px 20px;border-radius:8px;font-size:13px;z-index:999;opacity:0;transition:opacity .3s;pointer-events:none}
|
||||||
|
.toast.show{opacity:1}
|
||||||
|
@media(max-width:500px){.mg{grid-template-columns:1fr}.ig{grid-template-columns:repeat(3,1fr)}.sb{flex-direction:column}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="c">
|
||||||
|
<div class="hdr">
|
||||||
|
<h1>VIGIL</h1>
|
||||||
|
<div class="sub">Anti-Surveillance Shield by Setec Labs</div>
|
||||||
|
<div class="ver">v0.2.0</div>
|
||||||
|
</div>
|
||||||
|
<div class="sb">
|
||||||
|
<div class="sc"><span class="sd" id="dd"></span><span id="dt">Checking...</span></div>
|
||||||
|
<div class="sc"><span class="sd" id="ld"></span><span id="lt">Checking...</span></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cd"><div class="ch" onclick="tc(this)"><h2>Quick Actions</h2><span>▼</span></div>
|
||||||
|
<div class="cb"><div class="br">
|
||||||
|
<button class="bt p" onclick="run('scanner.sh quick',this)">Quick Scan</button>
|
||||||
|
<button class="bt p" onclick="run('deep_scan.sh deep',this)">Deep Scan</button>
|
||||||
|
<button class="bt g" onclick="run('antiforensics.sh harden',this)">Harden</button>
|
||||||
|
<button class="bt" onclick="run('antiforensics.sh sanitize',this)">Sanitize</button>
|
||||||
|
<button class="bt" onclick="run('ioc_updater.sh update',this)">Update IOCs</button>
|
||||||
|
<button class="bt" onclick="run('integrity.sh verify',this)">Check Integrity</button>
|
||||||
|
<button class="bt d" onclick="if(confirm('Enter BFU lockdown? Reboot to restore.'))run('key_wiper.sh lockdown',this)">LOCKDOWN</button>
|
||||||
|
</div></div></div>
|
||||||
|
|
||||||
|
<div class="cd"><div class="ch" onclick="tc(this)"><h2>Protection Modules</h2><span>▼</span></div>
|
||||||
|
<div class="cb" id="mb"></div></div>
|
||||||
|
|
||||||
|
<div class="cd"><div class="ch" onclick="tc(this)"><h2>Threat Database</h2><span>▼</span></div>
|
||||||
|
<div class="cb" id="ib"></div></div>
|
||||||
|
|
||||||
|
<div class="cd"><div class="ch" onclick="tc(this)"><h2>Recent Alerts</h2><span>▼</span></div>
|
||||||
|
<div class="cb"><div class="al" id="al"></div></div></div>
|
||||||
|
|
||||||
|
<div class="cd"><div class="ch" onclick="tc(this)"><h2>Settings</h2><span>▼</span></div>
|
||||||
|
<div class="cb h" id="stb"><div id="stl"></div><div class="br"><button class="bt p" onclick="saveCfg(this)">Save Settings</button></div></div></div>
|
||||||
|
|
||||||
|
<div class="cd"><div class="ch" onclick="tc(this)"><h2>System Log</h2><span>▼</span></div>
|
||||||
|
<div class="cb h" id="lb"><div class="lv" id="lv">Loading...</div><div class="br"><button class="bt" onclick="loadLog()">Refresh</button></div></div></div>
|
||||||
|
</div>
|
||||||
|
<div class="toast" id="toast"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const D='/data/adb/vigil';
|
||||||
|
const L='/data/adb/modules/vigil/vigil/lib';
|
||||||
|
const isKSU=typeof ksu!=='undefined';
|
||||||
|
|
||||||
|
async function ex(cmd){
|
||||||
|
if(isKSU){
|
||||||
|
try{const r=await ksu.exec(cmd);return (r&&(r.output||r.stdout))||''}catch(e){return ''}
|
||||||
|
}else{
|
||||||
|
try{const r=await fetch('/api/exec',{method:'POST',body:cmd});return await r.text()}catch(e){return ''}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function toast(m){const t=document.getElementById('toast');t.textContent=m;t.classList.add('show');setTimeout(()=>t.classList.remove('show'),2500)}
|
||||||
|
function tc(h){h.nextElementSibling.classList.toggle('h')}
|
||||||
|
async function run(s,b){
|
||||||
|
const lbl=b?b.textContent:'';
|
||||||
|
if(b){b.classList.add('ld');b.textContent='Running...';}
|
||||||
|
toast('Running '+s.split('.')[0]+'...');
|
||||||
|
await ex(L+'/'+s+' 2>&1');
|
||||||
|
if(b){b.classList.remove('ld');b.textContent=lbl;}
|
||||||
|
toast('Done');loadStatus();loadAlerts();
|
||||||
|
}
|
||||||
|
|
||||||
|
const MODS={
|
||||||
|
SCANNER_ENABLED:{l:'Threat Scanner',d:'1'},
|
||||||
|
FROSTGUARD_ENABLED:{l:'FrostGuard (Integrity)',d:'1'},
|
||||||
|
FORENSIC_SHIELD_ENABLED:{l:'Forensic Shield',d:'1'},
|
||||||
|
SMS_SHIELD_ENABLED:{l:'SMS Shield',d:'1'},
|
||||||
|
NETWORK_MONITOR_ENABLED:{l:'Network Monitor',d:'1'},
|
||||||
|
KEYWIPER_ENABLED:{l:'Key Wiper',d:'1'},
|
||||||
|
DEEP_SCAN_BACKGROUND:{l:'Deep Forensic Scan',d:'1'},
|
||||||
|
ANTIFORENSICS_ENABLED:{l:'Anti-Forensics',d:'1'},
|
||||||
|
DURESS_ENABLED:{l:'Duress / Panic',d:'0'},
|
||||||
|
SMS_FAKE_RESPONSE:{l:'SMS Honeypot',d:'0'},
|
||||||
|
APP_HONEYPOT_AUTO:{l:'App Honeypot',d:'0'},
|
||||||
|
QUARANTINE_ENABLED:{l:'Quarantine',d:'0'},
|
||||||
|
SMS_BLOCK_SILENT_INSTALL:{l:'Silent Install Block',d:'1'},
|
||||||
|
WEBUI_ENABLED:{l:'WebUI Dashboard',d:'1'},
|
||||||
|
};
|
||||||
|
|
||||||
|
const SETS={
|
||||||
|
SCANNER_INTERVAL:{l:'Scan Interval (sec)',d:'Time between auto scans'},
|
||||||
|
FROSTGUARD_INTERVAL:{l:'Integrity Check (sec)',d:'Time between checks'},
|
||||||
|
IOC_UPDATE_INTERVAL:{l:'IOC Update (sec)',d:'Time between updates'},
|
||||||
|
WEBUI_PORT:{l:'WebUI Port',d:'Dashboard port'},
|
||||||
|
DURESS_ACTION:{l:'Duress Action',d:'lockdown / wipe-session / wipe'},
|
||||||
|
DURESS_PIN:{l:'Duress PIN',d:'PIN that triggers panic'},
|
||||||
|
VIGIL_BACKEND_URL:{l:'Backend URL',d:'Autarch server'},
|
||||||
|
VIGIL_API_KEY:{l:'API Key',d:'Autarch API key'},
|
||||||
|
};
|
||||||
|
|
||||||
|
let cfg={};
|
||||||
|
|
||||||
|
async function loadCfg(){
|
||||||
|
const raw=await ex('cat '+D+'/vigil.conf 2>/dev/null');
|
||||||
|
cfg={};
|
||||||
|
raw.split('\n').forEach(line=>{
|
||||||
|
line=line.trim();if(!line||line[0]==='#')return;
|
||||||
|
const i=line.indexOf('=');if(i<0)return;
|
||||||
|
const k=line.substring(0,i).trim();
|
||||||
|
let v=line.substring(i+1).trim().replace(/^["']|["']$/g,'').replace(/\s*#.*$/,'');
|
||||||
|
cfg[k]=v;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function gc(k,d){return cfg[k]!==undefined?cfg[k]:d}
|
||||||
|
async function sc(k,v){cfg[k]=v;await ex("sed -i 's|^"+k+"=.*|"+k+"="+v+"|' "+D+"/vigil.conf")}
|
||||||
|
|
||||||
|
async function loadStatus(){
|
||||||
|
await loadCfg();
|
||||||
|
const pid=(await ex('cat '+D+'/vigild.pid 2>/dev/null')).trim();
|
||||||
|
const running=pid&&(await ex('kill -0 '+pid+' 2>/dev/null&&echo y')).trim()==='y';
|
||||||
|
document.getElementById('dd').className='sd '+(running?'on':'off');
|
||||||
|
document.getElementById('dt').textContent=running?'Daemon: PID '+pid:'Daemon: stopped';
|
||||||
|
const lk=(await ex('[ -f '+D+'/.lockdown ]&&echo y')).trim()==='y';
|
||||||
|
document.getElementById('ld').className='sd '+(lk?'off':'on');
|
||||||
|
document.getElementById('lt').textContent=lk?'LOCKDOWN ACTIVE':'Normal';
|
||||||
|
|
||||||
|
let h='<div class="mg">';
|
||||||
|
for(const[k,m]of Object.entries(MODS)){
|
||||||
|
const on=gc(k,m.d)==='1';
|
||||||
|
h+='<div class="mi"><span class="mn">'+m.l+'</span><button class="tg '+(on?'on':'')+'" onclick="tmod(this,\''+k+'\','+on+')"></button></div>';
|
||||||
|
}
|
||||||
|
document.getElementById('mb').innerHTML=h+'</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function tmod(el,k,cur){
|
||||||
|
const v=cur?'0':'1';await sc(k,v);el.classList.toggle('on');
|
||||||
|
toast(MODS[k].l+': '+(v==='1'?'ON':'OFF'));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadIOC(){
|
||||||
|
const files=['packages','certificates','domains','ips','hashes','cellebrite_hashes','hosts'];
|
||||||
|
let h='<div class="ig">',tot=0;
|
||||||
|
for(const f of files){
|
||||||
|
const n=parseInt((await ex('wc -l<'+D+'/'+f+'.txt 2>/dev/null||echo 0')).trim())||0;
|
||||||
|
tot+=n;h+='<div class="is"><div class="n">'+n.toLocaleString()+'</div><div class="l">'+f.replace(/_/g,' ')+'</div></div>';
|
||||||
|
}
|
||||||
|
document.getElementById('ib').innerHTML='<div class="ig"><div class="is"><div class="n">'+tot.toLocaleString()+'</div><div class="l">Total IOCs</div></div>'+h.replace('<div class="ig">','')+'</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadAlerts(){
|
||||||
|
const raw=await ex('tail -50 '+D+'/alerts/history 2>/dev/null');
|
||||||
|
const el=document.getElementById('al');
|
||||||
|
if(!raw.trim()){el.innerHTML='<div style="color:var(--g);padding:8px">No alerts</div>';return}
|
||||||
|
el.innerHTML=raw.trim().split('\n').reverse().map(line=>{
|
||||||
|
const p=line.split('|');if(p.length<4)return'';
|
||||||
|
const[sev,ts,mod,...mp]=p;const msg=mp.join('|');
|
||||||
|
const d=new Date(parseInt(ts)*1000);
|
||||||
|
const time=d.toLocaleDateString('en',{month:'short',day:'numeric'})+' '+d.toLocaleTimeString('en',{hour:'2-digit',minute:'2-digit'});
|
||||||
|
return'<div class="ai"><span class="sv '+sev+'">'+sev+'</span><span class="at">'+time+'</span><span class="am">'+msg+'</span></div>';
|
||||||
|
}).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadSettings(){
|
||||||
|
await loadCfg();
|
||||||
|
let h='';
|
||||||
|
for(const[k,m]of Object.entries(SETS)){
|
||||||
|
h+='<div class="sr"><div><div class="sl">'+m.l+'</div><div class="sd2">'+m.d+'</div></div><input class="si" data-key="'+k+'" value="'+(gc(k,'')||'')+'"></div>';
|
||||||
|
}
|
||||||
|
document.getElementById('stl').innerHTML=h;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveCfg(b){
|
||||||
|
if(b)b.classList.add('ld');
|
||||||
|
for(const inp of document.querySelectorAll('.si')){
|
||||||
|
const k=inp.dataset.key,v=inp.value;
|
||||||
|
if(v!==gc(k,''))await sc(k,v);
|
||||||
|
}
|
||||||
|
if(b)b.classList.remove('ld');
|
||||||
|
toast('Settings saved');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadLog(){
|
||||||
|
const raw=await ex('tail -100 '+D+'/vigil.log 2>/dev/null');
|
||||||
|
const el=document.getElementById('lv');
|
||||||
|
el.textContent=raw||'No log data';
|
||||||
|
el.scrollTop=el.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
(async()=>{await loadStatus();await loadIOC();await loadAlerts();await loadSettings();setInterval(()=>{loadStatus();loadAlerts()},30000)})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
241
webroot/index.html
Normal file
241
webroot/index.html
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Vigil — Anti-Surveillance Shield</title>
|
||||||
|
<style>
|
||||||
|
:root{--bg:#0a0a0f;--s:#12121a;--s2:#1a1a25;--b:#2a2a3a;--t:#e0e0e8;--t2:#8888a0;--a:#4a9eff;--g:#22c55e;--y:#eab308;--r:#ef4444;--o:#f97316}
|
||||||
|
*{margin:0;padding:0;box-sizing:border-box}
|
||||||
|
body{font-family:'SF Mono','Cascadia Code','Fira Code',monospace;background:var(--bg);color:var(--t);min-height:100vh;font-size:14px}
|
||||||
|
.c{max-width:900px;margin:0 auto;padding:16px}
|
||||||
|
.hdr{text-align:center;padding:24px 0 16px;border-bottom:1px solid var(--b);margin-bottom:20px}
|
||||||
|
.hdr h1{font-size:22px;font-weight:600;letter-spacing:2px}
|
||||||
|
.hdr .sub{color:var(--t2);font-size:12px;margin-top:4px}
|
||||||
|
.hdr .ver{color:var(--a);font-size:11px}
|
||||||
|
.sb{display:flex;gap:12px;margin-bottom:20px;flex-wrap:wrap}
|
||||||
|
.sc{display:flex;align-items:center;gap:6px;background:var(--s);border:1px solid var(--b);border-radius:6px;padding:8px 14px;font-size:12px;flex:1;min-width:140px}
|
||||||
|
.sd{width:8px;height:8px;border-radius:50%;flex-shrink:0}
|
||||||
|
.sd.on{background:var(--g);box-shadow:0 0 6px var(--g)}.sd.off{background:var(--r)}
|
||||||
|
.cd{background:var(--s);border:1px solid var(--b);border-radius:8px;margin-bottom:16px;overflow:hidden}
|
||||||
|
.ch{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid var(--b);background:var(--s2);cursor:pointer;user-select:none}
|
||||||
|
.ch h2{font-size:14px;font-weight:600}
|
||||||
|
.cb{padding:16px}.cb.h{display:none}
|
||||||
|
.mg{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:10px}
|
||||||
|
.mi{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;background:var(--s2);border-radius:6px;border:1px solid var(--b)}
|
||||||
|
.mn{font-size:13px}
|
||||||
|
.tg{position:relative;width:40px;height:22px;background:#333;border-radius:11px;cursor:pointer;transition:background .2s;border:none;outline:none}
|
||||||
|
.tg.on{background:var(--g)}
|
||||||
|
.tg::after{content:'';position:absolute;top:3px;left:3px;width:16px;height:16px;background:#fff;border-radius:50%;transition:transform .2s}
|
||||||
|
.tg.on::after{transform:translateX(18px)}
|
||||||
|
.ig{display:grid;grid-template-columns:repeat(auto-fill,minmax(110px,1fr));gap:8px}
|
||||||
|
.is{text-align:center;padding:12px 8px;background:var(--s2);border-radius:6px}
|
||||||
|
.is .n{font-size:20px;font-weight:700;color:var(--a)}.is .l{font-size:10px;color:var(--t2);margin-top:4px;text-transform:uppercase}
|
||||||
|
.al{max-height:300px;overflow-y:auto}
|
||||||
|
.ai{display:flex;gap:10px;padding:8px 0;border-bottom:1px solid var(--b);font-size:12px;align-items:flex-start}
|
||||||
|
.ai:last-child{border:none}
|
||||||
|
.sv{font-size:10px;font-weight:700;padding:2px 6px;border-radius:3px;flex-shrink:0;white-space:nowrap}
|
||||||
|
.sv.CRITICAL{background:var(--r);color:#fff}.sv.HIGH{background:var(--o);color:#fff}.sv.MEDIUM{background:var(--y);color:#000}.sv.LOW{background:var(--t2);color:#fff}.sv.INFO{background:var(--a);color:#fff}
|
||||||
|
.at{color:var(--t2);white-space:nowrap;font-size:11px}.am{color:var(--t);word-break:break-word}
|
||||||
|
.br{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}
|
||||||
|
.bt{padding:8px 16px;border:1px solid var(--b);border-radius:6px;background:var(--s2);color:var(--t);font-family:inherit;font-size:12px;cursor:pointer;transition:all .15s;outline:none}
|
||||||
|
.bt:hover{background:var(--b)}.bt:active{transform:scale(.97)}
|
||||||
|
.bt.d{border-color:var(--r);color:var(--r)}.bt.d:hover{background:var(--r);color:#fff}
|
||||||
|
.bt.p{border-color:var(--a);color:var(--a)}.bt.p:hover{background:var(--a);color:#fff}
|
||||||
|
.bt.g{border-color:var(--g);color:var(--g)}.bt.g:hover{background:var(--g);color:#fff}
|
||||||
|
.bt.ld{opacity:.5;pointer-events:none}
|
||||||
|
.lv{background:#000;border-radius:6px;padding:12px;max-height:250px;overflow-y:auto;font-size:11px;line-height:1.5;color:var(--t2);white-space:pre-wrap;word-break:break-all}
|
||||||
|
.sr{display:flex;justify-content:space-between;align-items:center;padding:10px 0;border-bottom:1px solid var(--b)}
|
||||||
|
.sr:last-child{border:none}
|
||||||
|
.sl{font-size:13px}.sd2{font-size:11px;color:var(--t2)}
|
||||||
|
.si{background:var(--s2);border:1px solid var(--b);border-radius:4px;color:var(--t);padding:4px 8px;font-family:inherit;font-size:12px;width:140px;text-align:right;outline:none}
|
||||||
|
.si:focus{border-color:var(--a)}
|
||||||
|
.toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);background:var(--s2);border:1px solid var(--a);color:var(--t);padding:10px 20px;border-radius:8px;font-size:13px;z-index:999;opacity:0;transition:opacity .3s;pointer-events:none}
|
||||||
|
.toast.show{opacity:1}
|
||||||
|
@media(max-width:500px){.mg{grid-template-columns:1fr}.ig{grid-template-columns:repeat(3,1fr)}.sb{flex-direction:column}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="c">
|
||||||
|
<div class="hdr">
|
||||||
|
<h1>VIGIL</h1>
|
||||||
|
<div class="sub">Anti-Surveillance Shield by Setec Labs</div>
|
||||||
|
<div class="ver">v0.2.0</div>
|
||||||
|
</div>
|
||||||
|
<div class="sb">
|
||||||
|
<div class="sc"><span class="sd" id="dd"></span><span id="dt">Checking...</span></div>
|
||||||
|
<div class="sc"><span class="sd" id="ld"></span><span id="lt">Checking...</span></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cd"><div class="ch" onclick="tc(this)"><h2>Quick Actions</h2><span>▼</span></div>
|
||||||
|
<div class="cb"><div class="br">
|
||||||
|
<button class="bt p" onclick="run('scanner.sh quick',this)">Quick Scan</button>
|
||||||
|
<button class="bt p" onclick="run('deep_scan.sh deep',this)">Deep Scan</button>
|
||||||
|
<button class="bt g" onclick="run('antiforensics.sh harden',this)">Harden</button>
|
||||||
|
<button class="bt" onclick="run('antiforensics.sh sanitize',this)">Sanitize</button>
|
||||||
|
<button class="bt" onclick="run('ioc_updater.sh update',this)">Update IOCs</button>
|
||||||
|
<button class="bt" onclick="run('integrity.sh verify',this)">Check Integrity</button>
|
||||||
|
<button class="bt d" onclick="if(confirm('Enter BFU lockdown? Reboot to restore.'))run('key_wiper.sh lockdown',this)">LOCKDOWN</button>
|
||||||
|
</div></div></div>
|
||||||
|
|
||||||
|
<div class="cd"><div class="ch" onclick="tc(this)"><h2>Protection Modules</h2><span>▼</span></div>
|
||||||
|
<div class="cb" id="mb"></div></div>
|
||||||
|
|
||||||
|
<div class="cd"><div class="ch" onclick="tc(this)"><h2>Threat Database</h2><span>▼</span></div>
|
||||||
|
<div class="cb" id="ib"></div></div>
|
||||||
|
|
||||||
|
<div class="cd"><div class="ch" onclick="tc(this)"><h2>Recent Alerts</h2><span>▼</span></div>
|
||||||
|
<div class="cb"><div class="al" id="al"></div></div></div>
|
||||||
|
|
||||||
|
<div class="cd"><div class="ch" onclick="tc(this)"><h2>Settings</h2><span>▼</span></div>
|
||||||
|
<div class="cb h" id="stb"><div id="stl"></div><div class="br"><button class="bt p" onclick="saveCfg(this)">Save Settings</button></div></div></div>
|
||||||
|
|
||||||
|
<div class="cd"><div class="ch" onclick="tc(this)"><h2>System Log</h2><span>▼</span></div>
|
||||||
|
<div class="cb h" id="lb"><div class="lv" id="lv">Loading...</div><div class="br"><button class="bt" onclick="loadLog()">Refresh</button></div></div></div>
|
||||||
|
</div>
|
||||||
|
<div class="toast" id="toast"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const D='/data/adb/vigil';
|
||||||
|
const L='/data/adb/modules/vigil/vigil/lib';
|
||||||
|
const isKSU=typeof ksu!=='undefined';
|
||||||
|
|
||||||
|
async function ex(cmd){
|
||||||
|
if(isKSU){
|
||||||
|
try{const r=await ksu.exec(cmd);return (r&&(r.output||r.stdout))||''}catch(e){return ''}
|
||||||
|
}else{
|
||||||
|
try{const r=await fetch('/api/exec',{method:'POST',body:cmd});return await r.text()}catch(e){return ''}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function toast(m){const t=document.getElementById('toast');t.textContent=m;t.classList.add('show');setTimeout(()=>t.classList.remove('show'),2500)}
|
||||||
|
function tc(h){h.nextElementSibling.classList.toggle('h')}
|
||||||
|
async function run(s,b){
|
||||||
|
const lbl=b?b.textContent:'';
|
||||||
|
if(b){b.classList.add('ld');b.textContent='Running...';}
|
||||||
|
toast('Running '+s.split('.')[0]+'...');
|
||||||
|
await ex(L+'/'+s+' 2>&1');
|
||||||
|
if(b){b.classList.remove('ld');b.textContent=lbl;}
|
||||||
|
toast('Done');loadStatus();loadAlerts();
|
||||||
|
}
|
||||||
|
|
||||||
|
const MODS={
|
||||||
|
SCANNER_ENABLED:{l:'Threat Scanner',d:'1'},
|
||||||
|
FROSTGUARD_ENABLED:{l:'FrostGuard (Integrity)',d:'1'},
|
||||||
|
FORENSIC_SHIELD_ENABLED:{l:'Forensic Shield',d:'1'},
|
||||||
|
SMS_SHIELD_ENABLED:{l:'SMS Shield',d:'1'},
|
||||||
|
NETWORK_MONITOR_ENABLED:{l:'Network Monitor',d:'1'},
|
||||||
|
KEYWIPER_ENABLED:{l:'Key Wiper',d:'1'},
|
||||||
|
DEEP_SCAN_BACKGROUND:{l:'Deep Forensic Scan',d:'1'},
|
||||||
|
ANTIFORENSICS_ENABLED:{l:'Anti-Forensics',d:'1'},
|
||||||
|
DURESS_ENABLED:{l:'Duress / Panic',d:'0'},
|
||||||
|
SMS_FAKE_RESPONSE:{l:'SMS Honeypot',d:'0'},
|
||||||
|
APP_HONEYPOT_AUTO:{l:'App Honeypot',d:'0'},
|
||||||
|
QUARANTINE_ENABLED:{l:'Quarantine',d:'0'},
|
||||||
|
SMS_BLOCK_SILENT_INSTALL:{l:'Silent Install Block',d:'1'},
|
||||||
|
WEBUI_ENABLED:{l:'WebUI Dashboard',d:'1'},
|
||||||
|
};
|
||||||
|
|
||||||
|
const SETS={
|
||||||
|
SCANNER_INTERVAL:{l:'Scan Interval (sec)',d:'Time between auto scans'},
|
||||||
|
FROSTGUARD_INTERVAL:{l:'Integrity Check (sec)',d:'Time between checks'},
|
||||||
|
IOC_UPDATE_INTERVAL:{l:'IOC Update (sec)',d:'Time between updates'},
|
||||||
|
WEBUI_PORT:{l:'WebUI Port',d:'Dashboard port'},
|
||||||
|
DURESS_ACTION:{l:'Duress Action',d:'lockdown / wipe-session / wipe'},
|
||||||
|
DURESS_PIN:{l:'Duress PIN',d:'PIN that triggers panic'},
|
||||||
|
VIGIL_BACKEND_URL:{l:'Backend URL',d:'Autarch server'},
|
||||||
|
VIGIL_API_KEY:{l:'API Key',d:'Autarch API key'},
|
||||||
|
};
|
||||||
|
|
||||||
|
let cfg={};
|
||||||
|
|
||||||
|
async function loadCfg(){
|
||||||
|
const raw=await ex('cat '+D+'/vigil.conf 2>/dev/null');
|
||||||
|
cfg={};
|
||||||
|
raw.split('\n').forEach(line=>{
|
||||||
|
line=line.trim();if(!line||line[0]==='#')return;
|
||||||
|
const i=line.indexOf('=');if(i<0)return;
|
||||||
|
const k=line.substring(0,i).trim();
|
||||||
|
let v=line.substring(i+1).trim().replace(/^["']|["']$/g,'').replace(/\s*#.*$/,'');
|
||||||
|
cfg[k]=v;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function gc(k,d){return cfg[k]!==undefined?cfg[k]:d}
|
||||||
|
async function sc(k,v){cfg[k]=v;await ex("sed -i 's|^"+k+"=.*|"+k+"="+v+"|' "+D+"/vigil.conf")}
|
||||||
|
|
||||||
|
async function loadStatus(){
|
||||||
|
await loadCfg();
|
||||||
|
const pid=(await ex('cat '+D+'/vigild.pid 2>/dev/null')).trim();
|
||||||
|
const running=pid&&(await ex('kill -0 '+pid+' 2>/dev/null&&echo y')).trim()==='y';
|
||||||
|
document.getElementById('dd').className='sd '+(running?'on':'off');
|
||||||
|
document.getElementById('dt').textContent=running?'Daemon: PID '+pid:'Daemon: stopped';
|
||||||
|
const lk=(await ex('[ -f '+D+'/.lockdown ]&&echo y')).trim()==='y';
|
||||||
|
document.getElementById('ld').className='sd '+(lk?'off':'on');
|
||||||
|
document.getElementById('lt').textContent=lk?'LOCKDOWN ACTIVE':'Normal';
|
||||||
|
|
||||||
|
let h='<div class="mg">';
|
||||||
|
for(const[k,m]of Object.entries(MODS)){
|
||||||
|
const on=gc(k,m.d)==='1';
|
||||||
|
h+='<div class="mi"><span class="mn">'+m.l+'</span><button class="tg '+(on?'on':'')+'" onclick="tmod(this,\''+k+'\','+on+')"></button></div>';
|
||||||
|
}
|
||||||
|
document.getElementById('mb').innerHTML=h+'</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function tmod(el,k,cur){
|
||||||
|
const v=cur?'0':'1';await sc(k,v);el.classList.toggle('on');
|
||||||
|
toast(MODS[k].l+': '+(v==='1'?'ON':'OFF'));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadIOC(){
|
||||||
|
const files=['packages','certificates','domains','ips','hashes','cellebrite_hashes','hosts'];
|
||||||
|
let h='<div class="ig">',tot=0;
|
||||||
|
for(const f of files){
|
||||||
|
const n=parseInt((await ex('wc -l<'+D+'/'+f+'.txt 2>/dev/null||echo 0')).trim())||0;
|
||||||
|
tot+=n;h+='<div class="is"><div class="n">'+n.toLocaleString()+'</div><div class="l">'+f.replace(/_/g,' ')+'</div></div>';
|
||||||
|
}
|
||||||
|
document.getElementById('ib').innerHTML='<div class="ig"><div class="is"><div class="n">'+tot.toLocaleString()+'</div><div class="l">Total IOCs</div></div>'+h.replace('<div class="ig">','')+'</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadAlerts(){
|
||||||
|
const raw=await ex('tail -50 '+D+'/alerts/history 2>/dev/null');
|
||||||
|
const el=document.getElementById('al');
|
||||||
|
if(!raw.trim()){el.innerHTML='<div style="color:var(--g);padding:8px">No alerts</div>';return}
|
||||||
|
el.innerHTML=raw.trim().split('\n').reverse().map(line=>{
|
||||||
|
const p=line.split('|');if(p.length<4)return'';
|
||||||
|
const[sev,ts,mod,...mp]=p;const msg=mp.join('|');
|
||||||
|
const d=new Date(parseInt(ts)*1000);
|
||||||
|
const time=d.toLocaleDateString('en',{month:'short',day:'numeric'})+' '+d.toLocaleTimeString('en',{hour:'2-digit',minute:'2-digit'});
|
||||||
|
return'<div class="ai"><span class="sv '+sev+'">'+sev+'</span><span class="at">'+time+'</span><span class="am">'+msg+'</span></div>';
|
||||||
|
}).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadSettings(){
|
||||||
|
await loadCfg();
|
||||||
|
let h='';
|
||||||
|
for(const[k,m]of Object.entries(SETS)){
|
||||||
|
h+='<div class="sr"><div><div class="sl">'+m.l+'</div><div class="sd2">'+m.d+'</div></div><input class="si" data-key="'+k+'" value="'+(gc(k,'')||'')+'"></div>';
|
||||||
|
}
|
||||||
|
document.getElementById('stl').innerHTML=h;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveCfg(b){
|
||||||
|
if(b)b.classList.add('ld');
|
||||||
|
for(const inp of document.querySelectorAll('.si')){
|
||||||
|
const k=inp.dataset.key,v=inp.value;
|
||||||
|
if(v!==gc(k,''))await sc(k,v);
|
||||||
|
}
|
||||||
|
if(b)b.classList.remove('ld');
|
||||||
|
toast('Settings saved');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadLog(){
|
||||||
|
const raw=await ex('tail -100 '+D+'/vigil.log 2>/dev/null');
|
||||||
|
const el=document.getElementById('lv');
|
||||||
|
el.textContent=raw||'No log data';
|
||||||
|
el.scrollTop=el.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
(async()=>{await loadStatus();await loadIOC();await loadAlerts();await loadSettings();setInterval(()=>{loadStatus();loadAlerts()},30000)})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user