Files
openclaw-skills/workspace-security/vt-monitor/scripts/cron-wrapper.sh
b3nw 3b7d6bb67c Initial commit: custom OpenClaw skills from docker-test
- workspace: capmetro-monitor, github-notifications, model-selector
- workspace-security: vt-monitor, monitor-unauthorized
- workspace-home: cron-manager, monitor-unauthorized
- extensions: vt-sentinel (VT-Sentinel plugin)

Includes sync.sh for pull/push, README, AGENTS.md, .gitignore.
2026-02-16 15:32:44 +00:00

137 lines
6.1 KiB
Bash
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# VT-Sentinel monitoring cron wrapper (incremental)
# Checks both VT-Sentinel file activity AND plugin updates
set -e
SCRIPT_DIR="$(dirname "$0")"
LOG_FILE="/tmp/openclaw/openclaw.log"
STATE_DIR="/home/node/.openclaw/workspace-security/memory"
SEEN_FILE="$STATE_DIR/seen-plugin-updates.txt"
mkdir -p "$STATE_DIR"
touch "$SEEN_FILE"
OUTPUT=""
# --- VT-Sentinel activity ---
REPORT=$("$SCRIPT_DIR/check.sh" 2>/dev/null)
TOTAL=$(echo "$REPORT" | jq '.totalEvents')
ALERTS=$(echo "$REPORT" | jq '.alerts')
ACTION_EVENTS=$(echo "$REPORT" | jq '[.summary.uploads, .summary.upload_complete, .summary.upload_failed, .summary.cache_hits, .summary.verdicts.clean, .summary.verdicts.malicious, .summary.verdicts.suspicious, .summary.quarantined, .summary.blocked] | add')
if [ "$ACTION_EVENTS" -gt 0 ]; then
OUTPUT+="🛡️ VT-Sentinel Activity Report\n===============================\n\n"
OUTPUT+="📊 Summary: ${ACTION_EVENTS} file events\n"
OUTPUT+=$(echo "$REPORT" | jq -r '
.summary |
(if .uploads > 0 then " ⬆️ Uploads initiated: \(.uploads)" else empty end),
(if .upload_complete > 0 then " ✅ Uploads completed: \(.upload_complete)" else empty end),
(if .upload_failed > 0 then " ❌ Upload failures: \(.upload_failed)" else empty end),
(if .cache_hits > 0 then " 💾 Cache hits: \(.cache_hits)" else empty end),
(if .verdicts.clean > 0 then " ✅ Clean verdicts: \(.verdicts.clean)" else empty end),
(if .verdicts.malicious > 0 then " 🚨 MALICIOUS: \(.verdicts.malicious)" else empty end),
(if .verdicts.suspicious > 0 then " ⚠️ SUSPICIOUS: \(.verdicts.suspicious)" else empty end),
(if .quarantined > 0 then " 📦 Quarantined: \(.quarantined)" else empty end),
(if .blocked > 0 then " 🚫 Blocked: \(.blocked)" else empty end)
')
OUTPUT+="\n\n📋 Event Details:\n"
OUTPUT+=$(echo "$REPORT" | jq -r '.events[] | select(.category != "lifecycle" and .category != "config") | " [\(.timestamp)] \(.category): \(.message)"')
if [ "$ALERTS" = "true" ]; then
OUTPUT+="\n\n⚠ SECURITY ALERT: Review malicious/suspicious/blocked events above!"
fi
fi
# --- VT scan thread creation for new uploads ---
PENDING_FILE="$STATE_DIR/pending-scans.json"
[ ! -f "$PENDING_FILE" ] || [ ! -s "$PENDING_FILE" ] && echo '[]' > "$PENDING_FILE"
SEEN_SCANS="$STATE_DIR/seen-scan-hashes.txt"
touch "$SEEN_SCANS"
UPLOADS=$(echo "$REPORT" | jq -r '[.events[] | select(.category == "upload")] | .[]' 2>/dev/null)
COMPLETES=$(echo "$REPORT" | jq -r '[.events[] | select(.category == "upload_complete")] | .[].message' 2>/dev/null)
if [ -n "$UPLOADS" ]; then
# Extract filenames + risk categories from upload events
echo "$REPORT" | jq -r '.events[] | select(.category == "upload") | .message' | while IFS= read -r msg; do
RISK_CAT=$(echo "$msg" | grep -oP 'Unknown \K[A-Z_]+' || echo "UNKNOWN")
FNAME=$(echo "$msg" | grep -oP 'file \K[^,]+' || echo "unknown")
[ -z "$FNAME" ] || [ "$FNAME" = "unknown" ] && continue
# Check if already tracked
grep -qF "$FNAME" "$SEEN_SCANS" 2>/dev/null && continue
# Find corresponding transaction ID from upload_complete events
HASH=""
if [ -n "$COMPLETES" ]; then
# Get first unmatched transaction ID, decode to extract hash
TXN_ID=$(echo "$COMPLETES" | grep -oP '\(\K[A-Za-z0-9+/=]+(?=\))' | head -1)
if [ -n "$TXN_ID" ]; then
HASH=$(echo "$TXN_ID" | base64 -d 2>/dev/null | cut -d: -f1)
fi
fi
# Create thread
RESULT=$("$SCRIPT_DIR/scan-thread.sh" "$FNAME" "$RISK_CAT" "${HASH:-pending}" 2>/dev/null)
THREAD_ID=$(echo "$RESULT" | jq -r '.threadId // empty')
if [ -n "$THREAD_ID" ] && [ -n "$HASH" ]; then
# Add to pending scans
PENDING=$(cat "$PENDING_FILE")
PENDING=$(echo "$PENDING" | jq --arg h "$HASH" --arg f "$FNAME" --arg t "$THREAD_ID" --arg r "$RISK_CAT" \
'. += [{"hash":$h,"filename":$f,"threadId":$t,"riskCategory":$r}]')
echo "$PENDING" > "$PENDING_FILE"
OUTPUT+="\n🛡 Created scan thread: [$RISK_CAT] $FNAME (hash: ${HASH:0:12}...)\n"
fi
echo "$FNAME" >> "$SEEN_SCANS"
done
fi
# --- Follow up on pending scans ---
PENDING_COUNT=$(jq 'length' "$PENDING_FILE" 2>/dev/null || echo 0)
if [ "$PENDING_COUNT" -gt 0 ]; then
FOLLOWUP=$("$SCRIPT_DIR/followup.sh" 2>/dev/null)
if [ -n "$FOLLOWUP" ]; then
OUTPUT+="\n$FOLLOWUP\n"
fi
fi
# --- Plugin update check (creates Discord forum threads) ---
# Filter: only plugins subsystem entries, strip ANSI + multibyte artifacts, deduplicate
PLUGIN_UPDATES=$(tail -c 5000000 "$LOG_FILE" 2>/dev/null \
| grep '"subsystem.*plugins"' \
| grep -oP 'Update available: [^"\\]+' \
| sed 's/\x1b\[[0-9;]*m//g; s/\[3[0-9]m//g' \
| tr -d '\r' \
| sort -u)
if [ -n "$PLUGIN_UPDATES" ]; then
while IFS= read -r line; do
[ -z "$line" ] && continue
grep -qF "$line" "$SEEN_FILE" 2>/dev/null && continue
# Parse: "Update available: 0.4.0 → 0.6.0. Run: openclaw plugins install plugin-name"
OLD_VER=$(echo "$line" | grep -oP 'Update available: \K[0-9.]+')
NEW_VER=$(echo "$line" | grep -oP '→ \K[0-9.]+')
INSTALL_CMD=$(echo "$line" | grep -oP 'Run: \K.*')
PLUGIN_NAME=$(echo "$INSTALL_CMD" | grep -oP 'install \K\S+')
if [ -n "$PLUGIN_NAME" ] && [ -n "$OLD_VER" ] && [ -n "$NEW_VER" ]; then
RESULT=$("$SCRIPT_DIR/plugin-update-thread.sh" "$PLUGIN_NAME" "$OLD_VER" "$NEW_VER" "$INSTALL_CMD" 2>/dev/null)
if echo "$RESULT" | jq -e '.ok == true' >/dev/null 2>&1; then
OUTPUT+="\n📦 Created thread for plugin update: ${PLUGIN_NAME} ${OLD_VER}${NEW_VER}\n"
else
OUTPUT+="\n📦 Plugin update: ${PLUGIN_NAME} ${OLD_VER}${NEW_VER} (thread creation failed)\n"
fi
fi
echo "$line" >> "$SEEN_FILE"
done <<< "$PLUGIN_UPDATES"
fi
# --- Output ---
if [ -z "$OUTPUT" ]; then
echo "NO_REPLY"
else
echo -e "$OUTPUT"
fi