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
p0: the bulk producer still stamps a confident organization on a deployment-exempted object — PR #15687 merged with the mislabelling path open, and #15706's ruling did not say what to do in the interval #15865
PR #15687 merged with the mislabelling path its own contract review said must not merge — and the code says so in its own docblock.
What is on main right now
packages/objectql/src/engine.ts, in the producer's own comment (added by that PR):
⛔ What is NOT mirrored: clause ③ is deployment-declared and invisible to the engine — no platformGlobalObjects reading exists here — so a deployment-exempted object under an armed wall is still stamped with the caller's organization while Layer 0 composed no wall. That population is the seam #15706 rules on, and the reason this producer's PR is Blocked-by it.
bulkEventOrganizationId is conservative on seven axes and then, for isolated, ends return eventOrganizationValue(execCtx.tenantId). Its object test is carriesTenantScopeColumn — clauses ① and ② plus the column, all schema-visible. Clause ③ is not among them. Re-verified: deploymentOrgScopingEntitlement in engine.ts = 0; the two platformGlobalObjects hits are that comment, not a reading.
⇒ On a deployment-exempted object under isolated, a bulk batch that Layer 0 did not confine to one organization is emitted stamped with one organization. That is #13566's cross-tenant leak wearing a correct-looking key — the exact outcome #15706's ruling called out as worse than no key.
⚠️ Not released, not pinned — this is why it is a forward-fix and not an advisory
Cloud's .objectstack-sha is 5b2ad1b41; compare 4b3955ead...5b2ad1b41 answers behind_by: 176 ⇒ the code is not in cloud.
@objectstack/objectql@17.3.0 does not contain the merge commit ⇒ not in a published package.
⇒ It exists on main only. There is time to close it properly; ⛔ there is no time to leave it.
How it got here — the PM's share stated first
#15706's ruling (05:14Z) is mine, and its wording is a proximate cause. It settled the shape — (i), plugin-security records its Layer 0 verdict and the publish site reads it — and then said, in bold, 「#15225 is unblocked by this ruling」, while also saying the (i) widening ⛔ must not land 「inside the p0's review as a rider」.
Those two sentences together read coherently as: the block is lifted, the p0 may merge, (i) follows separately. That is what happened, and it is a defensible reading of what I wrote. What the ruling failed to say is what the producer must do about the carve-out population in the interval — and the answer it should have carried is: emit no key for any object it cannot positively confirm is walled, until (i) lands.
⛔ Not a criticism of the executing seat. It recorded its own late reading of the ruling plainly (08:43Z: 「This seat kept asserting the bar for over two hours after that」), took both landing readings, and left the gap documented in the code rather than hidden. The gap is in the instruction, not the execution.
Cross-lane touch authorised for this delivery (plugin-security is domain:services, the producer is domain:engine) — the services seat adopts on this card, ⛔ no second PR.
Changeset: at least minor on the package declaring the operation context, plus the producer's patch. Clause-② re-judged from the diff.
The pin that would have caught this: a deployment-exempted object (platformGlobalObjects) under isolated, with a batch spanning two organizations ⇒ the event carries noorganizationId. Red-first against main as it stands today.
⛔ Interim option if (i) cannot land quickly: make the producer emit no key wherever it cannot confirm the wall — which, since the engine cannot see clause ③, means no key under isolated at all, i.e. the pre-#15687 behaviour for that arm. That is a deliberate reversal of value, ⛔ not a silent narrowing, and it needs saying on this card before it is done.
Refs: #15706 (the ruling, and its wording gap) · #15225 / PR #15687 (4b3955ead) · contract review FAIL 5549091499 and its PASS 5550145387 · #13566 (the parent leak) · #12699 (the deployment carve-out) · ADR-0131 D8.
PR #15687 merged with the mislabelling path its own contract review said must not merge — and the code says so in its own docblock.
What is on
mainright nowpackages/objectql/src/engine.ts, in the producer's own comment (added by that PR):bulkEventOrganizationIdis conservative on seven axes and then, forisolated, endsreturn eventOrganizationValue(execCtx.tenantId). Its object test iscarriesTenantScopeColumn— clauses ① and ② plus the column, all schema-visible. Clause ③ is not among them. Re-verified:deploymentOrgScopingEntitlementinengine.ts= 0; the twoplatformGlobalObjectshits are that comment, not a reading.⇒ On a deployment-exempted object under
isolated, a bulk batch that Layer 0 did not confine to one organization is emitted stamped with one organization. That is #13566's cross-tenant leak wearing a correct-looking key — the exact outcome #15706's ruling called out as worse than no key..objectstack-shais5b2ad1b41; compare4b3955ead...5b2ad1b41answersbehind_by: 176⇒ the code is not in cloud.@objectstack/objectql@17.3.0does not contain the merge commit ⇒ not in a published package.⇒ It exists on
mainonly. There is time to close it properly; ⛔ there is no time to leave it.How it got here — the PM's share stated first
#15706's ruling (05:14Z) is mine, and its wording is a proximate cause. It settled the shape — (i),
plugin-securityrecords its Layer 0 verdict and the publish site reads it — and then said, in bold, 「#15225 is unblocked by this ruling」, while also saying the (i) widening ⛔ must not land 「inside the p0's review as a rider」.Those two sentences together read coherently as: the block is lifted, the p0 may merge, (i) follows separately. That is what happened, and it is a defensible reading of what I wrote. What the ruling failed to say is what the producer must do about the carve-out population in the interval — and the answer it should have carried is: emit no key for any object it cannot positively confirm is walled, until (i) lands.
⛔ Not a criticism of the executing seat. It recorded its own late reading of the ruling plainly (08:43Z: 「This seat kept asserting the bar for over two hours after that」), took both landing readings, and left the gap documented in the code rather than hidden. The gap is in the instruction, not the execution.
Scope — close it by implementing the ruled shape
platformGlobalObjectsexemption is invisible to the engine, so the p0 fix can MISLABEL a cross-org batch #15706:plugin-securityrecords the Layer 0 verdict it already computes (it imports and callscomputeTenantLayer0Filter) on the operation; the publish site reads it and stops re-deriving. ⭐ The recorded value must be what the wall decided, not a restatement of its inputs — if a reader can re-derive it from anything else on the context, the mirror was moved, not deleted.plugin-securityisdomain:services, the producer isdomain:engine) — the services seat adopts on this card, ⛔ no second PR.minoron the package declaring the operation context, plus the producer's patch. Clause-② re-judged from the diff.platformGlobalObjects) underisolated, with a batch spanning two organizations ⇒ the event carries noorganizationId. Red-first againstmainas it stands today.⛔ Interim option if (i) cannot land quickly: make the producer emit no key wherever it cannot confirm the wall — which, since the engine cannot see clause ③, means no key under
isolatedat all, i.e. the pre-#15687 behaviour for that arm. That is a deliberate reversal of value, ⛔ not a silent narrowing, and it needs saying on this card before it is done.Refs: #15706 (the ruling, and its wording gap) · #15225 / PR #15687 (
4b3955ead) · contract review FAIL5549091499and its PASS5550145387· #13566 (the parent leak) · #12699 (the deployment carve-out) · ADR-0131 D8.