Repository navigation
Commit 2d04cd2
fix(runtime)!: an action that dispatches to a handler another declared action owns also answers with that owner's permission and activation gates (#30788)
Clause-②: no (narrowing) — the REST `POST
/api/v1/actions/:object/:action` door and the MCP `run_action` tool
refuse a `script` action whose dispatch reaches a handler that a
different declared action's own dispatch reaches, when the caller lacks
that action's `requiredPermissions` (403 `PERMISSION_DENIED`) or that
action is switched off (409 `ACTION_DISABLED`), and while the metadata
store cannot be read they refuse 503 `SERVICE_UNAVAILABLE` a call
reaching a handler built from another stored declaration; both doors
answered such a call with the handler's result before. Nothing is
widened, and no schema or authorable key moves.
Metadata-protocol: semantic
## 维护者速读
- 改了什么:动作分发命中的处理器若属于另一个已声明动作,REST `/actions` 与 MCP `run_action`
两个入口都会同时套用那个动作的
`requiredPermissions`(403)与启停开关(409),与被调用动作自身的门取并集,只收窄不放宽。
- 为什么改:加固规则——一个处理器只有一套门;任何声明经由它运行时,都要过拥有它的那个动作的权限与启停开关。
- 风险与回滚:只影响「两个声明共用一个处理器」的调用;仓内发运代码未见此形态,唯一命中的是一个测试夹具(已改为各用各的处理器键)。回滚即还原本
PR 的两个 runtime 源文件。
- 先看:`packages/runtime/src/action-execution.ts` 的
`actionHandlerOwnerRefusal`(归属判定与元数据面不可读分支);`packages/runtime/src/domains/actions.ts`
与 `invokeBusinessAction` 中的两处调用点。
- 两分钟验证:`pnpm --filter @objectstack/runtime exec vitest run
--maxWorkers=2 src/domains/action-handler-owner-gates.test.ts`,预期 17
条全绿。
- 需要你做的:无,席位落地。
## What changes
The hardening rule: **an action that dispatches to a handler another
declared action owns also answers with that owner's permission and
activation gates.**
- `actionHandlerOwnerRefusal`
(`packages/runtime/src/action-execution.ts`) is the one gate, called by
both doors right after their own ADR-0066 D4 capability gate and
ADR-0126 activation consult, ahead of the param contract, the
subject-record load and dispatch.
- Ownership is read at the handler **entry** the dispatch actually
reaches (`object:key`, one snapshot of `listRegisteredActions()`, in
`executeRegisteredAction`'s own rotation). A declared `script` action
owns the entry its own dispatch reaches. A declaration is one identity
(its owning object key and its name), so the same declaration seen
object-embedded and as a standalone copy is not a second owner, and
same-named actions on two objects stay independent while each object has
its own handler.
- Owner permissions are judged before owner switches, the doors' own
order. REST answers the refusal on its envelope; MCP throws it with
`code` and `status`.
- The owners are read from the engine's in-memory copies (registered
objects and standalone action items) and from the metadata plane's
listings. When the plane cannot be read, a handler a package or code
registered is still judged from the engine's copies, and an action's own
stored handler still runs; only a handler the authored-action binding
built for another stored declaration is refused 503.
- `content/docs/ui/actions.mdx` states the rule beside the existing
flow-target precedent (a `flow` action inherits its target flow's
`requiredPermissions`).
- One fixture in `actions-disabled-package.test.ts` declared two actions
with one handler key and different gates; the gated one now has its own
key, so the file pins what it pinned before.
## Tests
`packages/runtime/src/domains/action-handler-owner-gates.test.ts`,
organised per door:
- REST: 403 when the caller lacks the owner's `requiredPermissions`
(target-bound owner, body owner registered under its name, object-less
owner under the global key, metadata-plane owner); 409 when the owner is
switched off, even for a caller holding its permissions; 503 when the
plane is unreadable and the reached handler was built for another plane
declaration. Every refusal asserts `status` + `code` and that the
owner's handler never ran.
- MCP `run_action`: the 403 and 409 legs, asserting the thrown `code` +
`status` and that the handler never ran.
- Controls: a plain action with its own handler; the owner invoked
directly (403 for a member, 200 with the permission); a caller holding
the owner's permissions (200, owner's handler runs once); one
declaration carried twice; a same-named action on another object; an
action's own plane-built handler, and a package handler, with the plane
unreadable.
## Acceptance notes
- The UI gate is unchanged: a button still reads only its own action's
`requiredPermissions`, so a button whose handler belongs to a gated
action can be shown to a user the server then refuses.
- Fixture authors who give two actions one handler key now get one gate
across both; the doc bullet says to give each action its own handler key
unless the two should share one gate.
## Verification
Head `8f94aaf619f381a499d8755bea6abd821f682a62` (branch merged with
`origin/main` at `06cccd32e1`).
- Premise: the new test file run against the base sources fails 6 of its
13 legs then present, every one a refusal leg, and passes with the
change.
- Targeted at head: 5 runtime files (the new file,
`action-activation-dispatch`, `actions-disabled-package`,
`dispatcher-unique-violation-wire-code`,
`sandbox-unreadable-rejection-door`) — 91/91. `pnpm --filter
@objectstack/runtime typecheck` exit 0; `pnpm check:type-check-debt`
exit 0 (0 raw errors above the ledger).
- Full `@objectstack/runtime` suite at `9b2ba60b94` (the head before a
type-only follow-up): 545/545 files, 8726 passed, 19 skipped.
- Ablation (`scripts/ablation-replace.mjs`, each restored blob-equal,
tree clean afterwards): owner-permission refusal → 6 legs red;
owner-switch refusal → both 409 legs red; unreadable-plane refusal → the
503 leg red; REST call site → 7 legs red; MCP call site → both MCP
refusal legs red.
- `node scripts/pm/dispatch-gates.mjs --ran`: 90 derived, 90 run, 0
NOT-MEASURED, all exit 0.
- Dogfood battery at head, closure built first, under the verify lock:
421 files passed, 3 skipped (424); 3513 tests passed, 36 skipped (3549);
exit 0. No dogfood file is touched by this change.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L6bhgVfjk2i8T2D4RmxsKg)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01L6bhgVfjk2i8T2D4RmxsKg
<!-- signed: security#1 · os-sales · session_01L6bhgVfjk2i8T2D4RmxsKg
-->
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 55cd927 commit 2d04cd2
6 files changed
Lines changed: 733 additions & 1 deletion
File tree
- .changeset
- content/docs/ui
- packages/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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
433 | 433 | | |
434 | 434 | | |
435 | 435 | | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
436 | 446 | | |
437 | 447 | | |
438 | 448 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4343 | 4343 | | |
4344 | 4344 | | |
4345 | 4345 | | |
| 4346 | + | |
| 4347 | + | |
| 4348 | + | |
| 4349 | + | |
| 4350 | + | |
| 4351 | + | |
| 4352 | + | |
| 4353 | + | |
| 4354 | + | |
| 4355 | + | |
4346 | 4356 | | |
4347 | 4357 | | |
4348 | 4358 | | |
| |||
4909 | 4919 | | |
4910 | 4920 | | |
4911 | 4921 | | |
| 4922 | + | |
| 4923 | + | |
| 4924 | + | |
| 4925 | + | |
| 4926 | + | |
| 4927 | + | |
| 4928 | + | |
| 4929 | + | |
| 4930 | + | |
| 4931 | + | |
| 4932 | + | |
| 4933 | + | |
| 4934 | + | |
| 4935 | + | |
| 4936 | + | |
| 4937 | + | |
| 4938 | + | |
| 4939 | + | |
| 4940 | + | |
| 4941 | + | |
| 4942 | + | |
| 4943 | + | |
| 4944 | + | |
| 4945 | + | |
| 4946 | + | |
| 4947 | + | |
| 4948 | + | |
| 4949 | + | |
| 4950 | + | |
| 4951 | + | |
| 4952 | + | |
| 4953 | + | |
| 4954 | + | |
| 4955 | + | |
| 4956 | + | |
| 4957 | + | |
| 4958 | + | |
| 4959 | + | |
| 4960 | + | |
| 4961 | + | |
| 4962 | + | |
| 4963 | + | |
| 4964 | + | |
| 4965 | + | |
| 4966 | + | |
| 4967 | + | |
| 4968 | + | |
| 4969 | + | |
| 4970 | + | |
| 4971 | + | |
| 4972 | + | |
| 4973 | + | |
| 4974 | + | |
| 4975 | + | |
| 4976 | + | |
| 4977 | + | |
| 4978 | + | |
| 4979 | + | |
| 4980 | + | |
| 4981 | + | |
| 4982 | + | |
| 4983 | + | |
| 4984 | + | |
| 4985 | + | |
| 4986 | + | |
| 4987 | + | |
| 4988 | + | |
| 4989 | + | |
| 4990 | + | |
| 4991 | + | |
| 4992 | + | |
| 4993 | + | |
| 4994 | + | |
| 4995 | + | |
| 4996 | + | |
| 4997 | + | |
| 4998 | + | |
| 4999 | + | |
| 5000 | + | |
| 5001 | + | |
| 5002 | + | |
| 5003 | + | |
| 5004 | + | |
| 5005 | + | |
| 5006 | + | |
| 5007 | + | |
| 5008 | + | |
| 5009 | + | |
| 5010 | + | |
| 5011 | + | |
| 5012 | + | |
| 5013 | + | |
| 5014 | + | |
| 5015 | + | |
| 5016 | + | |
| 5017 | + | |
| 5018 | + | |
| 5019 | + | |
| 5020 | + | |
| 5021 | + | |
| 5022 | + | |
| 5023 | + | |
| 5024 | + | |
| 5025 | + | |
| 5026 | + | |
| 5027 | + | |
| 5028 | + | |
| 5029 | + | |
| 5030 | + | |
| 5031 | + | |
| 5032 | + | |
| 5033 | + | |
| 5034 | + | |
| 5035 | + | |
| 5036 | + | |
| 5037 | + | |
| 5038 | + | |
| 5039 | + | |
| 5040 | + | |
| 5041 | + | |
| 5042 | + | |
| 5043 | + | |
| 5044 | + | |
| 5045 | + | |
| 5046 | + | |
| 5047 | + | |
| 5048 | + | |
| 5049 | + | |
| 5050 | + | |
| 5051 | + | |
| 5052 | + | |
| 5053 | + | |
| 5054 | + | |
| 5055 | + | |
| 5056 | + | |
| 5057 | + | |
| 5058 | + | |
| 5059 | + | |
| 5060 | + | |
| 5061 | + | |
| 5062 | + | |
| 5063 | + | |
| 5064 | + | |
| 5065 | + | |
| 5066 | + | |
| 5067 | + | |
| 5068 | + | |
| 5069 | + | |
| 5070 | + | |
| 5071 | + | |
| 5072 | + | |
| 5073 | + | |
| 5074 | + | |
| 5075 | + | |
| 5076 | + | |
| 5077 | + | |
| 5078 | + | |
| 5079 | + | |
| 5080 | + | |
| 5081 | + | |
| 5082 | + | |
| 5083 | + | |
| 5084 | + | |
| 5085 | + | |
| 5086 | + | |
| 5087 | + | |
| 5088 | + | |
| 5089 | + | |
| 5090 | + | |
| 5091 | + | |
| 5092 | + | |
| 5093 | + | |
| 5094 | + | |
| 5095 | + | |
| 5096 | + | |
| 5097 | + | |
| 5098 | + | |
| 5099 | + | |
| 5100 | + | |
| 5101 | + | |
| 5102 | + | |
| 5103 | + | |
| 5104 | + | |
| 5105 | + | |
| 5106 | + | |
| 5107 | + | |
| 5108 | + | |
| 5109 | + | |
| 5110 | + | |
| 5111 | + | |
| 5112 | + | |
| 5113 | + | |
| 5114 | + | |
| 5115 | + | |
| 5116 | + | |
| 5117 | + | |
| 5118 | + | |
| 5119 | + | |
| 5120 | + | |
| 5121 | + | |
| 5122 | + | |
| 5123 | + | |
| 5124 | + | |
| 5125 | + | |
| 5126 | + | |
| 5127 | + | |
| 5128 | + | |
| 5129 | + | |
| 5130 | + | |
| 5131 | + | |
| 5132 | + | |
| 5133 | + | |
| 5134 | + | |
| 5135 | + | |
| 5136 | + | |
| 5137 | + | |
| 5138 | + | |
| 5139 | + | |
| 5140 | + | |
| 5141 | + | |
| 5142 | + | |
| 5143 | + | |
| 5144 | + | |
| 5145 | + | |
| 5146 | + | |
| 5147 | + | |
| 5148 | + | |
| 5149 | + | |
| 5150 | + | |
| 5151 | + | |
| 5152 | + | |
| 5153 | + | |
| 5154 | + | |
| 5155 | + | |
| 5156 | + | |
| 5157 | + | |
| 5158 | + | |
| 5159 | + | |
| 5160 | + | |
| 5161 | + | |
| 5162 | + | |
| 5163 | + | |
| 5164 | + | |
| 5165 | + | |
| 5166 | + | |
| 5167 | + | |
| 5168 | + | |
| 5169 | + | |
| 5170 | + | |
| 5171 | + | |
| 5172 | + | |
| 5173 | + | |
| 5174 | + | |
| 5175 | + | |
| 5176 | + | |
| 5177 | + | |
| 5178 | + | |
| 5179 | + | |
| 5180 | + | |
| 5181 | + | |
| 5182 | + | |
| 5183 | + | |
| 5184 | + | |
| 5185 | + | |
| 5186 | + | |
| 5187 | + | |
| 5188 | + | |
| 5189 | + | |
4912 | 5190 | | |
4913 | 5191 | | |
4914 | 5192 | | |
| |||
0 commit comments