secubox-openwrt/package/secubox/secubox-cve-triage/files/usr/lib/cve-triage/recommender.sh
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

222 lines
6.3 KiB
Bash

#!/bin/sh
# CVE Triage - Recommender Module
# Generates actionable recommendations from CVE analysis
RECOMMENDATIONS_FILE="/var/lib/cve-triage/recommendations.json"
ALERTS_FILE="/var/lib/cve-triage/alerts.json"
# Generate recommendation from analyzed CVE
generate_recommendation() {
local cve_id="$1"
local cvss="$2"
local severity="$3"
local affected_pkg="$4"
local analysis="$5"
local action=$(echo "$analysis" | jsonfilter -e '@.action' 2>/dev/null)
local urgency=$(echo "$analysis" | jsonfilter -e '@.urgency' 2>/dev/null)
local mitigation=$(echo "$analysis" | jsonfilter -e '@.mitigation' 2>/dev/null)
local impact=$(echo "$analysis" | jsonfilter -e '@.impact_level' 2>/dev/null)
action="${action:-monitor}"
urgency="${urgency:-scheduled}"
mitigation="${mitigation:-Update to latest version}"
impact="${impact:-$severity}"
local rec_id="rec_$(date +%s)_${cve_id}"
# Generate specific command based on action
local command=""
case "$action" in
patch)
if [ -n "$affected_pkg" ]; then
command="opkg update && opkg upgrade $affected_pkg"
else
command="opkg update && opkg list-upgradable"
fi
;;
mitigate)
command="# Manual mitigation required: $mitigation"
;;
monitor)
command="# No immediate action - continue monitoring"
;;
esac
# Escape strings for JSON
mitigation=$(echo "$mitigation" | sed 's/\\/\\\\/g; s/"/\\"/g')
command=$(echo "$command" | sed 's/\\/\\\\/g; s/"/\\"/g')
cat <<EOF
{
"id": "$rec_id",
"cve": "$cve_id",
"cvss": $cvss,
"severity": "$severity",
"impact": "$impact",
"affected_package": "$affected_pkg",
"action": "$action",
"urgency": "$urgency",
"mitigation": "$mitigation",
"command": "$command",
"status": "pending",
"created": "$(date -Iseconds)"
}
EOF
}
# Process analyzed CVEs and create recommendations
create_recommendations() {
local analyzed_cves="$1"
local max_recs="${max_recommendations:-10}"
mkdir -p "$(dirname "$RECOMMENDATIONS_FILE")"
local count=0
local recs='[]'
echo "$analyzed_cves" | jsonfilter -e '@[*]' 2>/dev/null | while read -r cve_data; do
[ $count -ge $max_recs ] && break
local cve_id=$(echo "$cve_data" | jsonfilter -e '@.cve' 2>/dev/null)
local cvss=$(echo "$cve_data" | jsonfilter -e '@.cvss' 2>/dev/null)
local severity=$(echo "$cve_data" | jsonfilter -e '@.severity' 2>/dev/null)
local affected_pkg=$(echo "$cve_data" | jsonfilter -e '@.affected_package' 2>/dev/null)
local analysis=$(echo "$cve_data" | jsonfilter -e '@.analysis' 2>/dev/null)
# Skip CVEs that don't affect OpenWrt (if analysis says so)
local affects_openwrt=$(echo "$analysis" | jsonfilter -e '@.affects_openwrt' 2>/dev/null)
[ "$affects_openwrt" = "false" ] && continue
# Skip CVEs with action=ignore
local action=$(echo "$analysis" | jsonfilter -e '@.action' 2>/dev/null)
[ "$action" = "ignore" ] && continue
generate_recommendation "$cve_id" "${cvss:-0}" "$severity" "$affected_pkg" "$analysis"
count=$((count + 1))
done | json_slurp
}
# Save recommendations to file
save_recommendations() {
local new_recs="$1"
# Load existing recommendations
local existing='[]'
[ -f "$RECOMMENDATIONS_FILE" ] && existing=$(cat "$RECOMMENDATIONS_FILE" 2>/dev/null)
# Merge (keep existing, add new if CVE not already present)
{
echo '['
local first=1
# Keep existing non-completed recommendations
echo "$existing" | jsonfilter -e '@[*]' 2>/dev/null | while read -r rec; do
local status=$(echo "$rec" | jsonfilter -e '@.status' 2>/dev/null)
[ "$status" = "completed" ] || [ "$status" = "rejected" ] && continue
[ $first -eq 0 ] && printf ','
first=0
printf '%s' "$rec"
done
# Add new recommendations (skip duplicates)
echo "$new_recs" | jsonfilter -e '@[*]' 2>/dev/null | while read -r rec; do
local cve=$(echo "$rec" | jsonfilter -e '@.cve' 2>/dev/null)
# Check if CVE already in existing
if ! echo "$existing" | grep -q "\"cve\":\"$cve\""; then
[ $first -eq 0 ] && printf ','
first=0
printf '%s' "$rec"
fi
done
echo ']'
} > "${RECOMMENDATIONS_FILE}.tmp"
mv "${RECOMMENDATIONS_FILE}.tmp" "$RECOMMENDATIONS_FILE"
}
# Create alert for immediate attention CVEs
create_alert() {
local cve_id="$1"
local severity="$2"
local message="$3"
mkdir -p "$(dirname "$ALERTS_FILE")"
local alert=$(cat <<EOF
{
"id": "alert_$(date +%s)_$cve_id",
"cve": "$cve_id",
"severity": "$severity",
"message": "$message",
"created": "$(date -Iseconds)",
"acknowledged": false
}
EOF
)
# Append to alerts file
if [ -f "$ALERTS_FILE" ]; then
local existing=$(cat "$ALERTS_FILE")
# Check if alert for this CVE already exists
if ! echo "$existing" | grep -q "\"cve\":\"$cve_id\""; then
echo "$existing" | sed "s/\]$/,$alert]/" > "${ALERTS_FILE}.tmp"
mv "${ALERTS_FILE}.tmp" "$ALERTS_FILE"
fi
else
echo "[$alert]" > "$ALERTS_FILE"
fi
log_warn "ALERT: $cve_id ($severity) - $message"
}
# Process recommendations and create alerts for critical/immediate items
process_recommendations() {
local recs="$1"
echo "$recs" | jsonfilter -e '@[*]' 2>/dev/null | while read -r rec; do
local cve=$(echo "$rec" | jsonfilter -e '@.cve' 2>/dev/null)
local severity=$(echo "$rec" | jsonfilter -e '@.severity' 2>/dev/null)
local urgency=$(echo "$rec" | jsonfilter -e '@.urgency' 2>/dev/null)
local affected=$(echo "$rec" | jsonfilter -e '@.affected_package' 2>/dev/null)
# Create alert for critical CVEs or immediate urgency
if [ "$severity" = "critical" ] || [ "$urgency" = "immediate" ]; then
create_alert "$cve" "$severity" "Critical vulnerability affecting ${affected:-system}. Immediate action required."
fi
done
}
# Get pending recommendations
get_pending_recommendations() {
[ -f "$RECOMMENDATIONS_FILE" ] || { echo '[]'; return; }
echo '['
local first=1
jsonfilter -i "$RECOMMENDATIONS_FILE" -e '@[*]' 2>/dev/null | while read -r rec; do
local status=$(echo "$rec" | jsonfilter -e '@.status' 2>/dev/null)
[ "$status" = "pending" ] || continue
[ $first -eq 0 ] && printf ','
first=0
printf '%s' "$rec"
done
echo ']'
}
# Get active alerts
get_active_alerts() {
[ -f "$ALERTS_FILE" ] || { echo '[]'; return; }
echo '['
local first=1
jsonfilter -i "$ALERTS_FILE" -e '@[*]' 2>/dev/null | while read -r alert; do
local ack=$(echo "$alert" | jsonfilter -e '@.acknowledged' 2>/dev/null)
[ "$ack" = "true" ] && continue
[ $first -eq 0 ] && printf ','
first=0
printf '%s' "$alert"
done
echo ']'
}