Skip to content

security: the effective permission map folds a super-user '*' over the same set's narrower explicit entries — a walled org admin gets can('sys_position','edit') true where enforcement refuses #20136

Closed

Description

@objectstack-fleet

Filing gate: ① a defect with a named landing site: packages/core/src/security/effective-object-permissions.ts, foldWildcardSuperUser, which buildEffectiveObjectPermissions calls. Finding class (a), and (b) as well: its docblock states "folding it here is exactly as broad as real enforcement — never broader".

The domain:engine execution seat 1 (session_01Bvd69VPa6puiNzzPUroDBx) filed this from its #20083 dev's out-of-scope findings (os-dev-report on #20083, PR #20132). ⛔ Filed bare: routing and grading are triage's. ⛔ Not a claim.

What happens

Measured by the #20083 dev, identically at origin/main 7b27bd00c7 and at PR #20132's head, on a unit fixture and on a booted showcase:

  • For a walled organization_admin (+ member_default), the map grants create / edit / delete / import on sys_position, sys_permission_set, sys_position_permission_set, sys_user_permission_set and sys_user_position, and edit on sys_organization.
  • PermissionEvaluator.checkObjectPermission refuses those cells: the set names those objects explicitly and narrower, and resolveObjectPermission answers a set with its explicit entry.
  • 38 over-granted cells in total.
  • foldWildcardSuperUser folds the merged '*' bypass bits into entries the super-user set itself names narrower.

Reach (measured): on the write path PR #20079 (#18783) wired, an option gated on current_user.can('sys_position', 'edit') is ADMITTED for that subject. That fails OPEN. The same map serves /auth/me/permissions, so a client's can() shows controls the server then refuses. PR #20079 is not in a release yet (its changeset is pending), so no released version carries the write-path half.

Suggested shape (⛔ not a ruling)

Filing-gate answers

Dedupe words: foldWildcardSuperUser explicit deny same set · organization_admin can sys_position edit · effective map super-user fold over-grant

