Remove Supabase
The database moved to a container of our own; the platform is gone. This takes out what was left of it — and, where the leftovers were load bearing, moves rather than deletes. Moved, not deleted: supabase/migrations/ -> db/migrations/ the schema's source of truth supabase/build-org.ts -> scripts/build-org.ts lib/supabase/types.ts -> lib/types.ts 52 import sites repointed The bookkeeping needed care. It lived in `supabase_migrations.schema_migrations`, and simply renaming the schema would have left the runner facing an empty table: it would have called all 67 migrations pending and replayed them against a database that is long since current. So the runner now creates `migrationen.schema_migrations` and, once, copies the old rows across — guarded so a second run does nothing and a fresh database skips it entirely. Only then does migration 20260907100000 drop the old schema. Deleted: the CLI config, the seed, the historical schema/function dumps (nothing read them), scripts/umzug-von-supabase.sh (the move is done), and both Supabase packages plus the CLI. Nothing in the application imported them — the build now succeeds with no environment variables at all, which is the proof. Integration tests: six of them signed in through Supabase Auth and asserted against the anon key and the service role. That model is gone, so the tests were not portable — they are deleted. session-context and employee-status-filter already ran on pg and are untouched; om-reporting is ported to a direct connection because it guards a real risk (the reporting line rule exists twice, once in SQL and once in TypeScript). CI: the integration job started a Supabase stack. It now runs a postgres service, applies deploy/db-init and every migration to an empty database — that was the valuable part, and it still holds — then checks that a second run is a no-op, which is what proves the bookkeeping works. Docs: security-review.md audited a service-role key, a cookie adapter and auth.users, none of which exist. Restating findings about removed components would suggest today's system had been reviewed; it has not. It now records what was removed and says a fresh review is due. data-model.md was already marked obsolete and described the pre-OM schema; azure-migration.md was a plan for a route not taken. Both deleted. Verified: npm ci, typecheck, lint, 445 tests, build — all clean without the packages. Integration tests skip cleanly with no database. Migration SQL and the runner are reviewed but NOT executed: no Docker here, and the old instance no longer resolves. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
77
db/migrations/20260727140000_pin_function_search_path.sql
Normal file
77
db/migrations/20260727140000_pin_function_search_path.sql
Normal file
@@ -0,0 +1,77 @@
|
||||
-- search_path für alle eigenen Funktionen festnageln.
|
||||
--
|
||||
-- Der Supabase-Linter meldet 34 Funktionen mit „Function Search Path Mutable".
|
||||
-- Nachgezählt sind das alles SECURITY-INVOKER-Funktionen; die vier
|
||||
-- SECURITY-DEFINER-Funktionen (is_hr_user, current_hr_user_id,
|
||||
-- apply_due_pending_changes, fn_track_employee_assignment) setzen den Pfad
|
||||
-- längst. Deshalb steht im Advisor auch 0 errors.
|
||||
--
|
||||
-- Warum das trotzdem behoben wird:
|
||||
--
|
||||
-- Der Angriff braucht SECURITY DEFINER. Wer in einem Schema, das im
|
||||
-- search_path früher liegt, eine eigene Tabelle `employees` anlegt, bringt
|
||||
-- eine unqualifiziert schreibende Funktion dazu, auf die untergeschobene
|
||||
-- zuzugreifen — mit den Rechten der Eigentümerin der Funktion. Bei INVOKER
|
||||
-- läuft alles mit den Rechten der aufrufenden Person, es gibt also nichts zu
|
||||
-- gewinnen, und die RLS-Policies greifen unverändert.
|
||||
--
|
||||
-- Zur Lücke wird die Warnung erst, wenn eine dieser Funktionen später auf
|
||||
-- SECURITY DEFINER umgestellt wird, etwa weil eine Mutation an RLS vorbei
|
||||
-- schreiben muss. In dem Moment denkt niemand mehr an den search_path.
|
||||
-- Einmal festnageln räumt die Falle weg und ändert kein Verhalten.
|
||||
--
|
||||
-- `pg_temp` steht ausdrücklich am Ende: ohne die Angabe durchsucht Postgres
|
||||
-- das temporäre Schema *zuerst*, und dort darf jede Sitzung anlegen, was sie
|
||||
-- will.
|
||||
|
||||
do $$
|
||||
declare
|
||||
v_func record;
|
||||
v_count int := 0;
|
||||
begin
|
||||
for v_func in
|
||||
select p.oid::regprocedure as signature
|
||||
from pg_proc p
|
||||
join pg_namespace n on n.oid = p.pronamespace
|
||||
where n.nspname = 'public'
|
||||
-- Nur Funktionen, keine Prozeduren oder Aggregate.
|
||||
and p.prokind = 'f'
|
||||
-- Erweiterungen gehören uns nicht: pg_trgm legt show_trgm und show_limit
|
||||
-- in public ab. Daran zu drehen bricht bei der nächsten Aktualisierung
|
||||
-- der Erweiterung oder wird stillschweigend zurückgesetzt.
|
||||
and not exists (
|
||||
select 1 from pg_depend d where d.objid = p.oid and d.deptype = 'e'
|
||||
)
|
||||
-- Bereits gesetzte nicht anfassen: die vier DEFINER-Funktionen stehen
|
||||
-- auf `search_path = public` und sollen so bleiben.
|
||||
and not exists (
|
||||
select 1 from unnest(coalesce(p.proconfig, '{}')) c where c like 'search_path=%'
|
||||
)
|
||||
loop
|
||||
execute format('alter function %s set search_path = public, pg_temp', v_func.signature);
|
||||
v_count := v_count + 1;
|
||||
end loop;
|
||||
|
||||
raise notice 'search_path festgenagelt für % Funktion(en)', v_count;
|
||||
end;
|
||||
$$;
|
||||
|
||||
-- Gegenprobe: danach darf in public keine eigene Funktion ohne search_path
|
||||
-- mehr stehen. Schlägt das an, hat die Schleife oben etwas übersehen — besser
|
||||
-- hier, als es im Advisor stehen zu lassen.
|
||||
do $$
|
||||
declare v_offen int;
|
||||
begin
|
||||
select count(*) into v_offen
|
||||
from pg_proc p
|
||||
join pg_namespace n on n.oid = p.pronamespace
|
||||
where n.nspname = 'public'
|
||||
and p.prokind = 'f'
|
||||
and not exists (select 1 from pg_depend d where d.objid = p.oid and d.deptype = 'e')
|
||||
and not exists (select 1 from unnest(coalesce(p.proconfig, '{}')) c where c like 'search_path=%');
|
||||
|
||||
if v_offen > 0 then
|
||||
raise exception 'Es stehen noch % Funktion(en) ohne search_path in public.', v_offen;
|
||||
end if;
|
||||
end;
|
||||
$$;
|
||||
Reference in New Issue
Block a user