#!/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 </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