Repository navigation
Commit 3299873
fix(rest, runtime)!: the /meta action doors and MCP list_actions serve an action entry as the object door projects it (ADR-0106) (#31104)
security#1 · os-tesla · session_01L9qibvynRbCtMndW8EHNGc
Clause-②: no (narrowing) — the `/meta` `action` reads (the list, the
item read, `/layers` with its `?layers=true` spelling, and `/published`,
on both transports) and MCP `list_actions` now answer a caller only the
action entries the `/meta` object door's projection serves that caller,
which ADR-0106 D1 ("a field the caller cannot read does not exist for
that caller, on any plane") already promises, so no stated contract is
narrowed; what narrows is the answer to a caller with a `readable:
false` field. Nothing is widened: no answer member, error code or status
is added; the new exports are the shared projection the doors ask.
Metadata-protocol: none
Skills-docs: no move — `skills/**` teaches no `/meta` action read;
`content/docs/ai/connect-mcp.mdx` and
`content/docs/deployment/environment-variables.mdx` state the
projection.
## 维护者速读
- **改了什么**:`/meta` 的 `action` 读取(列表、单项、`/layers` 及
`?layers=true`、`/published`,REST 与 runtime dispatcher 两端)与 MCP
`list_actions`,对每个独立动作条目按 `/meta` 对象门对该调用者的 ADR-0106
投影判定:参数绑定了调用者不可读字段的动作(按参数所指对象判定:有 `objectOverride` 取其目标,否则取动作的
`objectName`;含无所属对象的 global 动作)不再列出,单项读取的回答与不存在的名字逐字节相同;`list_actions` 对
`objectOverride` 参数的摘要改读目标对象的字段。
- **为什么改**:ADR-0106 D1 要求不可读字段在任何门都不存在;`/meta/object` 已隐去这类动作,但独立
`action` 条目的各个读取门与 MCP 的 global 动作仍未投影。
- **风险与回滚**:对带 `readable: false` 字段的调用者是收窄(标题带
`!`);动作所属对象的字段可见性无法评估时与对象门一样拒绝(`FIELD_VISIBILITY_UNRESOLVED` /
503)。无限制调用者、D4 豁免者(`manage_metadata`、system)、无 security
服务或关闭掩码的部署逐字节不变;执行门(MCP `run_action`、`POST /actions`)及其参数校验与拒绝文案不变。回滚即
revert 本 PR。
-
**先看**:`packages/rest/src/meta-item-read-gate.ts`(`projectMetaActionForCaller`
及列表 7a、单项 4a、分层 3a
三步)、`packages/runtime/src/domains/mcp-object-schema-mask.test.ts`(新增的最后一个
describe)。
- **两分钟验证**:`pnpm --filter @objectstack/runtime exec vitest run
src/domains/mcp-object-schema-mask.test.ts`,预期 `Tests 45 passed (45)`。
- **需要你做的**:无,席位落地。
## What changes
- `packages/rest/src/meta-item-read-gate.ts` —
`projectMetaActionForCaller(sources, action, ownerName)`: one action
entry as this caller is served it. The action is projected as its own
object's only `actions` entry through `projectMetaObjectSchema` over the
posture related to that document (the object door's
`maskDeniedFieldReferences` → `actionParamReadsDenied`, `objectOverride`
judged on the object it names), not a second rule. A `passthrough`
posture serves the entry with no object read. An action with no
own-object definition has only its `objectOverride` params judged, each
on the caller's posture for the object it names; a posture that cannot
be evaluated or is undetermined withholds it.
- The list chain (step 7a) leaves a withheld entry out. The item chain
(step 4a), the layered chain (step 3a, every present layer) and both
`/published` doors answer it as a name with nothing behind it.
`projectMetaActionItem` is the item-read entry the cached arm and the
published doors ask.
- `packages/rest/src/meta-item-read-routes.ts` — the cached item arm
(the default door for `action`) asks the same projection, and judges
`If-None-Match` after it, so a withheld entry is answered `404`, never
`304`.
- `packages/metadata-core/src/object-schema-fls.ts` —
`actionObjectOverrideTargets` and `actionServedWithoutOwnObject`, the
object door's own rule for an action with no own-object definition.
- `packages/runtime/src/domains/mcp.ts` — `servedActionObject` delegates
to `projectMetaActionForCaller`, so an object-less action is judged on
the objects its `objectOverride` params name; `listActions` hands
`summarizeAction` the served object of each override target.
- `packages/runtime/src/action-execution.ts` — `summarizeAction`,
`summarizeActionParams` and `resolveDeclaredActionParams` take an
optional object lookup and read an `objectOverride` param's field on the
object it names; a target the lookup does not resolve reads the own
object's field, as before. `enforceActionParams` passes no lookup, so
the run doors' validation and refusal text are unchanged.
- `packages/rest/src/rest-server.ts`,
`packages/runtime/src/domains/meta.ts` — each transport hands the item,
layered and published chains its `object` masker
(`resolveObjectMasker`).
- Docs: `connect-mcp.mdx` and `environment-variables.mdx` state the
projection. Changeset: a patch for `@objectstack/metadata-core`,
`@objectstack/rest` and `@objectstack/runtime`, the BREAKING banner and
the ADR-0087 `not-required` disposition.
## Tests
A new top-level `describe` in
`packages/runtime/src/domains/mcp-object-schema-mask.test.ts`: the real
`SecurityPlugin` on a real `ObjectQL` over a real `SqlDriver`, the
protocol over that engine, `RestServer`'s registered handlers (cached
arm, and a deployment with the cache off) and the runtime dispatcher,
plus the MCP bridge.
- A member denied a bound field (an own-object `field`, and a global
action's `objectOverride` field) is not listed the action, and its item,
`/layers`, `?layers=true` and `/published` reads answer byte-identical
to an unknown name, on every door. None of the field's label, help text
or the param's label appears in the list.
- The cached arm answers a member holding the staff's validator `404`,
and the staff still revalidates `304`.
- MCP `list_actions` withholds the global action; an `objectOverride`
param is summarised with the target field's label and type.
- Controls: a readable-field action, a field-less action and a readable
global override are served to the member as to the staff; an author
holding `manage_metadata` and the system context are answered
byte-identical to the same caller with no security service, on every
sub-door.
- Unit pins: `resolveDeclaredActionParams` with and without the lookup
(and `enforceActionParams` unchanged); `actionObjectOverrideTargets` and
`actionServedWithoutOwnObject`.
## Acceptance notes
- Ablating the projection (`projectMetaActionForCaller` returns `serve`
first) turns the 16 refused legs red (list, item, `/layers`,
`?layers=true`, `/published` per door, the `304` leg, MCP global, and
the two #31075 legs) while the 29 controls stay green. Ablating the
target-field lookup turns the two summary pins red.
- The `/meta` doors read an action's own object from its spec
`objectName`; `@objectstack/rest` cannot reach objectql's legacy
`object` rung, so an entry carrying only that key is judged as
object-less there.
## Verification
All on head `c97eb416db` (origin/main merged in), under the verify lock:
- `pnpm --filter @objectstack/metadata-core test` — Test Files 25 passed
(25), Tests 966 passed (966); `typecheck` exit 0.
- `pnpm --filter @objectstack/rest test` — Test Files 393 passed (393),
Tests 7486 passed, 332 skipped (7818); `typecheck` exit 0.
- `pnpm --filter @objectstack/runtime test` — Test Files 553 passed
(553), Tests 8909 passed, 19 skipped (8928); `typecheck` exit 0.
- `pnpm --filter @objectstack/runtime exec vitest run
src/domains/mcp-object-schema-mask.test.ts` — Tests 45 passed (45).
- Dogfood battery (`pnpm --filter '@objectstack/dogfood^...' build`,
then `pnpm --filter @objectstack/dogfood test`) — Test Files 434 passed,
3 skipped (437), Tests 3596 passed, 41 skipped (3637).
- `node scripts/pm/dispatch-gates.mjs --commands` — 93 derived gate
commands, all exit 0 (`check:skill-examples` and
`check:dual-build-cjs-loads` re-run after their build prerequisite);
`--ran` reconciles 93 derived, 93 run, 0 unrun.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L9qibvynRbCtMndW8EHNGc)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 0ba68b6 commit 3299873
15 files changed
Lines changed: 870 additions & 36 deletions
File tree
- .changeset
- content/docs
- ai
- deployment
- packages
- metadata-core/src
- rest/src
- runtime/src
- domains
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
401 | 401 | | |
402 | 402 | | |
403 | 403 | | |
404 | | - | |
| 404 | + | |
| 405 | + | |
405 | 406 | | |
406 | 407 | | |
407 | 408 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
339 | 339 | | |
340 | 340 | | |
341 | 341 | | |
342 | | - | |
| 342 | + | |
343 | 343 | | |
344 | 344 | | |
345 | 345 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
| 12 | + | |
11 | 13 | | |
12 | 14 | | |
13 | 15 | | |
| |||
428 | 430 | | |
429 | 431 | | |
430 | 432 | | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
696 | 696 | | |
697 | 697 | | |
698 | 698 | | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
699 | 732 | | |
700 | 733 | | |
701 | 734 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
179 | 179 | | |
180 | 180 | | |
181 | 181 | | |
| 182 | + | |
| 183 | + | |
182 | 184 | | |
183 | 185 | | |
184 | 186 | | |
| |||
187 | 189 | | |
188 | 190 | | |
189 | 191 | | |
| 192 | + | |
| 193 | + | |
190 | 194 | | |
191 | 195 | | |
192 | 196 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
170 | 170 | | |
171 | 171 | | |
172 | 172 | | |
173 | | - | |
| 173 | + | |
174 | 174 | | |
175 | 175 | | |
176 | 176 | | |
| |||
1094 | 1094 | | |
1095 | 1095 | | |
1096 | 1096 | | |
| 1097 | + | |
| 1098 | + | |
1097 | 1099 | | |
1098 | 1100 | | |
1099 | 1101 | | |
| |||
1246 | 1248 | | |
1247 | 1249 | | |
1248 | 1250 | | |
| 1251 | + | |
| 1252 | + | |
| 1253 | + | |
| 1254 | + | |
| 1255 | + | |
| 1256 | + | |
| 1257 | + | |
| 1258 | + | |
| 1259 | + | |
| 1260 | + | |
| 1261 | + | |
| 1262 | + | |
| 1263 | + | |
| 1264 | + | |
| 1265 | + | |
| 1266 | + | |
| 1267 | + | |
| 1268 | + | |
| 1269 | + | |
1249 | 1270 | | |
1250 | 1271 | | |
1251 | 1272 | | |
| |||
0 commit comments