fix(security): prevent path traversal in memory content API

This commit is contained in:
zhayujie
2026-04-01 10:03:58 +08:00
parent 1c336380c0
commit 174ee0cafc
4 changed files with 25 additions and 6 deletions

View File

@@ -134,6 +134,8 @@ class MemoryService:
else: else:
return {"action": action, "code": 400, "message": f"unknown action: {action}", "payload": None} 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: except FileNotFoundError as e:
return {"action": action, "code": 404, "message": str(e), "payload": None} return {"action": action, "code": 404, "message": str(e), "payload": None}
except Exception as e: except Exception as e:
@@ -145,14 +147,26 @@ class MemoryService:
# ------------------------------------------------------------------ # ------------------------------------------------------------------
def _resolve_path(self, filename: str) -> str: 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`` - ``MEMORY.md`` → ``{workspace_root}/MEMORY.md``
- ``2026-02-20.md`` → ``{workspace_root}/memory/2026-02-20.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": if filename == "MEMORY.md":
return os.path.join(self.workspace_root, filename) base_dir = self.workspace_root
return os.path.join(self.memory_dir, filename) 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 @staticmethod
def _file_info(path: str, filename: str, file_type: str) -> dict: def _file_info(path: str, filename: str, file_type: str) -> dict:

View File

@@ -1365,6 +1365,8 @@ class MemoryContentHandler:
service = MemoryService(workspace_root) service = MemoryService(workspace_root)
result = service.get_content(params.filename) result = service.get_content(params.filename)
return json.dumps({"status": "success", **result}, ensure_ascii=False) return json.dumps({"status": "success", **result}, ensure_ascii=False)
except ValueError:
return json.dumps({"status": "error", "message": "invalid filename"})
except FileNotFoundError: except FileNotFoundError:
return json.dumps({"status": "error", "message": "file not found"}) return json.dumps({"status": "error", "message": "file not found"})
except Exception as e: except Exception as e:

View File

@@ -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) - **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)) - **上下文丢失**:修复上下文裁剪后丢失的问题 ([393f0c0](https://github.com/zhayujie/chatgpt-on-wechat/commit/393f0c0))
- **系统提示词**:修复系统提示词未在每轮重建的问题 ([13f5fde](https://github.com/zhayujie/chatgpt-on-wechat/commit/13f5fde)) - **系统提示词**:修复系统提示词未在每轮重建的问题 ([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) - **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)) - **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)
## 📦 升级方式 ## 📦 升级方式

View File

@@ -599,7 +599,7 @@ class CowCliPlugin(Plugin):
page = min(page, total_pages) page = min(page, total_pages)
installed = set(load_skills_config().keys()) installed = set(load_skills_config().keys())
lines = [f"🌐 技能广场 (共 {total} 个技能)", ""] lines = ["🌐 技能广场", ""]
for s in skills: for s in skills:
name = s.get("name", "") name = s.get("name", "")
display = s.get("display_name", "") or name display = s.get("display_name", "") or name