-- Ausführungsrechte auf den SECURITY-DEFINER-Funktionen zurechtrücken. -- -- Der Advisor meldet alle vier als „Public Can Execute" und „Signed-In Users -- Can Execute". Das ist nicht bei allen vieren dasselbe Problem — nachgemessen -- mit dem anon-Schlüssel gegen die laufende Datenbank: -- -- anon.rpc(is_hr_user) -> false -- anon.rpc(current_hr_user_id) -> null -- anon.rpc(apply_due_pending_changes) -> 0 ← das ist der Befund -- -- Nur der dritte ist einer. -- ── Bleibt offen, und zwar mit Absicht ─────────────────────────── -- -- is_hr_user() und current_hr_user_id() werden *aus den RLS-Policies heraus* -- aufgerufen. Ein Policy-Ausdruck wird mit den Rechten der abfragenden Rolle -- ausgewertet; ohne EXECUTE für anon und authenticated scheitert damit jede -- Abfrage auf jeder Tabelle mit „permission denied for function". Der Entzug -- würde die Anwendung vollständig lahmlegen. -- -- Preisgegeben wird dabei nichts: beide nehmen keine Argumente und beantworten -- ausschliesslich eine Frage über die aufrufende Person selbst. Wer nicht -- angemeldet ist, bekommt false beziehungsweise null — siehe Messung oben. -- ── Wird entzogen ──────────────────────────────────────────────── -- -- apply_due_pending_changes() wendet vorgemerkte Versetzungen, Beförderungen -- und Abwesenheiten an, sobald ihr Datum erreicht ist. Es ist SECURITY -- DEFINER, umgeht also RLS, und war bis hierher ohne Anmeldung aufrufbar — der -- anon-Schlüssel steht im ausgelieferten Browser-Bündel. -- -- Der Schaden wäre begrenzt, weil nur ohnehin fällige Änderungen angewandt -- werden. Aber es ist ein Schreibpfad, den Fremde auslösen können, und er -- macht das Geheimnis der Cron-Route (app/api/cron/apply-pending-changes) -- wirkungslos. -- -- Diese Route ist der einzige Aufrufer und benutzt createAdminClient(), also -- die service_role — der Entzug für anon und authenticated bricht sie nicht. revoke execute on function apply_due_pending_changes() from anon, authenticated; -- rls_auto_enable() stammt nicht aus diesen Migrationen und wird von der -- Anwendung nirgends aufgerufen. Was sie tut, ist von hier aus nicht -- feststellbar; eine Funktion, die RLS umschaltet und ohne Anmeldung -- aufrufbar ist, wäre allerdings ernst. Der Entzug ist risikolos, weil kein -- Aufrufer existiert — und falls doch jemand sie braucht, meldet er sich mit -- einer klaren Fehlermeldung statt still etwas zu verstellen. do $$ begin if exists ( select 1 from pg_proc p join pg_namespace n on n.oid = p.pronamespace where n.nspname = 'public' and p.proname = 'rls_auto_enable' ) then execute 'revoke execute on function public.rls_auto_enable() from anon, authenticated'; end if; end; $$; -- ── Was bewusst *nicht* passiert ───────────────────────────────── -- -- „Extension in Public" (pg_trgm) bleibt stehen. Die Erweiterung trägt die -- Operatorklasse gin_trgm_ops, auf der zwei GIN-Indizes auf employees liegen -- (20260714120400_performance_indexes.sql). Ein Schemawechsel müsste die -- Indizes und jeden search_path mitziehen, der sie erreichen soll — gerade -- jetzt, wo jede Funktion auf `public, pg_temp` festgenagelt ist. Das ist -- Aufwand und Risiko für einen Hinweis, der keine Rechteausweitung beschreibt, -- sondern eine Konvention. -- -- „Leaked Password Protection Disabled" ist gegenstandslos: die -- Passwort-Anmeldung ist abgeschaltet. Eine Anmeldung mit E-Mail und Passwort -- gegen die API antwortet mit `email_provider_disabled` (422). Es gibt kein -- Passwort, dessen Kompromittierung geprüft werden könnte.