# -*- coding: utf-8 -*- """修两个问题: 1. 【安全】文件功能允许对「允许目录」本身下手 —— 能把整个 Downloads/Desktop 目录重命名、移动或删掉。这是刚才跑测试时真实踩到的(Downloads 被改名成 x)。 加一道守卫:目标等于任何一个允许根目录时,直接拒绝。 2. download_file 改成了 kind="file",但测试的合法 kind 集合没更新, 而且手表端也需要认识这个新类型(改成发 file_pull 拉取)。 """ from __future__ import annotations import sys from pathlib import Path ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote") FEATURES = ROOT / "windows" / "lanremote" / "features.py" TEST = ROOT / "_tests" / "test_features.py" MAIN = ROOT / "android" / "src" / "com" / "lanremote" / "watch" / "MainActivity.java" FIXES = [ # ---------------- 1. 根目录守卫 ---------------- (FEATURES, ''' def resolve_in_roots(self, raw: str) -> Path:''', ''' def assert_not_root(self, path: Path) -> None: """不允许对「允许目录」本身动手。 否则一条 rename_file(path="Downloads", name="x") 就能把整个 下载目录改名(delete_file / move_file 同理,能直接删掉)。 """ try: resolved = path.resolve() except OSError: return for root in self.allowed_roots(): try: if resolved == root.resolve(): raise ValueError("不能对允许目录本身做这个操作") except OSError: continue def resolve_in_roots(self, raw: str) -> Path:''', "新增 assert_not_root"), (FEATURES, ''' if not source.exists(): raise ValueError("文件不存在") target = source.parent / name if target.exists(): raise ValueError("同名文件已存在")''', ''' if not source.exists(): raise ValueError("文件不存在") self.assert_not_root(source) target = source.parent / name if target.exists(): raise ValueError("同名文件已存在")''', "重命名守卫"), (FEATURES, ''' target = self.resolve_in_roots(str(params.get("path") or "")) if not target.exists(): raise ValueError("文件不存在") try: if IS_WINDOWS: self._recycle(target)''', ''' target = self.resolve_in_roots(str(params.get("path") or "")) if not target.exists(): raise ValueError("文件不存在") self.assert_not_root(target) try: if IS_WINDOWS: self._recycle(target)''', "删除守卫"), (FEATURES, ''' if not source.exists(): raise ValueError("源文件不存在") if source == target: raise ValueError("源和目标是同一个位置") if target.exists(): raise ValueError("目标已存在,不覆盖(先改名或删掉)") try: if source.is_dir(): _shutil.copytree(source, target)''', ''' if not source.exists(): raise ValueError("源文件不存在") self.assert_not_root(source) self.assert_not_root(target) if source == target: raise ValueError("源和目标是同一个位置") if target.exists(): raise ValueError("目标已存在,不覆盖(先改名或删掉)") try: if source.is_dir(): _shutil.copytree(source, target)''', "复制守卫"), (FEATURES, ''' if not source.exists(): raise ValueError("源文件不存在") if source == target: raise ValueError("源和目标是同一个位置") if target.exists(): raise ValueError("目标已存在,不覆盖(先改名或删掉)") try: target.parent.mkdir(parents=True, exist_ok=True)''', ''' if not source.exists(): raise ValueError("源文件不存在") self.assert_not_root(source) self.assert_not_root(target) if source == target: raise ValueError("源和目标是同一个位置") if target.exists(): raise ValueError("目标已存在,不覆盖(先改名或删掉)") try: target.parent.mkdir(parents=True, exist_ok=True)''', "移动守卫"), # ---------------- 2. 测试的 kind 集合 ---------------- (TEST, ''' bad_kind = [i["name"] for i in defs if i.get("kind") not in ("toggle", "action", "value", "input")]''', ''' bad_kind = [i["name"] for i in defs if i.get("kind") not in ("toggle", "action", "value", "input", "file")]''', "测试 kind 集合加上 file"), (TEST, ''' need_input = [i for i in defs if i.get("kind") == "input"]''', ''' need_input = [i for i in defs if i.get("kind") in ("input", "file")]''', "file 类型也要有 key"), # ---------------- 3. 手表端认识 file 类型 ---------------- (MAIN, ''' if ("input".equals(kind)) {''', ''' // 文件类:不在电脑上"执行",而是让手表把文件拉过来 if ("file".equals(kind)) { promptInput(label, Json.str(feature, "hint", "文件路径"), "", new InputHandler() { @Override public void onInput(String path) { if (!path.isEmpty()) { pullFile(path); } } }); return; } if ("input".equals(kind)) {''', "手表端 file 类型 → 拉取"), ] def main() -> int: applied, skipped = 0, [] for path, old, new, tag in FIXES: if not path.is_file(): skipped.append(tag) print(f" [跳过] {tag} —— 文件不存在") continue text = path.read_text(encoding="utf-8") if old not in text: skipped.append(tag) print(f" [跳过] {tag} —— 锚点没找到") continue path.write_text(text.replace(old, new, 1), encoding="utf-8") applied += 1 print(f" {tag}: OK") # 导出给其他文件用 if applied and " def assert_not_root" in FEATURES.read_text(encoding="utf-8"): pass print(f"\n 应用 {applied} / {len(FIXES)}") if skipped: print(f" 跳过: {', '.join(skipped)}") import ast for label, path in (("features.py", FEATURES), ("test_features.py", TEST)): try: ast.parse(path.read_text(encoding="utf-8")) print(f" {label}: 语法 OK") except SyntaxError as exc: print(f" {label}: 语法错误 {exc}") return 1 return 0 if __name__ == "__main__": sys.exit(main())