#!/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 </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 < "${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 ']' }