mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-19 13:00:44 +00:00
2.3.0:手表界面精简 + 播放器 + 鼠标重做
手表端 - 功能页从 10 个杂项精简到 7 个(屏幕/鼠标/媒体/文件/电脑/语音/电源) - 删掉:游戏封锁、待办、掷骰子、抽奖、壁纸、显示器开关、定时关机、 剪贴板历史、性能/磁盘/电量、文件夹浏览、教室限制等 - 改成显式列按钮,不再把电脑端所有功能组无脑铺到表盘 - 远程鼠标控制重做:滑动区 + 左键/滚轮/右键三按钮 滚轮点按=中键、上下滑=滚动 - 新增视频/音乐播放器(手表本地播放):进度拖动、左右滑快进退10秒、 点画面播放暂停、上一/下一、播完自动下一首 电脑端 - 新增 kill_all(结束所有进程),白名单保护系统关键进程和自身 - 新增 media_list / media_pull(播放器数据源) 安全边界:只允许媒体目录,用 relative_to 判断,防同前缀兄弟目录绕过 中转站 - known_devices 从 ID 集合改成「设备ID -> 配对码」映射 - 修复:管理员改码后关机超过 6 小时,改码会被超时清理冲掉 - 老格式 state 文件平滑升级
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""2.3.0 新命令的单元测试:media_list / media_pull / kill_all。
|
||||
|
||||
重点验**安全边界**,不是"能跑就行":
|
||||
|
||||
* `media_pull` 必须拒绝媒体目录之外的文件(否则就是个远程任意文件读取器)
|
||||
* 同前缀的兄弟目录(`音乐-evil`)不能被误判成子目录
|
||||
* `kill_all` 必须保护自己、父进程链、系统关键进程
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(ROOT / "windows"))
|
||||
|
||||
passed = 0
|
||||
failed = 0
|
||||
|
||||
|
||||
def check(name, ok, detail=""):
|
||||
global passed, failed
|
||||
if ok:
|
||||
passed += 1
|
||||
print(" [PASS] " + name)
|
||||
else:
|
||||
failed += 1
|
||||
print(" [FAIL] " + name + ((" —— " + str(detail)) if detail else ""))
|
||||
return ok
|
||||
|
||||
|
||||
def main() -> int:
|
||||
print("=" * 62)
|
||||
print(" 2.3.0 新命令测试")
|
||||
print("=" * 62)
|
||||
|
||||
# ---------- 1. 静态检查:实现存在且关键点齐 ----��------
|
||||
print()
|
||||
print("一、代码存在性")
|
||||
server = (ROOT / "windows" / "lanremote" / "server.py").read_text(
|
||||
encoding="utf-8")
|
||||
features = (ROOT / "windows" / "lanremote" / "features.py").read_text(
|
||||
encoding="utf-8")
|
||||
|
||||
check("server.py 有 _media_list", "def _media_list(self)" in server)
|
||||
check("server.py 有 _media_pull", "def _media_pull(self" in server)
|
||||
check("路由 media_list", 'cmd == "media_list"' in server)
|
||||
check("路由 media_pull", 'cmd == "media_pull"' in server)
|
||||
check("features.py 有 _do_kill_all", "def _do_kill_all(self" in features)
|
||||
check("注册了 kill_all 功能", 'add("kill_all"' in features)
|
||||
|
||||
# ---------- 2. 安全边界:只允许媒体目录 ----------
|
||||
print()
|
||||
print("二、media_pull 的安全边界")
|
||||
check("用 relative_to 判断(不是字符串前缀)",
|
||||
"wanted.relative_to(root)" in server,
|
||||
"字符串前缀会被「音乐-evil」这种同前缀兄弟目录绕过")
|
||||
check("有「不在允许的媒体目录里」的拒绝",
|
||||
"不在允许的媒体目录里" in server)
|
||||
check("有文件大小上限(512MB)", "512 * 1024 * 1024" in server)
|
||||
check("拒绝空文件", "文件是空的" in server)
|
||||
|
||||
# ---------- 3. kill_all 的保护名单 ----------
|
||||
print()
|
||||
print("三、kill_all 的保护名单")
|
||||
for name in ("csrss.exe", "winlogon.exe", "lsass.exe", "svchost.exe",
|
||||
"explorer.exe", "python.exe", "ykxmz.exe"):
|
||||
check("保护 %s" % name, '"%s"' % name in features,
|
||||
"少了它一按就可能蓝屏/自断后路")
|
||||
check("保护自己的 PID", "_protected_pids" in features)
|
||||
check("追父进程链", "ParentProcessId" in features)
|
||||
|
||||
# ---------- 4. 真的跑一次 _media_list(用临时目录)----------
|
||||
print()
|
||||
print("四、_media_list 实跑")
|
||||
try:
|
||||
import os
|
||||
os.environ.setdefault("PYTHONIOENCODING", "utf-8")
|
||||
from lanremote import server as srv_mod
|
||||
# 造一个假的 store(不需要 Config —— 那个类不存在,之前是我写错了)
|
||||
tmp = Path(tempfile.mkdtemp(prefix="media_"))
|
||||
music = tmp / "Music"
|
||||
music.mkdir()
|
||||
(music / "a.mp3").write_bytes(b"x" * 100)
|
||||
(music / "b.mp4").write_bytes(b"y" * 200)
|
||||
(music / "c.txt").write_text("not media", encoding="utf-8")
|
||||
|
||||
class FakeStore:
|
||||
def received_dir(self):
|
||||
return tmp
|
||||
|
||||
# 直接借 Server 的方法:构造太麻烦,用 types 绑定
|
||||
import types
|
||||
obj = types.SimpleNamespace()
|
||||
obj.store = FakeStore()
|
||||
obj.AUDIO_EXT = srv_mod.RemoteServer.AUDIO_EXT
|
||||
obj.VIDEO_EXT = srv_mod.RemoteServer.VIDEO_EXT
|
||||
obj.MEDIA_LIST_LIMIT = srv_mod.RemoteServer.MEDIA_LIST_LIMIT
|
||||
obj.MEDIA_SUBDIRS = ("Music", "Videos")
|
||||
obj._media_roots = types.MethodType(srv_mod.RemoteServer._media_roots, obj)
|
||||
# Path.home() 是真实用户目录,这里把 _media_roots 换成只返回 music。
|
||||
# ⚠ 必须返回**装文件的那一层**,不是它的父目录 ——
|
||||
# _media_list 只扫根目录下的文件,不递归子目录。
|
||||
# (第一版写成了 tmp,扫到的是 Music 这个文件夹本身,全被跳过。)
|
||||
obj._media_roots = lambda: [music.resolve()]
|
||||
|
||||
result = srv_mod.RemoteServer._media_list(obj)
|
||||
names = [f["name"] for f in result["files"]]
|
||||
check("列出了 a.mp3", "a.mp3" in names)
|
||||
check("列出了 b.mp4", "b.mp4" in names)
|
||||
check("**排除了 c.txt**", "c.txt" not in names,
|
||||
"非媒体扩展名不该出现")
|
||||
kinds = {f["name"]: f["kind"] for f in result["files"]}
|
||||
check("a.mp3 标成 audio", kinds.get("a.mp3") == "audio")
|
||||
check("b.mp4 标成 video", kinds.get("b.mp4") == "video")
|
||||
except Exception as exc: # noqa: BLE001
|
||||
check("_media_list 实跑", False, "%s: %s" % (type(exc).__name__, exc))
|
||||
|
||||
print()
|
||||
print("=" * 62)
|
||||
print(" 通过 %d 项,失败 %d 项" % (passed, failed))
|
||||
print("=" * 62)
|
||||
return 0 if failed == 0 else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user