---
title: "Supabase \"function search path mutable\": mitä se tarkoittaa"
description: "Mitä varoitus function_search_path_mutable tarkoittaa, kuka sen voi hyödyntää, miksi search_path = public ei riittänyt testissäni ja miten se korjataan."
url: "https://madsalt.tech/fi/notes/supabase-function-search-path-mutable/"
author: "Denys Zaichenko"
author_url: "https://madsalt.tech/fi/about/"
language: "fi"
published: 2026-11-01
---

# Supabase "function search path mutable": mitä se tarkoittaa

TL;DR

Varoitus tarkoittaa, että funktiolla ei ole kiinteää `search_path`-asetusta, joten jokainen sen sisällä oleva ilman skeemaa kirjoitettu nimi, kuten `admins`, haetaan niin kuin kutsujan istunto sanoo. Paikallisessa testissäni `SECURITY DEFINER` -funktio `is_admin()` palautti `true` käyttäjälle, joka ei ole admin, kun tämä oli luonut väliaikaisen taulun nimeltä `admins`, eikä kiinteä `search_path = public` pysäyttänyt sitä. Toimi `set search_path = ''`, kun jokainen taulu kirjoitetaan muodossa `public.admins`.

Supabasen Security Advisor listaa tämän lintiksi 0011, "Function Search Path Mutable", taso WARN, tarkenteella "Function `public.x` has a role mutable search_path". Se liputtaa jokaisen funktion, jolla asetusta ei ole, myös `SECURITY INVOKER` -funktiot, joten projektissa, jossa on paljon apufunktioita, lista venyy pitkäksi: yksi avoimen lähdekoodin projekti [korjasi niistä 99 yhdellä migraatiolla](https://github.com/bdougie/contributor.info/commit/3c73dda01df80faccb75278ec0f826a84e01a6d9).

## Mitä "role mutable search_path" tarkoittaa?

`search_path` on lista skeemoista, joista Postgres etsii nimeä, jonka edessä ei ole skeemaa. Funktio, jolla ei ole omaa asetusta, käyttää kutsujan listaa. Supabasen [lint-sivu](https://supabase.com/docs/guides/database/database-linter?lint=0011_function_search_path_mutable) suosittelee asettamaan sen tyhjäksi merkkijonoksi, mikä pakottaa kirjoittamaan jokaisen funktion sisällä olevan viittauksen täydellä skeemanimellä.

`SECURITY DEFINER` -funktiolle tällä on eniten merkitystä: se ajetaan omistajansa oikeuksilla, yleensä `postgres`, joka ohittaa RLS:n. Jos kutsuja voi ohjata, mitä `admins`-taulua funktio lukee, kutsuja ohjaa tarkistusta, joka ajetaan käyttäjänä `postgres`.

## Voiko tätä hyödyntää testissäni?

Paikallisessa Postgres 17.10:ssä tein taulun `public.admins`, jossa oli yksi admin, ja neljä versiota samasta funktiosta, `select exists (select 1 from admins where user_id = uid)`, kaikki `SECURITY DEFINER`. Sitten kirjauduin sisään vähäoikeuksisena roolina, joka voi ajaa SQL:ää ja jolla on oletuksena oikeus luoda väliaikaisia tauluja, ja loin taulun `pg_temp.admins`, jossa oli käyttäjä A, joka ei ole admin:

![is_admin(A) väliaikaisen admins-taulun jälkeen. SECURITY DEFINER -funktio, paikallinen Postgres 17.10. Ei 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/fi/supabase-function-search-path-mutable.svg)

*Toistoni paikallisella Postgresilla, 24. lokakuuta 2026.*

| Funktion asetus                                  | Lint-ehto | `is_admin(A)` väliaikaisen taulun jälkeen |
| ------------------------------------------------ | --------- | ----------------------------------------- |
| ei mitään                                        | liputettu | `true`                                    |
| `set search_path = public`                       | läpäisee  | `true`                                    |
| `set search_path = ''`, rungossa `public.admins` | läpäisee  | `false`                                   |
| `set search_path = public, pg_temp`              | läpäisee  | `false`                                   |

Toinen rivi yllätti minut. Supabasen ylläpitäjä [kirjoitti vuonna 2024](https://github.com/supabase/splinter/issues/100), ettei kiinteä, ei-tyhjä polku aiheuta tietoturvariskiä, ja lint hyväksyy sen. Postgres kuitenkin hakee ensin väliaikaisesta skeemasta, ellei polussa mainita `pg_temp`, kuten sen [SECURITY DEFINER -funktioiden kirjoittamista käsittelevä dokumentaatio](https://www.postgresql.org/docs/current/sql-createfunction.html#SQL-CREATEFUNCTION-SECURITY) varoittaa. Siis `search_path = public` antaa väliaikaisen taulun silti peittää skeematon nimi.

## Kuka tämän voi oikeasti tehdä Supabasessa?

Ei sivuston kävijä sovelluksesi kautta. Data API ei voi luoda väliaikaisia tauluja eikä muuttaa istunnon `search_path`-asetusta, joten pelkkä publishable key ei riitä. Tarvitaan suora tietokantayhteys: toinen palvelu omalla tunnuksellaan, BI- tai raportointityökalu, ulkopuolisen tekijän vain luku -rooli tai vuotanut connection string. Useimmissa pienissä sovelluksissa riski on pieni; projektissa, jossa tietokantatunnuksia on muillakin ihmisillä tai työkaluilla kuin sinulla, korjaa definer-funktiot ensin.

## Miten se korjataan?

Luo funktio uudelleen tyhjällä polulla ja skeemanimillä rungossa:

```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` on yhtä tärkeä kuin polku: `public`-skeeman definer-funktio on kutsuttavissa Data API:n kautta, joten ilman sitä kuka tahansa vieras voi kysyä osoitteesta `/rpc/is_admin`, onko tietty käyttäjä admin. Anna oikeus takaisin vain rooleille, jotka sitä tarvitsevat, tai pidä tällaiset apufunktiot skeemassa, jota API ei julkaise.

Älä aja pelkkää `alter function public.is_admin(uuid) set search_path = ''`. Se vaientaa Advisorin ja rikkoo funktion: ajossani seuraava kutsu epäonnistui virheeseen `42P01: relation "admins" does not exist`. Laajennusten operaattorit tarvitsevat myös skeemansa, esimerkiksi `operator(extensions.<=>)` pgvectorille.

Lista siitä, mitä lint liputtaa `public`-skeemassa, definer-funktiot ensin:

```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;
```

## Maksaako korjaus jotain?

Joskus. Mikä tahansa `set`-lauseke estää Postgresia upottamasta tavallista SQL-funktiota sitä kutsuvaan kyselyyn, kuten [GitHubissa on raportoitu](https://github.com/supabase/supabase/issues/33131). Mittasin molemmat muodot 200 000 rivillä, suodatettavassa sarakkeessa indeksi (mediaani 7 ajosta):

| SQL-funktio, sitten suodatus omistajan mukaan              | Ilman `set`                | `set search_path = ''` |
| ---------------------------------------------------------- | -------------------------- | ---------------------- |
| Suodatin ulkopuolella: `visible_items() where owner = ...` | 0,053 ms, indeksi käytössä | 36,7 ms, full scan     |
| Suodatin sisällä: `items_of(owner)`                        | 0,056 ms                   | 0,082 ms               |

Jos joukon palauttavaa SQL-funktiota kysytään suodattimella sen ulkopuolella, siirrä suodatin parametriksi funktion sisään ennen kuin lukitset polun.

## Tarkista projektisi

- Korjaa Advisorin listalta ensin `SECURITY DEFINER` -funktiot; invoker-funktiot ajetaan joka tapauksessa kutsujan oikeuksilla.
- Ilmainen [Supabase-tietoturvatarkistus](https://madsalt.tech/fi/tools/supabase-security-check/) näyttää Advisorin havainnot omien sääntöjensä vieressä, mukaan lukien API:n kautta kutsuttavat definer-funktiot ja definer-funktiot, jotka tarkistavat `current_user`in.
- Lisää siitä, mitä definer-funktiot voivat ohittaa, on muistiinpanossa [PL/pgSQL IF NOT ja NULL ohitti tarkistuksen 3683 tehtävällä](https://madsalt.tech/fi/notes/plpgsql-if-not-null-fails-open/), ja tällaisia apufunktioita kutsuvat politiikat löytyvät artikkelista [Supabase RLS policy -esimerkit testattuna API:n kautta](https://madsalt.tech/fi/blog/supabase-rls-policy-examples/).
