#!/usr/bin/env bash set -u PASS=0 WARN=0 FAIL=0 ok(){ printf "[PASS] %s\n" "$*"; PASS=$((PASS+1)); } warn(){ printf "[WARN] %s\n" "$*"; WARN=$((WARN+1)); } fail(){ printf "[FAIL] %s\n" "$*"; FAIL=$((FAIL+1)); } if [[ ${EUID:-$(id -u)} -ne 0 ]]; then exec sudo bash "$0" "$@" fi TEST_DISCORD=0 [[ "${1:-}" == "--test-discord" ]] && TEST_DISCORD=1 printf "malware-watch verification | host=%s | time=%s\n\n" "$(hostname)" "$(date -Is)" for f in \ /etc/malware-watch/config \ /usr/local/sbin/malware-watch.py \ /usr/local/sbin/malware-quarantine.py \ /usr/local/sbin/malware-feed-update.py \ /usr/local/sbin/clamav-watch-scan.sh \ /usr/local/sbin/block-malware-iocs; do if [[ -f "$f" ]]; then ok "file exists: $f"; else fail "missing file: $f"; fi done if [[ -f /etc/malware-watch/config ]]; then mode=$(stat -c %a /etc/malware-watch/config 2>/dev/null || true) [[ "$mode" == "600" ]] && ok "config permissions are 600" || warn "config permissions are $mode (expected 600)" if grep -q '^DISCORD_WEBHOOK=' /etc/malware-watch/config && grep -q '^ABUSECH_AUTH_KEY=' /etc/malware-watch/config; then ok "Discord webhook and abuse.ch key entries exist" else fail "Discord webhook or abuse.ch key entry is missing" fi fi for t in malware-watch.timer malware-feed-update.timer; do if systemctl is-enabled "$t" >/dev/null 2>&1 && systemctl is-active "$t" >/dev/null 2>&1; then ok "$t is enabled and active" else fail "$t is not enabled/active" fi done if systemctl is-enabled block-malware-iocs.service >/dev/null 2>&1 && systemctl is-active block-malware-iocs.service >/dev/null 2>&1; then ok "block-malware-iocs.service is enabled and active" else fail "block-malware-iocs.service is not enabled/active" fi TF=/var/lib/malware-watch/feeds/threatfox.json MB=/var/lib/malware-watch/feeds/malwarebazaar.json if [[ -s "$TF" ]]; then n=$(python3 - "$TF" <<'PY' import json,sys try: d=json.load(open(sys.argv[1])); print(len(d.get("iocs",{}))) except Exception: print(-1) PY ) [[ "$n" =~ ^[0-9]+$ ]] && (( n > 0 )) && ok "ThreatFox feed loaded: $n IOCs" || warn "ThreatFox feed exists but contains no parsed IOCs" else fail "ThreatFox feed missing/empty" fi if [[ -s "$MB" ]]; then n=$(python3 - "$MB" <<'PY' import json,sys try: d=json.load(open(sys.argv[1])); print(len(d.get("hashes",{}))) except Exception: print(-1) PY ) [[ "$n" =~ ^[0-9]+$ ]] && (( n > 0 )) && ok "MalwareBazaar feed loaded: $n hashes" || warn "MalwareBazaar feed exists but contains no parsed hashes" else fail "MalwareBazaar feed missing/empty" fi if [[ -x /usr/local/bin/clamscan ]]; then ok "ClamAV installed: $(/usr/local/bin/clamscan --version 2>/dev/null | head -n1)" if id -u clamav >/dev/null 2>&1; then ok "clamav service account exists"; else fail "clamav service account is missing"; fi if compgen -G '/var/lib/clamav/*.cvd' >/dev/null || compgen -G '/var/lib/clamav/*.cld' >/dev/null; then ok "ClamAV signature database exists" else warn "ClamAV signature DB is not ready yet; freshclam timer may still need to download it" fi for t in clamav-freshclam-local.timer clamav-risk-scan.timer clamav-deep-scan.timer; do if systemctl is-enabled "$t" >/dev/null 2>&1 && systemctl is-active "$t" >/dev/null 2>&1; then ok "$t is enabled and active" else fail "$t is not enabled/active" fi done else warn "ClamAV is not installed (INSTALL_CLAMAV may have been disabled)" fi if [[ -x /usr/local/sbin/malware-watch.py ]]; then out=$(/usr/local/sbin/malware-watch.py 2>&1); rc=$? if (( rc == 0 )); then ok "malware-watch one-shot execution succeeded: $out" else fail "malware-watch one-shot execution failed (rc=$rc): $out" fi fi if (( TEST_DISCORD )); then if [[ -r /etc/malware-watch/config ]]; then set -a . /etc/malware-watch/config set +a payload=$(python3 - <<'PY' import json,socket print(json.dumps({"content":f"malware-watch verify OK | server: {socket.gethostname()}"},ensure_ascii=False)) PY ) code=$(curl -sS -o /tmp/malware-watch-discord-verify.out -w '%{http_code}' \ -A 'Mozilla/5.0 (compatible; malware-watch/verify)' \ -H 'Content-Type: application/json' --data "$payload" "$DISCORD_WEBHOOK" || true) if [[ "$code" == "204" || "$code" == "200" ]]; then ok "Discord webhook test succeeded (HTTP $code)" else fail "Discord webhook test failed (HTTP ${code:-000})" fi rm -f /tmp/malware-watch-discord-verify.out else fail "cannot test Discord: config unavailable" fi fi printf "\nSummary: PASS=%d WARN=%d FAIL=%d\n" "$PASS" "$WARN" "$FAIL" (( FAIL == 0 ))