1+ name : Secure Media + Docs Validation
2+
3+ on :
4+ push :
5+ pull_request :
6+
7+ permissions :
8+ contents : read
9+
10+ concurrency :
11+ group : validation-${{ github.ref }}
12+ cancel-in-progress : true
13+
14+ jobs :
15+ validate :
16+ runs-on : ubuntu-latest
17+
18+ steps :
19+ - name : Checkout repository
20+ uses : actions/checkout@v5
21+
22+ - name : Install dependencies
23+ run : |
24+ set -euo pipefail
25+
26+ sudo apt-get update
27+
28+ sudo apt-get install -y \
29+ file \
30+ ffmpeg \
31+ imagemagick \
32+ jq \
33+ clamav \
34+ clamav-freshclam
35+
36+ - name : Update ClamAV signatures
37+ run : |
38+ set -euo pipefail
39+
40+ sudo systemctl stop clamav-freshclam || true
41+
42+ # Allow fallback if signature update fails (common on CI)
43+ sudo freshclam || true
44+
45+ echo "ClamAV databases:"
46+ ls -lah /var/lib/clamav || true
47+
48+ - name : Scan for malware
49+ run : |
50+ set -euo pipefail
51+
52+ SCAN_TARGETS=()
53+ [ -d media ] && SCAN_TARGETS+=(media)
54+ [ -d docs ] && SCAN_TARGETS+=(docs)
55+
56+ if [ ${#SCAN_TARGETS[@]} -eq 0 ]; then
57+ echo "Neither media/ nor docs/ exists"
58+ exit 1
59+ fi
60+
61+ if ls /var/lib/clamav/*.cvd >/dev/null 2>&1 || \
62+ ls /var/lib/clamav/*.cld >/dev/null 2>&1; then
63+
64+ clamscan -r "${SCAN_TARGETS[@]}" \
65+ --infected \
66+ --no-summary \
67+ --max-filesize=50M \
68+ --max-scansize=100M
69+ else
70+ echo "WARNING: No ClamAV signatures found. Skipping scan."
71+ fi
72+
73+ - name : Validate media files
74+ run : |
75+ set -euo pipefail
76+
77+ if [ ! -d media ]; then
78+ echo "media/ directory not found"
79+ exit 1
80+ fi
81+
82+ find media -type f -print0 | while IFS= read -r -d '' file; do
83+ echo "Checking media: $file"
84+
85+ mime=$(file --mime-type -b "$file")
86+
87+ case "$mime" in
88+ image/jpeg|image/png|image/gif|image/webp)
89+ identify "$file" >/dev/null
90+ ;;
91+
92+ video/mp4|video/webm|video/quicktime)
93+ ffprobe -v error "$file" >/dev/null
94+ ;;
95+
96+ audio/mpeg|audio/wav|audio/ogg)
97+ ffprobe -v error "$file" >/dev/null
98+ ;;
99+
100+ *)
101+ echo "ERROR: Unsupported media type"
102+ echo "File: $file"
103+ echo "MIME: $mime"
104+ exit 1
105+ ;;
106+ esac
107+ done
108+
109+ - name : Validate docs files
110+ run : |
111+ set -euo pipefail
112+
113+ if [ ! -d docs ]; then
114+ echo "docs/ directory not found"
115+ exit 1
116+ fi
117+
118+ find docs -type f -print0 | while IFS= read -r -d '' file; do
119+ echo "Checking docs: $file"
120+
121+ case "$file" in
122+ *.md)
123+ # 1. Must be valid text file (reject binaries properly)
124+ if ! grep -Iq . "$file"; then
125+ echo "ERROR: Non-text or binary markdown detected"
126+ echo "File: $file"
127+ exit 1
128+ fi
129+
130+ # 2. Enforce UTF-8 encoding (prevents UTF-16 issues)
131+ encoding=$(file --mime-encoding -b "$file" || true)
132+
133+ case "$encoding" in
134+ utf-8|us-ascii) ;;
135+ *)
136+ echo "ERROR: Invalid encoding in markdown: $encoding"
137+ echo "File: $file"
138+ exit 1
139+ ;;
140+ esac
141+ ;;
142+
143+ *.json)
144+ jq -e . "$file" >/dev/null
145+ ;;
146+
147+ *)
148+ echo "ERROR: Unsupported file in docs/"
149+ echo "File: $file"
150+ exit 1
151+ ;;
152+ esac
153+ done
154+
155+ - name : Reject executable files
156+ run : |
157+ set -euo pipefail
158+
159+ mapfile -d '' exec_files < <(find media docs -type f -executable -print0 2>/dev/null || true)
160+
161+ if [ ${#exec_files[@]} -gt 0 ]; then
162+ echo "ERROR: Executable files found"
163+ printf '%s\n' "${exec_files[@]}"
164+ exit 1
165+ fi
0 commit comments