From f05d95b8ac38f7b279a71d2af49d9dc3be520b58 Mon Sep 17 00:00:00 2001 From: ymq Date: Wed, 16 Sep 2026 15:13:37 +0800 Subject: [PATCH] =?UTF-8?q?fix(project=5Ffile):=20path/project=5Fid?= =?UTF-8?q?=E5=BE=AA=E7=8E=AFunquote=E5=85=BC=E5=AE=B9=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E5=8F=8C=E9=87=8D=E7=BC=96=E7=A0=81=E2=80=94=E2=80=94bricks.tg?= =?UTF-8?q?et=E5=AF=B9=E5=B7=B2=E7=BC=96=E7=A0=81query=E5=80=BC=E5=86=8Den?= =?UTF-8?q?codeURIComponent=E4=B8=80=E6=AC=A1(=E5=8F=8C=E9=87=8D=E7=BC=96?= =?UTF-8?q?=E7=A0=81),=E6=9C=8D=E5=8A=A1=E7=AB=AF=E5=8F=AA=E8=A7=A3?= =?UTF-8?q?=E4=B8=80=E5=B1=82=E6=AE=8B=E7=95=99%XX=E7=BC=96=E7=A0=81?= =?UTF-8?q?=E6=80=81=E4=B8=AD=E6=96=87=E2=86=92=E9=A1=B9=E7=9B=AE=E5=90=8D?= =?UTF-8?q?=5Fident=5Fok=E6=A0=A1=E9=AA=8C=E5=A4=B1=E8=B4=A5=E8=AF=AF?= =?UTF-8?q?=E6=8A=A5'=E9=9D=9E=E6=B3=95=E8=B7=AF=E5=BE=84'(2026-09-16?= =?UTF-8?q?=E6=8A=95=E6=A0=87=E6=A0=87=E4=B9=A6md=E5=9C=A8=E7=BA=BF?= =?UTF-8?q?=E6=9F=A5=E7=9C=8B=E5=AE=9E=E6=B5=8B=E6=A0=B9=E5=9B=A0;?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E8=B5=B0window.open=E5=8D=95=E5=B1=82?= =?UTF-8?q?=E7=BC=96=E7=A0=81=E4=B8=8D=E5=8F=97=E5=BD=B1=E5=93=8D,?= =?UTF-8?q?=E6=95=85=E5=8F=AA=E6=9C=89=E5=9C=A8=E7=BA=BF=E6=9F=A5=E7=9C=8B?= =?UTF-8?q?=E4=B8=AD=E6=8B=9B)=E3=80=82=E5=AF=B9=E9=BD=90workspace=5Fview.?= =?UTF-8?q?dspy=E7=9A=84=5Fdecode=5Fid=E5=BE=AA=E7=8E=AF=E8=A7=A3=E7=A0=81?= =?UTF-8?q?=E5=88=B0=E6=97=A0%=E3=80=82=E5=85=B1=E4=BA=AB=E7=AB=AF?= =?UTF-8?q?=E7=82=B9:task=5Fio/todo=5Fdetail/workspace=E5=85=A8=E9=83=A8md?= =?UTF-8?q?=E5=9C=A8=E7=BA=BF=E6=9F=A5=E7=9C=8B=E4=B8=80=E5=B9=B6=E6=A0=B9?= =?UTF-8?q?=E6=B2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wwwroot/api/project_file.dspy | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/wwwroot/api/project_file.dspy b/wwwroot/api/project_file.dspy index 913a9c1..653eace 100644 --- a/wwwroot/api/project_file.dspy +++ b/wwwroot/api/project_file.dspy @@ -4,15 +4,32 @@ # 安全:仅登录用户;解析后校验文件真实路径必须落在该项目目录内(防路径穿越/跨项目越权)。 import os -from urllib.parse import quote +from urllib.parse import quote, unquote from aiohttp.web_fileresponse import FileResponse + +def _decode_path(s): + # 前端 bricks.tget 会对已编码的 query 值再 encodeURIComponent 一次(双重编码), + # 服务端框架只解一层 → 残留 %XX 编码态中文 → 项目名/文件名校验失败, + # 误报「非法路径」或「文件不存在」(2026-09-16 投标标书 md 在线查看实测根因; + # 下载按钮走 window.open 单层编码不受影响,故只有「在线查看」中招)。 + # 循环 unquote 到无 % 为止,兼容任意编码层数(对齐 workspace_view.dspy 的 _decode_id)。 + for _ in range(4): + if '%' not in s: + break + s2 = unquote(s) + if s2 == s: + break + s = s2 + return s + + uid = await get_user() if not uid: return {"widgettype": "Message", "options": {"title": "未登录", "message": "请先登录"}} -project_id = ((params_kw or {}).get('project_id') or '').strip() -fpath = ((params_kw or {}).get('path') or '').strip() +project_id = _decode_path(((params_kw or {}).get('project_id') or '').strip()) +fpath = _decode_path(((params_kw or {}).get('path') or '').strip()) download = ((params_kw or {}).get('download') or '').strip() if not project_id: