Repository navigation
Commit 4e644f0
docs: a system-elevated hook or action body is refused identity-assignment writes (#26501)
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9D7NoqduWp2tw8aB42huhVy)_
Fixes #26466
Clause-②: no
Metadata-protocol: none
Skills-docs: follow-up owed — skills/objectstack-automation/SKILL.md
(the `runAs: 'system'` guidance at L164-191 never says these four writes
are refused; it also owes an eval prompt per the package's convention;
`domain:skills`, Tier H, triage 6434140653)
## 维护者速读
- 改了什么:两页作者文档各补一句——`hooks.mdx` 的 `runAs: 'system'` 行和 `actions.mdx`
的「动作内授权」段,写明系统提权的钩子/动作对岗位持有、权限集授予、邀请、成员四个对象的写入会被拒,并给出拒绝信息里点名的补救办法,链到
`system-context.mdx` 的 1b 行;删掉「full-access」这种过宽的说法。
- 为什么改:运行时(#25653
系列)和保存时(#26430,已合入)都已经拒绝这类写入,但这两页仍告诉作者「提权就是全权」,作者照做后只会在运行或保存时碰壁,且看不到该怎么改。
- 风险与回滚:纯文档,不动任何代码与发布包;回滚即撤回本 PR。
- 先看:`content/docs/automation/hooks.mdx`(`'system'`
表格行)、`content/docs/ui/actions.mdx`(Authorization inside an action 段首)。
- 两分钟验证:`pnpm check:doc-anchors`,预期 exit 0(两条新链接
`#1-authorization-and-scoping` 都能解析)。
- 需要你做的:无,席位落地。
## What changes
- `content/docs/automation/hooks.mdx`, the `runAs` table's `'system'`
row: drops "full-access" and adds one sentence. Elevation stops at
identity assignment. An insert, update, delete, transfer, restore or
purge of `sys_user_position`, `sys_user_permission_set`,
`sys_invitation` or `sys_member` from an app-authored hook's system
context (`runAs: 'system'`, an inherited system context, or
`ctx.api.sudo()`) is refused with `PERMISSION_DENIED`. A `runAs:
'system'` hook whose `language: 'js'` body writes one of them by name is
refused at save. The sentence gives the remedies the refusal names:
`runAs: 'user'` for positions and permission sets, so the
delegated-administration gate judges the user; the organization
endpoints for invitations and memberships. It links `system-context.mdx`
row 1b.
- `content/docs/ui/actions.mdx`, "Authorization inside an action": the
same sentence for an action body and a registered handler. A `type:
'script'` action whose body names such a write is refused at save. The
assignment remedy is the one the action refusal names: a flow whose
`runAs` is `'user'`, run by an action of `type: 'flow'`. The next
sentence now reads "The other consequence is…" so the
caller-specific-rule guidance still follows.
The pages describe the refusal and its remedy only. They name no way
around it.
## Why
Both pages told authors that system elevation bypasses every check.
Since #25653 the data middleware refuses these four writes from
app-authored system automation. Since #26430 (merged 2026-10-27T04:43Z)
`HookSchema` and `ActionSchema` also refuse a body that names one at
save. `system-context.mdx` row 1b already states the rule. These two
pages contradicted it and left out the remedy.
## Tests
Docs only: no behaviour changed, so no test and no ablation.
## Acceptance notes
Wording was read against `origin/main` `00a4593a`:
- Run-time refusal and remedy text:
`packages/plugins/plugin-security/src/flow-system-identity-write-refusal.ts`
(`SUBJECT`, `ASSIGNMENT_PRESCRIPTION`, `MEMBERSHIP_PRESCRIPTION`).
- Who carries the mark:
`packages/runtime/src/action-execution.ts#buildActionExecutionContext`
serves every handler an action door dispatches, a host code handler
included. That is why the actions sentence says "either".
- Save-time half:
`packages/spec/src/identity/identity-assignment-body-writes.ts`,
`hook.zod.ts#refuseElevatedIdentityAssignmentBodyWrite` (only `runAs:
'system'` + `language: 'js'`) and
`action.zod.ts#refuseIdentityAssignmentBodyWrite` (only `type: 'script'`
+ `language: 'js'`). The pages claim save-time refusal only for those
shapes.
Not done here, by the claim:
- `skills/objectstack-automation/SKILL.md`: see the `Skills-docs:` line.
- The card's two "Also carried" items: `FlowSchema` has no save-time
twin of the body check, and the four-object set and prescription text
are duplicated between the spec leaf and plugin-security. Both are
reported to the seat as out-of-scope findings and are not built here.
## Verification
Readings are at head `a5d2534c`.
<details><summary>Derived gate families</summary>
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`: 43 commands derived for the 2 changed paths
(16 changed lines).
- On the first pass, 42 exited 0. `check:skill-examples` exited 3
(PREREQUISITE NOT MET: the client-react dist was missing). After `pnpm
--filter '@objectstack/client-react...' build` under `os-verify-lock`
(VERDICT command-exit 0), it exited 0: "265 prose examples type-check
across 3 surface(s)".
- `dispatch-gates --ran` with every exit code recorded: "43 derived, 43
run, 0 NOT-MEASURED, 0 UNRUN" (a DERIVED zero).
</details>
<details><summary>Card acceptance</summary>
- `pnpm check:doc-authoring`: exit 0 (in the derived set).
- docs-audit: `pnpm check:docs-audit-scope` and `pnpm
check:docs-transcript-drift`: exit 0 (in the derived set).
- Doc links: `pnpm check:doc-anchors`: exit 0. Negative control: after
the `actions.mdx` link fragment was changed to
`#1-authorization-and-scopingX`, it exited 1 and named
`content/docs/ui/actions.mdx:455` ("renders no heading with id"). The
file was restored with `git checkout HEAD --`, and `git diff HEAD` is
empty. The lychee lane (`check-links.yml`) is CI's run.
</details>
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(No schema touched.)
- [x] 2. Every name I added uses the casing for its kind. (No names
added.)
- [x] 3. No formatter ran; `pnpm lint` passes. (No code touched; the
repo-wide run is CI's.)
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset. (Docs only,
nothing publishes: `skip-changeset`.)
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (Not touched.)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01ADHkmQoXzgoMtkwN9diP2g
---
_Generated by [Claude
Code](https://claude.ai/code/session_01ADHkmQoXzgoMtkwN9diP2g)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 2e5746d commit 4e644f0
2 files changed
Lines changed: 13 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
154 | 154 | | |
155 | 155 | | |
156 | 156 | | |
157 | | - | |
| 157 | + | |
158 | 158 | | |
159 | 159 | | |
160 | 160 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
442 | 442 | | |
443 | 443 | | |
444 | 444 | | |
445 | | - | |
446 | | - | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
447 | 457 | | |
448 | 458 | | |
449 | 459 | | |
| |||
0 commit comments