You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
getMetaItemLayered is the third instalment of the ungated-org-read series — a raw active organization reaches its overlay layer, and its orgId is bound BEFORE the canonical type fold so the plural/singular one-liner does not port #14907
Filed by the domain:engine execution seat while implementing #14770 (the singular getMetaItem door). Out of scope there and deliberately not fixed in that PR — see "Why it is not folded into #14770" below.
⛔ Ungraded and unrouted on purpose — no pm:* state, no domain:*, no assignee. Grading and routing are triage's.
The defect
The series #9454 → #14683 (plural getMetaItems) → #14770 (singular getMetaItem) is "a read door spends a raw active organization on a type that has no per-org read channel, so a pre-#6190 phantom org-scoped row is resurrected". getMetaItemLayered — the third /meta read verb — still applies no gate of its own.
Verified at origin/main84b8190ae, packages/metadata-protocol/src/protocol.ts:
:7798 — async getMetaItemLayered(request: { … })
:7838 — const orgId = request.organizationId; ⇠ no gate
type is generic here, so it is not confined to the org-overridable five. When it is a type the registry declares allowOrgOverride: false, a phantom org-scoped row becomes the overlay layer — and this branch returns that layer as the response, so the caller is told the item has a customization it does not have.
The REST /layers door at packages/rest/src/rest-server.ts:3172does gate (organizationIdForMetaRead(canonicalMetaUrlType(req.params.type), layeredCtx?.tenantId)), and the plugin-security call sites pass no organization at all. So the runtime dispatcher site above is the reachable one.
⚠️ Direction differs from the other two verbs and should be measured before it is graded. On the plural verb a phantom can only ADD a row; on the singular verb it REPLACES the served document (#14770). Here the affected value is the overlay layer of a three-layer diagnostic whose whole purpose is to answer "what did this tenant customize" — so the harm is a false positive customization claim, which the Studio "Code default vs Overlay vs Effective" diff tab renders as evidence. That may grade differently from either twin.
⭐ Why it is not folded into #14770, and why it is not a copy of that fix
⚠️Corrected 2026-09-03 (contract review of PR fix(metadata-protocol): gate getMetaItem's overlay read on the metadata registry #14908, advisory A2): this line first said sixplugin-security sites. The actual non-test getMetaItemLayered( invocations there are five — packaged-permission-set-lock-gate.ts:82 and permission-set-projection.ts:770, :938, :995, :1406. permission-set-overlay-discard.ts:243 only feature-detects (typeof protocol.getMetaItemLayered === 'function') and delegates to projectPermissionMutation, so it is not a call site. The two REST doors (rest-server.ts:3189, :7330) and the runtime dispatcher (runtime/src/domains/meta.ts:346) are confirmed as stated.
MCP search_issues (repo-scoped REST is 403 from this container and gh is absent, so the one targeted MCP call was the channel) — "getMetaItemLayered ungated organization overlay layer raw active organization phantom org-scoped row runtime domains meta resolveActiveOrganizationId registry read gate singular layered door" → 10 results. Firing control: #14770 ranks first and #14683 is in the set. Distinguished:
Nothing open names getMetaItemLayered's ungated organization read.
Refs: #9454 · #14683 · #14770 (the singular twin, and the PR whose implementation surfaced this) · #6190 / #7018 (the phantom rows and the write-side predicate) · #10340 (the raw-vs-folded measurement) · #13753.
Filed by the
domain:engineexecution seat while implementing #14770 (the singulargetMetaItemdoor). Out of scope there and deliberately not fixed in that PR — see "Why it is not folded into #14770" below.⛔ Ungraded and unrouted on purpose — no
pm:*state, nodomain:*, no assignee. Grading and routing are triage's.The defect
The series #9454 → #14683 (plural
getMetaItems) → #14770 (singulargetMetaItem) is "a read door spends a raw active organization on a type that has no per-org read channel, so a pre-#6190 phantom org-scoped row is resurrected".getMetaItemLayered— the third/metaread verb — still applies no gate of its own.Verified at
origin/main84b8190ae,packages/metadata-protocol/src/protocol.ts::7798—async getMetaItemLayered(request: { … }):7838—const orgId = request.organizationId;⇠ no gate:7842—request = canonicalizeMetaRequestType(request);Control on the same file (expect the two GATED bindings, in
getMetaItemsandgetMetaItem, after #14770 lands):The live ungated caller
packages/runtime/src/domains/meta.ts:345resolves the session organization and passes it straight through:typeis generic here, so it is not confined to the org-overridable five. When it is a type the registry declaresallowOrgOverride: false, a phantom org-scoped row becomes theoverlaylayer — and this branch returns that layer as the response, so the caller is told the item has a customization it does not have.The REST
/layersdoor atpackages/rest/src/rest-server.ts:3172does gate (organizationIdForMetaRead(canonicalMetaUrlType(req.params.type), layeredCtx?.tenantId)), and theplugin-securitycall sites pass no organization at all. So the runtime dispatcher site above is the reachable one.overlaylayer of a three-layer diagnostic whose whole purpose is to answer "what did this tenant customize" — so the harm is a false positive customization claim, which the Studio "Code default vs Overlay vs Effective" diff tab renders as evidence. That may grade differently from either twin.⭐ Why it is not folded into #14770, and why it is not a copy of that fix
Two reasons, the second one substantive:
The idempotence proof does not carry. The Where does the allowOrgOverride read gate belong for metadata sweeps that read MORE THAN ONE type per request?
getMetaItemsapplies none of its own #14683 ruling made the callee-side gate conditional on proving no already-gating caller is double-scoped or wrongly denied, discharged per door over that door's own caller population.getMetaItemLayeredhas a different population — fiveplugin-securitycall sites, two REST doors, the runtime dispatcher — none of whichgetMetaItem(SINGULAR) has the same ungated-caller defect asgetMetaItemsand it is sharper — its overlay read is??PRECEDENCE, not a union, so a pre-#6190 phantom SHADOWS the live env-wide row and becomes the served document #14770's proof covers.getMetaItem's overlay read on the metadata registry #14908, advisory A2): this line first said sixplugin-securitysites. The actual non-testgetMetaItemLayered(invocations there are five —packaged-permission-set-lock-gate.ts:82andpermission-set-projection.ts:770,:938,:995,:1406.permission-set-overlay-discard.ts:243only feature-detects (typeof protocol.getMetaItemLayered === 'function') and delegates toprojectPermissionMutation, so it is not a call site. The two REST doors (rest-server.ts:3189,:7330) and the runtime dispatcher (runtime/src/domains/meta.ts:346) are confirmed as stated.getMetaItemsapplies none of its own #14683 andgetMetaItem(SINGULAR) has the same ungated-caller defect asgetMetaItemsand it is sharper — its overlay read is??PRECEDENCE, not a union, so a pre-#6190 phantom SHADOWS the live env-wide row and becomes the served document #14770 the fix is "replace theorgIdbinding with the gated call", and it is correct there because the binding sits aftercanonicalizeMetaRequestType. IngetMetaItemLayeredthe binding is at:7838and the fold is at:7842— the binding comes first. Dropping the same expression in place would gate on the RAW type, which is exactly what/metaorg scope is decided from the RAW url spelling:translations/email_templatesread and write env-wide where their singular twin is org-scoped #10340 measured the cost of:declaresOrgOverridetolerates the manifest plurals but not the URL-only ones (translations/email_templateshave no manifest key), so a raw segment splits one item across two partitions. The fix here needs the binding moved below the fold — a reorder, in a method whosecodeandoverlaylayers must answer for one namespace (that is what the fold's own comment at:7842is about). Not mechanical, so it did not qualify for a bounded in-scope repair.Dedup
MCP
search_issues(repo-scoped REST is 403 from this container andghis absent, so the one targeted MCP call was the channel) — "getMetaItemLayered ungated organization overlay layer raw active organization phantom org-scoped row runtime domains meta resolveActiveOrganizationId registry read gate singular layered door" → 10 results. Firing control: #14770 ranks first and #14683 is in the set. Distinguished:getMetaItemLayered's overlay read used a barecatch, painting "read failed" as "no overlay". Same method, the READ-FAILURE half; says nothing about which org partition is read./meta/diagnostics,/meta/:type/:name/references) never forward the caller's organization — the "Used by" graph tells an operator an org-referenced item is safe to delete #13753 (open) —/meta/diagnosticsand/referencesnever forward the caller's organization: also the not-forwarded direction, and neither door is this verb.environmentId, never on org #6602 (closed) — org-scoped overlay row reaching the process-wideSchemaRegistry: the write/hydration side./forms/:slugcannot serve an org overlay of itsview— the route is anonymous by construction, so there is no session-derived org #9725 (closed) — anonymous/forms/:slughas no session org by construction.Nothing open names
getMetaItemLayered's ungated organization read.Refs: #9454 · #14683 · #14770 (the singular twin, and the PR whose implementation surfaced this) · #6190 / #7018 (the phantom rows and the write-side predicate) · #10340 (the raw-vs-folded measurement) · #13753.