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.
This commit is contained in:
136
workspace-security/vt-monitor/scripts/cron-wrapper.sh
Executable file
136
workspace-security/vt-monitor/scripts/cron-wrapper.sh
Executable file
@@ -0,0 +1,136 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user