secubox-openwrt/package/secubox/luci-app-cve-triage/root/usr/libexec/rpcd/luci.cve-triage
CyberMind-FR 44493ebfe3 feat: Add CVE Triage Agent and Vortex DNS, fix webmail login
New Packages:
- secubox-cve-triage: AI-powered CVE analysis and vulnerability management
  - NVD API integration for CVE data
  - CrowdSec CVE alert correlation
  - LocalAI-powered impact analysis
  - Approval workflow for patch recommendations
  - Multi-source monitoring (opkg, LXC, Docker)

- luci-app-cve-triage: Dashboard with alerts, pending queue, risk score

- secubox-vortex-dns: Meshed multi-dynamic subdomain delegation
  - Master/slave hierarchical DNS delegation
  - Wildcard domain management
  - First Peek auto-registration
  - Gossip-based exposure config sync
  - Submastering for nested hierarchies

Fixes:
- Webmail 401 login: config.docker.inc.php was overriding IMAP host
  to ssl://mail.secubox.in:993 which Docker couldn't reach
- Fixed mailctl webmail configure to use socat proxy (172.17.0.1:10143)

Documentation:
- Added LXC cgroup:mixed fix to FAQ-TROUBLESHOOTING.md
- Updated CLAUDE.md to include FAQ consultation at startup

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 12:19:54 +01:00

209 lines
5.4 KiB
Bash

