重大重构:引擎模块拆分 + P0插件实现 + 55个Bug修复

核心变更:
- engine.py(1781行)拆分为8个独立模块: lifecycle/security/deps/
  datastore/pl_injector/watcher/signature/manager
- 新增plugin-bridge: 事件总线 + 服务注册 + RPC通信
- 新增i18n: 国际化/多语言翻译支持
- 新增plugin-storage: 插件键值/文件存储
- 新增ws-api: WebSocket实时通信(pub/sub + 自定义处理器)
- nodejs-adapter统一为Plugin ABC模式

Bug修复:
- 修复load_all()中store_dir未定义崩溃
- 修复DependencyResolver入度计算(拓扑排序)
- 修复PermissionError隐藏内置异常
- 修复CORS中间件头部未附加到响应
- 修复IntegrityChecker跳过__pycache__目录
- 修复版本号不一致(v2.0.0→v1.2.0)
- 修复测试文件的Logger导入/路径/私有方法调用
- 修复context.py缺少typing导入
- 修复config.py STORE_DIR默认路径(./mods→./store)

测试覆盖: 14→91个测试, 全部通过
This commit is contained in:
Falck
2026-05-12 11:40:06 +08:00
parent 3a096f59a9
commit bce27db4ac
57 changed files with 3669 additions and 2367 deletions

View File

@@ -78,16 +78,17 @@ class NBPFLoader:
try:
with zipfile.ZipFile(nbpf_path, 'r') as zf:
# 1. 外层验签
signer_name = self._verify_outer_signature(zf)
Log.info("NBPF", f"外层签名验证通过 (signer: {signer_name})")
# 1. 外层验签(先用包内公钥验签,再查信任状态)
signer_pub_key, is_trusted, trusted_name = self._verify_outer_signature(zf)
status = "已信任" if is_trusted else "未信任"
Log.info("NBPF", f"外层签名验证通过 (signer: {trusted_name or 'unknown'}, {status})")
# 2. 外层解密
key1, meta_inf = self._decrypt_outer(zf)
key1_buf = bytearray(key1)
# 3. 中层验签
rsa_signer = self._verify_inner_signature(zf, meta_inf)
# 3. 中层验签(传入外层签名者名称,确保内外签名者一致)
rsa_signer = self._verify_inner_signature(zf, meta_inf, trusted_name)
Log.info("NBPF", f"中层签名验证通过 (signer: {rsa_signer})")
# 4. 中层解密
@@ -115,14 +116,17 @@ class NBPFLoader:
instance, module = self._deserialize_and_exec(nir_data, name)
# 10. 构建插件信息
author_name = meta.get("author", trusted_name or "<unknown>")
info = {
"name": name,
"version": meta.get("version", ""),
"author": meta.get("author", ""),
"author": author_name,
"description": meta.get("description", ""),
"manifest": manifest,
"nbpf_path": str(nbpf_path),
"signer": signer_name,
"signer": trusted_name or author_name,
"signer_public_key": base64.b64encode(signer_pub_key).decode(),
"trusted": is_trusted,
}
Log.ok("NBPF", f"插件 '{name}' 加载成功")
@@ -137,11 +141,19 @@ class NBPFLoader:
# ── 外层验签 ──
def _verify_outer_signature(self, zf: zipfile.ZipFile) -> str:
"""外层 Ed25519 签名验证,返回签名者名称
def _verify_outer_signature(self, zf: zipfile.ZipFile) -> tuple[bytes, bool, str | None]:
"""外层 Ed25519 签名验证
先用包内公钥验签(不依赖外部信任列表),验签通过后再检查信任状态。
签名计算方式与 full_encrypt_package 一致:
SHA256(outer_encryption_json + sorted_module_names_and_ciphertexts)
Returns:
(signer_pub_key_bytes, is_trusted, trusted_name)
- signer_pub_key_bytes: 签名者 Ed25519 公钥(用于上层判断信任)
- is_trusted: 公钥是否在本地信任列表中
- trusted_name: 信任列表中的名称(不信任时为 None
"""
if NBPFFormatter.SIGNATURE not in zf.namelist():
raise NBPFLoadError("缺少外层签名文件")
@@ -151,16 +163,6 @@ class NBPFLoader:
signature_b64 = zf.read(NBPFFormatter.SIGNATURE).decode().strip()
signer_pub_key = zf.read(NBPFFormatter.SIGNER_PEM)
# 查找匹配的信任公钥
signer_name = None
for name, trusted_key in self.trusted_ed25519_keys.items():
if trusted_key == signer_pub_key:
signer_name = name
break
if signer_name is None:
raise NBPFLoadError("签名者公钥不在信任列表中")
# 计算包摘要(与 full_encrypt_package 一致)
encryption_data = json.loads(zf.read(NBPFFormatter.ENCRYPTION).decode("utf-8"))
digest = hashlib.sha256()
@@ -178,12 +180,21 @@ class NBPFLoader:
digest.update(mod_name.encode())
digest.update(nir_modules[mod_name]["ciphertext"].encode())
# 验签
# 直接用包内公钥验签(不依赖外部信任列表)
signature = base64.b64decode(signature_b64)
if not self.crypto.outer_verify(digest.digest(), signature, signer_pub_key):
raise NBPFLoadError("外层签名验证失败,包可能被篡改")
return signer_name
# 验签通过后,检查公钥是否在本地信任列表中
is_trusted = False
trusted_name = None
for name, trusted_key in self.trusted_ed25519_keys.items():
if trusted_key == signer_pub_key:
is_trusted = True
trusted_name = name
break
return signer_pub_key, is_trusted, trusted_name
# ── 外层解密 ──
@@ -207,11 +218,23 @@ class NBPFLoader:
# ── 中层验签 ──
def _verify_inner_signature(self, zf: zipfile.ZipFile, meta_inf: dict) -> str:
def _verify_inner_signature(self, zf: zipfile.ZipFile, meta_inf: dict, ed25519_signer: str = None) -> str:
"""中层 RSA-4096 签名验证,返回签名者名称
签名计算方式与 full_encrypt_package 一致
SHA256(sorted_module_names + inner_encrypted_ciphertexts)
签名计算方式与 full_encrypt_package 一致
如果传入了 ed25519_signer优先使用同名 RSA 密钥验签;
否则遍历所有信任的 RSA 密钥。
Args:
zf: 打开的 ZIP 文件
meta_inf: 解密后的 META-INF 数据
ed25519_signer: 外层 Ed25519 签名者名称
Returns:
RSA 签名者名称
Raises:
NBPFLoadError: 所有信任密钥均无法验证签名时抛出
"""
inner_sig_b64 = meta_inf.get("inner_signature")
if not inner_sig_b64:
@@ -232,7 +255,16 @@ class NBPFLoader:
# 查找匹配的 RSA 公钥
inner_sig = base64.b64decode(inner_sig_b64)
for name, rsa_pub_key in self.trusted_rsa_keys.items():
# 优先使用与外层签名者同名的 RSA 密钥
candidates: list[tuple[str, bytes]] = []
if ed25519_signer and ed25519_signer in self.trusted_rsa_keys:
candidates.append((ed25519_signer, self.trusted_rsa_keys[ed25519_signer]))
else:
# 未指定或未找到同名密钥,遍历全部
candidates = list(self.trusted_rsa_keys.items())
for name, rsa_pub_key in candidates:
if self.crypto.inner_verify(nir_digest.digest(), inner_sig, rsa_pub_key):
return name
@@ -334,7 +366,12 @@ class NBPFLoader:
return instance, main_module
def _build_safe_globals(self, plugin_name: str) -> dict:
"""构建安全的全局命名空间"""
"""构建安全的全局命名空间
注意Python 沙箱无法完全阻止通过 ()__class__.__bases__[0].__subclasses__()
等反射方式逃逸。本沙箱仅用于防止意外访问危险模块,真正的安全隔离
需要 OS 级容器化。
"""
safe_builtins = {
'True': True, 'False': False, 'None': None,
'dict': dict, 'list': list, 'str': str, 'int': int,
@@ -344,12 +381,11 @@ class NBPFLoader:
'sorted': sorted, 'reversed': reversed,
'min': min, 'max': max, 'sum': sum, 'abs': abs,
'round': round, 'isinstance': isinstance, 'issubclass': issubclass,
'type': type, 'id': id, 'hash': hash, 'repr': repr,
'print': print, 'object': object, 'property': property,
'id': id, 'hash': hash, 'repr': repr,
'print': print, 'property': property,
'staticmethod': staticmethod, 'classmethod': classmethod,
'super': super, 'iter': iter, 'next': next,
'any': any, 'all': all, 'callable': callable,
'hasattr': hasattr, 'getattr': getattr,
'ValueError': ValueError, 'TypeError': TypeError,
'KeyError': KeyError, 'IndexError': IndexError,
'Exception': Exception, 'BaseException': BaseException,