From d33a1296ffe62329c87e586e76d0c2cb15ab6e44 Mon Sep 17 00:00:00 2001 From: chenwei Date: Fri, 31 Jul 2026 01:02:07 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20ppclock-mcp=20--allowed-hosts=20?= =?UTF-8?q?=E7=A9=BA=E4=B8=B2=E6=8C=89=20None=20=E5=A4=84=E7=90=86?= =?UTF-8?q?=EF=BC=88=E7=A9=BA=E7=99=BD=E5=90=8D=E5=8D=95=E8=87=AA=E9=94=81?= =?UTF-8?q?=20localhost=20421=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 空串逗号拆分得空列表,TransportSecuritySettings(allowed_hosts=[]) 启用 DNS 重绑定防护后连 localhost 族一并 421。main() 解析层抽出 _parse_allowed_hosts,空列表以 or None 归一,保持 mcp 默认;含回归测试。 Co-Authored-By: Claude --- src/ppclock/mcp_server.py | 12 ++++++++++-- tests/test_mcp_server.py | 8 ++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/ppclock/mcp_server.py b/src/ppclock/mcp_server.py index 52f6493..8255170 100644 --- a/src/ppclock/mcp_server.py +++ b/src/ppclock/mcp_server.py @@ -34,6 +34,15 @@ def build_server(manager: DeviceManager, return mcp +def _parse_allowed_hosts(value: str | None) -> list[str] | None: + """解析 --allowed-hosts:None 原样返回;逗号拆分并去空白项。 + 空串(或拆分后全空)按 None 处理——空白名单会启用 DNS 重绑定防护 + 并连 localhost 族一并 421(空串自锁)。""" + if value is None: + return None + return [h.strip() for h in value.split(",") if h.strip()] or None + + def parse_args(argv=None) -> argparse.Namespace: p = argparse.ArgumentParser(prog="ppclock-mcp", description="ppclock 墨水屏 MCP 服务器") @@ -86,8 +95,7 @@ def main(argv=None) -> None: manager = DeviceManager(mac=args.mac, connect_timeout=args.connect_timeout, idle_timeout=args.idle_timeout) - allowed_hosts = (None if args.allowed_hosts is None else - [h.strip() for h in args.allowed_hosts.split(",") if h.strip()]) + allowed_hosts = _parse_allowed_hosts(args.allowed_hosts) mcp = build_server(manager, allowed_hosts=allowed_hosts) if args.transport == "stdio": try: diff --git a/tests/test_mcp_server.py b/tests/test_mcp_server.py index c672ebc..f4fe7c7 100644 --- a/tests/test_mcp_server.py +++ b/tests/test_mcp_server.py @@ -119,3 +119,11 @@ class TestAllowedHosts: def test_evil_host_421(self): r = self._initialize(self._make_app(), "evil.example.com") assert r.status_code == 421 + + def test_empty_string_allowed_hosts_to_none(self): + """回归:`--allowed-hosts ""` 空串自锁——逗号拆分结果为空列表时须按 + None 处理(保持 mcp 默认 localhost 族),否则空白名单启用防护后 + 连 localhost 请求也被 421。""" + from ppclock.mcp_server import _parse_allowed_hosts + a = parse_args(["--allowed-hosts", ""]) + assert _parse_allowed_hosts(a.allowed_hosts) is None