Files
ykxmz/_tests/fuzz_features.py
T
xmz7891 b705216e4a ykxmz 1.5.0锛氫慨澶嶆墜琛ㄧ TLS 璇佷功鏍¢獙 + MTU 淇缃戠粶绋冲畾鎬?
琚帶绔?- 淇 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 + 鍙岃繛鎺?
鏂囨。
- 璁稿彲鍗忚 / 瀹夎鍓嶅繀璇?/ 瀹夎鍚庨』鐭ワ紙浣滆€咃細灏忛害瀛愶級
2026-10-06 19:01:05 +08:00

274 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""功能全量模糊测试:找出会"崩"的功能。
约定:参数不合法应该抛 ValueError(会被转成给用户看的错误提示)。
抛其它异常(TypeError / KeyError / AttributeError / IndexError / OSError…)
就是 bug。
设计要点(第一版太慢,这里改过):
* 只 fuzz 功能**自己声明**的参数(key / key2),不再瞎塞 13 个 key。
之前 perf / installed_apps 这类会真去调 PowerShell,被调了几百次。
* 有副作用的功能用 DANGEROUS 列表跳过实测。
* 全屏类每次调用后立刻关掉,避免堆窗口。
"""
from __future__ import annotations
import sys
import tempfile
import tkinter as tk
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT / "windows"))
# 会真的动系统,不实测
DANGEROUS = {
"empty_recycle", "close_windows", "sign_out", "sleep", "hibernate",
"restart_graphics", "minimize_all", "task_view", "restart_explorer",
"lock_only", "lock_and_off", "clear_clip", "open_recycle", "open_file",
"display_mode", "fade_volume", "monitor_on", "volume_up", "volume_down",
"mute_all", "flush_dns", "password_gen", "cinema_mode", "random_song",
"play_video", "stop_after", "pomodoro", "reminder", "cheer",
"clip_fav_add", "download_file", "qr_code", "speed_test", "public_ip",
}
# 会开全屏窗口的:能测,但测完要关
FULLSCREEN = {
"big_text", "big_text_quiet", "countdown_screen", "roll_call", "whiteboard",
"danmaku", "marquee", "lottery", "dice", "coin_flip", "countdown_321",
"score_board", "score_change", "matrix_rain", "starfield", "dashboard",
"stopwatch",
}
# 读一次就够、不跟参数走的(避免重复 shell 调用拖慢测试)
EXPENSIVE = {
"perf", "disk", "battery", "uptime", "installed_apps", "top_services",
"event_errors", "temperature", "hardware_info", "startup_items",
"scheduled_tasks", "system_detail", "recent_files", "network_speed",
"gpu_info", "arp_devices", "net_adapters", "wifi_password", "disk_by_type",
"world_clock", "timestamp_now", "uuid_gen", "screen_color",
}
JUNK = [None, [], {}, "", "abc", 999999, -1, 0, "a" * 3000]
PASSED = 0
CRASHES: list[tuple[str, str, str]] = []
SLOW_SKIPPED: list[str] = []
def main() -> int:
global PASSED
from lanremote.actions import Actions
from lanremote.config import Store
from lanremote.features import Features
tmp = Path(tempfile.mkdtemp())
store = Store(tmp)
class FakeUI:
root = tk.Tk()
root.withdraw()
def ui_call(self, fn, timeout=5.0):
return fn()
def screen_size(self):
return (1920, 1080)
def notify(self, *a, **k):
pass
def log(self, *a, **k):
pass
def show_message(self, *a, **k):
pass
def close_image(self):
pass
actions = Actions(FakeUI(), store)
features = Features(actions)
defs = {d["name"]: d for d in features.defs()}
print(f"功能总数: {len(defs)}", flush=True)
def cleanup_fullscreen(name):
if name in FULLSCREEN:
try:
features.run("big_text_clear", {})
except Exception: # noqa: BLE001
pass
try:
FakeUI.root.update()
except tk.TclError:
pass
# ---------------- 一、空参数 ----------------
print()
print("=" * 74)
print("一、空参数({})")
print("=" * 74, flush=True)
for name in sorted(defs):
if name in DANGEROUS:
continue
try:
features.run(name, {})
PASSED += 1
except ValueError:
PASSED += 1
except Exception as exc: # noqa: BLE001
CRASHES.append((name, "{}", f"{type(exc).__name__}: {exc}"))
print(f" [崩溃] {name}({{}}) -> {type(exc).__name__}: {exc}", flush=True)
cleanup_fullscreen(name)
# ---------------- 二、垃圾参数 ----------------
print()
print("=" * 74)
print("二、垃圾参数(只喂功能自己声明的 key)")
print("=" * 74, flush=True)
for name in sorted(defs):
if name in DANGEROUS:
continue
spec = defs[name]
keys = [k for k in (spec.get("key"), spec.get("key2")) if k]
if not keys:
continue # 不接参数的功能,上面那轮已经覆盖
if name in EXPENSIVE:
SLOW_SKIPPED.append(name)
continue
for key in keys:
for junk in JUNK:
try:
features.run(name, {key: junk})
PASSED += 1
except ValueError:
PASSED += 1
except Exception as exc: # noqa: BLE001
CRASHES.append((name, f"{key}={junk!r}"[:40],
f"{type(exc).__name__}: {exc}"))
print(f" [崩溃] {name}({key}={junk!r}) -> "
f"{type(exc).__name__}: {exc}", flush=True)
break
cleanup_fullscreen(name)
# ---------------- 三、越权路径 ----------------
print()
print("=" * 74)
print("三、路径越权(必须全部拒绝)")
print("=" * 74, flush=True)
escapes = [
r"C:\Windows\System32\drivers\etc\hosts",
r"..\..\Windows\win.ini",
r"C:\Windows",
"/etc/passwd",
r"\\?\C:\Windows\win.ini",
r"C:Windows\win.ini",
r"\Windows\win.ini",
r"C:\Windows\..\Windows\win.ini",
" C:\\Windows\\win.ini ",
]
for path in escapes:
try:
resolved = features.resolve_in_roots(path)
allowed = [str(r.resolve()).lower() for r in features.allowed_roots()]
inside = any(str(resolved).lower().startswith(a) for a in allowed)
if inside:
PASSED += 1
else:
CRASHES.append(("resolve_in_roots", repr(path),
f"逃出允许范围: {resolved}"))
print(f" [越权!] {path!r} -> {resolved}", flush=True)
except ValueError:
PASSED += 1
except Exception as exc: # noqa: BLE001
CRASHES.append(("resolve_in_roots", repr(path),
f"{type(exc).__name__}: {exc}"))
print(f" [崩溃] {path!r} -> {type(exc).__name__}: {exc}", flush=True)
# 注意:search_files 接的是 keyword(拿去当文件名匹配),不是路径,
# 它本来就逃不出允许目录,所以不放在路径测试里。
file_features = ["list_dir", "make_dir", "rename_file",
"delete_file", "open_file", "copy_file", "move_file",
"zip_dir", "unzip", "file_info", "folder_size"]
for name in file_features:
for path in escapes[:5]:
try:
features.run(name, {"path": path, "to": path, "name": "zz",
"keyword": "zz"})
CRASHES.append((name, repr(path), "越权路径竟然被执行了"))
print(f" [越权!] {name}({path!r}) 竟然执行了", flush=True)
except ValueError:
PASSED += 1
except Exception as exc: # noqa: BLE001
CRASHES.append((name, repr(path), f"{type(exc).__name__}: {exc}"))
print(f" [崩溃] {name}({path!r}) -> "
f"{type(exc).__name__}: {exc}", flush=True)
# ---------------- 四、目录守卫 ----------------
print()
print("=" * 74)
print("四、目录守卫(不能对允许目录本身动手)")
print("=" * 74, flush=True)
for name, params in (("rename_file", {"path": "Downloads", "name": "zz"}),
("delete_file", {"path": "Desktop"}),
("move_file", {"path": "Downloads", "to": "Documents"}),
("copy_file", {"path": "Desktop", "to": "Downloads"})):
try:
features.run(name, params)
CRASHES.append((name, str(params), "竟然允许对允许目录本身动手"))
print(f" [危险!] {name} 竟然允许了", flush=True)
except ValueError:
PASSED += 1
except Exception as exc: # noqa: BLE001
CRASHES.append((name, str(params), f"{type(exc).__name__}: {exc}"))
print(f" [崩溃] {name} -> {type(exc).__name__}: {exc}", flush=True)
# ---------------- 五、数据目录 ----------------
print()
print("五、数据目录(配对码 / 设备令牌)不可访问")
# 用真实的 store.dir 构成绝对路径来测 —— 相对路径会解析到 Downloads 下,
# 那是允许目录,测不出东西。真正的 CWD 相对路径由端到端测试覆盖。
probes = [str(store.dir / "settings.json"), str(store.dir / "devices.json"),
str(store.dir)]
for probe in probes:
try:
features.resolve_in_roots(probe)
CRASHES.append(("resolve_in_roots", probe, "数据目录竟然可访问"))
print(f" [危险!] {probe} 竟然可访问", flush=True)
except ValueError:
PASSED += 1
except Exception as exc: # noqa: BLE001
CRASHES.append(("resolve_in_roots", probe,
f"{type(exc).__name__}: {exc}"))
print(f" [崩溃] {probe} -> {type(exc).__name__}: {exc}", flush=True)
try:
FakeUI.root.destroy()
except tk.TclError:
pass
print()
print("=" * 74)
print("汇总")
print("=" * 74)
print(f" 正常: {PASSED}")
print(f" 崩溃/越权: {len(CRASHES)}")
if SLOW_SKIPPED:
print(f" 跳过的慢功能(只测了空参数): {', '.join(SLOW_SKIPPED)}")
if CRASHES:
print()
print(" ---- 需要修 ----")
seen = set()
for name, arg, err in CRASHES:
key = (name, err.split(":")[0])
if key in seen:
continue
seen.add(key)
print(f" {name}({arg})\n {err}")
return 1 if CRASHES else 0
if __name__ == "__main__":
sys.exit(main())