feat(spec): share-link enforcement 路径改收完整 ExecutionContext,窄类型只服务路由 401 (#6430) - #6511
Merged
Merged
Conversation
…6430) `IShareLinkService.createLink` / `revokeLink` / `listLinks` now declare their context parameter as the complete `ExecutionContext` envelope instead of the five-field `ShareLinkExecutionContext`. All three adjudicate access, so each needs the whole `resolveAuthzContext` result — `accessible_org_ids`, `org_user_ids`, `systemPermissions`, `posture`, `tabPermissions` included. `ShareLinkExecutionContext` is retained, unchanged in shape, as the route's own 401 vocabulary; its TSDoc now states the boundary and why tsc cannot enforce it (structural subtyping accepts a narrow object wherever the wide type is expected). Implements the maintainer ruling on #6206 (option A, 2026-08-07). Contract half only — the `@objectstack/plugin-sharing` consumer is the follow-up. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011M7UwH25Unfi73UHim7ajY
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 112 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
qq9340100
marked this pull request as ready for review
August 8, 2026 03:11
This was referenced Aug 8, 2026
This was referenced Aug 8, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #6430
#6206 维护者 2026-08-07 裁决 A 案(comment 5219847367)的契约半边。只动
packages/spec契约面,plugin-sharing消费半边一行未碰。改了什么
packages/spec/src/contracts/share-link-service.ts:三个 enforcement 方法改收完整
ExecutionContext——createLink/revokeLink/listLinks的 context 参数从五字段的ShareLinkExecutionContext换成ExecutionContext(z.input,即 caller 侧信封形状)。三个方法都在裁定访问:createLink—— [Finding-2] 的可见性重读(只能为自己看得见的记录建链接);revokeLink—— ADR-0111 D8 的 record share-manager 探针吃context;listLinks—— 列表本身在context下读,行可见性由它决定。所以三处都需要整份
resolveAuthzContext结果:accessible_org_ids、org_user_ids、systemPermissions、posture、tabPermissions。resolveToken不收 context,未动。窄类型保留、形状不变,但 TSDoc 把边界写死:
ShareLinkExecutionContext明确降级为路由自身 401 的词汇(userId在 ⇒ 放行,不在 ⇒ 401 —— 这个判断不读任何授权维度,所以不需要授权信封),并写明 ⛔ 不得进入任何 enforcement 路径,附上失效实据(group姿态下accessible_org_ids就是 Layer 0 墙,ADR-0105 D2,缺席即拒 ⇒ 建链全量 403)与治理理由(ADR-0095 D2:posture 解析一次、随上下文流动、永不在 enforcement 处重推;本例是同族第三处组装,前两处 HookContext 契约表把before*的input.options记成 DriverOptions —— 实测那里仍是调用方的 engine options(含 where),两个 break-glass 守卫正读它 #5997 / 两处手写的 ExecutionContext 组装已漂移:REST 传输不带principalKind/onBehalfOf,而 explain / security 会读它 #6071)。pin 测试
packages/spec/src/contracts/share-link-service.test.ts(3 cases + 7 条类型级断言)。分诊评论预期「今天能编译的 caller 之后编译不过」。实测不成立,方向要反过来读:
ShareLinkExecutionContext可以赋给ExecutionContext(五个字段在宽类型里都存在且类型兼容,而宽类型没有任何必填字段);方法参数又是双变的,所以ShareLinkService implements IShareLinkService也照样成立。pnpm --filter @objectstack/plugin-sharing typecheck在本分支上绿。也就是说,没有编译器在等着推消费半边一把 —— 那半边必须被有意识地做掉,不能指望 CI 变红来提醒。这条我已写进窄类型的 TSDoc 与 pin 测试的头注释,免得下一个读者把「没有
@ts-expect-error」误读成疏漏。契约能做的、也是本 PR 做到的,是把 enforcement 参数类型声明成完整信封,于是裁剪动作在调用点可见(调用点写着
ExecutionContext,评审能看见一个五字段对象被塞进去),而不再藏在一个「看起来就是为这活儿设计的」类型后面。反向验证(方向在跑之前先定,结果与预测一致)
预测:把三处签名改回
ShareLinkExecutionContext,测试层应当变红 —— 身份断言 TS2344 + 整信封字面量的 TS2353 excess-property。实测(tsc --noEmit -p tsconfig.test.json)恰好 7 条新错误(274 vs. debt 基线 267):前 6 条是三个方法的类型身份 pin(参数类型 是
ExecutionContext、不是窄类型);第 7 条是本文件真正的 before-red:整份信封写成对象字面量,excess-property 检查因此生效 —— 旧签名下accessible_org_ids等五个键每一个都是 TS2353,这正是当初路由宁可手工拼一个子集也不肯把已解析好的信封原样传下去的原因。三个运行时 case 不是空跑:fake service 记录收到的 context,断言
accessible_org_ids/posture/org_user_ids/systemPermissions/tabPermissions确实整份抵达。下游半边的解锁点
本卡落地后,identity 车道的消费半边(
Blocked-by: #6430)可以开工,落点两处:packages/plugins/plugin-sharing/src/sharing-plugin.ts的contextFromRequest(:633-639)—— 不再裁剪,把resolveAuthzContext结果整份传下去;packages/plugins/plugin-sharing/src/share-link-service.ts—— 实现类三个方法的参数类型跟着契约改成ExecutionContext(以及ShareLinkServiceOptions.canManageShares的 context 参数),engine.find处因此吃到完整信封。ShareLinkExecutionContext保留导出,share-link-routes.ts的contextFromRequest类型位仍可用它 —— 但按裁决,它到路由 401 为止。before-red 证据同时兑现分诊留下的 repro 义务(
group姿态建链 403 实测 → 修后 200);若 repro 不复现,按分诊 caveat 摘target:v17并回报。测试与闸门(实跑输出)
pnpm --filter @objectstack/spec typecheck—— 绿(含check:scripts-typecheck与check:test-typecheck:OK — test layer compiles; 58 file(s) / 267 error(s) held in test-typecheck-debt.json,债务未增)。pnpm --filter @objectstack/spec test——Test Files 340 passed (340) / Tests 8702 passed (8702)。pnpm --filter @objectstack/spec check:generated——✓ All 10 generated artifacts are up to date.(未增删导出,api-surface/contracts.json不动;contracts 无生成参考文档)。pnpm --filter @objectstack/plugin-sharing typecheck—— 绿(见上文,这是「不会红」的实据)。pnpm lint全仓绿;family gates 逐个实跑绿:adr-anchors/authz-resolver/role-word/org-identifier/doc-authoring/error-code-casing/route-envelope/query-options-erasure/slot-lookup/spec-parsed-alias/engine-double-contract/wildcard-fallthrough/meta-type-normalized。node scripts/check-nul-bytes.mjs——OK (scanned 6099 tracked text file(s));改动文件另做控制字节自扫,无命中。ADR-0122 的 pin 计数陷阱(
type-alias-convention.pin.test.ts:1491硬编码 751)不受影响:本次未新增任何 zod schema 或裸别名,check:spec-parsed-alias只扫*.zod.ts,已实跑绿。🤖 Generated with Claude Code
https://claude.ai/code/session_011M7UwH25Unfi73UHim7ajY
Generated by Claude Code