feat(rest,runtime): 元数据写入的其余三扇门同样要求 manage_metadata (#7019) - #7043
Conversation
#6603 只给 PUT /api/v1/meta/:type/:name 落了 manage_metadata 门,同一个写 操作还有三扇门没有门。本次补齐,各自照抄所在文件的既有先例,不引入新写法: - PUT /api/v1/meta/:type/:section/:name(复合名保存) - DELETE /api/v1/meta/:type/:name(重置为构件默认值) - 运行时 dispatcher 自己的 /meta PUT(同一操作的第二条传输) 三处门都落在解析 protocol 之前,使未授权调用方无法用 501-vs-200 指纹探测 内核能力,且拒绝时什么都没写、什么都没删。isSystem 照例旁路。 复合名那扇门的前后实测(同一往返、真实内存库): 加门前 PUT 200 / saveMetaItem 1 次 / 库中只剩 id,name 加门后 PUT 403 / saveMetaItem 0 次 / 库中四个字段完好 DELETE 那扇门是另一条理由:不往返、不掩码,只是把定制覆盖层整个丢掉。 连带修改 3 个既有测试文件的 boot 桩(补 manage_metadata):它们的桩把 「只要有 session 就能写元数据」当作既成前提,而这正是本次要消灭的东西。 被测机制、断言、期望值一字未动。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017uFVNMmTxLpmfQYiuKM1Yx
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 2 package(s): 24 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also reference the affected code. These are read-only:
|
Fixes #7019
从含 #7027 门的 main(
623d00897)开分支,三条肢体各自照抄所在文件的既有先例,不引入新写法。推送前已git merge origin/main(至08863dd18)。0. 先更正卡里/评论里的两处坐标 —— 都自己实测过
meta.ts:264:264是错的,真正的写在:152-159」5231054845在 PM 指名的那个 commit
91cefb849上(以及c3f491626、当时的 main),packages/runtime/src里唯一的method === 'PUT'就是domains/meta.ts:264;:152-159落在maskObjectSchemaList/handleMetadataRequest开头。结论的实质不变(dispatcher 确有一条无能力门的 PUT),错的只是那条更正。meta.ts既有manage_metadata门的坐标(两个互相矛盾的读数,卡里要求自己定位):落地前该文件共 602 行,门在:464-476(_migrate-stored)。:331-339与:460-476都不对。1. 三条肢体的 before / after
PUT /meta/:type/:section/:name(复合名)resolveProtocol后直落saveMetaItem,无任何能力检查resolveProtocol之前,403{error:{code:'FORBIDDEN'}}DELETE /meta/:type/:name?dropStorage=true还会拆掉对象物理表Resetting a metadata item requires ...PUT(runtime/src/domains/meta.ts)if (method === 'PUT' && body)直接resolveService('protocol')→saveMetaItemdeps.error(msg, 403)两个包的 403 code 不同,是刻意的:REST 侧照抄
_migrate-stored与 #7027,写死FORBIDDEN;dispatcher 侧照抄同文件的_migrate-stored门,走deps.error(msg, 403),code 由standardErrorCodeForHttpStatus(403)派生为PERMISSION_DENIED。统一两种拼写是契约变更,不属于本卡。2. 真红:预测先写后跑
动手前写下的预测(方向 RED),与实测对照:
http-dispatcher.test.ts约 8 例转红rest-5xx-message-sanitization.test.ts的mountRest补了manage_metadata)http-dispatcher.test.ts一个文件受连带预测错的那一处,如实记下:我用
grep "'PUT'"扫测试文件来估连带面,漏掉了两个经dispatcher.dispatch(...)(而非handleMetadata)驱动的文件 ——http-dispatcher.error-leak.test.ts(4 例)与domains/error-passthrough.test.ts(2 例)。合计连带 14 例 / 3 文件,不是预测的 8 例 / 1 文件。反向验证(删门重跑),方向与预测一致
packages/rest新钉:18 例中 10 例转红packages/runtime新钉:9 例中 5 例转红没转红的那些是护栏而非证据,按要求明说:
isSystem旁路、持manage_metadata放行、匿名 401、读路径不受影响 —— 这几例加门前后都绿。缺陷本身的前后实测(不是只看状态码)
复现 PM 量到的那条链路(真实路由 + 真实内存库,受限调用方
{userId:'u_portal', systemPermissions:[]}):3. 钉的是「没进去」,不只是状态码
每条肢体都断言
status+code(ADR-0112 信封) 且目标函数一次没进(spy),并直接读库:saveMetaItem零调用 + 库中四字段完好 +label未变;DELETE:deleteMetaItem零调用 + 覆盖层仍在,?dropStorage=true变体单独一例 ——「先删了再 403」正是最值得防的失败形态,只断状态码看不见;saveMetaItem零调用 + 库不变;另钉MetadataService回退分支(门必须在分支之前,否则第二个 writer 仍无门)与复合名形式。这些路由是发送而非抛出,
toThrow形状的断言在这里分不清「拒绝但信封错」与「根本没拒绝」。4. 连带修改的 3 个既有测试文件 —— 为什么是必然连带
与 #7027 第 5.1 节同一类:这些用例的 boot 桩把「只要有 session 就能写元数据」当作既成前提,而这正是本 PR 要消灭的东西。门一落,请求在到达被测机制之前就被 403 拦住。
http-dispatcher.test.tsMETADATA_AUTHOR()(与该文件既有AUTHED_CALLER同款,#5519 立的先例),只替换驱动 PUT 的 8 处;GET 用例一处未动http-dispatcher.error-leak.test.tsmakeDispatcher里覆写timedResolveExecutionContextdomains/error-passthrough.test.tsputMetaViaMetadataService里同上后两个文件走
dispatch(),而dispatch()会重新解析执行上下文(覆盖调用方传入的),这两个桩没有 objectql,解析出的调用方不持任何能力 —— 实测确认传入systemPermissions无效。覆写timedResolveExecutionContext是这个类在 dispatcher 侧的写法,对应 REST 套件用的(rest as any).resolveExecCtx = ...。被测机制、断言、期望值一字未动。5. 改了什么
packages/rest/src/rest-server.ts—— 复合名PUT+DELETE两处门packages/runtime/src/domains/meta.ts—— dispatcherPUT的门packages/rest/src/meta-compound-save-and-reset-capability-gate.test.ts—— 新建,18 例packages/runtime/src/domains/meta-save-capability-gate.test.ts—— 新建,9 例packages/rest/src/rest-route-ledger.ts—— 两行补note,记法与 feat(rest): PUT /meta/:type/:name 要求 manage_metadata 能力 (#6603) #7027 一致.changeset/meta-write-doors-manage-metadata-gate.md——minor× 2 包6. 本地验证
pnpm --filter @objectstack/rest test→ 76 文件 / 1187 例全绿pnpm --filter @objectstack/runtime test→ 116 文件 / 1753 例全绿(合并 main 后复跑)pnpm --filter @objectstack/rest --filter @objectstack/runtime typecheck→ 通过check:route-envelope/check:error-code-casing/check:empty-changeset/check:authz-resolver/check:adr-anchors/check:meta-type-normalized/check:nul-bytes→ 全部 PASSeslint --no-inline-config→ 无输出7. 不属于本卡(未捎带)
三个相邻 handler 的错误信封三形状(
{error:{code,message}}/ 裸{error:'...'}/{error:'...',code:'...'}兄弟键)按派单要求一律未改 —— 我新加的 403 一律用正确的嵌套形状,但既有的 501 出口原样保留。#7020 记录的「门要求的能力集 ≠ ADR-0106 D4 掩码豁免集」仍然成立,本 PR 不替维护者选对齐方向。
Generated by Claude Code