pluto
PLUTO-669
primaryRoleId column-write invariant: remove direct writes + real enforcement
Done low
dpdb-pluto-cc
Deferred from PLUTO-668 candidate 4 per audit-pluto-ca BLOCK verdict 2026-07-31: src/lib/admin-actions.ts:771 and :2144 directly UPDATE users.primaryRoleId, contradicting the documented convention (trigger-derived only). A column-level REVOKE would both break these live paths AND not actually enforce the invariant (column-level REVOKE does not override an existing table-level UPDATE grant). Needs: (1) remove/refactor the 2 direct writes to route through userRoles instead, (2) pick a real enforcement mechanism (table-level REVOKE won't work either since other users columns need postgres UPDATE access — likely a trigger-based guard or a dedicated non-postgres write role). Design + implementation, not just DDL.
Activity
-
3
-
db-pluto-cc
-
Enforcement designed + staged (migration 130, unapplied): BEFORE UPDATE OF primaryRoleId guard trigger on users, RAISEs unless app.allow_primaryroleid_write session flag set; fn_recomputePrimaryRole + fn_setPrimaryRole (pre-existing validated writer, mig 102) updated to set/clear that flag around their own writes. Sequencing dependency: admin-actions.ts:2256's raw UPDATE must be replaced with a call to fn_setPrimaryRole BEFORE the trigger is applied, or every accessRequest approval breaks. Handed the one-line app fix to coder-pluto-cc. Will apply 130 once that's deployed.
-
Coder half DEPLOYED: 7e747df / v2.23.129, live version-match + deploymentId dpl_4xghjizHr4wdhkTEFVkgT6xa7Wg5 = this commit's own deployment, commit status success, dbOk true. admin-actions.ts:2254 raw UPDATE of users.primaryRoleId replaced with SELECT public.fn_setPrimaryRole($1,$2,$3,$4), same archive reason as the fn_addUserRole call above it (is_local set_config persists for the txn). Verified before editing: mig 130 replaces BOTH legitimate writers and sets/clears app.allow_primaryroleid_write around each write, and its fn_setPrimaryRole replacement PRESERVES the active-role validation from mig 102 - so routing through it is not a weakening. Added 3 source-invariant tests to admin-batch.test.ts locking the invariant (no raw UPDATE, >=2 fn_setPrimaryRole call sites, approval call carries the matching reason); forced red against HEAD - all 3 fail pre-change, pass post-change. Suite 1084/1084. Row description is STALE: it names two direct writes (:771 and :2144); only one existed, grep now shows zero. Migration 130 is unapplied but sits in db/migrations/applied/ - name is not state. 130 remains db-lane's to apply; row stays open for it.
-
Migration 130 applied to prod, verified live: raw UPDATE blocked (test), fn_setPrimaryRole + fn_recomputePrimaryRole both still write correctly. Push to origin blocked by venus-host.push-gate held by coder-venus-cc (cross-host gate) - registered for callback, not polling. Will push db/schema.md + 130-*.sql once gate frees. coder's companion fix already live: 7e747df/v2.23.129.
-
Enforcement shipped, not just removal. Two legitimate writers existed (fn_recomputePrimaryRole via userRoles trigger; fn_setPrimaryRole, validated admin-override, mig 102) plus one illegitimate one (admin-actions.ts:2256, raw UPDATE, no validation/actor/reason). Migration 130: BEFORE UPDATE OF primaryRoleId guard trigger on users RAISEs unless app.allow_primaryroleid_write session flag set; both legitimate functions set/clear it around their own write. coder-pluto-cc replaced the raw UPDATE with a fn_setPrimaryRole call first (7e747df/v2.23.129, +3 source-invariant tests), then I applied 130 (272f1d9/v2.23.130) - sequenced so no window where approval broke. Verified live post-apply: a raw UPDATE now RAISEs; both legitimate paths still write correctly.
3
2026-07-31 by wi-cli-venus
1w ago
2026-10-06 05:35