Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .changeset/high-privilege-wildcard-example-viewer-readonly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
'@objectstack/spec': patch
---

**`describeHighPrivilegeBits`'s wildcard example is a set that still has a wildcard (#6696).**

The JSDoc illustrated "a plain `'*'` wildcard grant is NOT high-privilege by
itself" with the platform's own `member_default`. After #5491 (PR #6684) that
set ships **no `'*'` entry at all** — the platform baseline narrowed to
explicit-allow by maintainer ruling — so the worked example named a set that no
longer has the shape being described, and a reader checking it would find
nothing and be left doubting the rule instead of the illustration.

The rule itself was never wrong and has not moved. `describeHighPrivilegeBits`
never reads `member_default`; it evaluates whatever set it is handed, and the
ADR-0090 D5 predicate is unchanged. Only the illustration is replaced, measured
against the shipped `defaultPermissionSets` array rather than transcribed:

- `viewer_readonly` carries `'*': { allowRead: true }` with every write, VAMA
and transfer/purge bit explicitly `false`, and no `systemPermissions`.
- `describeHighPrivilegeBits(viewer_readonly)` is `null`, and
`describeAnchorForbiddenBits(viewer_readonly, 'everyone')` is `null` — it is
genuinely anchor-bindable, which is the property the sentence claims.
- `describeAnchorForbiddenBits(viewer_readonly, 'guest')` refuses it *for the
wildcard*, so the replacement demonstrates both halves of the same sentence:
anchor-safe for `everyone`, wildcard-banned at the stricter GUEST tier.
- `member_default` returns `undefined` for `objects['*']`, confirming the
premise rather than assuming it.

The surrounding prose kept two things deliberately. The D5 shape widened from
"a read/create/edit-own baseline" to "a read — or read/create/edit-own —
baseline" because `viewer_readonly` is read-**only**: swapping the name without
widening the shape would have replaced one false illustration with another. And
the #2753 history — why the former blanket wildcard rejection was wrong — stays,
now reading "the then-wildcard-carrying default baseline" so the tense is
unambiguous next to the new `#5491` clause.

Documentation only — no implementation changed, no schema's accepted key set
moved, and this file is not a `.zod.ts`, so it reaches no page under
`content/docs/references/`. It is versioned because the comment ships to
consumers in the package's `.d.ts` (`dist/security/index.d.ts`), which is where
an editor hover reads it.
12 changes: 7 additions & 5 deletions packages/spec/src/security/high-privilege.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ function coerceRecord(v: unknown): Record<string, unknown> | undefined {
* Offending bits — the ADR-0090 D5 list: any `systemPermissions`,
* View/Modify All Data (VAMA), or delete/purge/transfer on any object, plus
* bulk `export` (#3544).
* A plain `'*'` wildcard grant is NOT high-privilege by itself (D5 permits
* a read/create/edit-own baseline to cover all objects — the platform's own
* `member_default` is exactly that shape); the wildcard ban is the GUEST
* A plain `'*'` wildcard grant is NOT high-privilege by itself (D5 permits a
* read — or read/create/edit-own — baseline to cover all objects; the
* platform's own `viewer_readonly` is exactly that shape, and `member_default`
* has carried no wildcard since #5491); the wildcard ban is the GUEST
* tier's stricter rule (D9 "explicit objects only") — see
* {@link describeAnchorForbiddenBits}. Fixes #2753: the former blanket
* wildcard rejection made the default baseline unbindable to `everyone`,
* forcing it through the separate fallback channel D5 explicitly rejected.
* wildcard rejection made the then-wildcard-carrying default baseline
* unbindable to `everyone`, forcing it through the separate fallback channel
* D5 explicitly rejected.
*
* [#3544] `allowExport` joins the list because the export axis is an OPT-IN
* grant: making export deliberate at the permission-set layer accomplishes
Expand Down
Loading