Skip to content

Commit 2edea4d

Browse files
committed
chore(rest): pin the new fixture's findOne and re-anchor the elevation census
Three gate-owned follow-ons to the refusal-envelope repair, none of them a behaviour change: - `check:engine-double-contract`: the new fixture's `findOne()` now opens with `assertEngineFindOnePredicate` — a fake looser than `ObjectQL.findOne` is how a dead REST route once shipped with a green suite — and the RETAINED ledger records the new pin so it protects this file from here on. - `check:system-context-census`: inserting the refusal arm and its helper moved every elevation read site below them in `rest-server.ts` (+88 above the route, +109 below it). Ten anchors in `content/docs/permissions/system-context.mdx` re-anchored by the gate's own `--fix`; pure line rot, no row added, removed or re-worded. Part of #15685 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N
1 parent ffb9b42 commit 2edea4d

3 files changed

Lines changed: 18 additions & 5 deletions

File tree

content/docs/permissions/system-context.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ not on any flag.
6464
## How the flag is set
6565

6666
`isSystem` is **server-constructed and never client-supplied**. Inbound HTTP
67-
cannot set it (`packages/rest/src/rest-server.ts:1651`, `:1680`), and neither
67+
cannot set it (`packages/rest/src/rest-server.ts:1739`, `:1768`), and neither
6868
can an action body (`packages/runtime/src/domains/actions.ts:414`). It is
6969
written by internal callers only, as an option on the engine call:
7070

@@ -103,7 +103,7 @@ that silently does not happen.
103103
| 14 | MCP stdio bridge skips the object API-exposure gate | mcp | Get: the bridge reaches objects whose `apiEnabled` / `apiMethods` would refuse an external caller | `stdio-data-bridge.ts:250` |
104104
| 15 | **Read-audit rows are not written** | plugin-audit | Lose: the "a person opened this record" trail. `sudo()` keeps the caller's `userId`, so this flag is the only thing separating a human read from a platform one | `read-audit.ts:556` |
105105
| 16 | Approval snapshot payload redaction skipped | plugin-approvals | Get: the whole snapshot on `find` / `findOne` — the audit/replay channel. Lose: field-visibility redaction over approval payloads | `payload-redaction-middleware.ts:115` |
106-
| 17 | REST anonymous-deny seam satisfied | rest | Get: `enforceAuth` passes with no `userId`. Not reachable from the wire — `isSystem` is never set on an inbound request | `rest-server.ts:1683` |
106+
| 17 | REST anonymous-deny seam satisfied | rest | Get: `enforceAuth` passes with no `userId`. Not reachable from the wire — `isSystem` is never set on an inbound request | `rest-server.ts:1771` |
107107

108108
### 2. Write pipeline and data integrity
109109

@@ -158,7 +158,7 @@ The largest single consumer — **17 of the 105 sites**.
158158
|:--|:---|:---|:---|:---|
159159
| 48 | Object API-exposure gate bypassed (`apiEnabled` / `apiMethods`) | runtime | Get: internal self-writes ignore exposure declarations — these govern **external** exposure, not engine self-writes | `action-execution.ts:138` |
160160
| 49 | Action `requiredPermissions` bypassed | runtime | Get: engine self-invocation runs any action | `action-execution.ts:401` |
161-
| 50 | `manage_metadata` bypassed on metadata writes | runtime, rest | Get: schema writes without the capability | `domains/meta.ts:471`, `:874`, `rest-server.ts:5231`, `:6657`, `:6905`, `:7336`, `:7529` |
161+
| 50 | `manage_metadata` bypassed on metadata writes | runtime, rest | Get: schema writes without the capability | `domains/meta.ts:471`, `:874`, `rest-server.ts:5319`, `:6766`, `:7014`, `:7445`, `:7638` |
162162
| 51 | The shared metadata-write verdict itself returns `allowed` | metadata-core | Get: the one function all of row 50's doors consult answers yes before any capability is examined | `meta-write-capability.ts:134` |
163163
| 52 | Anonymous-deny seam satisfied on the domain dispatchers and the package/federation routes | runtime, rest | Get: passes with no `userId` | `domains/actions.ts:421`, `domains/ai.ts:60`, `domains/automation.ts:989`, `domains/meta.ts:232`, `domains/security.ts:78`, `domains/packages.ts:535`, `external-datasource-routes.ts:302`, `package-routes.ts:97` |
164164
| 53 | MCP principal check satisfied | runtime | Get: MCP surface reachable with no user | `domains/mcp.ts:61` |
@@ -199,7 +199,7 @@ assuming `isSystem` covers it is a documented source of bugs.
199199
| "It preserves a supplied `updated_at` / `updated_by`" | **No.** That is `preserveAudit`, a separate opt-in — and an UPDATE-path exemption only | `field.zod.ts:1590` (#3493 / #6640) |
200200
| "It stamps `created_by`" | **No.** Audit stamping reads `userId` from the context. A user-less system write stamps nothing — that is today's behaviour, not an error | `runtime-identity.ts:280``281` |
201201
| "It bypasses every guard" | **No.** The last-admin guard applies to **every** context, `isSystem` included — the deprovision path that actually locks an org out is the system one | `last-admin-guard.ts:299` |
202-
| "A client can request it" | **No.** Never settable from inbound HTTP or from an action body | `rest-server.ts:1651`, `:1680`; `domains/actions.ts:414` |
202+
| "A client can request it" | **No.** Never settable from inbound HTTP or from an action body | `rest-server.ts:1739`, `:1768`; `domains/actions.ts:414` |
203203

204204
---
205205

packages/rest/src/rest-server-meta-references-refusal-envelope.test.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
// finding.
4141

4242
import { describe, it, expect } from 'vitest';
43+
import { assertEngineFindOnePredicate } from '@objectstack/metadata-core';
4344
import { INTERNAL_ERROR_MESSAGE } from '@objectstack/types';
4445
import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol';
4546
import { RestServer } from './rest-server.js';
@@ -83,7 +84,14 @@ function mockRes() {
8384
function emptyEngine(): any {
8485
return {
8586
find: async () => [],
86-
findOne: async () => null,
87+
async findOne(table: string, opts: { where: Record<string, unknown> }) {
88+
// The `check:engine-double-contract` pin: a fake looser than
89+
// `ObjectQL.findOne` is how a dead REST route once shipped with its
90+
// suite green. This double answers "no rows" — but it answers it to
91+
// the same dispatch predicate the real engine enforces.
92+
assertEngineFindOnePredicate(table, opts);
93+
return null;
94+
},
8795
count: async () => 0,
8896
aggregate: async () => [],
8997
registry: {

scripts/engine-double-contract.pinned.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3141,6 +3141,11 @@
31413141
"verb": "update",
31423142
"pinned": 1
31433143
},
3144+
{
3145+
"file": "packages/rest/src/rest-server-meta-references-refusal-envelope.test.ts",
3146+
"verb": "findOne",
3147+
"pinned": 1
3148+
},
31443149
{
31453150
"file": "packages/runtime/src/action-declarative-update.test.ts",
31463151
"verb": "update",

0 commit comments

Comments
 (0)