From 9a8b41f7b300c008ab216eac45faa3f11c7794d5 Mon Sep 17 00:00:00 2001 From: baldnerd Date: Sat, 9 May 2026 12:57:27 -0400 Subject: [PATCH] Create test.sh to ease admin testing --- test.sh | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100755 test.sh diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..be7c7fc --- /dev/null +++ b/test.sh @@ -0,0 +1,236 @@ +#!/bin/bash +# BaldCanary Diagnostic / Webhook Test Script + +set -euo pipefail + +APP_ROOT="/opt/baldcanary" +APP_DB="${APP_ROOT}/db/baldcanary.sqlite" +DISPATCHER_SERVICE="baldcanary-dispatcher" +OPENCANARY_SERVICE="opencanary" +COLLECTOR_SERVICE="baldcanary-decoylog" +NGINX_SERVICE="nginx" + +fail() { + echo "ERROR: $*" >&2 + exit 1 +} + +section() { + echo + echo "============================================================" + echo "$*" + echo "============================================================" +} + +run() { + echo "+ $*" + "$@" +} + +require_root() { + if [[ "${EUID}" -ne 0 ]]; then + fail "Run this script as root: sudo $0" + fi +} + +check_file() { + local file="$1" + if [[ -e "$file" ]]; then + echo "OK: $file exists" + ls -lh "$file" + else + echo "MISSING: $file" + fi +} + +sqlite_query() { + local query="$1" + + if [[ ! -f "$APP_DB" ]]; then + echo "SQLite DB not found: $APP_DB" + return 1 + fi + + sqlite3 -header -column "$APP_DB" "$query" +} + +insert_test_alert() { + section "Insert Test Alert" + + local now + now="$(date -u '+%Y-%m-%d %H:%M:%S')" + + sqlite3 "$APP_DB" " +INSERT INTO events(source,severity,event_type,src_ip,path,matched_bait,raw_json) +VALUES( + 'system', + 'high', + 'test_alert', + '127.0.0.1', + 'manual-test', + 'manual webhook test', + '{\"test\":true,\"created_by\":\"test.sh\",\"created_at\":\"${now}\"}' +); +" + + local event_id + event_id="$(sqlite3 "$APP_DB" "SELECT id FROM events ORDER BY id DESC LIMIT 1;")" + + echo "Inserted test alert event ID: $event_id" + echo "Waiting 15 seconds for dispatcher..." + sleep 15 + + section "Alert Log Result For Test Event" + sqlite_query " +SELECT + id, + event_id, + target_id, + attempted_at, + ok, + response_code, + substr(response_body,1,160) AS response_body, + substr(error,1,250) AS error +FROM alert_log +WHERE event_id = ${event_id} +ORDER BY id DESC; +" +} + +main() { + require_root + + section "BaldCanary Diagnostic Test" + + echo "Host: $(hostname)" + echo "Time: $(date)" + echo "User: $(whoami)" + + section "Required Files" + + check_file "$APP_DB" + check_file "${APP_ROOT}/config/opencanary_event_labels.json" + check_file "${APP_ROOT}/scripts/dispatch_alerts.py" + check_file "${APP_ROOT}/scripts/opencanary_collector.py" + check_file "/usr/local/bin/baldcanary" + check_file "/etc/opencanaryd/opencanary.conf" + + section "Service Status" + + systemctl --no-pager --full status "$NGINX_SERVICE" || true + systemctl --no-pager --full status "$OPENCANARY_SERVICE" || true + systemctl --no-pager --full status "$COLLECTOR_SERVICE" || true + systemctl --no-pager --full status "$DISPATCHER_SERVICE" || true + + section "Listening Ports" + + ss -ltnup || true + + section "SQLite Integrity" + + sqlite3 "$APP_DB" "PRAGMA integrity_check;" || true + + section "BaldCanary Settings" + + sqlite_query " +SELECT key, value, updated_at +FROM settings +ORDER BY key; +" || true + + section "Webhook Targets" + + sqlite_query " +SELECT + id, + name, + type, + enabled, + created_at, + substr(url,1,80) || CASE WHEN length(url) > 80 THEN '...' ELSE '' END AS url_preview +FROM webhook_targets +ORDER BY id; +" || true + + section "Recent Events" + + sqlite_query " +SELECT + id, + event_time, + source, + severity, + event_type, + src_ip, + path, + matched_bait +FROM events +ORDER BY id DESC +LIMIT 15; +" || true + + section "Recent Alert Attempts" + + sqlite_query " +SELECT + id, + event_id, + target_id, + attempted_at, + ok, + response_code, + substr(response_body,1,160) AS response_body, + substr(error,1,250) AS error +FROM alert_log +ORDER BY id DESC +LIMIT 15; +" || true + + section "Dispatcher Python Syntax Check" + + if [[ -f "${APP_ROOT}/scripts/dispatch_alerts.py" ]]; then + run python3 -m py_compile "${APP_ROOT}/scripts/dispatch_alerts.py" || true + fi + + section "Collector Python Syntax Check" + + if [[ -f "${APP_ROOT}/scripts/opencanary_collector.py" ]]; then + run python3 -m py_compile "${APP_ROOT}/scripts/opencanary_collector.py" || true + fi + + section "Recent Dispatcher Logs" + + journalctl -u "$DISPATCHER_SERVICE" -n 80 --no-pager || true + + section "Recent Collector Logs" + + journalctl -u "$COLLECTOR_SERVICE" -n 60 --no-pager || true + + section "Recent OpenCanary Logs" + + journalctl -u "$OPENCANARY_SERVICE" -n 60 --no-pager || true + + section "Optional Live Webhook Test" + + echo "This will insert a new high-severity test event and wait for the dispatcher." + read -r -p "Insert test alert now? [y/N] " answer + + case "$answer" in + y|Y|yes|YES) + insert_test_alert + ;; + *) + echo "Skipped test alert insertion." + ;; + esac + + section "Done" + + echo "If Teams did not receive the alert, check:" + echo " 1. webhook_targets has enabled=1" + echo " 2. alert_log has ok=1 and a 2xx response_code" + echo " 3. baldcanary-dispatcher logs show no Python errors" + echo " 4. the Teams/Power Automate workflow accepts the payload schema" +} + +main "$@"