mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 13:40:45 +00:00
琚帶绔?- 淇 MTU 闂锛氬す MSS + 璺緞 MTU 鎺㈡祴锛堝叕缃戝彲鐢ㄧ巼 48% -> 100%锛?- 澶嶇敤 HTTP 杩炴帴锛堝厤鎺夋瘡娆℃柊寤?TLS 鐨?30% 鎻℃墜澶辫触锛?- 姣忓垎閽熷洖澶存壘灞€鍩熺綉锛岃嚜鍔ㄥ垏鍥炲揩 400 鍊嶇殑璺緞 - 鐢婚潰蹇冭烦甯э細闈欐鏃剁渷娴侀噺锛屽鎴风浠嶇煡閬撴祦鏄椿鐨?- 閿佸睆鏃剁粰鍑哄彲鎿嶄綔鐨勬彁绀? 鎵嬭〃绔?- 鍐呯疆 GTS Root R4 / GlobalSign Root CA锛屼慨 SSLHandshakeException - 杩炴帴閿欒缈昏瘧鎴愪汉璇濓紙璇佷功/鏃堕棿/瑙f瀽/瓒呮椂鍒嗗埆鎻愮ず锛?- 缁忎腑杞繛涓婂悗鐢ㄦ湇鍔″櫒鎶ョ殑鍦板潃鍒锋柊宸查厤瀵硅褰?- 鏂板灞€鍩熺綉杩炴帴椤甸潰銆佹湇鍔″櫒寤惰繜妫€娴? 鏈嶅姟鍣ㄧ - 鏀寔 chunked 璇锋眰浣擄紙淇瘡 16 绉掍竴娆$殑杩炴帴閿欎贡涓庡ぇ闈㈢Н 502锛?- 缃戠粶璋冧紭鑴氭湰 net-tune.sh锛堝紑鏈鸿嚜鍔ㄥ簲鐢級 - 绠$悊椤碉細鎵归噺鍒犻櫎銆佺綉缁滀綋妫€銆佷笅杞藉尯 - 闅ч亾鏀?HTTP/2 + 鍙岃繛鎺? 鏂囨。 - 璁稿彲鍗忚 / 瀹夎鍓嶅繀璇?/ 瀹夎鍚庨』鐭ワ紙浣滆€咃細灏忛害瀛愶級
216 lines
7.3 KiB
Python
216 lines
7.3 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""全项目编码审计(收窄版)。
|
||
|
||
上一版太吵:把 Java 字节码、测试用的 .bin、还有审计脚本自己
|
||
(源码里就写着乱码样本)全当成文本查了。
|
||
|
||
这一版:
|
||
* 用**内容**判断是不是二进制(不是只看扩展名)
|
||
* 跳过编译产物和测试数据目录
|
||
* 跳过审计脚本自己
|
||
|
||
只查真正要交付和要跑的东西。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
import zipfile
|
||
from pathlib import Path
|
||
|
||
ROOT = Path(__file__).resolve().parent.parent
|
||
SELF = Path(__file__).resolve()
|
||
|
||
# 这些目录是产物/数据,不是源码
|
||
SKIP_DIRS = {
|
||
"_build", "__pycache__", ".git", "dist_exe", "dist", "pyinstaller_work",
|
||
"node_modules", "out_java", "received", "gen", "javacheck", "javacheck2",
|
||
"javacheck3", "javacheck4", "javacheck5", "build_tmp", "toolchain",
|
||
}
|
||
SKIP_SUFFIX = {
|
||
".pyc", ".png", ".jpg", ".jpeg", ".gif", ".ico", ".apk", ".exe", ".dll",
|
||
".pyd", ".zip", ".idsig", ".so", ".class", ".bin", ".jar", ".keystore",
|
||
".jks", ".der", ".pem", ".7z",
|
||
}
|
||
|
||
MOJIBAKE = [
|
||
"锟斤拷", "�", "â€", "ä", "é", "è", "а", "Ñ€",
|
||
"\ufffd",
|
||
"ä¸", "å¤", "æµ", "è¯", "é€",
|
||
]
|
||
|
||
# 只查这些扩展名(其余一律不问)
|
||
TEXT_SUFFIX = {".py", ".java", ".ps1", ".sh", ".bat", ".cmd", ".txt",
|
||
".md", ".json", ".yml", ".yaml", ".xml", ".gradle", ".cfg",
|
||
".ini", ".properties", ".pro", ".service", ".timer"}
|
||
|
||
problems = []
|
||
stats = {}
|
||
scanned = 0
|
||
|
||
|
||
def report(level: str, path, message: str) -> None:
|
||
label = str(path.relative_to(ROOT)) if isinstance(path, Path) \
|
||
and str(path).startswith(str(ROOT)) else str(path)
|
||
problems.append((level, label, message))
|
||
|
||
|
||
def looks_binary(body: bytes) -> bool:
|
||
if b"\x00" in body[:4096]:
|
||
return True
|
||
# 非文本字节比例过高就当二进制
|
||
sample = body[:4096]
|
||
if not sample:
|
||
return False
|
||
weird = sum(1 for byte in sample
|
||
if byte < 8 or (14 <= byte < 32 and byte != 27))
|
||
return weird / len(sample) > 0.10
|
||
|
||
|
||
def check_bytes(path, name: str, suffix: str, raw: bytes) -> None:
|
||
"""对一份内容做编码检查。path 只用于报错定位。"""
|
||
global scanned
|
||
scanned += 1
|
||
stats[suffix] = stats.get(suffix, 0) + 1
|
||
|
||
has_bom = raw.startswith(b"\xef\xbb\xbf")
|
||
body = raw[3:] if has_bom else raw
|
||
|
||
# .bat 走特殊逻辑:cmd.exe 在中文 Windows 上默认代码页 936,
|
||
# 所以 .bat 里要写中文就必须是 **GBK**(UTF-8 的中文会变乱码)。
|
||
# 纯 ASCII 的 .bat 两种代码页下都对,最保险。
|
||
if suffix in (".bat", ".cmd"):
|
||
if has_bom:
|
||
report("警告", path, f"{name} 有 BOM(cmd 会把 BOM 当命令)")
|
||
try:
|
||
text = body.decode("gbk")
|
||
encoding_name = "GBK"
|
||
except UnicodeDecodeError:
|
||
try:
|
||
text = body.decode("utf-8")
|
||
if any("\u4e00" <= ch <= "\u9fff" for ch in text):
|
||
report("严重", path,
|
||
f"{name} 是 UTF-8 且含中文 —— cmd.exe 下会乱码,应为 GBK")
|
||
encoding_name = "UTF-8(纯ASCII)"
|
||
except UnicodeDecodeError as exc:
|
||
report("严重", path, f"{name} GBK/UTF-8 都解不开:{exc}")
|
||
return
|
||
crlf = body.count(b"\r\n")
|
||
lf_only = body.count(b"\n") - crlf
|
||
if lf_only:
|
||
report("警告", path, f"{name} 有 {lf_only} 处纯 LF(.bat 应为 CRLF)")
|
||
stats[suffix] = stats.get(suffix, 0) + 1
|
||
scanned += 1
|
||
return
|
||
|
||
try:
|
||
text = body.decode("utf-8")
|
||
except UnicodeDecodeError as exc:
|
||
report("严重", path, f"{name} 不是合法 UTF-8:{exc}")
|
||
return
|
||
|
||
if suffix == ".ps1" and not has_bom:
|
||
report("严重", path, f"{name} 缺 UTF-8 BOM(PowerShell 5.1 会按 GBK 读)")
|
||
if suffix in (".py", ".java", ".sh") and has_bom:
|
||
report("警告", path, f"{name} 不应有 BOM")
|
||
if suffix in (".bat", ".cmd") and has_bom:
|
||
report("警告", path, f"{name} 不应有 BOM")
|
||
|
||
for token in MOJIBAKE:
|
||
if token in text:
|
||
line = next((i + 1 for i, l in enumerate(text.splitlines())
|
||
if token in l), 0)
|
||
report("严重", path, f"{name} 疑似乱码 {token!r}(第 {line} 行)")
|
||
break
|
||
|
||
crlf = body.count(b"\r\n")
|
||
lf_only = body.count(b"\n") - crlf
|
||
cr_only = body.count(b"\r") - crlf
|
||
|
||
if suffix == ".sh" and (crlf or cr_only):
|
||
report("严重", path, f"{name} 有 CR(CRLF {crlf} 处)—— Linux 上跑不起来")
|
||
if suffix in (".bat", ".cmd") and lf_only:
|
||
report("警告", path, f"{name} 有 {lf_only} 处纯 LF")
|
||
if cr_only:
|
||
report("警告", path, f"{name} 有 {cr_only} 处孤立 CR")
|
||
if crlf and lf_only and suffix not in (".bat", ".cmd"):
|
||
report("提示", path, f"{name} 行尾混用(CRLF {crlf} / LF {lf_only})")
|
||
|
||
|
||
def check_zip(zip_path: Path) -> None:
|
||
if not zip_path.is_file():
|
||
return
|
||
print(f" 交付包:{zip_path.name}")
|
||
with zipfile.ZipFile(zip_path) as archive:
|
||
for name in archive.namelist():
|
||
if name.endswith("/"):
|
||
continue
|
||
suffix = Path(name).suffix.lower()
|
||
if suffix not in TEXT_SUFFIX:
|
||
continue
|
||
raw = archive.read(name)
|
||
if not raw or looks_binary(raw):
|
||
continue
|
||
check_bytes(zip_path, f"包内 {name}", suffix, raw)
|
||
|
||
|
||
def main() -> int:
|
||
print("=" * 74)
|
||
print(" 编码审计(只查要交付 / 要跑的文本文件)")
|
||
print("=" * 74)
|
||
|
||
targets = []
|
||
for path in ROOT.rglob("*"):
|
||
if not path.is_file() or path.resolve() == SELF:
|
||
continue
|
||
rel = path.relative_to(ROOT)
|
||
if set(rel.parts) & SKIP_DIRS:
|
||
continue
|
||
if path.suffix.lower() in SKIP_SUFFIX:
|
||
continue
|
||
if path.suffix.lower() not in TEXT_SUFFIX:
|
||
continue
|
||
targets.append(path)
|
||
|
||
print(f" 源码目录扫描 {len(targets)} 个文本文件…")
|
||
for path in sorted(targets):
|
||
raw = path.read_bytes()
|
||
if not raw or looks_binary(raw):
|
||
continue
|
||
check_bytes(path, path.name, path.suffix.lower(), raw)
|
||
|
||
print()
|
||
check_zip(ROOT / "dist" / "ykxmz-1.4.0.zip")
|
||
|
||
print()
|
||
print(" 按类型统计:")
|
||
for suffix, count in sorted(stats.items(), key=lambda x: -x[1]):
|
||
print(f" {suffix:<12} {count:>5} 个")
|
||
|
||
print()
|
||
print("=" * 74)
|
||
if not problems:
|
||
print(" ✓ 全部通过,没有编码问题")
|
||
return 0
|
||
|
||
by_level: dict = {}
|
||
for level, path, message in problems:
|
||
by_level.setdefault(level, []).append((path, message))
|
||
|
||
for level in ("严重", "警告", "提示"):
|
||
items = by_level.get(level, [])
|
||
if not items:
|
||
continue
|
||
print(f" 【{level}】{len(items)} 条")
|
||
for path, message in items[:20]:
|
||
print(f" {path}")
|
||
print(f" {message}")
|
||
if len(items) > 20:
|
||
print(f" …还有 {len(items) - 20} 条")
|
||
print()
|
||
print(f" 合计 {len(problems)} 条")
|
||
return 1 if by_level.get("严重") else 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|