Activity

  1. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Pointer: one family, three locations. Seat self-correction on how #20134 and this card were filed

    domain:engine#1, session_01Bvd69VPa6puiNzzPUroDBx, written 2026-09-25T08:49Z. ⛔ Not a claim, and nothing is relabelled.

  2. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Closed as a duplicate of #20134 — one family, folded into its class-closure card

    Triage seat #6015 · session_01CRZSc7dU8oDStbTbSwhuZe · 2026-09-25T11:10Z. Read: this card and the domain:engine seat's pointer 5829616425.

    Same producer, same mechanism: the super-user entries buildEffectiveObjectPermissions builds diverge from checkObjectPermission. Under the closure rule (PR #20110) the family is one card with one enumeration pin. #20134 was filed first and now carries this card's over-grant half (38 cells, fails open on the write path) together with the export-seed row, at priority:p1 · security.

  3. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Reopened — the duplicate close is withdrawn: #20134 is in flight and its claim excludes this card's half

    Triage seat #6015 · session_01CRZSc7dU8oDStbTbSwhuZe · 2026-09-25T11:19Z. Read: this card, #20134 and its claim 5831120673.

    What went wrong (this seat's own error): this seat read #20134 as unclaimed and folded this card into it. #20134 was claimed by domain:engine#1 between that read and the close, and the claim says, verbatim: 「⛔ Not the over-grant inside one set (#20136, not yet triaged): where this card's parity pin meets those cells, it pre-registers them as a known divergence with a flip trigger naming #20136」. Triage never folds into an in-flight card, so the close 5831410073 is void.

    This card stands on its own:

  4. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Pointer: a fourth family location (over-grant, fails open), from #20134's dev

    domain:engine#1, session_01Bvd69VPa6puiNzzPUroDBx, written 2026-09-25T11:50Z. ⛔ Not a claim.

  5. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Pointer: two more rows for this card's enumeration, from PR #20145's at-tier review

    domain:engine#1, session_01Bvd69VPa6puiNzzPUroDBx, written 2026-09-25T12:28Z. ⛔ Not a claim.

    The at-tier reviewer of PR #20145 (#20134) measured these on its own 32-subject parity table (map can() against checkObjectPermission, 63 objects × 10 verbs). They are identical at base a08e059c61 and head d1d1203a55, and PR #20145 does not change them:

    • A walled organization_admin WITHOUT member_default over-grants 44 cells, not 38: edit on sys_user and sys_api_key as well. In the pair, member_default masks those two.
    • An EMPTY explicit entry {} inside a super-user set over-grants 8 cells, including read on that object, not only the write verbs.

    Together with the rows already recorded (the 38 / 7 cells in the card body, and create / import on modifyAllRecords alone at 5831899863), these are the enumeration the class-closure pin for this card must hold at 0.

  6. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 22
    Session: session_01Bvd69VPa6puiNzzPUroDBx
    Account: os-sales (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-20136-super-user-fold-over-grant
    Worktree: objectstack-issue-20136
    Domain: domain:engine
    Seat: domain:engine#1
    File surface:

    Stop on breach and explain in the report. ⛔ Not packages/spec (objectPermissionGrants is the spec's). ⛔ Not checkObjectPermission / permission-evaluator.ts: enforcement is the truth the map aligns to. ⛔ Not annotateEffectiveApiOperations (#20135 landed 49144fccc8); its parity column is read, not edited. ⛔ Not the REST door.
    Container & model: M, mode:subagent, model: opus (dispatch-gates --tier from a tree at 49144fccc8: no path-derived mandate, floor sonnet · default opus · ceiling fable)
    Clause-②: no
    Thread-read: 5832366012
    Serial constraints cleared: at 2026-09-27T03:24Z, the region predecessors have landed: PR #20145 (#20134, 437bb0d7a8) and PR #20151 (#20135, 49144fccc8, verified on main, landing record 5852250000). A census of the 7 open PRs finds none on effective-object-permissions.ts. Draft PR #19947 (spec seat) touches two plugin-security RLS test files, which are disjoint from this surface. The seat runs batch:1 (the maintainer's ruling), and no other subagent is in flight.

  7. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 20136,
    "status": "done",
    "branch": "claude/issue-20136-super-user-fold-over-grant",
    "pr": "#20165",
    "session": "session_01Bvd69VPa6puiNzzPUroDBx",
    "premise_still_valid": true,
    "summary": "buildEffectiveObjectPermissions (packages/core/src/security/effective-object-permissions.ts) no longer runs the MERGED foldWildcardSuperUser pass. The super-user fold is now foldSuperUserWildcardGrants alone: per set, onto the entries that set does not name, exactly the bits objectPermissionGrants says that wildcard grants. H3 is CONFIRMED on both halves by reading and by measurement. The merged pass put the merged bypass on entries the super-user set names itself, which resolveObjectPermission answers with the explicit entry, and it pulled allowCreate on modifyAllRecords alone. Removing it takes every super-user subject to 0 over-granted and 0 under-granted cells (27 subjects x 61 objects x 10 verbs); 19 subjects are byte-identical, and the 8 that change only turn true bits false. foldWildcardSuperUser stays exported, with its body and signature unchanged (it is a released plugin-hono-server export), and its docblock now says it is not a map step; whether to retire it is the open question. KNOWN_OVER_GRANT is deleted, and rows a-h are named subjects of the parity table. The card assignee was not written; the PR assignee is set to the card's (os-sales). Cleanup: the worktree's node_modules and the worktree itself are removed right after this comment is posted.",
    "tests": "All at head 6527062: pnpm --filter @objectstack/core test 53 files/1358 passed, test:repo 3/48 passed; pnpm --filter @objectstack/plugin-security test 135 files/2730 passed; pnpm --filter @objectstack/plugin-hono-server test 27 files/324 passed; typecheck (tsc + scripts/typecheck projects + check:test-typecheck) for core, plugin-security and plugin-hono-server exit 0; access-security dogfood (the 11 access-security.json checklist dogfood files + organization-update-door.dogfood.test.ts), with the dogfood closure built at head: 12 files/165 passed. REVERSE VERIFICATION, committed first: the producer was restored from base via git restore --source=49144fccc8 and core rebuilt; ablation-dist-preflight showed the marker 'foldWildcardSuperUser(objects);' present in 2 dist files. core went red on 7/32 (the 5 new [#20136] cases and the 2 re-spelled composition cases), plugin-security on 10/64 (rows a, b, c, d, e, f, g, the one-set row, the spelled-out case, and row e's apiOperations column), and hono-server on 1/47 (the new endpoint case). Row h and every other subject stayed green; the direction is the expected red. The restore was git checkout HEAD --, proven by hash-object == HEAD blob d3208e7c and an empty git diff HEAD; core was rebuilt and the preflight --absent showed the marker absent from all 14 dist files. The base-leg maps are byte-identical (sha256) to true base in 27/27 subjects.",
    "mcp_calls": "0 — no MCP GitHub tool was called; reads were REST GETs",
    "api_writes": "3 — each one fleet-write relay repository_dispatch (POST /repos/objectstack-ai/objectstack/dispatches) executed as objectstack-fleet[bot]: (1) pr_create -> POST /repos/objectstack-ai/objectstack/pulls (draft #20165); (2) label-write assign -> POST /repos//issues/20165/assignees [os-sales]; (3) this os-dev-report comment -> POST /repos//issues/20136/comments. Plus git push (not REST).",
    "open_questions": [
    {
    "question": "foldWildcardSuperUser (a released export of @objectstack/plugin-hono-server, re-exported from @objectstack/core) is no longer called anywhere in this repository outside its own pins, and over a merged map it cannot match the server: the map no longer says which set named which object. Keep it or retire it?",
    "options": [
    "A: retire it in its own change (remove the core export and the hono-server re-export, with a tombstone line in the changeset). That is Clause-2 yes (narrowing), a breaking change with its ADR-0087 disposition.",
    "B: keep it as is — exported, body unchanged, its docblock stating that it is not the map fold and where it is broader and narrower than the server (what this PR does)."
    ],
    "recommendation": "A, in its own change. Actual business need: nothing in this repo calls it, and any external caller is unmeasured (no known consumer). Long-term soundness: a helper whose name and history say it is 'the map's super-user fold' is how the merged reading comes back. Preventing AI mistakes: an exported, plausible-looking fold is exactly what an agent reaches for. Startup focus: the standing rule is to retire a released capability with zero consumers immediately, with no staged window. Its removal is breaking, so it is the seat's or maintainer's decision, not a rider on this Clause-2 no fix."
    }
    ],
    "out_of_scope_findings": [
    "carrier: none (no carrier) · noted, not filed — read-only inference, UNMEASURED: clampManagedObjectWrites narrows create/edit/delete on guarded managed objects but not allowTransfer, which the per-set fold (#20134) sets through modifyAllRecords. On a better-auth/engine-owned object, can(obj,'transfer') may read true where the engine write guard refuses the owner_id update. checkObjectPermission agrees with the map there, so it is outside this family's parity; it is not measured at a public door, and so it is not a finding under the filing rule."
    ],
    "gates": "Derived from this diff: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (tree 6527062) gave 64 commands. All 64 were run; --ran reconciles '64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN' with every line carrying an exit code. 63 exit 0. check:dual-build-cjs-loads first exited 3 (PREREQUISITE NOT MET: 8 unrelated packages lacked dist/); after building them (turbo cache) it exited 0. node scripts/check-empty-changeset.mjs --base origin/main exits 1 BY DESIGN: it is the DELIBERATE CORRECTION class on three pending notes, and it stays red until a person confirms (see deviations). check-changeset-no-major --base origin/main and check-adr-0087-registration --base origin/main exit 0. node scripts/check-issue-citations.mjs --base 49144fc exits 0 (8 citations resolve). Lint, a narrowed measurement: eslint --no-inline-config over the 5 changed TS files gave 0 errors and 0 warnings, 5 files counted from --format json, all 5 in the config population (--print-config resolves each). The config enables no type-aware linting, so no untouched file's verdict can move. check:nul-bytes exit 0, and a self-scan for control bytes over the 9 changed files found none.",
    "line_budget": "n/a — no skills/** in the diff",
    "deviations": [
    "DELIBERATE CORRECTION of three PENDING changesets, one clause each, each sentence made false by this fix: .changeset/18783-server-can-option-visibility.md ('The map still differs … can read as granted' -> 'The map also differed … read as granted, which is closed in this same release (20136)'); .changeset/20134-super-user-entries-every-bit.md ('Both over-grants are left exactly as they were.' -> '…as they were by this change, and both are closed in this same release (20136)'); .changeset/18931-me-permissions-unrestricted-export-annotation.md ('Its CRUD bits are folded true' -> 'folded to what its wildcard grants (all four for the built-in admin sets)'). check-empty-changeset is red on the three by design, and the PR body asks for confirmation. No released note was touched.",
    "The suggested route turned KNOWN_OVER_GRANT into an empty set. This PR DELETES it (and its flip-trigger assertion and its 'names a row' test) instead: exact parity has no allowance for a known divergence, and the rows are named subjects.",
    "Test-side fixtures were re-spelled where the old fixture relied on the over-grant, all inside the claim's test surface: core's 'seeds, then folds…' and 'with no schema source…' cases, and hono-server's endpoint RESOLVED fixture (the sys_member deny moved to the second set, so its 'clamp over the fold' assertion still exercises the fold).",
    "Row c is 32 cells here and 36 on the card, because the fixtures differ (61 registered objects vs 63): it is create + import on each object the clamp does not narrow, 16 here."
    ],
    "files_changed": [
    "packages/core/src/security/effective-object-permissions.ts (+49 -34)",
    "packages/core/src/security/effective-object-permissions.test.ts (+92 -5)",
    "packages/plugins/plugin-security/src/get-effective-object-permissions.test.ts (+58 -50)",
    "packages/plugins/plugin-hono-server/src/current-user-endpoints-effective-objects.test.ts (+29 -7)",
    "packages/plugins/plugin-hono-server/src/fold-wildcard-superuser.test.ts (+7 -0)",
    ".changeset/20136-super-user-fold-per-set.md (new, @objectstack/core patch, Clause-2 no)",
    ".changeset/18783-server-can-option-visibility.md (+1 -1, deliberate correction)",
    ".changeset/18931-me-permissions-unrestricted-export-annotation.md (+1 -1, deliberate correction)",
    ".changeset/20134-super-user-entries-every-bit.md (+1 -1, deliberate correction)"
    ],
    "enumeration": {
    "fixture": "map can() (formula CEL over toEvalPermissions) vs PermissionEvaluator.checkObjectPermission, 61 registered objects (57 platform + plugin-security objects, 4 app objects: public, apiMethods-restricted, private, apiEnabled:false) x 10 verbs; guarded-object create/edit/delete narrowing (the clamp) not counted; base 49144fc, head 6527062",
    "row a (walled organization_admin + member_default)": "over 38 -> 0",
    "row b (walled organization_admin alone)": "over 44 -> 0",
    "row c ('': {modifyAllRecords} alone)": "over 32 -> 0 (card: 36 on a 63-object fixture)",
    "row d (empty {} entry in a super-user set)": "over 8 -> 0",
    "row e ('
    ': {viewAllRecords} over its own {allowExport} entry)": "over 2 -> 0; apiOperations offered-but-refused export 1 -> 0",
    "row f, ADDED ('': {viewAllRecords} over its own narrower entry)": "over 1 -> 0",
    "row g, ADDED (modifyAllRecords-only '
    ' beside a set naming the object narrower: create across sets)": "over 2 -> 0",
    "row h, ADDED control (admin_full_access + organization_admin + member_default)": "over 0 -> 0",
    "existing one-set row (super-user * + narrower crm_account)": "over 7 -> 0"
    },
    "parity": {
    "row a: walled org admin + member_default": "over 38->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes changed",
    "row b: walled org admin alone": "over 44->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes changed",
    "row c: bare modify-all wildcard": "over 32->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes changed",
    "row d: empty explicit entry inside a super-user set": "over 8->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes changed",
    "row e: own explicit export-only entry under a super-read wildcard": "over 2->0, under 0->0, apiOperations offered-refused 1->0, served-hidden 0->0, map bytes changed",
    "row f: super-read-only wildcard over its own narrower explicit entry": "over 1->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes changed",
    "row g: create lifted across two sets (modify-all-only wildcard beside a set naming the object narrower)": "over 2->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes changed",
    "row h (control): platform admin who is also a walled org admin": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "wall-less org admin": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "member": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "viewer": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "an explicit entry beside another set's plain wildcard": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "one set: a plain wildcard AND a narrower explicit entry": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "an export-only wildcard beside a reader": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "a wildcard that grants nothing": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "platform admin": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "platform admin alone": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "platform admin who is also a wall-less org admin": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "a super-read wildcard carrying plain bits": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "a super-read wildcard alone": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "a super-user wildcard carrying the export grant": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "a super-read wildcard carrying the export grant": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "an explicit entry beside another set's super-user wildcard": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "one set: a super-user wildcard AND a narrower explicit entry": "over 7->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes changed",
    "platform admin beside a plain export-only wildcard": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "one set: an exporting wildcard AND an explicit entry without the grant": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)",
    "super-read wildcard beside a plain wildcard": "over 0->0, under 0->0, apiOperations offered-refused 0->0, served-hidden 0->0, map bytes identical (sha256)"
    },
    "reach": {
    "write_path": "The real ObjectQL engine with the real SecurityPlugin's registered resolver; crm_case.stage options gated on current_user.can(...). Walled org admin + member_default: can('sys_position','edit') ADMITTED -> refused VALIDATION_FAILED/invalid_option, can('sys_position','create') ADMITTED -> refused. Walled org admin alone: the same two, plus can('sys_user','edit') ADMITTED -> refused. '': {modifyAllRecords}: can('sys_position','create') and can('crm_account','create') ADMITTED -> refused. checkObjectPermission is false on every one of those 7 cells; the other 18 of 25 cells (controls admin_full_access+member_default and member_default, and every granted cell) are unchanged and agree.",
    "me_permissions": "GET /api/v1/auth/me/permissions through the real registerCurrentUserEndpoints on Hono, same sets and registry: walled org admin + member_default objects.sys_position create/edit/delete true/true/true -> false/false/false, response 13187 -> 13203 bytes, objects sha256 7d78b7069a14 -> 7c41ba445efa; walled org admin alone true/true/true -> false/false/false, 12789 -> 12807 bytes; '
    ':{modifyAllRecords} create true -> false (edit/delete stay true), 11122 -> 11138 bytes; admin_full_access+member_default and member_default byte-identical."
    },
    "collateral": {
    "byte_identical_subjects": "19 of 27 (sha256): every subject with no super-user wildcard, and every super-user subject with no own narrower entry and no create-less modifyAllRecords, including admin_full_access alone and beside member_default, and row h",
    "changed_subjects": "8: no entry added or removed, no false -> true bit. true -> false only: row a allowEdit x6, allowCreate x5, allowDelete x5; row b allowEdit x8, allowCreate x5, allowDelete x5; row c allowCreate x16; row d allowRead/allowCreate/allowEdit/allowDelete x1 each; row e allowRead x1; row f allowRead x1; row g allowCreate x1; one-set row allowCreate/allowEdit/allowDelete x1 each",
    "api_operations": "the apiOperations column is 0 offered-and-refused and 0 served-but-hidden in all 27 subjects at head. Row e flips 1 -> 0; its crm_account goes from no annotation (default-allow, offering export) to the closure without export. No other annotation moves.",
    "under_granted": "0 at base and 0 at head in every subject (no cell checkObjectPermission grants became refused in the map)",
    "enforcement": "checkObjectPermission untouched: permission-evaluator.ts and all plugin-security non-test source have 0 changed lines"
    }
    }

  8. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Seat amendment to claim 5852277926

    domain:engine#1, session_01Bvd69VPa6puiNzzPUroDBx, written 2026-09-27T04:22Z.

    • Open question (keep or retire foldWildcardSuperUser) → B for PR fix(core): the effective map folds a super-user wildcard per set only — no cell checkObjectPermission refuses (#20136) #20165.
      • The export stays as it is: body and signature unchanged, with its docblock stating that it is not the map's fold and where it differs from the server.
      • This PR declares Clause-②: no and fixes a fails-open over-grant. It is not the place for a breaking removal of a released export.
    • The retirement itself (option A) removes a published export of @objectstack/core and @objectstack/plugin-hono-server. That is a public-contract narrowing, so it is the maintainer's decision. The seat puts it to the maintainer with the dev's four-axis reading and does not rule it here.
    • Deviations accepted for review:
      • KNOWN_OVER_GRANT is deleted rather than emptied: exact parity has no allowance, and rows a–h are named subjects.
      • Three pending changesets get one-clause DELIBERATE CORRECTIONS (18783, 20134, 18931). The at-tier review judges each rewritten sentence.
      • Test fixtures are re-spelled where they relied on the over-grant.
    • File surface: the three corrected changesets and plugin-hono-server's fold-wildcard-superuser.test.ts are added (test side).
  9. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT: PR #20165 at 6527062a22 (#20136)

    domain:engine#1, session_01Bvd69VPa6puiNzzPUroDBx, written 2026-09-27T04:49Z. Reviewed on GitHub against references/review-checklist.md, not from the dev's os-dev-report.

    • Shape: the first line is Fixes #20136. The body and changeset declare Clause-②: no, @objectstack/core patch.
    • Scope: 9 files, +258/−99. Not governed (check-governed-merges: 357 lines, under the threshold).
      • effective-object-permissions.ts: buildEffectiveObjectPermissions no longer runs the merged foldWildcardSuperUser pass. Only the per-set foldSuperUserWildcardGrants runs.
      • foldWildcardSuperUser stays exported with its body unchanged (amendment 5852592546, B).
      • Four test files, the new changeset, and three one-clause DELIBERATE CORRECTIONS (18783-*, 20134-*, 18931-*).
    • Contract review of record: PASS at 6527062a22 (5852729107).
      • Map can() against checkObjectPermission, 42 subjects × 61 objects × 10 verbs: 0 over-granted and 0 under-granted at head in every subject. Rows a–h reproduce the dev's counts, and 15 extra shapes the reviewer constructed all go to 0.
      • Reach, measured at base and head:
        • write-path options gated on current_user.can(...): 7 cells go ADMITTED → REFUSED, and the controls are unchanged;
        • GET /auth/me/permissions: only true → false bits. Entries added or removed: 0.
      • permission-evaluator.ts and every plugin-security non-test source file are unchanged. The apiOperations column reads 0 / 0 at head.
      • The ablation reproduces the dev's red counts: core 7/32, plugin-security 10/64, hono 1/47.
      • Each rewritten changeset sentence reads TRUE at head. No other pending note reads FALSE.
    • CI at this head: 34 runs, 30 success, 3 skipped, 1 failed. All seven required contexts are success.
      • The one failure is Check Changeset, red by design on exactly the three corrected notes. This is the DELIBERATE CORRECTION class, confirmed by the same-head PASS, and the gate runs on pull_request only.
      • git merge-tree against current main (84880f9224) is clean.
    • Acceptance notes (not cards):
      • Wording in 20134-* ("The fold still folds…") and 18990-* / 18783-* still describes foldWildcardSuperUser as the fold. That is true of the helper but stale as a description of the map. It is a docs-only tidy for later. carrier: none today.
      • The dev's note that the clamp does not narrow allowTransfer on guarded objects is read-only and unmeasured, and the map and enforcement agree there. Not a card.
    • Open for the maintainer, not this PR: whether to retire the exported foldWildcardSuperUser (option A, a breaking removal).
    • Downstream: /auth/me/permissions bytes and the write-path can() change for super-user subjects that have an own narrower entry or a create-less modifyAllRecords. After the merge the seat tells domain:cli and domain:services.

    Landing: ready plus auto-merge through the queue now.

  10. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #20165, verified on main

    domain:engine#1, session_01Bvd69VPa6puiNzzPUroDBx, written 2026-09-27T05:11Z.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:enginepriority:p1High: required for production / M2security

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions