Filed by the domain:services PM seat (#6021) from PR #17115's out_of_scope_findings (card #17042). The delivering seat recorded it as "pre-existing, orthogonal to this card" and explicitly did not assert a defect. ⛔ That restraint is right and is preserved here: the guard is measured; the consequence is NOT ESTABLISHED. This card exists so the unestablished half gets measured rather than staying in a merged PR's notes.
What is MEASURED, on origin/main 50b6f17d4
The ADR-0058 D4 write-check gate in packages/plugins/plugin-security/src/security-plugin.ts is entered only when the payload is not an array (:2843-2848):
if (
(opCtx.operation === 'insert' || opCtx.operation === 'update') &&
opCtx.data &&
typeof opCtx.data === 'object' &&
!Array.isArray(opCtx.data) && // ⇐ the guard
permissionSets.length > 0 &&
!!opCtx.context?.userId
) {
const checkFilter = await this.computeWriteCheckFilter(…); // :2850
…
opCtx.postHookWriteImageCheck = insertCheckSeam; // :2914
}
And that block is the only production site that computes it. Measured repo-wide over packages/**/*.ts excluding *.test.ts:
computeWriteCheckFilter → :2850 (the call), :2860 (the delegator call, same block), :6218 (the definition)
+ two docblock mentions and one in @objectstack/lint's prose
opCtx.postHookWriteImageCheck = … → :2914 only
⇒ There is no second call site, no bulk-specific branch, and the insert seam handed to the engine is installed inside the same guarded block. PR #17115 does not touch any of this.
⛔ What is NOT ESTABLISHED, and is the whole card
Whether a bulk write ever reaches this middleware with array-shaped opCtx.data at all.
If ObjectQL fans a bulk insert/update out into per-row operations before the middleware sees it, every row arrives non-array, the guard never bites, and there is nothing here. If a bulk write arrives as one operation carrying an array, then an authored check clause is not evaluated for it — a write gate that does not run on a shape the API accepts.
⛔ Neither this seat nor the delivering seat traced that, and ⛔ neither is asserting it. The delivering seat encountered the guard only because it made its first write-face probe read PERMITTED across the board — it had handed engine.insert an array — which is evidence the guard is live at that seam, and not evidence about the bulk API's own path.
What would settle it, written to be executed
- Trace the bulk insert and bulk update paths end to end from the public API to this middleware and name the shape
opCtx.data carries at :2843.
- If it is an array: build the cell. An object with an RLS
check the row violates → single-row write refused; the same row inside a bulk write → measure. ⭐ The control is the single-row arm: without it, a bulk write that happens to be refused for some other reason reads as coverage.
- If it is never an array: say so with the trace, and ⛔ propose deleting the guard or replacing it with an assertion rather than leaving a condition nothing can satisfy — a dead guard reads as protection.
⛔ A zero from a probe that cannot answer "yes" is NOT MEASURED. Whatever sweep you use to establish "no bulk path reaches here", it needs a positive control that re-finds the single-row path.
Fences
Provenance
PR #17115 (card #17042), out_of_scope_findings — "the write-side check gate at step 3.6 is guarded by !Array.isArray(opCtx.data), so a BULK insert is not judged by the post-image check at all. … I did not verify whether a separate bulk path judges it, so I am not asserting a defect — but whether a bulk write is covered by an authored check clause is worth a deliberate answer."
Related: #16608 / PR #16805 (the ruling that moved the insert post-image to postHookWriteImageCheck) · #17042 (the phantom-column fail-open at the compiler) · ADR-0058 D4.
Filed by the
domain:servicesPM seat (#6021) from PR #17115'sout_of_scope_findings(card #17042). The delivering seat recorded it as "pre-existing, orthogonal to this card" and explicitly did not assert a defect. ⛔ That restraint is right and is preserved here: the guard is measured; the consequence is NOT ESTABLISHED. This card exists so the unestablished half gets measured rather than staying in a merged PR's notes.What is MEASURED, on
origin/main50b6f17d4The ADR-0058 D4 write-
checkgate inpackages/plugins/plugin-security/src/security-plugin.tsis entered only when the payload is not an array (:2843-2848):And that block is the only production site that computes it. Measured repo-wide over
packages/**/*.tsexcluding*.test.ts:⇒ There is no second call site, no bulk-specific branch, and the insert seam handed to the engine is installed inside the same guarded block. PR #17115 does not touch any of this.
⛔ What is NOT ESTABLISHED, and is the whole card
Whether a bulk write ever reaches this middleware with array-shaped
opCtx.dataat all.If
ObjectQLfans a bulk insert/update out into per-row operations before the middleware sees it, every row arrives non-array, the guard never bites, and there is nothing here. If a bulk write arrives as one operation carrying an array, then an authoredcheckclause is not evaluated for it — a write gate that does not run on a shape the API accepts.⛔ Neither this seat nor the delivering seat traced that, and ⛔ neither is asserting it. The delivering seat encountered the guard only because it made its first write-face probe read
PERMITTEDacross the board — it had handedengine.insertan array — which is evidence the guard is live at that seam, and not evidence about the bulk API's own path.What would settle it, written to be executed
opCtx.datacarries at:2843.checkthe row violates → single-row write refused; the same row inside a bulk write → measure. ⭐ The control is the single-row arm: without it, a bulk write that happens to be refused for some other reason reads as coverage.⛔ A zero from a probe that cannot answer "yes" is NOT MEASURED. Whatever sweep you use to establish "no bulk path reaches here", it needs a positive control that re-finds the single-row path.
Fences
RLSCompiler.compileFilterand is orthogonal; this is about whether the gate runs, not what it compiles.priority:p2provisionally, on the reachability being unknown. ⇒ If step 1 establishes that a bulk write does arrive array-shaped and is therefore unjudged, this is a security fail-open on a shipped API and the card becomespriority:p1— say so plainly rather than delivering quietly at p2.Provenance
PR #17115 (card #17042),
out_of_scope_findings— "the write-side check gate at step 3.6 is guarded by!Array.isArray(opCtx.data), so a BULK insert is not judged by the post-image check at all. … I did not verify whether a separate bulk path judges it, so I am not asserting a defect — but whether a bulk write is covered by an authoredcheckclause is worth a deliberate answer."Related: #16608 / PR #16805 (the ruling that moved the insert post-image to
postHookWriteImageCheck) · #17042 (the phantom-column fail-open at the compiler) · ADR-0058 D4.