pluto
PLUTO-129
Hardening: roster-add forms allow adding a fraud-table membership into a NON-current/closed period — isPeriodoValid checks only deletedAt IS NULL, not isCurrent. Affects BOTH the new docente-detail add-form (PLUTO-128) AND the already-shipped list-page dialog (docentes/page.tsx) — PARITY, not a 128 regression. If cross-period roster-add is NOT an intended workflow (pre-stage future / correct past period), gate both forms on isCurrent. PENDING Elazar's intent call (is cross-period add a feature?). Surfaced by audit on PLUTO-128 pre-push review.
Done normal
cpcoder-pluto-cc
Questions
No questions.
Activity
-
coder-pluto-cc / coder
-
Elazar ruled 'no' — cross-period roster-add is NOT an intended feature. Gate BOTH forms (docente-detail add-form + docentes/page.tsx dialog) on isCurrent, parity. Predicate change on fraud-sensitive write gate → coder sends 4-line invariant note pre-impl → audit design-ping → impl → audit pre-push → PTD.
-
AUDIT DESIGN REVIEW = PASS-to-implement the addDocenteMembership guard (correctly closes the ?periodo= URL hole, fails closed). THREE decisions before ship: (a) gate updateDocenteMembership too [DECIDED YES — add+edit, one predicate; leave removeDocenteMembership ungated, removing access from a closed period is benign]. (b) zero-current-período edge is REACHABLE (soft-delete the current período → no isCurrent=true → guard blocks ALL adds fleet-wide); fail-closed/no-corruption but must error 'no hay período actual' not a generic reject; needs Elazar accept 'no current período ⇒ roster frozen' [PM rec: ACCEPT]. (c) ELAZAR'S CALL: períodos carry only isCurrent/isActive/deletedAt — NO closed-vs-future flag — so current-only cannot distinguish a PAST closed período (the fraud vector) from an UPCOMING one; strict current-only ALSO hard-blocks future pre-rostering (standard semester rollover: load next período's roster before flipping it current). Elazar's 'no cross-period add' almost certainly targeted PAST periods; blocking FUTURE pre-load is an unstated side-effect → surface for explicit ruling. Audit does pre-push diff review against whatever scope lands.
-
AUDIT COMBINED VERDICT — repo-wide grep found the COMPLETE runtime write-set into the 3 fraud tables = {add, update, RESTORE}. THIRD path restoreDocenteMembership (:451, UPDATE deletedAt=NULL :468) un-soft-deletes a fraud row → re-GRANTS access into the row's (possibly closed) período = a delete-then-restore re-add vector, most fraud-relevant after add. Gating add+edit but NOT restore leaves a live re-add hole. Ruled OUT (clean boundary): applyImport/rosterImport (writes studentAssignments only), setAdjuntoIsPrincipal/isPrincipal-preclear (flips boolean only, gate optional), direct-psql migrations (deliberate DBA out-of-band, inherently bypass app predicates). Full containment = gate {add,update,restore} on isCurrent; leave removeDocenteMembership ungated. Audit leans STRICT (freeze-on-close) over ADD-ONLY; add-only that leaves restore open = the re-add hole. Elazar A/B ruling pending; 'A strict' now means all 3 paths frozen on non-current período.
-
Elazar ruled A = STRICT freeze-on-close. Final locked scope: gate {add, update, restore}DocenteMembership on isCurrent via shared assertPeriodoIsCurrent helper; remove ungated; two Spanish messages (zero-current 'No hay un período vigente' + target-not-current 'No se puede modificar el roster de un período que no es el vigente'); client isCurrentPeriodo parity on both add-forms. Correction escape-hatch OUT of scope. Coder building → audit pre-push diff review → push → PTD.
-
PUSHED SHA 11fd7f8 (--minor), audit PASS pre-push. 5 files +110/-6; strict --only held (sibling 048/049 + solo CLAUDE.md NOT swept). Linear on top of db's f0724e7. Strict gate live: periodoNotCurrentError helper blocks add/update/restore on non-current períodos; remove ungated. Awaiting audit post-push PTD. TWO NON-BLOCKING audit flags = candidate follow-up WIs only if Elazar wants (NOT part of 129): (1) setAdjuntoIsPrincipal stays ungated — boolean isPrincipal reweight among already-present adjuntos, not a re-grant vector, only matters under a literal 'closed período fully immutable' reading; (2) edit/restore client buttons stay visible on non-current períodos, server-blocked w/ Spanish toast — UX wart, security-inert.
-
SHIPPED + PTD-PASS (SHA 11fd7f8, live tip, deploymentId dpl_2crG READY/alias-bound/runtime-clean). Strict freeze-on-close roster gate live: periodoNotCurrentError helper blocks add/update/restore into any non-current período; remove ungated. Elazar ruled A (strict). Two non-blocking follow-up candidates recorded (setAdjuntoIsPrincipal ungated; edit/restore client buttons visible-but-server-blocked) — Elazar's call if he wants them filed.
chore
2026-06-14 by wi-cli-venus
6w ago
2026-07-03 09:47