From 98507ae4fe1a3adb43676140f7ddd2b9e866471f Mon Sep 17 00:00:00 2001 From: santhreal <64453045+santhreal@users.noreply.github.com> Date: Sun, 2 Aug 2026 20:19:16 -0700 Subject: [PATCH] fix(wecom): fallback to default filename when sanitize strips to empty --- nanobot/channels/wecom/runtime.py | 9 ++++----- nanobot/channels/wecom/tests/test_wecom_channel.py | 7 ++++++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/nanobot/channels/wecom/runtime.py b/nanobot/channels/wecom/runtime.py index 066de3b3a..fb82f5de5 100644 --- a/nanobot/channels/wecom/runtime.py +++ b/nanobot/channels/wecom/runtime.py @@ -30,12 +30,12 @@ WECOM_UPLOAD_MAX_BYTES = 1024 * 1024 * 200 # 200MB _SAFE_NAME_RE = re.compile(r"[^\w.\-()\[\]()【】\u4e00-\u9fff]+", re.UNICODE) -def _sanitize_filename(name: str) -> str: +def _sanitize_filename(name: str, fallback: str = "unnamed") -> str: """Sanitize filename to avoid traversal and problematic chars.""" name = (name or "").strip() name = Path(name).name name = _SAFE_NAME_RE.sub("_", name).strip("._ ") - return name + return name or fallback _IMAGE_EXTS = {".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"} @@ -399,9 +399,8 @@ class WecomChannel(BaseChannel): return None media_dir = get_media_dir("wecom") - if not filename: - filename = fname or f"{media_type}_{hash(file_url) % 100000}" - filename = _sanitize_filename(cast(str, filename)) + fallback_name = fname or f"{media_type}_{hash(file_url) % 100000}" + filename = _sanitize_filename(cast(str, filename or fallback_name), fallback=fallback_name) file_path = media_dir / filename await asyncio.to_thread(file_path.write_bytes, data) diff --git a/nanobot/channels/wecom/tests/test_wecom_channel.py b/nanobot/channels/wecom/tests/test_wecom_channel.py index f44ad81b3..0d3c50b5d 100644 --- a/nanobot/channels/wecom/tests/test_wecom_channel.py +++ b/nanobot/channels/wecom/tests/test_wecom_channel.py @@ -93,9 +93,14 @@ def test_sanitize_filename_keeps_chinese_chars() -> None: def test_sanitize_filename_empty_input() -> None: - assert _sanitize_filename("") == "" + assert _sanitize_filename("") == "unnamed" +def test_sanitize_filename_empty_or_dots_fallback() -> None: + assert _sanitize_filename("...") == "unnamed" + assert _sanitize_filename("..", fallback="fallback.txt") == "fallback.txt" + assert _sanitize_filename("") == "unnamed" + def test_guess_wecom_media_type_image() -> None: for ext in (".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"): assert _guess_wecom_media_type(f"photo{ext}") == "image"