Audit sweep: which 'use server' exports are reachable unguarded by anonymous POST (PLUTO-27 found three; the surface is much larger and needs a real analysis, not a grep)
PLUTO-27 fixed three unguarded roster reads in administrar-actions.ts (getPeridosForSelector, getComisionesForPeriodo, isPeriodoValid), each of which enumerated periodo/comision data to an anonymous POST because every export of a 'use server' file is an independently POST-callable endpoint regardless of which page imports it (PLUTO-635). While verifying, a crude scanner over all 'use server' files listed ~120 exported async functions with no guard token in their body, including ~110 in src/db/queries.ts. DO NOT TREAT THAT LIST AS FINDINGS. The scanner is unreliable in both directions: it cannot see a guard enforced at the caller, nor the documented pattern where a pre-authorized UserWithRoles is passed in as an argument and the scope filter IS the guard (resolveDataAccessScope / accessibleComisionIds), which covers most of queries.ts by design. It also splits function bodies by the next export, so long functions are misattributed. I deliberately did not act on it. What is actually wanted: a per-export determination of (a) is this reachable as a server-action endpoint, (b) if reached with no session, does it return data or throw, (c) if it takes a user/scope argument, can that argument be forged by the caller. Only (b)-returns-data or (c)-forgeable is a finding. Guard to use when one is needed, per PLUTO-27: requireCapForRead / requireAuthForRead for anything called during RSC render. NOT requireCapForAction - it is the write chokepoint and throws under view-as, so on a read it 500s an admin viewing as another user (PLUTO-638). That distinction is the main way a fix here goes wrong. Priority 2 rather than 1 because no exploitation is known and the three measured cases leaked roster metadata (periodo dates, comision names), not clinical or personal data. Whether that holds across the rest of the surface is exactly what this sweep is for - queries.ts reads practicas and student data.
Activity
-
queries.ts taken off the server-action surface entirely (v2.23.69 3843414 + v2.23.70 2f44ad6). The file-level use server made all ~110 exports anonymous POST endpoints taking raw identity/scope/filter args. Audit's premise that client components call these helpers was false as measured: all 19 client-component imports are import-type-only, no RSC passes one across the client boundary (server tables are URL-driven). Replaced with import server-only. Guard test src/db/queries-not-an-action-surface.test.ts: no bare directive (structural), server-only present, src/-wide walker rejecting client value imports in both quote forms and any dynamic import(), mutation-checked twice against planted imports. Stale getUserById guard commentary + unused requireAuthForRead import removed. audit-pluto-ca PASS on both commits; two pre-existing unrelated eslint warnings deliberately left out of this security WI per audit.