Skip to content

Commit 88f8908

Browse files
committed
Fix VirusTotal: use /analyses/ endpoint with base64 analysis ID
1 parent 1d0889e commit 88f8908

File tree

1 file changed

+29
-24
lines changed

1 file changed

+29
-24
lines changed

.github/workflows/release.yml

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -533,66 +533,71 @@ jobs:
533533
534534
# ── Wait for ALL VirusTotal engines to complete, then check ──
535535
# The action outputs comma-separated "file=URL" pairs.
536-
# We wait until every engine has reported (not just the first few),
537-
# then check for any malicious or suspicious detection.
538-
# Minimum 60 engines required — below that we consider the scan incomplete.
536+
# URLs are /gui/file-analysis/<base64_id>/detection — we extract the
537+
# base64 analysis ID and poll /api/v3/analyses/<id> until completed.
539538
- name: Check VirusTotal scan results (wait for 100% completion)
540539
env:
541540
VT_API_KEY: ${{ secrets.VIRUS_TOTAL_SCANNER_API_KEY }}
542541
VT_ANALYSIS: ${{ steps.virustotal.outputs.analysis }}
543542
run: |
544543
echo "=== Waiting for VirusTotal scans to fully complete ==="
545544
MIN_ENGINES=60
545+
rm -f /tmp/vt_gate_fail
546546
547547
echo "$VT_ANALYSIS" | tr ',' '\n' | while IFS= read -r entry; do
548548
[ -z "$entry" ] && continue
549549
FILE=$(echo "$entry" | cut -d'=' -f1)
550550
URL=$(echo "$entry" | cut -d'=' -f2-)
551551
BASENAME=$(basename "$FILE")
552552
553-
SHA256=$(echo "$URL" | grep -oE '[a-f0-9]{64}')
554-
if [ -z "$SHA256" ]; then
555-
echo "WARNING: Could not extract SHA256 from $URL — skipping"
556-
continue
553+
# Extract base64 analysis ID from URL: /gui/file-analysis/<ID>/detection
554+
ANALYSIS_ID=$(echo "$URL" | sed -n 's|.*/file-analysis/\([^/]*\)/.*|\1|p')
555+
if [ -z "$ANALYSIS_ID" ]; then
556+
echo "WARNING: Could not extract analysis ID from $URL"
557+
# Try SHA256 fallback (older action versions use /gui/file/<sha256>)
558+
ANALYSIS_ID=$(echo "$URL" | grep -oE '[a-f0-9]{64}')
559+
if [ -z "$ANALYSIS_ID" ]; then
560+
echo "BLOCKED: Cannot parse VirusTotal URL: $URL"
561+
echo "FAIL" >> /tmp/vt_gate_fail
562+
continue
563+
fi
557564
fi
558565
559-
# Poll until ALL engines have completed (max 20 minutes per binary)
566+
# Poll /api/v3/analyses/<id> until status=completed (max 20 min)
560567
SCAN_COMPLETE=false
561568
for attempt in $(seq 1 120); do
562569
RESULT=$(curl -sf --max-time 10 \
563570
-H "x-apikey: $VT_API_KEY" \
564-
"https://www.virustotal.com/api/v3/files/$SHA256" 2>/dev/null || echo "")
571+
"https://www.virustotal.com/api/v3/analyses/$ANALYSIS_ID" 2>/dev/null || echo "")
565572
566573
if [ -z "$RESULT" ]; then
567-
echo " $BASENAME: waiting for file to be processed (attempt $attempt)..."
574+
echo " $BASENAME: waiting (attempt $attempt)..."
568575
sleep 10
569576
continue
570577
fi
571578
572-
# Parse all stats in one python call
573579
STATS=$(echo "$RESULT" | python3 -c "
574580
import json, sys
575581
d = json.loads(sys.stdin.read())
576-
stats = d.get('data', {}).get('attributes', {}).get('last_analysis_stats', {})
582+
attrs = d.get('data', {}).get('attributes', {})
583+
status = attrs.get('status', 'queued')
584+
stats = attrs.get('stats', {})
577585
malicious = stats.get('malicious', 0)
578586
suspicious = stats.get('suspicious', 0)
579587
undetected = stats.get('undetected', 0)
580588
harmless = stats.get('harmless', 0)
581-
failure = stats.get('failure', 0)
582-
timeout = stats.get('timeout', 0)
583-
unsupported = stats.get('type-unsupported', 0)
584589
total = sum(stats.values())
585-
# 'confirmed-timeout' may also exist
586590
completed = malicious + suspicious + undetected + harmless
587-
print(f'{malicious},{suspicious},{completed},{total}')
588-
" 2>/dev/null || echo "0,0,0,0")
591+
print(f'{status},{malicious},{suspicious},{completed},{total}')
592+
" 2>/dev/null || echo "queued,0,0,0,0")
589593
590-
MALICIOUS=$(echo "$STATS" | cut -d',' -f1)
591-
SUSPICIOUS=$(echo "$STATS" | cut -d',' -f2)
592-
COMPLETED=$(echo "$STATS" | cut -d',' -f3)
593-
TOTAL=$(echo "$STATS" | cut -d',' -f4)
594+
STATUS=$(echo "$STATS" | cut -d',' -f1)
595+
MALICIOUS=$(echo "$STATS" | cut -d',' -f2)
596+
SUSPICIOUS=$(echo "$STATS" | cut -d',' -f3)
597+
COMPLETED=$(echo "$STATS" | cut -d',' -f4)
598+
TOTAL=$(echo "$STATS" | cut -d',' -f5)
594599
595-
if [ "$TOTAL" -ge "$MIN_ENGINES" ] && [ "$COMPLETED" -ge "$MIN_ENGINES" ]; then
600+
if [ "$STATUS" = "completed" ]; then
596601
echo "$BASENAME: $MALICIOUS malicious, $SUSPICIOUS suspicious ($COMPLETED completed, $TOTAL total engines)"
597602
598603
if [ "$MALICIOUS" -gt 0 ] || [ "$SUSPICIOUS" -gt 0 ]; then
@@ -603,7 +608,7 @@ jobs:
603608
break
604609
fi
605610
606-
echo " $BASENAME: $COMPLETED/$TOTAL engines completed (attempt $attempt, need $MIN_ENGINES)..."
611+
echo " $BASENAME: $STATUS (attempt $attempt)..."
607612
sleep 10
608613
done
609614

0 commit comments

Comments
 (0)