mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-10-25 17:36:29 +08:00
fix(dingtalk): cap remote download time and keep URL query out of temp file names
Co-authored-by: cowagent <cow@cowagent.ai>
This commit is contained in:
1 parent
0198418729
commit
353eb7b95a
3 files changed
+27
-5
No files matched your search
@@ -10,6 +10,8 @@ import json
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from urllib.parse import unquote, urlparse
|
||||
|
||||
import requests
|
||||
|
||||
import dingtalk_stream
|
||||
@@ -21,6 +23,7 @@ from dingtalk_stream.card_replier import CardReplier
|
||||
from bridge.context import Context, ContextType
|
||||
from bridge.reply import Reply, ReplyType
|
||||
from channel.chat_channel import ChatChannel
|
||||
from channel.chat_message import safe_filename
|
||||
from common import state_dir
|
||||
from common.media_download import MAX_FILE_BYTES, MediaTooLargeError, download_to_file
|
||||
from channel.dingtalk.dingtalk_message import DingTalkMessage
|
||||
@@ -34,6 +37,8 @@ from common.singleton import singleton
|
||||
from common.time_check import time_checker
|
||||
from config import conf
|
||||
|
||||
_MAX_REMOTE_FILE_SECONDS = 300
|
||||
|
||||
|
||||
def _markdown_preview_title(markdown: str, limit: int = 30) -> str:
|
||||
"""Plain-text title for a webhook markdown message.
|
||||
@@ -447,17 +452,25 @@ class DingTalkChanel(ChatChannel, dingtalk_stream.ChatbotHandler):
|
||||
if file_path.startswith("http://") or file_path.startswith("https://"):
|
||||
try:
|
||||
import uuid
|
||||
file_name = os.path.basename(file_path) or f"media_{uuid.uuid4()}"
|
||||
# Query strings may carry tokens and characters Windows rejects
|
||||
# in file names; keep only the sanitized last path segment.
|
||||
file_name = (
|
||||
safe_filename(unquote(os.path.basename(urlparse(file_path).path)))
|
||||
or f"media_{uuid.uuid4()}"
|
||||
)
|
||||
temp_file = os.path.join(str(state_dir.tmp_dir()), file_name)
|
||||
try:
|
||||
download_to_file(file_path, temp_file, MAX_FILE_BYTES, timeout=(5, 60))
|
||||
download_to_file(
|
||||
file_path, temp_file, MAX_FILE_BYTES,
|
||||
timeout=(5, 60), max_seconds=_MAX_REMOTE_FILE_SECONDS,
|
||||
)
|
||||
except MediaTooLargeError:
|
||||
logger.error(f"[DingTalk] Downloaded file exceeds size limit: {file_path}")
|
||||
logger.error("[DingTalk] Remote file exceeds size limit, skipped upload")
|
||||
return None
|
||||
file_path = temp_file
|
||||
logger.info(f"[DingTalk] Downloaded file to {file_path}")
|
||||
except Exception as e:
|
||||
logger.error(f"[DingTalk] Error downloading file: {e}")
|
||||
logger.error(f"[DingTalk] Error downloading file: {type(e).__name__}")
|
||||
return None
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
|
||||
@@ -109,7 +109,7 @@ Thanks @liuns-yang (#3191)
|
||||
|
||||
本版本对外部资源访问和本地文件写入做了系统性加固:
|
||||
|
||||
- **下载限制**:通道收发媒体、终端图片、视觉识别、关键词文件等外部下载统一限制文件大小和总耗时,避免超大文件或慢速连接拖垮服务;视觉识别下载图片时对每次重定向重新校验地址。Thanks @rudycelekli (#3255, #3256, #3257, #3264, #3266, #3267, #3268, #3269, #3270, #3271, #3272)、@c020627 (#3219, #3225, #3252, #3300)
|
||||
- **下载限制**:通道收发媒体、终端图片、视觉识别、关键词文件等外部下载统一限制文件大小和总耗时,避免超大文件或慢速连接拖垮服务;视觉识别下载图片时对每次重定向重新校验地址。Thanks @rudycelekli (#3255, #3256, #3257, #3264, #3266, #3267, #3268, #3269, #3270, #3271, #3272)、@c020627 (#3219, #3225, #3252, #3300, #3322)
|
||||
- **请求超时**:模型、语音、翻译等外部请求全部设置超时,上游无响应时不再卡住对话。Thanks @c020627 (#3217, #3220, #3240, #3254, #3275)
|
||||
- **配置安全写入**:`config.json`、插件配置、模型列表、定时任务、技能配置、凭证文件、`MEMORY.md` 等改为原子写入,写入中途失败不会损坏原文件;定时任务文件损坏时自动从备份恢复。Thanks @c020627 (#3223, #3239, #3241, #3242, #3259, #3273, #3274, #3276, #3279, #3280, #3281)、@rudycelekli (#3260)
|
||||
- **临时文件管理**:通道接收的文件名限制在临时目录内并限制长度(Thanks @Lesereingrape #3247, #3248、@c020627 #3212, #3301);下载的媒体、回复附件和语音合成文件统一存放在对应 Agent 的临时目录(Thanks @c020627 #3198, #3207, #3208, #3213)。
|
||||
|
||||
@@ -79,6 +79,15 @@ def test_http_url_over_size_limit_rejected(monkeypatch, tmp_path):
|
||||
assert _channel().upload_media("https://evil.example/huge.bin", "file") is None
|
||||
|
||||
|
||||
def test_http_url_query_string_stays_out_of_the_file_name(monkeypatch, tmp_path):
|
||||
get = FakeResp(content=b"<media data>")
|
||||
post = FakeResp(json_data={"errcode": 0, "media_id": "mid-3"})
|
||||
_stub(monkeypatch, get, post, tmp_path)
|
||||
url = "https://cdn.example/dir/%E6%8A%A5%E5%91%8A.pdf?token=secret&x=a/b"
|
||||
assert _channel().upload_media(url, "file") == "mid-3"
|
||||
assert [p.name for p in tmp_path.iterdir()] == ["报告.pdf"]
|
||||
|
||||
|
||||
def test_local_file_url_still_uploaded(monkeypatch, tmp_path):
|
||||
local = tmp_path / "clip.mp4"
|
||||
local.write_bytes(b"<media data>")
|
||||
|
||||
Reference in new issue
Block a user