Two ungated getters exported from a use-server module return bulk student/docente name+email
src/lib/administrar-actions.ts is a "use server" module, so EVERY exported function is registered as an invocable server action, not merely an internal helper. Its mutators are gated (requireRosterManager at 492, 512, 542, 578, 606, 650, 703, 739, 770, 799, 843, ...) and getAssignableTeachersForComision self-gates at 248 on manageRoster OR viewStudents. But TWO getters have no session check at all inside the function: - getDocenteMemberships(periodoId, showDeleted) at :383 - SELECTs u.displayName, u.email for every docente in a periodo. - getAlumnoAssignments(periodoId, showDeleted) at :425 - SELECTs u.displayName AS studentName, u.email AS studentEmail for the ENTIRE student roster of a periodo, plus JTP and ayudante names. So the shape of the exposure is a bulk name+email dump of a whole cohort in one call, taking only a periodoId. WHAT IS VERIFIED (support-evolutiva-cc, source read 2026-09-19): the two functions have no gate; the module is "use server"; the import "server-only" does NOT prevent server-action invocation, it only blocks client bundling; the only importers are src/app/(protected)/alumnos/page.tsx and src/app/(protected)/docentes/page.tsx, both SERVER components; src/middleware.ts performs NO authentication - it only stamps x-mars-pathname - so there is no global gate behind them; the (protected) layout redirects an unauthenticated session to /login but does not cap-check. WHAT IS NOT VERIFIED, and the claim must not be made without it: whether an action ID for these exports is obtainable by a caller. Both importers are server components, so the IDs are probably not emitted into any client bundle. Next registers "use server" exports in the SERVER action manifest regardless of client reference, so the question is whether an ID can be derived or leaked, not whether the endpoint exists. Nobody has attempted an invocation. Treat this as defence-in-depth with an unproven exploit path, NOT as a demonstrated breach - and do not downgrade it on that basis either, because the gate costs one line. The UI routes themselves are correctly gated and are NOT the issue: alumnos/page.tsx:36 requireCap(viewStudents, manageRoster), docentes/page.tsx:22 requireCap(manageRoster). FIX, per audit-general-cc: add requireRosterManager (or the viewStudents-OR-manageRoster predicate the sibling getter already uses) inside both functions, or move them out of the "use server" module into a plain server-only data module that the pages import directly. The second is the structural fix: a data getter has no business being an action endpoint. Pre-existing and independent of the lector work (MARS-744) - lector was simply the first role that made anyone look. Also flagged by audit-general-cc and unverified by anyone: /api/profile-image serves profile photos to any authenticated user, and whether student photos render on case pages was not checked.
Activity
-
Closed as a duplicate of MARS-748 (coder-mars-cc filed it in parallel for the same finding, with a broader export list). All content merged into MARS-748 as a note. Track there; nothing outstanding here.