From 6160a8025327112c507e49dd3f7f6c669220a105 Mon Sep 17 00:00:00 2001 From: liuhao1024 Date: Wed, 8 Jul 2026 06:21:33 +0800 Subject: [PATCH] fix(gateway/platforms): migrate all Weixin fallbacks to get_secret() for consistent profile-scoped resolution Per egilewski's security review, WEIXIN_BASE_URL and WEIXIN_CDN_BASE_URL were still resolved from process-global environment variables, leaving mixed-scope bypasses in multiplex mode. Changed files: - gateway/platforms/weixin.py: Added get_secret import, replaced os.getenv() with get_secret() for WEIXIN_ACCOUNT_ID, WEIXIN_TOKEN, WEIXIN_BASE_URL, WEIXIN_CDN_BASE_URL in WeixinAdapter.__init__() and send_weixin_direct() - tools/send_message_tool.py: Added get_secret import, replaced os.getenv() with get_secret() for all WEIXIN_* fallbacks in _handle_send() All runtime Weixin send paths now resolve both credentials and endpoint configuration from the same profile-scoped source. --- gateway/platforms/weixin.py | 17 +++++++++-------- tools/send_message_tool.py | 9 +++++---- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/gateway/platforms/weixin.py b/gateway/platforms/weixin.py index 980be1170..69ccc80cc 100644 --- a/gateway/platforms/weixin.py +++ b/gateway/platforms/weixin.py @@ -68,6 +68,7 @@ from gateway.platforms.base import ( ) from hermes_constants import get_hermes_home from utils import atomic_json_write +from agent.secret_scope import get_secret ILINK_BASE_URL = "https://ilinkai.weixin.qq.com" WEIXIN_CDN_BASE_URL = "https://novac2c.cdn.weixin.qq.com/c2c" @@ -1159,11 +1160,11 @@ class WeixinAdapter(BasePlatformAdapter): self._poll_task: Optional[asyncio.Task] = None self._dedup = MessageDeduplicator(ttl_seconds=MESSAGE_DEDUP_TTL_SECONDS) - self._account_id = str(extra.get("account_id") or os.getenv("WEIXIN_ACCOUNT_ID", "")).strip() - self._token = str(config.token or extra.get("token") or os.getenv("WEIXIN_TOKEN", "")).strip() - self._base_url = str(extra.get("base_url") or os.getenv("WEIXIN_BASE_URL", ILINK_BASE_URL)).strip().rstrip("/") + self._account_id = str(extra.get("account_id") or get_secret("WEIXIN_ACCOUNT_ID", "")).strip() + self._token = str(config.token or extra.get("token") or get_secret("WEIXIN_TOKEN", "")).strip() + self._base_url = str(extra.get("base_url") or get_secret("WEIXIN_BASE_URL", ILINK_BASE_URL)).strip().rstrip("/") self._cdn_base_url = str( - extra.get("cdn_base_url") or os.getenv("WEIXIN_CDN_BASE_URL", WEIXIN_CDN_BASE_URL) + extra.get("cdn_base_url") or get_secret("WEIXIN_CDN_BASE_URL", WEIXIN_CDN_BASE_URL) ).strip().rstrip("/") self._send_chunk_delay_seconds = float( extra.get("send_chunk_delay_seconds") or os.getenv("WEIXIN_SEND_CHUNK_DELAY_SECONDS", "1.5") @@ -2292,10 +2293,10 @@ async def send_weixin_direct( This bypasses the long-poll adapter lifecycle and uses the raw API directly. """ - account_id = str(extra.get("account_id") or os.getenv("WEIXIN_ACCOUNT_ID", "")).strip() - base_url = str(extra.get("base_url") or os.getenv("WEIXIN_BASE_URL", ILINK_BASE_URL)).strip().rstrip("/") - cdn_base_url = str(extra.get("cdn_base_url") or os.getenv("WEIXIN_CDN_BASE_URL", WEIXIN_CDN_BASE_URL)).strip().rstrip("/") - resolved_token = str(token or extra.get("token") or os.getenv("WEIXIN_TOKEN", "")).strip() + account_id = str(extra.get("account_id") or get_secret("WEIXIN_ACCOUNT_ID", "")).strip() + base_url = str(extra.get("base_url") or get_secret("WEIXIN_BASE_URL", ILINK_BASE_URL)).strip().rstrip("/") + cdn_base_url = str(extra.get("cdn_base_url") or get_secret("WEIXIN_CDN_BASE_URL", WEIXIN_CDN_BASE_URL)).strip().rstrip("/") + resolved_token = str(token or extra.get("token") or get_secret("WEIXIN_TOKEN", "")).strip() if not resolved_token: return {"error": "Weixin token missing. Configure WEIXIN_TOKEN or platforms.weixin.token."} if not account_id: diff --git a/tools/send_message_tool.py b/tools/send_message_tool.py index 24e8386ce..6cccee7ed 100644 --- a/tools/send_message_tool.py +++ b/tools/send_message_tool.py @@ -15,6 +15,7 @@ import time from email.utils import formatdate from agent.redact import redact_sensitive_text +from agent.secret_scope import get_secret logger = logging.getLogger(__name__) @@ -409,8 +410,8 @@ def _handle_send(args): # Weixin can be configured purely via .env; synthesize a pconfig so # send_message and cron delivery work without a gateway.yaml entry. if platform_name == "weixin": - wx_token = os.getenv("WEIXIN_TOKEN", "").strip() - wx_account = os.getenv("WEIXIN_ACCOUNT_ID", "").strip() + wx_token = get_secret("WEIXIN_TOKEN", "").strip() + wx_account = get_secret("WEIXIN_ACCOUNT_ID", "").strip() if wx_token and wx_account: from gateway.config import PlatformConfig pconfig = PlatformConfig( @@ -418,8 +419,8 @@ def _handle_send(args): token=wx_token, extra={ "account_id": wx_account, - "base_url": os.getenv("WEIXIN_BASE_URL", "").strip(), - "cdn_base_url": os.getenv("WEIXIN_CDN_BASE_URL", "").strip(), + "base_url": get_secret("WEIXIN_BASE_URL", "").strip(), + "cdn_base_url": get_secret("WEIXIN_CDN_BASE_URL", "").strip(), }, ) else: