From a821c4a3b4201135d541bb5f77be2c4d9c5dd151 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sat, 29 Aug 2026 10:38:48 +0800 Subject: [PATCH] fix(plugin): tolerate invalid legacy IDs in identity reads --- app/db/adapters/pluginidentity.py | 37 +++++++++++++++++++++---------- tests/test_plugin_identity.py | 15 +++++++++++++ 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/app/db/adapters/pluginidentity.py b/app/db/adapters/pluginidentity.py index d34ef9d1f..66bf3d1cd 100644 --- a/app/db/adapters/pluginidentity.py +++ b/app/db/adapters/pluginidentity.py @@ -29,6 +29,16 @@ def _parse_datetime(value: str | None) -> datetime | None: return datetime.fromisoformat(value) if value else None +def _normalize_plugin_id_for_read(plugin_id: str) -> str | None: + """读取历史安装清单时,将不属于身份合同的条目标记为无身份。""" + if not isinstance(plugin_id, str): + return None + try: + return normalize_physical_plugin_id(plugin_id) + except ValueError: + return None + + def _to_record(model: IdentityModel) -> PluginIdentity: """把持久化模型映射为已校验的应用身份。""" return PluginIdentity( @@ -134,26 +144,29 @@ class TransactionalPluginIdentityStore: self._session_factory = session_factory def get(self, plugin_id: str) -> PluginIdentity | None: - """在短会话内读取指定物理插件身份。""" + """在短会话内读取指定物理插件身份;不合规历史 ID 视为未建立身份。""" + normalized_id = _normalize_plugin_id_for_read(plugin_id) + if normalized_id is None: + return None session = self._session_factory() try: - return _SqlAlchemyIdentityRepository(session).get( - normalize_physical_plugin_id(plugin_id) - ) + return _SqlAlchemyIdentityRepository(session).get(normalized_id) finally: session.close() def list(self, plugin_ids: Sequence[str]) -> list[PluginIdentity]: - """在一个短会话内批量读取规范化插件身份。""" - normalized_ids = tuple( - dict.fromkeys( - normalize_physical_plugin_id(plugin_id) - for plugin_id in plugin_ids - ) - ) + """批量读取规范化插件身份,并忽略不合规的历史安装清单项。""" + normalized_ids: list[str] = [] + seen: set[str] = set() + for plugin_id in plugin_ids: + normalized_id = _normalize_plugin_id_for_read(plugin_id) + if normalized_id is None or normalized_id in seen: + continue + seen.add(normalized_id) + normalized_ids.append(normalized_id) session = self._session_factory() try: - return _SqlAlchemyIdentityRepository(session).list(normalized_ids) + return _SqlAlchemyIdentityRepository(session).list(tuple(normalized_ids)) finally: session.close() diff --git a/tests/test_plugin_identity.py b/tests/test_plugin_identity.py index cbe2a7b8d..29af0eca6 100644 --- a/tests/test_plugin_identity.py +++ b/tests/test_plugin_identity.py @@ -114,6 +114,21 @@ def test_plugin_identity_store_normalizes_reads_and_rejects_case_duplicate( ) +def test_plugin_identity_store_ignores_invalid_legacy_ids_on_read( + identity_store, +) -> None: + """历史安装清单含不合规插件 ID 时,身份读取不得阻断插件列表。""" + created = identity_store.compare_and_set( + _identity("DemoPlugin"), + expected_revision=None, + ) + + assert identity_store.list( + ["legacy-plugin", "115Cloud", "DemoPlugin", "demoplugin"] + ) == [created] + assert identity_store.get("legacy-plugin") is None + + def test_plugin_identity_store_rejects_stale_revision(identity_store) -> None: """旧安装事务不能覆盖已经提交的新身份。""" original = identity_store.compare_and_set(