From 37d6c36e2c3129b49a9c6ce6c0fc16865bd9190e Mon Sep 17 00:00:00 2001 From: liuwei Date: Thu, 23 Apr 2026 13:50:02 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=AD=E6=96=87=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=90=8D=E4=B8=8A=E4=BC=A0wav=E8=AF=AF=E5=88=A4?= =?UTF-8?q?=E4=B8=BA=E4=B8=8D=E6=94=AF=E6=8C=81=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 上传扩展名校验改为基于原始文件名提取,避免secure_filename清洗后丢失点号导致误判。 2. 重建安全文件名时分离basename和扩展名,兼容中文文件名与无效basename场景。 3. 继续保留媒体类型白名单校验,不放宽安全边界。 --- .../dashboard/blueprints/fun_command_rules.py | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/admin/dashboard/blueprints/fun_command_rules.py b/admin/dashboard/blueprints/fun_command_rules.py index 9cfa0be..5170884 100644 --- a/admin/dashboard/blueprints/fun_command_rules.py +++ b/admin/dashboard/blueprints/fun_command_rules.py @@ -81,12 +81,11 @@ def _validate_payload(payload: Dict[str, Any], service) -> str: return "" -def _allowed_file(filename: str, media_type: str) -> bool: - """判断上传文件扩展名是否允许。""" - if "." not in filename: - return False - ext = filename.rsplit(".", 1)[1].lower() - return ext in ALLOWED_EXTENSIONS.get(media_type, set()) +def _extract_file_ext(filename: str) -> str: + """从原始文件名提取扩展名(小写,不含点)。""" + if "." not in str(filename or ""): + return "" + return str(filename).rsplit(".", 1)[1].lower().strip() @fun_command_rules_bp.route("/") @@ -115,9 +114,19 @@ def api_upload_media(): if not upload_file or not upload_file.filename: return jsonify({"success": False, "message": "文件名为空"}), 400 - filename = secure_filename(upload_file.filename) - if not _allowed_file(filename, media_type): - return jsonify({"success": False, "message": f"不支持的文件类型: {filename}"}), 400 + # 注意:secure_filename 对中文文件名会清洗成 “wav” 这类无点字符串, + # 若直接基于清洗后文件名做扩展名校验会误判。这里改为优先使用原始文件名提取扩展名。 + raw_filename = str(upload_file.filename or "").strip() + file_ext = _extract_file_ext(raw_filename) + if file_ext not in ALLOWED_EXTENSIONS.get(media_type, set()): + return jsonify({"success": False, "message": f"不支持的文件类型: {raw_filename}"}), 400 + + # 生成安全文件名:basename 走 secure_filename,扩展名使用原始提取值兜底。 + raw_basename = raw_filename.rsplit(".", 1)[0] if "." in raw_filename else raw_filename + safe_basename = secure_filename(raw_basename).strip() + if not safe_basename: + safe_basename = "upload_file" + filename = f"{safe_basename}.{file_ext}" # 按媒体类型分目录,便于后续清理和排查。 upload_root = os.path.join(current_app.root_path, "static", "uploads", "fun_command_rules", media_type)