diff --git a/agent/memory/service.py b/agent/memory/service.py index 6456e296..567a1f5f 100644 --- a/agent/memory/service.py +++ b/agent/memory/service.py @@ -134,6 +134,8 @@ class MemoryService: else: return {"action": action, "code": 400, "message": f"unknown action: {action}", "payload": None} + except ValueError as e: + return {"action": action, "code": 403, "message": "invalid filename", "payload": None} except FileNotFoundError as e: return {"action": action, "code": 404, "message": str(e), "payload": None} except Exception as e: @@ -145,14 +147,26 @@ class MemoryService: # ------------------------------------------------------------------ def _resolve_path(self, filename: str) -> str: """ - Resolve a filename to its absolute path. + Safely resolve a filename to its absolute path within the allowed directory. - ``MEMORY.md`` → ``{workspace_root}/MEMORY.md`` - ``2026-02-20.md`` → ``{workspace_root}/memory/2026-02-20.md`` + + Raises ValueError if the resolved path escapes the allowed directory + (path traversal protection). """ if filename == "MEMORY.md": - return os.path.join(self.workspace_root, filename) - return os.path.join(self.memory_dir, filename) + base_dir = self.workspace_root + else: + base_dir = self.memory_dir + + resolved = os.path.realpath(os.path.join(base_dir, filename)) + allowed = os.path.realpath(base_dir) + + if resolved != allowed and not resolved.startswith(allowed + os.sep): + raise ValueError(f"Invalid filename: path traversal detected") + + return resolved @staticmethod def _file_info(path: str, filename: str, file_type: str) -> dict: diff --git a/channel/web/web_channel.py b/channel/web/web_channel.py index c19fdd63..e23c5e32 100644 --- a/channel/web/web_channel.py +++ b/channel/web/web_channel.py @@ -1365,6 +1365,8 @@ class MemoryContentHandler: service = MemoryService(workspace_root) result = service.get_content(params.filename) return json.dumps({"status": "success", **result}, ensure_ascii=False) + except ValueError: + return json.dumps({"status": "error", "message": "invalid filename"}) except FileNotFoundError: return json.dumps({"status": "error", "message": "file not found"}) except Exception as e: diff --git a/docs/releases/v2.0.5.mdx b/docs/releases/v2.0.5.mdx index 6c6f29d0..5983056b 100644 --- a/docs/releases/v2.0.5.mdx +++ b/docs/releases/v2.0.5.mdx @@ -68,12 +68,15 @@ Thanks [@WecomTeam](https://github.com/WecomTeam) ## 🐛 其他优化与修复 - **DeepSeek 独立模块**:新增独立的 DeepSeek Bot 模块,支持 `deepseek_api_key` 专属配置,无需再通过 OpenAI 兼容方式接入([#2719](https://github.com/zhayujie/chatgpt-on-wechat/pull/2719))。Thanks [@6vision](https://github.com/6vision) -- **Web 控制台斜杠菜单**:输入框输入 `/` 弹出指令快捷菜单([#2731](https://github.com/zhayujie/chatgpt-on-wechat/pull/2731))。Thanks [@zkjqd](https://github.com/zkjqd) +- **Web 控制台优化**:新增斜杠指令菜单和输入历史回溯,新增模型选项,优化移动端适配([#2731](https://github.com/zhayujie/chatgpt-on-wechat/pull/2731))。Thanks [@zkjqd](https://github.com/zkjqd) - **上下文丢失**:修复上下文裁剪后丢失的问题 ([393f0c0](https://github.com/zhayujie/chatgpt-on-wechat/commit/393f0c0)) - **系统提示词**:修复系统提示词未在每轮重建的问题 ([13f5fde](https://github.com/zhayujie/chatgpt-on-wechat/commit/13f5fde)) +- **Agent 响应**:去除 Agent 响应首尾空白字符 ([f890318](https://github.com/zhayujie/chatgpt-on-wechat/commit/f890318)) +- **视觉压缩**:优化视觉图片压缩策略 ([22b8ca0](https://github.com/zhayujie/chatgpt-on-wechat/commit/22b8ca0)) - **Gemini 模型**:修复 GoogleGeminiBot 缺少 model 属性的问题([#2716](https://github.com/zhayujie/chatgpt-on-wechat/pull/2716))。Thanks [@cowagent](https://github.com/cowagent) -- **微信通道**:修复文件发送失败、文件名丢失等问题 ([6d9b7ba](https://github.com/zhayujie/chatgpt-on-wechat/commit/6d9b7ba)、[45faa9c](https://github.com/zhayujie/chatgpt-on-wechat/commit/45faa9c)) +- **微信通道**:修复文件发送失败、文件名丢失等问题 ([6d9b7ba](https://github.com/zhayujie/chatgpt-on-wechat/commit/6d9b7ba)、[baf66a1](https://github.com/zhayujie/chatgpt-on-wechat/commit/baf66a1)、[45faa9c](https://github.com/zhayujie/chatgpt-on-wechat/commit/45faa9c)) - **Docker 优化**:修复卷权限问题,精简镜像体积 ([3eb8348](https://github.com/zhayujie/chatgpt-on-wechat/commit/3eb8348)、[4470d4c](https://github.com/zhayujie/chatgpt-on-wechat/commit/4470d4c)) +- **README 排版**:优化中英文排版空格([#2723](https://github.com/zhayujie/chatgpt-on-wechat/pull/2723))。Thanks [@Xiaozhou345](https://github.com/Xiaozhou345) ## 📦 升级方式 diff --git a/plugins/cow_cli/cow_cli.py b/plugins/cow_cli/cow_cli.py index 993df4c0..27e653ef 100644 --- a/plugins/cow_cli/cow_cli.py +++ b/plugins/cow_cli/cow_cli.py @@ -599,7 +599,7 @@ class CowCliPlugin(Plugin): page = min(page, total_pages) installed = set(load_skills_config().keys()) - lines = [f"🌐 技能广场 (共 {total} 个技能)", ""] + lines = ["🌐 技能广场", ""] for s in skills: name = s.get("name", "") display = s.get("display_name", "") or name