92 lines
2.9 KiB
Bash
Executable File
92 lines
2.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Wait for VirusTotal scans to complete and check results.
|
|
# Expects: VT_API_KEY, VT_ANALYSIS (comma-separated "file=URL" pairs)
|
|
set -euo pipefail
|
|
|
|
MIN_ENGINES=60
|
|
rm -f /tmp/vt_gate_fail
|
|
|
|
echo "=== Waiting for VirusTotal scans to fully complete ==="
|
|
|
|
echo "$VT_ANALYSIS" | tr ',' '\n' | while IFS= read -r entry; do
|
|
[ -z "$entry" ] && continue
|
|
FILE=$(echo "$entry" | cut -d'=' -f1)
|
|
URL=$(echo "$entry" | cut -d'=' -f2-)
|
|
BASENAME=$(basename "$FILE")
|
|
|
|
# Extract base64 analysis ID from URL
|
|
ANALYSIS_ID=$(echo "$URL" | sed -n 's|.*/file-analysis/\([^/]*\)/.*|\1|p')
|
|
if [ -z "$ANALYSIS_ID" ]; then
|
|
ANALYSIS_ID=$(echo "$URL" | grep -oE '[a-f0-9]{64}')
|
|
if [ -z "$ANALYSIS_ID" ]; then
|
|
echo "BLOCKED: Cannot parse VirusTotal URL: $URL"
|
|
echo "FAIL" >> /tmp/vt_gate_fail
|
|
continue
|
|
fi
|
|
fi
|
|
|
|
# Poll until completed (max 120 min)
|
|
SCAN_COMPLETE=false
|
|
for attempt in $(seq 1 720); do
|
|
RESULT=$(curl -sf --max-time 10 \
|
|
-H "x-apikey: $VT_API_KEY" \
|
|
"https://www.virustotal.com/api/v3/analyses/$ANALYSIS_ID" 2>/dev/null || echo "")
|
|
|
|
if [ -z "$RESULT" ]; then
|
|
echo " $BASENAME: waiting (attempt $attempt)..."
|
|
sleep 10
|
|
continue
|
|
fi
|
|
|
|
STATS=$(echo "$RESULT" | python3 -c "
|
|
import json, sys
|
|
d = json.loads(sys.stdin.read())
|
|
attrs = d.get('data', {}).get('attributes', {})
|
|
status = attrs.get('status', 'queued')
|
|
stats = attrs.get('stats', {})
|
|
malicious = stats.get('malicious', 0)
|
|
suspicious = stats.get('suspicious', 0)
|
|
undetected = stats.get('undetected', 0)
|
|
harmless = stats.get('harmless', 0)
|
|
total = sum(stats.values())
|
|
completed = malicious + suspicious + undetected + harmless
|
|
print(f'{status},{malicious},{suspicious},{completed},{total}')
|
|
" 2>/dev/null || echo "queued,0,0,0,0")
|
|
|
|
STATUS=$(echo "$STATS" | cut -d',' -f1)
|
|
MALICIOUS=$(echo "$STATS" | cut -d',' -f2)
|
|
SUSPICIOUS=$(echo "$STATS" | cut -d',' -f3)
|
|
COMPLETED=$(echo "$STATS" | cut -d',' -f4)
|
|
TOTAL=$(echo "$STATS" | cut -d',' -f5)
|
|
|
|
if [ "$STATUS" = "completed" ]; then
|
|
SCAN_COMPLETE=true
|
|
if [ "$COMPLETED" -lt "$MIN_ENGINES" ]; then
|
|
echo "NOTE: $BASENAME completed with only $COMPLETED/$TOTAL engines (< $MIN_ENGINES)"
|
|
fi
|
|
if [ "$MALICIOUS" -gt 0 ] || [ "$SUSPICIOUS" -gt 0 ]; then
|
|
echo "BLOCKED: $BASENAME flagged ($MALICIOUS malicious, $SUSPICIOUS suspicious / $COMPLETED engines)"
|
|
echo " $URL"
|
|
echo "FAIL" >> /tmp/vt_gate_fail
|
|
else
|
|
echo "OK: $BASENAME clean ($COMPLETED engines, 0 detections)"
|
|
fi
|
|
break
|
|
fi
|
|
|
|
echo " $BASENAME: $COMPLETED/$TOTAL engines ($STATUS, attempt $attempt)..."
|
|
sleep 10
|
|
done
|
|
|
|
if [ "$SCAN_COMPLETE" != "true" ]; then
|
|
echo "WARNING: $BASENAME scan did not complete in time"
|
|
echo "FAIL" >> /tmp/vt_gate_fail
|
|
fi
|
|
done
|
|
|
|
if [ -f /tmp/vt_gate_fail ]; then
|
|
echo "BLOCKED: One or more VirusTotal checks failed"
|
|
exit 1
|
|
fi
|
|
echo "=== All VirusTotal scans passed ==="
|