Skip to content

Commit 3b130f7

Browse files
ci: add Encoding Defense (L1-L5) mojibake guard (#72)
- L5: mojibake scan (U+FFFD + invalid UTF-8) in source files, excludes .spz binaries - L4: verify .gitattributes eol=lf encoding config - L3: detect shell encoding bypass patterns (iconv/dos2unix/recode) - L2: CRLF scan in .sh scripts - L1: detect unexpected UTF-8 BOM in text sources Adapted from TDAI 1.0: gatekeeper sources contain legit Chinese comments, so L5 rejects mojibake features instead of all non-ASCII. Co-authored-by: GitHub Action <action@github.com>
1 parent ddb2275 commit 3b130f7

1 file changed

Lines changed: 108 additions & 1 deletion

File tree

.github/workflows/ci.yml

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ permissions:
1010

1111
on:
1212
push:
13-
branches: [ main, feature/spz-v2-profile-core, feature/spz-gatekeeper-2.0.2-r1, feature/spz-gatekeeper-2.0.2-r2, feature/spz-gatekeeper-2.0.2-r3, feature/spz-gatekeeper-2.0.2-r4, feature/spz-gatekeeper-2.0.2-r5, feature/spz-gatekeeper-2.0.2-r6, feature/spz-gatekeeper-2.0.4-r7, feature/spz-gatekeeper-2.0.5.1, fix/pages-deploy-on-tag ]
13+
branches: [ main, feature/spz-v2-profile-core, feature/spz-gatekeeper-2.0.2-r1, feature/spz-gatekeeper-2.0.2-r2, feature/spz-gatekeeper-2.0.2-r3, feature/spz-gatekeeper-2.0.2-r4, feature/spz-gatekeeper-2.0.2-r5, feature/spz-gatekeeper-2.0.2-r6, feature/spz-gatekeeper-2.0.4-r7, feature/spz-gatekeeper-2.0.5.1, fix/pages-deploy-on-tag, feature/spz-gatekeeper-encoding-defense ]
1414
pull_request:
1515
branches: [ main ]
1616
workflow_dispatch:
@@ -226,6 +226,113 @@ jobs:
226226
- name: Scan GitHub Actions workflows
227227
run: zizmor --format plain --min-severity medium .github/workflows/
228228

229+
# 乱码防护(移植自 TDAI 1.0 pr-ci.yml encoding-defense job,L1-L5 五层)
230+
# 适配:门卫源码含合法中文注释,L5 从"禁止非 ASCII"改为"乱码特征检测";
231+
# 检测目标 = U+FFFD 替换符、无效 UTF-8、意外 BOM、.sh CRLF、编码绕过模式。
232+
encoding-defense:
233+
name: Encoding Defense (L1-L5)
234+
runs-on: ubuntu-latest
235+
timeout-minutes: 5
236+
steps:
237+
- name: Checkout
238+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
239+
with:
240+
persist-credentials: false
241+
242+
- name: L5 - Detect garbled Unicode (U+FFFD / invalid UTF-8) in source
243+
# TDAI L5 扫描非 ASCII 字符;门卫源码含中文注释(合法),故改为检测乱码特征:
244+
# U+FFFD (EF BF BD) 替换符 = 编码损坏的直接证据;无效 UTF-8 序列同样拦截。
245+
# 排除 .spz 二进制数据文件(压缩内容天然含任意字节)。
246+
run: |
247+
echo "── Layer 5: mojibake scan in source files ──"
248+
violations=0
249+
while IFS= read -r -d '' f; do
250+
if ! python3 -c "open('$f','rb').read().decode('utf-8')" 2>/dev/null; then
251+
echo " INVALID UTF-8: $f"
252+
violations=$((violations + 1))
253+
continue
254+
fi
255+
if grep -q $'\xef\xbf\xbd' "$f" 2>/dev/null; then
256+
echo " U+FFFD (mojibake): $f"
257+
violations=$((violations + 1))
258+
fi
259+
done < <(find . \
260+
-path './.git' -prune -o \
261+
-path './build' -prune -o \
262+
-path './build-*' -prune -o \
263+
-type f \( -name '*.cc' -o -name '*.h' -o -name '*.js' -o -name '*.html' \
264+
-o -name '*.sh' -o -name '*.yml' -o -name '*.yaml' -o -name '*.md' \
265+
-o -name '*.cmake' -o -name 'CMakeLists.txt' \) \
266+
-print0 2>/dev/null)
267+
if [ "$violations" -gt 0 ]; then echo "FAIL: $violations file(s) with encoding errors"; exit 1; fi
268+
echo "OK: $violations encoding violations (0 = clean)"
269+
270+
- name: L4 - Verify .gitattributes encoding config
271+
run: |
272+
echo "── Layer 4: git pipeline encoding config ──"
273+
fails=0
274+
if [ -f .gitattributes ]; then
275+
if grep -q "eol=lf" .gitattributes 2>/dev/null; then
276+
echo " .gitattributes declares eol=lf rules"
277+
else
278+
echo " WARN: .gitattributes exists but no eol=lf"
279+
fails=$((fails + 1))
280+
fi
281+
else
282+
echo " WARN: no .gitattributes found"
283+
fails=$((fails + 1))
284+
fi
285+
ENC=$(git config --local core.autocrlf 2>/dev/null || echo "unset")
286+
echo " core.autocrlf: $ENC"
287+
if [ "$fails" -gt 0 ]; then echo "WARN: $fails pipeline encoding issue(s) (non-fatal)"; fi
288+
echo "OK: pipeline encoding check"
289+
290+
- name: L3 - Detect shell encoding bypass patterns
291+
run: |
292+
echo "── Layer 3: shell encoding bypass scan ──"
293+
fails=0
294+
while IFS= read -r -d '' f; do
295+
if grep -q 'iconv.*UTF-16\|sed.*s/BOM\|dos2unix\|recode' "$f" 2>/dev/null; then
296+
echo " SUSPICIOUS: $f may bypass encoding"
297+
fails=$((fails + 1))
298+
fi
299+
done < <(find . -path './.git' -prune -o -name '*.sh' -print 2>/dev/null | tr '\n' '\0')
300+
if [ "$fails" -gt 0 ]; then echo "WARN: $fails shell file(s) with bypass patterns (non-fatal)"; fi
301+
echo "OK: shell encoding bypass check"
302+
303+
- name: L2 - Detect CRLF in shell scripts
304+
run: |
305+
echo "── Layer 2: CRLF scan in .sh ──"
306+
fails=0
307+
while IFS= read -r -d '' f; do
308+
if grep -lU $'\r' "$f" 2>/dev/null | grep -q .; then
309+
echo " CRLF: $f"
310+
fails=$((fails + 1))
311+
fi
312+
done < <(find . -path './.git' -prune -o -name '*.sh' -print 2>/dev/null | tr '\n' '\0')
313+
if [ "$fails" -gt 0 ]; then echo "FAIL: $fails .sh file(s) with CRLF"; exit 1; fi
314+
echo "OK: all .sh use LF"
315+
316+
- name: L1 - Detect unexpected UTF-8 BOM in text sources
317+
run: |
318+
echo "── Layer 1: UTF-8 BOM scan ──"
319+
fails=0
320+
while IFS= read -r -d '' f; do
321+
header=$(od -A n -t x1 -N 3 "$f" 2>/dev/null | tr -d ' \n')
322+
if [ "$header" = "efbbbf" ]; then
323+
echo " UNEXPECTED BOM: $f"
324+
fails=$((fails + 1))
325+
fi
326+
done < <(find . \
327+
-path './.git' -prune -o \
328+
-path './build' -prune -o \
329+
-path './build-*' -prune -o \
330+
-type f \( -name '*.cc' -o -name '*.h' -o -name '*.js' -o -name '*.html' \
331+
-o -name '*.sh' -o -name '*.yml' -o -name '*.yaml' -o -name '*.md' \) \
332+
-print0 2>/dev/null)
333+
if [ "$fails" -gt 0 ]; then echo "FAIL: $fails file(s) with unexpected BOM"; exit 1; fi
334+
echo "OK: no unexpected BOM"
335+
229336
wasm:
230337
runs-on: ubuntu-latest
231338
timeout-minutes: 30

0 commit comments

Comments
 (0)