---
title: "Supabase „function search path mutable“: čo to znamená"
description: "Čo znamená varovanie Advisora function_search_path_mutable, kto ho môže zneužiť, prečo search_path = public v mojom teste nestačil a ako to opraviť."
url: "https://madsalt.tech/sk/notes/supabase-function-search-path-mutable/"
author: "Denys Zaichenko"
author_url: "https://madsalt.tech/sk/about/"
language: "sk"
published: 2026-11-01
---

# Supabase „function search path mutable“: čo to znamená

TL;DR

Varovanie znamená, že funkcia nemá pevný `search_path`, takže každé nekvalifikované meno vnútri nej, napríklad `admins`, sa hľadá tak, ako hovorí relácia volajúceho. V mojom lokálnom teste vrátila funkcia `is_admin()` so `SECURITY DEFINER` pre používateľa, ktorý nie je admin, hodnotu `true`, keď si vytvoril dočasnú tabuľku s menom `admins`, a ani pevné `search_path = public` to nezastavilo. Fungovalo `set search_path = ''` s každou tabuľkou zapísanou ako `public.admins`.

Security Advisor od Supabase to uvádza ako lint 0011, „Function Search Path Mutable“, úroveň WARN, s detailom „Function `public.x` has a role mutable search_path“. Označí každú funkciu bez tohto nastavenia, vrátane tých so `SECURITY INVOKER`, takže pri projekte s mnohými pomocnými funkciami je zoznam dlhý: jeden open-source projekt [opravil 99 z nich jednou migráciou](https://github.com/bdougie/contributor.info/commit/3c73dda01df80faccb75278ec0f826a84e01a6d9).

## Čo znamená „role mutable search_path“?

`search_path` je zoznam schém, v ktorých Postgres hľadá meno bez schémy pred ním. Funkcia bez vlastného nastavenia použije zoznam volajúceho. [Stránka lintu](https://supabase.com/docs/guides/database/database-linter?lint=0011_function_search_path_mutable) Supabase odporúča nastaviť ho na prázdny reťazec, čo vynúti plne kvalifikované každé odkazovanie vnútri funkcie.

Pri funkcii so `SECURITY DEFINER` na tom záleží najviac: beží s právami svojho vlastníka, zvyčajne `postgres`, ktorý RLS obchádza. Ak volajúci môže riadiť, ktorú tabuľku `admins` funkcia číta, riadi kontrolu, ktorá beží ako `postgres`.

## Dá sa to v mojom teste zneužiť?

Na lokálnom Postgrese 17.10 som vytvoril tabuľku `public.admins` s jedným adminom a štyri verzie tej istej funkcie, `select exists (select 1 from admins where user_id = uid)`, všetky so `SECURITY DEFINER`. Potom som sa prihlásil ako rola s nízkymi oprávneniami, ktorá smie spúšťať SQL a má predvolené právo vytvárať dočasné tabuľky, a vytvoril `pg_temp.admins` obsahujúcu používateľa A, ktorý admin nie je:

![is_admin(A) po dočasnej tabuľke admins. Funkcia SECURITY DEFINER, lokálny Postgres 17.10. Bez search_path: pg_temp.admins → admins → temp → true; search_path = public: pg_temp.admins → admins → temp → true; search_path = '' + public.admins: pg_temp.admins → public.admins → false.](https://madsalt.tech/charts/sk/supabase-function-search-path-mutable.svg)

*Moja reprodukcia na lokálnom Postgrese, 24. októbra 2026.*

| Nastavenie funkcie                                   | Podmienka lintu | `is_admin(A)` po dočasnej tabuľke |
| ---------------------------------------------------- | --------------- | --------------------------------- |
| žiadne                                               | označené        | `true`                            |
| `set search_path = public`                           | prejde          | `true`                            |
| `set search_path = ''`, telo používa `public.admins` | prejde          | `false`                           |
| `set search_path = public, pg_temp`                  | prejde          | `false`                           |

Druhý riadok ma prekvapil. Správca Supabase [v roku 2024 napísal](https://github.com/supabase/splinter/issues/100), že pevná neprázdna cesta nenesie žiadne bezpečnostné riziko, a lint ju prijíma. Postgres však hľadá najprv v dočasnej schéme, ak v ceste nie je uvedené `pg_temp`, ako varujú jeho [dokumenty o písaní funkcií SECURITY DEFINER](https://www.postgresql.org/docs/current/sql-createfunction.html#SQL-CREATEFUNCTION-SECURITY). Takže `search_path = public` stále nechá dočasnú tabuľku zatieniť nekvalifikované meno.

## Kto to na Supabase naozaj môže urobiť?

Nie návštevník cez vašu aplikáciu. Data API nevie vytvárať dočasné tabuľky ani meniť `search_path` relácie, takže samotný publishable key to nezvládne. Treba priame pripojenie k databáze: druhá služba s vlastným prihlásením, BI alebo reportovací nástroj, read-only rola dodávateľa, uniknutý connection string. Pri väčšine malých aplikácií je riziko nízke; pri každom projekte, kde prihlásenie do databázy držia iní ľudia alebo nástroje než vy, opravte najprv definer funkcie.

## Ako to opraviť?

Vytvorte funkciu znova s prázdnou cestou a menami schém v tele:

```sql
create or replace function public.is_admin(uid uuid)
returns boolean
language sql stable security definer
set search_path = ''
as $$ select exists (select 1 from public.admins where user_id = uid) $$;

revoke execute on function public.is_admin(uuid) from public, anon;
```

`revoke` je rovnako dôležité ako cesta: definer funkcia v `public` je volateľná cez Data API, takže bez neho sa ktorýkoľvek hosť môže cez `/rpc/is_admin` spýtať, či je daný používateľ admin. Vráťte ho len rolám, ktoré ho potrebujú, alebo držte takéto pomocné funkcie v schéme, ktorú API nevystavuje.

Nespúšťajte len `alter function public.is_admin(uuid) set search_path = ''`. Umlčí to Advisor a pokazí funkciu: v mojom behu ďalšie volanie zlyhalo s `42P01: relation "admins" does not exist`. Operátory rozšírení tiež potrebujú svoju schému, napríklad `operator(extensions.<=>)` pre pgvector.

Zoznam toho, čo lint v `public` označí, s definer funkciami na začiatku:

```sql
select p.proname, p.prosecdef as security_definer
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
where n.nspname = 'public'
  and not exists (select 1 from unnest(coalesce(p.proconfig, '{}')) c
                  where c like 'search_path=%')
order by p.prosecdef desc, p.proname;
```

## Stojí oprava niečo?

Niekedy. Akákoľvek klauzula `set` bráni Postgresu vložiť jednoduchú SQL funkciu do dopytu, ktorý ju volá, ako [uvádza GitHub](https://github.com/supabase/supabase/issues/33131). Zmeral som oba tvary na 200 000 riadkoch s indexom na filtrovanom stĺpci (medián zo 7 behov):

| SQL funkcia, potom filtrovaná podľa vlastníka     | Bez `set`               | `set search_path = ''` |
| ------------------------------------------------- | ----------------------- | ---------------------- |
| Filter vonku: `visible_items() where owner = ...` | 0,053 ms, index použitý | 36,7 ms, full scan     |
| Filter vnútri: `items_of(owner)`                  | 0,056 ms                | 0,082 ms               |

Ak sa SQL funkcia vracajúca množinu dopytuje s filtrom mimo nej, presuňte filter dovnútra ako parameter, skôr než pevne nastavíte cestu.

## Skontrolujte svoj projekt

- V Advisore opravte najprv funkcie `SECURITY DEFINER` na zozname; invoker funkcie aj tak bežia s právami volajúceho.
- Bezplatný [bezpečnostný checker pre Supabase](https://madsalt.tech/sk/tools/supabase-security-check/) ukazuje nálezy Advisora vedľa vlastných pravidiel, vrátane definer funkcií volateľných cez API a definer funkcií, ktoré kontrolujú `current_user`.
- Viac o tom, čo definer funkcie môžu preskočiť, je v poznámke [NULL v PL/pgSQL IF NOT preskočil kontrolu pri 3683 úlohách](https://madsalt.tech/sk/notes/plpgsql-if-not-null-fails-open/) a politiky, ktoré takéto pomocné funkcie volajú, sú v článku [Supabase RLS policy: príklady otestované cez API](https://madsalt.tech/sk/blog/supabase-rls-policy-examples/).
