@@ -482,6 +482,80 @@ jobs:
482482 assets/*.tar.gz
483483 assets/*.zip
484484
485+ # ── Wait for VirusTotal results and check for detections ──
486+ - name : Check VirusTotal scan results
487+ env :
488+ VT_API_KEY : ${{ secrets.VIRUS_TOTAL_SCANNER_API_KEY }}
489+ VT_ANALYSIS : ${{ steps.virustotal.outputs.analysis }}
490+ run : |
491+ echo "=== Waiting for VirusTotal scan results ==="
492+ DETECTIONS=0
493+
494+ while IFS= read -r line; do
495+ [ -z "$line" ] && continue
496+ URL=$(echo "$line" | cut -d'=' -f2-)
497+
498+ # Extract analysis ID from URL (last path segment)
499+ ANALYSIS_ID=$(echo "$URL" | grep -oE '[a-zA-Z0-9_-]+$')
500+ if [ -z "$ANALYSIS_ID" ]; then
501+ echo "WARNING: Could not extract analysis ID from $URL"
502+ continue
503+ fi
504+
505+ FILE=$(echo "$line" | cut -d'=' -f1)
506+ BASENAME=$(basename "$FILE")
507+
508+ # Poll until analysis completes (max 5 minutes per file)
509+ for attempt in $(seq 1 30); do
510+ RESULT=$(curl -sf --max-time 10 \
511+ -H "x-apikey: $VT_API_KEY" \
512+ "https://www.virustotal.com/api/v3/analyses/$ANALYSIS_ID" 2>/dev/null || echo "{}")
513+
514+ STATUS=$(echo "$RESULT" | python3 -c "
515+ import json, sys
516+ d = json.loads(sys.stdin.read())
517+ print(d.get('data', {}).get('attributes', {}).get('status', 'queued'))
518+ " 2>/dev/null || echo "queued")
519+
520+ if [ "$STATUS" = "completed" ]; then
521+ MALICIOUS=$(echo "$RESULT" | python3 -c "
522+ import json, sys
523+ d = json.loads(sys.stdin.read())
524+ stats = d.get('data', {}).get('attributes', {}).get('stats', {})
525+ print(stats.get('malicious', 0))
526+ " 2>/dev/null || echo "0")
527+
528+ SUSPICIOUS=$(echo "$RESULT" | python3 -c "
529+ import json, sys
530+ d = json.loads(sys.stdin.read())
531+ stats = d.get('data', {}).get('attributes', {}).get('stats', {})
532+ print(stats.get('suspicious', 0))
533+ " 2>/dev/null || echo "0")
534+
535+ echo "$BASENAME: malicious=$MALICIOUS suspicious=$SUSPICIOUS"
536+
537+ if [ "$MALICIOUS" -gt 0 ] || [ "$SUSPICIOUS" -gt 0 ]; then
538+ echo "BLOCKED: $BASENAME flagged by $MALICIOUS malicious + $SUSPICIOUS suspicious engine(s)!"
539+ DETECTIONS=$((DETECTIONS + 1))
540+ fi
541+ break
542+ fi
543+
544+ echo " $BASENAME: scan $STATUS (attempt $attempt/30)..."
545+ sleep 10
546+ done
547+ done <<< "$VT_ANALYSIS"
548+
549+ if [ "$DETECTIONS" -gt 0 ]; then
550+ echo ""
551+ echo "=== VIRUSTOTAL GATE FAILED ==="
552+ echo "$DETECTIONS binary(ies) flagged as malicious or suspicious."
553+ echo "Draft release will NOT be published. Investigate before retrying."
554+ exit 1
555+ fi
556+
557+ echo "=== All binaries clean ==="
558+
485559 # ── OpenSSF Scorecard ────────────────────────────────────
486560 - name : Run OpenSSF Scorecard
487561 uses : ossf/scorecard-action@v2
0 commit comments