Skip to content

fix(plugin-sharing): 记录删除后 sys_share_link 令牌立即失效 —— resolve 存在性校验 + 删除级联 (#5190) - #5214

Merged
os-zhuang merged 3 commits into
mainfrom
claude/issue-5190-share-link-existence
Aug 4, 2026
Merged

fix(plugin-sharing): 记录删除后 sys_share_link 令牌立即失效 —— resolve 存在性校验 + 删除级联 (#5190)#5214
os-zhuang merged 3 commits into
mainfrom
claude/issue-5190-share-link-existence

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #5190

ShareLinkService.resolveToken 逐项检查 token、revoked_atexpires_at、audience、密码,唯独不问 (object_name, record_id) 指向的记录还在不在sys_share_link 也没有任何删除级联(#5103 的级联只覆盖 sys_record_share)。于是链接在记录删除后照常 resolve,并继续 stamp use_count / last_used_at

这比 #5103 修的孤儿更危险,原因是结构性的:共享行点名了它的受益人,而分享链接是无身份的能力令牌——持有 URL 即拥有权限。一旦记录 id 被复用(自定义主键、保留原 id 的导入、未来的 id 回收),一个早该随记录消失的链接会直接对新记录生效,而持有者可以是当初被分享过的任意外部人员。

两半同时落地,且第一半不依赖任何钩子跑过

1. resolveToken 重新追问记录是否存在,并走与 revoked / expired 完全相同的分支返回 null——不另立状态码、不另立错误,不让未授权持有者从端点里读出「那条记录被删了」这件事本身(否则每一个泄漏的令牌都成了该对象上的存在性 oracle)。探测位置在廉价的内存态门之后(revoked 的链接仍然不付任何查询代价)、在使用打点之前(死记录不再 stamp use_count / last_used_at)。探测抛错时 fail-closed:问不到 ≠ 放行。

2. 记录删除级联到 sys_share_link,走 #5103 已有的 seam 而不是另起一套:同一对全局 beforeDelete 行集暂存 + afterDelete 集合式撤销、同一条串行 sweep 队列、同一个 kernel:bootstrapped 孤儿清扫(keyset 分页、自报截断、每对象每页一次批量存在性探测、探测失败一行不删)。两半互相隔离——撤销 grant 时的驱动错误不会连带跳过令牌。

链接一侧的姿态判定读 publicSharing,它与 sharingModel 正交:最可能挂链接的对象恰恰是记录共享谓词跳过的那类平台对象。publicSharing 声明过就算数,即便当前 enabled: false——之前铸出的链接必须仍被清理。

孤儿链接行是删除而非 stamp revoked_at:它的主体已经没了,不存在还需要留撤销记录的「链接」,而且这张表本来只增不减(Setup 的链接列表还会展示指向不存在记录的行)。管理员主动撤销的链接,其审计行原样保留。

一致性方向

两条路径都拒绝在「问不到」的情况下行动,只是安全方向相反,这是刻意的:resolve 侧「问不到」必须拒绝授权,sweep 侧「问不到」必须一行不删(把瞬时驱动错误变成永久失访是不可接受的,没人能重新铸出一个别人已经持有的令牌)。

机制本身抽到 record-orphan-cleanup.ts 由两张表共用(chunk、扫描上限、失败探测规则各一份),避免出现第二份必须与之保持一致的 walk。#5103 的既有行为逐字未变。

验证

packages/spec/**packages/objectql/** 未改动一行;无 metadata / spec / API 形状变更。

  • pnpm --filter @objectstack/plugin-sharing test13 files / 347 tests passed
  • pnpm --filter @objectstack/plugin-sharing typecheck → clean
  • eslint --no-inline-config(8 个改动文件)→ exit 0
  • check:durability-log-level → ✓ 14 durability-critical catch seam(s), all loud or rethrowing
  • 变异检查(证明用例真的会咬):
    • 摘掉 resolveToken 的存在性检查 → 5 failed(含 THE REPRO、不可区分性、不 stamp、fail-closed)
    • 把级联的 links 半边关掉 → 15 failed(有界/无界删除、boot sweep、有规则对象三条)

已合入 origin/main(6 个提交,均不触及 plugin-sharing)后复跑,仍全绿。

🤖 Generated with Claude Code

https://claude.ai/code/session_015W6nhsDrz6zWQc8je12a1t


Generated by Claude Code

claude added 3 commits August 4, 2026 09:15
…除级联 (#5190)

resolveToken 逐项检查 token/revoked_at/expires_at/audience/密码,唯独不问
「(object_name, record_id) 指向的记录还在不在」;sys_share_link 也没有任何
删除级联(#5103 的级联只覆盖 sys_record_share)。分享链接是无身份的能力令牌,
持有 URL 即拥有权限,所以这类孤儿比 #5103 更危险:记录 id 一旦被复用,早该
随记录消失的链接会直接对新记录生效。

两半同时落地,且第一半不依赖任何钩子跑过:

1. resolveToken 增加记录存在性检查,走与 revoked/expired 完全相同的分支返回
   null(不区分、不另立错误码,避免把记录存在与否泄露给未授权持有者)。位置
   在内存态检查之后、use_count/last_used_at 打点之前,因此死记录不再被计数;
   探测抛错时 fail-closed(问不到 ≠ 放行)。
2. 记录删除级联到 sys_share_link,复用 #5103 已有的 seam:同一对全局
   beforeDelete 行集暂存 + afterDelete 按 id 集合撤销、同一条串行 sweep 队列、
   同一个 kernel:bootstrapped 孤儿清扫(keyset 分页、自报截断、每对象每页一次
   批量存在性探测、探测失败一行不删)。两半互相隔离,撤销 grant 失败不会连带
   跳过令牌。

链接一侧的姿态判定读 publicSharing(与 sharingModel 正交:最可能挂链接的对象
恰恰是记录共享谓词跳过的那类),且 publicSharing 声明过就算数——enabled 关掉
之前铸出的链接必须仍被清理。

机制本身抽到 record-orphan-cleanup.ts 由两张表共用,避免出现第二份必须与之
保持一致的 walk(chunk、上限、失败探测规则)。#5103 的既有行为逐字未变,其
305 条测试全绿即为证据。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015W6nhsDrz6zWQc8je12a1t
#5190 的链接级联用例全部跑在没有共享规则的对象上(contract / sys_report),
漏掉了唯一只在「携带最多共享机制」的对象上才存在的交互:一旦对象有规则,
#5102 的 bindRuleHooks 会以另一个 hook package 注册自己的 beforeDelete /
afterDelete,两个 package 读同一份 AFFECTED_ROWS_STASH_KEY 行集。只在自己是
唯一 beforeDelete 写入者时才成立的链接级联,能通过上面全部用例,却恰好在风险
最高的对象上继续泄漏令牌。

补三条:同一次删除里规则授权 + 手工共享 + 能力令牌三者一起消失;有界谓词删除
跨两个 hook package 仍只发一条集合式 sys_share_link 语句;两个 package 的绑定
互不干扰。

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

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 4, 2026 10:18am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/xl labels Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-sharing.

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

  • content/docs/kernel/services-checklist.mdx (via @objectstack/plugin-sharing)
  • content/docs/permissions/authorization.mdx (via packages/plugins/plugin-sharing)
  • content/docs/permissions/permissions-matrix.mdx (via packages/plugins/plugin-sharing)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-sharing)
  • content/docs/protocol/objectql/security.mdx (via packages/plugins/plugin-sharing)
  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-sharing)

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.

@os-zhuang
os-zhuang marked this pull request as ready for review August 4, 2026 10:20
@os-zhuang
os-zhuang enabled auto-merge August 4, 2026 10:20
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 4, 2026
Merged via the queue into main with commit 6e66cbe Aug 4, 2026
24 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-5190-share-link-existence branch August 4, 2026 10:29
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/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

记录删除后 sys_share_link 能力令牌仍然有效 —— 与 #5103 同族,但因为是无身份令牌所以更糟

2 participants