@@ -1183,191 +1183,6 @@ def request_capability(self) -> RPCClient:
11831183 return self ._rpc_client
11841184
11851185
1186- # ─── sys.path 隔离 ────────────────────────────────────────
1187-
1188-
1189- def _isolate_sys_path (plugin_dirs : List [str ]) -> None :
1190- """清理 sys.path,限制 Runner 子进程只能访问标准库、SDK 和插件目录。
1191-
1192- 同时阻止插件代码直接导入主程序内部 ``src.*`` 模块,并清理可直接从
1193- ``sys.modules`` 摸到的高权限叶子模块,避免绕过 SDK / capability 边界。
1194- """
1195- from importlib import util as importlib_util
1196- from types import ModuleType
1197-
1198- import builtins
1199- import importlib
1200- import sysconfig
1201-
1202- # 保留: 标准库路径 + site-packages(含 SDK 和依赖)
1203- stdlib_paths = set ()
1204- for key in ("stdlib" , "platstdlib" , "purelib" , "platlib" ):
1205- if path := sysconfig .get_path (key ):
1206- stdlib_paths .add (os .path .normpath (path ))
1207-
1208- runtime_paths = set (stdlib_paths )
1209- if os .name == "nt" :
1210- # Windows 的部分平台扩展模块和依赖会通过 <prefix>/DLLs 暴露在 sys.path 中。
1211- for prefix in {sys .prefix , sys .exec_prefix , sys .base_prefix , sys .base_exec_prefix }:
1212- if prefix :
1213- runtime_paths .add (os .path .normpath (os .path .join (prefix , "DLLs" )))
1214-
1215- allowed = set ()
1216- for p in sys .path :
1217- norm = os .path .normpath (p )
1218- # 保留标准库和 site-packages
1219- if any (norm .startswith (runtime_path ) for runtime_path in runtime_paths ):
1220- allowed .add (p )
1221- # 保留 site-packages(第三方库 + SDK)
1222- if "site-packages" in norm or "dist-packages" in norm :
1223- allowed .add (p )
1224-
1225- # 添加插件目录
1226- plugin_dir_paths = [os .path .normpath (d ) for d in plugin_dirs ]
1227- for d in plugin_dir_paths :
1228- allowed .add (d )
1229-
1230- preserved_paths = [p for p in sys .path if p in allowed ]
1231- for extra_path in plugin_dir_paths :
1232- if extra_path not in preserved_paths :
1233- preserved_paths .append (extra_path )
1234- sys .path [:] = preserved_paths
1235-
1236- # 仅为旧版插件兼容层保留极小的 src.* 可见面:
1237- # - src.plugin_system.*: 通过 maibot_sdk.compat 导入钩子重定向
1238- # - src.common.logger: 仓库内仍有少量旧插件沿用该日志入口
1239- allowed_src_exact_modules = frozenset (
1240- {
1241- "src" ,
1242- "src.common" ,
1243- "src.common.logger" ,
1244- "src.common.logger_color_and_mapping" ,
1245- }
1246- )
1247- allowed_src_prefixes = ("src.plugin_system" ,)
1248- plugin_module_prefix = "_maibot_plugin_"
1249-
1250- def _is_allowed_src_module (fullname : str ) -> bool :
1251- """判断给定 src.* 模块是否在 Runner 允许列表中。"""
1252- if fullname in allowed_src_exact_modules :
1253- return True
1254- return any (fullname == prefix or fullname .startswith (f"{ prefix } ." ) for prefix in allowed_src_prefixes )
1255-
1256- def _resolve_requester_name (import_globals : Any = None ) -> str :
1257- """解析当前导入请求的发起模块名。"""
1258- if isinstance (import_globals , dict ):
1259- for key in ("__name__" , "__package__" ):
1260- value = import_globals .get (key )
1261- if isinstance (value , str ) and value :
1262- return value
1263-
1264- frame = inspect .currentframe ()
1265- try :
1266- current = frame .f_back if frame is not None else None
1267- while current is not None :
1268- module_name = current .f_globals .get ("__name__" , "" )
1269- if not isinstance (module_name , str ) or not module_name :
1270- current = current .f_back
1271- continue
1272- if module_name == __name__ or module_name .startswith ("importlib" ):
1273- current = current .f_back
1274- continue
1275- return module_name
1276- return ""
1277- finally :
1278- del frame
1279-
1280- def _is_plugin_import_request (import_globals : Any = None ) -> bool :
1281- """判断当前导入是否由插件模块直接发起。"""
1282- requester_name = _resolve_requester_name (import_globals )
1283- return requester_name .startswith (plugin_module_prefix )
1284-
1285- def _format_block_message (fullname : str ) -> str :
1286- """构造统一的拒绝导入错误信息。"""
1287- return (
1288- f"Runner 子进程不允许导入主程序模块: { fullname } 。"
1289- "请改用 maibot_sdk 或 src.plugin_system 兼容层提供的接口。"
1290- )
1291-
1292- def _iter_requested_src_modules (name : str , fromlist : Any ) -> List [str ]:
1293- """展开本次导入请求涉及的 src.* 模块名。"""
1294- requested_modules = [name ]
1295- if not name .startswith ("src" ) or not fromlist :
1296- return requested_modules
1297-
1298- for item in fromlist :
1299- if not isinstance (item , str ) or not item or item == "*" :
1300- continue
1301- requested_modules .append (f"{ name } .{ item } " )
1302- return requested_modules
1303-
1304- def _assert_plugin_import_allowed (name : str , import_globals : Any = None , fromlist : Any = ()) -> None :
1305- """在插件发起导入时校验目标 src.* 模块是否允许访问。"""
1306- if not _is_plugin_import_request (import_globals ):
1307- return
1308-
1309- for requested_module in _iter_requested_src_modules (name , fromlist ):
1310- if not requested_module .startswith ("src" ):
1311- continue
1312- if _is_allowed_src_module (requested_module ):
1313- continue
1314- raise ImportError (_format_block_message (requested_module ))
1315-
1316- def _detach_module_from_parent (fullname : str , module : ModuleType ) -> None :
1317- """从父模块上移除已清理模块的属性引用。"""
1318- parent_name , _ , child_name = fullname .rpartition ("." )
1319- if not parent_name or not child_name :
1320- return
1321-
1322- parent_module = sys .modules .get (parent_name )
1323- if parent_module is None :
1324- return
1325- if getattr (parent_module , child_name , None ) is module :
1326- with contextlib .suppress (AttributeError ):
1327- delattr (parent_module , child_name )
1328-
1329- # 仅清理已加载的叶子模块,保留包对象给 Runner 自己的延迟导入和相对导入使用。
1330- existing_src_modules = sorted (
1331- (
1332- (module_name , module )
1333- for module_name , module in list (sys .modules .items ())
1334- if module_name == "src" or module_name .startswith ("src." )
1335- ),
1336- key = lambda item : item [0 ].count ("." ),
1337- reverse = True ,
1338- )
1339- for module_name , module in existing_src_modules :
1340- if _is_allowed_src_module (module_name ) or hasattr (module , "__path__" ):
1341- continue
1342- _detach_module_from_parent (module_name , module )
1343- sys .modules .pop (module_name , None )
1344-
1345- # ``import`` 语句与 ``importlib.import_module`` 走的是不同入口,因此两边都需要兜底。
1346- builtins_module = cast (Any , builtins )
1347- original_import = getattr (builtins_module , "__maibot_runner_original_import__" , builtins .__import__ )
1348- builtins_module .__maibot_runner_original_import__ = original_import
1349-
1350- def _guarded_import (name : str , globals : Any = None , locals : Any = None , fromlist : Any = (), level : int = 0 ) -> Any :
1351- if level == 0 :
1352- _assert_plugin_import_allowed (name , import_globals = globals , fromlist = fromlist )
1353- return original_import (name , globals , locals , fromlist , level )
1354-
1355- cast (Any , _guarded_import ).__maibot_runner_plugin_import_guard__ = True
1356- builtins .__import__ = _guarded_import
1357-
1358- importlib_module = cast (Any , importlib )
1359- original_import_module = getattr (importlib_module , "__maibot_runner_original_import_module__" , importlib .import_module )
1360- importlib_module .__maibot_runner_original_import_module__ = original_import_module
1361-
1362- def _guarded_import_module (name : str , package : Optional [str ] = None ) -> Any :
1363- resolved_name = importlib_util .resolve_name (name , package ) if name .startswith ("." ) else name
1364- _assert_plugin_import_allowed (resolved_name )
1365- return original_import_module (name , package )
1366-
1367- cast (Any , _guarded_import_module ).__maibot_runner_plugin_import_guard__ = True
1368- importlib .import_module = _guarded_import_module
1369-
1370-
13711186# ─── 进程入口 ──────────────────────────────────────────────
13721187
13731188
@@ -1392,9 +1207,6 @@ async def _async_main() -> None:
13921207 logger .warning ("外部依赖插件版本映射格式非法,已回退为空映射" )
13931208 external_plugin_ids = {}
13941209
1395- # sys.path 隔离: 只保留标准库、SDK 包、插件目录
1396- _isolate_sys_path (plugin_dirs )
1397-
13981210 runner = PluginRunner (
13991211 host_address ,
14001212 session_token ,
0 commit comments