Uh oh!
There was an error while loading. Please reload this page.
fix(security): an organization-less permission-set row grants again — #11121 revoked standing access silently - #11610
Merged
Conversation
…11121 residue) The per-organization loader read resolveOwnOrganizationRow().own alone, which is never a residue once an organization is supplied — so every walled deployment carrying pre-#11121 rows silently lost those grants on upgrade, while the SAME row's system/tab permissions kept applying through the unscoped by-id read. The helper is written for seeders, where not seeing a residue as 'already seeded' is the point; enforcement needs the opposite reading. This implements the fallback the loader's own comment already promised. Own still beats residue, so #11121's cross-tenant fix is intact. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 14 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
os-zhuang
enabled auto-merge
August 24, 2026 08:33
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What broke
#11121 tenant-scoped the request-time permission-set loader so two organizations
holding a row for the same name stop answering each other's requests. It shipped
the other half as a COMMENT:
resolveOwnOrganizationRowis written for SEEDERS, and its docstring says soplainly: an organization-less row "is reported separately and never returned
as the organization's own row" — because reading it as "already seeded" is the
silent no-op the per-organization catalog exists to prevent. Enforcement needs
the opposite reading. The loader took the seeder's answer.
What it cost
Every walled deployment carrying pre-#11121
sys_permission_setrows — or anyrow authored without a tenant, which includes admin-UI-authored sets — silently
lost those grants on upgrade. The asymmetry is observable on a single row:
system_permissions,tab_permissionsresolve-authz-context, unscoped, by idobject_permissions,admin_scopeOne row, two enforcement planes, opposite verdicts, and the only signal was a
boot WARN about "leftovers" whose text states the organization's catalog is
complete.
The same commit states the no-silent-revocation rule twice — in
per-organization-catalog.ts's header ("grant TARGETS … deleting them revokesstanding access with no signal at the moment of loss") and at
resolve-authz-context.ts:537-540, where thesys_positionread deliberatelykeeps org-less rows for exactly this reason. This restores it in the third
place.
Found by
cloud's
apps/ee-group-showcasedogfood suites, which had been failing fourADR-0111 / ADR-0105 assertions on cloud main while turbo replayed them from
cache (cloud#1636). The suites hand-seed their catalog rows as system with no
tenant, so those rows land as residue the moment they are written — an exact
model of the customer case.
The change
One line of behaviour:
own ?? organizationLessResidue. Preference order isunchanged, so #11121's cross-tenant fix is intact — this organization's own row
still WINS wherever it exists; a leftover is consulted only in its absence.
Three cases added to
resolve-permission-sets-for-context.pin.test.ts, which#11121's own suite did not cover (it covers seeding and the
sys_positionsweep):
resolves WHOLE (the dropped columns are asserted, not just presence);
grant does not leak in behind it;
Reverting the one-line fix reddens exactly the first case, with the message
"the grant vanished for a caller in an organization — this is the silent
revocation". Verified both directions locally (8 passed with the fix; 1 failed /
7 passed without).