Skip to content

fix(security): revoke package permission rows on uninstall — no ghost grants (#2747) - #2760

Merged
os-zhuang merged 2 commits into
mainfrom
claude/loving-knuth-y1g4d3
Jul 10, 2026
Merged

fix(security): revoke package permission rows on uninstall — no ghost grants (#2747)#2760
os-zhuang merged 2 commits into
mainfrom
claude/loving-knuth-y1g4d3

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

摘要

修复 #2747:此前卸载包只删 sys_metadata 行和 sys_packages 持久化记录,包属数据面权限行原样保留——卸载后授权继续生效(ghost grants),违反 ADR-0090 D5 的 "uninstalling the package revokes it everywhere at once"。而且排查发现线上(hono/rest)栈的 DELETE /api/v1/packages/:id 只执行裸 DELETE FROM sys_packages,连元数据清理都没走。

metadata-protocol

  • deletePackage 新增卸载清理接缝(与发布物化器完全对镜):领域插件通过 registerUninstallCleanup(name, fn) 注册具名清理,每个清理拿到被卸载的 packageId 执行,结果上报在响应新增的 cleanups 数组——撤销失败(安全事件)对调用方可见,绝不静默。
  • deletePackage 顺带把包从内存 SchemaRegistry 卸载(best-effort),运行中的内核立即停止服务该包,不必等重启(dispatcher 原本单独做这步,rest 路由完全没做)。

plugin-security

  • 注册 security.package-permissions 清理:删除该包自有的 sys_permission_set 行(仅 managed_by:'package'package_id 匹配——环境自建与他包的行绝不触碰,ADR-0086 D4)、指向它们的 sys_position_permission_set / sys_user_permission_set 绑定(先删绑定,不留悬挂授权),以及该包的 sys_audience_binding_suggestion 行(重装后重新提示,D5)。
  • 顺带修复建议模块的引擎调用签名:find/deletecontext第二个参数里;此前尾随的 { context } 参数被忽略,删除以无主体上下文执行(D12 门禁对受治理表正确地 fail closed——这正是首次 e2e 里只删掉 1 行的根因)。

rest

  • DELETE /api/v1/packages/:id(不带 version)改走 protocol.deletePackage——一个卸载语义,不再是三种方言;响应携带 deletedCount + cleanups。带 ?version= 的删除保留原有窄语义(仅持久化注册表)。

测试与验证

  • 新增单测:cleanup-package-permissions.test.ts(4 用例:全量回收、provenance 保护、幂等、空参兜底)、durable-package.test.ts 新增 3 用例(清理钩子调用/失败上报/幂等替换)。
  • pnpm turbo test(metadata-protocol、plugin-security、rest 及全部下游)75 个任务全绿。
  • 真实环境端到端(fresh showcase):确认建议产生绑定 → DELETE /api/v1/packages/com.example.showcasecleanups: [{name: 'security.package-permissions', removed: 10}](8 权限集 + 1 绑定 + 1 建议),库中包属行清零,平台/环境自有权限集(4 个)原样保留。

Closes #2747

🤖 Generated with Claude Code

https://claude.ai/code/session_01DP13MuGwFWLcVxVtuA7rgq


Generated by Claude Code

… grants (#2747)

Uninstall previously removed only sys_metadata rows and the durable
sys_packages record; the package's data-plane permission rows survived,
so grants kept working after uninstall (ghost grants), violating
ADR-0090 D5's "uninstalling the package revokes it everywhere at once".

metadata-protocol:
- deletePackage gains an uninstall-cleanup seam (mirror of the publish
  materializer): registerUninstallCleanup(name, fn); outcomes reported
  on the response's new `cleanups` array — a failed revocation is
  visible, never silent
- deletePackage also unregisters the package from the in-memory
  SchemaRegistry (best-effort), so the running kernel stops serving it
  without waiting for a restart

plugin-security:
- registers the security.package-permissions cleanup: deletes the
  package's own sys_permission_set rows (managed_by 'package' +
  matching package_id only — env-authored/foreign rows never touched,
  ADR-0086 D4), their position/user bindings (bindings first), and the
  package's sys_audience_binding_suggestion rows
- fixes the engine-call signature in the suggestion module: find/delete
  read `context` from their second argument; the previous trailing
  { context } arg was ignored, so deletes ran principal-less (the D12
  gate correctly failed them closed on governed tables)

rest:
- DELETE /api/v1/packages/:id (no version pin) now goes through
  protocol.deletePackage — one uninstall semantic, not three dialects;
  response carries deletedCount + cleanups. Version-scoped deletes keep
  the narrow durable-registry semantics

Verified end-to-end on a fresh showcase: confirm a suggestion (binding
created) → DELETE the package → cleanups removed 10 rows (8 sets +
1 binding + 1 suggestion), zero package rows left, platform/env sets
untouched. 75 turbo test tasks green across affected packages.

Closes #2747

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP13MuGwFWLcVxVtuA7rgq
@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Canceled Canceled Jul 10, 2026 9:00am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Jul 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/metadata-protocol, @objectstack/plugin-security, @objectstack/rest.

14 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/api/error-catalog.mdx (via @objectstack/rest)
  • content/docs/api/error-handling-server.mdx (via @objectstack/rest)
  • content/docs/api/index.mdx (via @objectstack/rest)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/metadata-protocol)
  • content/docs/getting-started/cli.mdx (via @objectstack/plugin-security)
  • content/docs/permissions/access-recipes.mdx (via packages/plugins/plugin-security)
  • content/docs/permissions/authorization.mdx (via packages/plugins/plugin-security, packages/rest)
  • content/docs/permissions/permissions-matrix.mdx (via packages/plugins/plugin-security)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/plugin-security)
  • content/docs/plugins/index.mdx (via @objectstack/plugin-security, @objectstack/rest)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-security, @objectstack/rest)
  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-security, @objectstack/rest)
  • content/docs/ui/dashboards.mdx (via @objectstack/plugin-security)
  • content/docs/ui/role-based-interfaces.mdx (via packages/plugins/plugin-security)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…l-defined (#2747)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DP13MuGwFWLcVxVtuA7rgq
@os-zhuang
os-zhuang marked this pull request as ready for review July 10, 2026 09:24
@os-zhuang
os-zhuang merged commit 1056c5f into main Jul 10, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/loving-knuth-y1g4d3 branch July 10, 2026 09:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Package uninstall does not clean up data-plane permission rows by package_id (ADR-0086 D3 wiring gap)

2 participants