#!/bin/sh
# RPCD handler for CVE Triage
. /usr/share/libubox/jshn.sh
CONFIG="cve-triage"
STATE_DIR="/var/lib/cve-triage"
uci_get() { uci -q get "${CONFIG}.$1"; }
# JSON output helpers
json_output() {
local tmpfile="/tmp/cve_triage_output_$$.json"
cat > "$tmpfile"
cat "$tmpfile"
rm -f "$tmpfile"
}
case "$1" in
list)
cat <<'EOF'
{
"status": {},
"get_pending": {},
"get_alerts": {},
"get_recommendations": { "limit": 50 },
"get_summary": {},
"scan": {},
"analyze": { "cve": "string" },
"run": {},
"approve": { "id": "string" },
"reject": { "id": "string", "reason": "string" },
"approve_all": {},
"clear_pending": {},
"ack_alert": { "id": "string" }
}
EOF
;;
call)
case "$2" in
status)
local enabled=$(uci_get main.enabled)
local interval=$(uci_get main.interval)
local localai_url=$(uci_get main.localai_url)
local min_severity=$(uci_get main.min_severity)
# Check LocalAI
local localai_status="offline"
wget -q -O /dev/null --timeout=2 "${localai_url:-http://127.0.0.1:8081}/v1/models" 2>/dev/null && localai_status="online"
# Count pending
local pending_count=0
[ -f "$STATE_DIR/pending_actions.json" ] && \
pending_count=$(jsonfilter -i "$STATE_DIR/pending_actions.json" -e '@[*]' 2>/dev/null | wc -l)
# Count alerts
local alert_count=0
[ -f "$STATE_DIR/alerts.json" ] && \
alert_count=$(jsonfilter -i "$STATE_DIR/alerts.json" -e '@[*]' 2>/dev/null | grep -c '"acknowledged":false')
# Last run
local last_run=""
[ -f "$STATE_DIR/last_run" ] && last_run=$(cat "$STATE_DIR/last_run")
# Package counts
local opkg_count=$(opkg list-installed 2>/dev/null | wc -l)
local lxc_count=$(ls -d /srv/lxc/*/ 2>/dev/null | wc -l)
local docker_count=$(docker ps -q 2>/dev/null | wc -l || echo 0)
cat <<EOF
{
"enabled": $([ "$enabled" = "1" ] && echo "true" || echo "false"),
"interval": ${interval:-3600},
"min_severity": "${min_severity:-high}",
"localai_status": "$localai_status",
"localai_url": "${localai_url:-http://127.0.0.1:8081}",
"pending_count": $pending_count,
"alert_count": $alert_count,
"last_run": "$last_run",
"packages": {
"opkg": $opkg_count,
"lxc": $lxc_count,
"docker": $docker_count
}
}
EOF
;;
get_pending)
if [ -f "$STATE_DIR/pending_actions.json" ]; then
printf '{"pending":'
cat "$STATE_DIR/pending_actions.json"
printf '}'
else
echo '{"pending":[]}'
fi
;;
get_alerts)
if [ -f "$STATE_DIR/alerts.json" ]; then
printf '{"alerts":'
cat "$STATE_DIR/alerts.json"
printf '}'
else
echo '{"alerts":[]}'
fi
;;
get_recommendations)
read -r input
local limit=$(echo "$input" | jsonfilter -e '@.limit' 2>/dev/null)
limit="${limit:-50}"
if [ -f "$STATE_DIR/recommendations.json" ]; then
printf '{"recommendations":'
cat "$STATE_DIR/recommendations.json"
printf '}'
else
echo '{"recommendations":[]}'
fi
;;
get_summary)
if [ -f "$STATE_DIR/last_summary.json" ]; then
printf '{"summary":'
cat "$STATE_DIR/last_summary.json"
printf '}'
else
echo '{"summary":{"risk_score":0,"summary":"No analysis available"}}'
fi
;;
scan)
local output=$(/usr/bin/cve-triage scan 2>&1)
local pkg_count=$(opkg list-installed 2>/dev/null | wc -l)
echo "{\"success\":true,\"packages\":$pkg_count,\"output\":\"$(echo "$output" | sed 's/"/\\"/g' | tr '\n' ' ')\"}"
;;
analyze)
read -r input
local cve=$(echo "$input" | jsonfilter -e '@.cve' 2>/dev/null)
if [ -z "$cve" ]; then
echo '{"error":"CVE ID required"}'
else
local output=$(/usr/bin/cve-triage analyze "$cve" 2>&1)
echo "{\"cve\":\"$cve\",\"analysis\":\"$(echo "$output" | sed 's/"/\\"/g' | tr '\n' ' ')\"}"
fi
;;
run)
/usr/bin/cve-triage run >/dev/null 2>&1 &
echo '{"success":true,"message":"Triage cycle started in background"}'
;;
approve)
read -r input
local rec_id=$(echo "$input" | jsonfilter -e '@.id' 2>/dev/null)
if [ -z "$rec_id" ]; then
echo '{"error":"Recommendation ID required"}'
else
local output=$(/usr/bin/cve-triage approve "$rec_id" 2>&1)
echo "{\"success\":true,\"id\":\"$rec_id\",\"output\":\"$(echo "$output" | sed 's/"/\\"/g' | tr '\n' ' ')\"}"
fi
;;
reject)
read -r input
local rec_id=$(echo "$input" | jsonfilter -e '@.id' 2>/dev/null)
local reason=$(echo "$input" | jsonfilter -e '@.reason' 2>/dev/null)
if [ -z "$rec_id" ]; then
echo '{"error":"Recommendation ID required"}'
else
local output=$(/usr/bin/cve-triage reject "$rec_id" "$reason" 2>&1)
echo "{\"success\":true,\"id\":\"$rec_id\"}"
fi
;;
approve_all)
/usr/bin/cve-triage approve-all >/dev/null 2>&1
echo '{"success":true}'
;;
clear_pending)
/usr/bin/cve-triage clear-pending >/dev/null 2>&1
echo '{"success":true}'
;;
ack_alert)
read -r input
local alert_id=$(echo "$input" | jsonfilter -e '@.id' 2>/dev/null)
if [ -z "$alert_id" ]; then
echo '{"error":"Alert ID required"}'
else
# Update alert as acknowledged
if [ -f "$STATE_DIR/alerts.json" ]; then
sed -i "s/\"id\":\"$alert_id\",\\(.*\\)\"acknowledged\":false/\"id\":\"$alert_id\",\\1\"acknowledged\":true/" "$STATE_DIR/alerts.json"
fi
echo "{\"success\":true,\"id\":\"$alert_id\"}"
fi
;;
*)
echo '{"error":"Unknown method"}'
;;
esac
;;
esac