---
title: "Supabase „function search path mutable“: ką tai reiškia"
description: "Ką reiškia Advisor įspėjimas function_search_path_mutable, kas gali juo pasinaudoti, kodėl search_path = public mano teste nepakako ir kaip tai ištaisyti."
url: "https://madsalt.tech/lt/notes/supabase-function-search-path-mutable/"
author: "Denys Zaichenko"
author_url: "https://madsalt.tech/lt/about/"
language: "lt"
published: 2026-11-01
---

# Supabase „function search path mutable“: ką tai reiškia

TL;DR

Įspėjimas reiškia, kad funkcija neturi fiksuoto `search_path`, todėl kiekvienas nekvalifikuotas vardas joje, pavyzdžiui, `admins`, ieškomas taip, kaip nurodo iškvietėjo sesija. Mano vietiniame teste funkcija `is_admin()` su `SECURITY DEFINER` vartotojui, kuris nėra adminas, grąžino `true`, kai jis sukūrė laikinąją lentelę `admins`, ir fiksuotas `search_path = public` to nesustabdė. Padėjo `set search_path = ''` su kiekviena lentele, užrašyta kaip `public.admins`.

Supabase Security Advisor tai nurodo kaip lint 0011, „Function Search Path Mutable“, lygis WARN, su detale „Function `public.x` has a role mutable search_path“. Jis pažymi kiekvieną funkciją be šio nustatymo, įskaitant `SECURITY INVOKER`, todėl projekte su daug pagalbinių funkcijų sąrašas ilgas: vienas atvirojo kodo projektas [ištaisė 99 iš jų viena migracija](https://github.com/bdougie/contributor.info/commit/3c73dda01df80faccb75278ec0f826a84e01a6d9).

## Ką reiškia „role mutable search_path“?

`search_path` yra schemų sąrašas, kuriame Postgres ieško vardo be schemos priekyje. Funkcija be savo nustatymo naudoja iškvietėjo sąrašą. Supabase [lint puslapis](https://supabase.com/docs/guides/database/database-linter?lint=0011_function_search_path_mutable) rekomenduoja jį nustatyti į tuščią eilutę, o tai priverčia kiekvieną nuorodą funkcijos viduje rašyti pilnai kvalifikuotą.

Funkcijai su `SECURITY DEFINER` tai svarbiausia: ji veikia su savo savininko teisėmis, paprastai `postgres`, kuris apeina RLS. Jei iškvietėjas gali valdyti, kurią lentelę `admins` funkcija skaito, jis valdo patikrą, veikiančią kaip `postgres`.

## Ar mano teste tuo galima pasinaudoti?

Vietiniame Postgres 17.10 sukūriau lentelę `public.admins` su vienu adminu ir keturias tos pačios funkcijos versijas, `select exists (select 1 from admins where user_id = uid)`, visas su `SECURITY DEFINER`. Tada prisijungiau kaip mažų teisių rolė, kuri gali vykdyti SQL ir pagal nutylėjimą gali kurti laikinąsias lenteles, ir sukūriau `pg_temp.admins` su vartotoju A, kuris nėra adminas:

![is_admin(A) po laikinosios lentelės admins. SECURITY DEFINER funkcija, vietinis Postgres 17.10. Be 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/lt/supabase-function-search-path-mutable.svg)

*Mano atkūrimas vietiniame Postgres, 2026 m. spalio 24 d.*

| Funkcijos nustatymas                                  | Lint sąlyga | `is_admin(A)` po laikinosios lentelės |
| ----------------------------------------------------- | ----------- | ------------------------------------- |
| nėra                                                  | pažymėta    | `true`                                |
| `set search_path = public`                            | praeina     | `true`                                |
| `set search_path = ''`, kūnas naudoja `public.admins` | praeina     | `false`                               |
| `set search_path = public, pg_temp`                   | praeina     | `false`                               |

Antroji eilutė mane nustebino. Supabase prižiūrėtojas [2024 m. parašė](https://github.com/supabase/splinter/issues/100), kad fiksuotas netuščias kelias nekelia jokios saugumo rizikos, ir lint jį priima. Tačiau Postgres pirmiausia ieško laikinojoje schemoje, nebent kelyje nurodytas `pg_temp`, kaip įspėja jo [dokumentacija apie SECURITY DEFINER funkcijų rašymą](https://www.postgresql.org/docs/current/sql-createfunction.html#SQL-CREATEFUNCTION-SECURITY). Taigi `search_path = public` vis tiek leidžia laikinajai lentelei užgožti nekvalifikuotą vardą.

## Kas tai iš tikrųjų gali padaryti Supabase?

Ne lankytojas per jūsų programą. Data API negali kurti laikinųjų lentelių ar keisti sesijos `search_path`, todėl vien publishable key iki to nepasiekia. Reikia tiesioginio prisijungimo prie duomenų bazės: antros paslaugos su savo prisijungimu, BI ar ataskaitų įrankio, rangovo tik skaitymo rolės, nutekėjusio connection string. Daugumoje mažų programų rizika maža; bet kuriame projekte, kur duomenų bazės prisijungimus turi kiti žmonės ar įrankiai, o ne jūs, pirmiausia ištaisykite definer funkcijas.

## Kaip tai ištaisyti?

Sukurkite funkciją iš naujo su tuščiu keliu ir schemų vardais kūne:

```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` svarbus ne mažiau nei kelias: definer funkciją `public` schemoje galima iškviesti per Data API, todėl be jo bet kuris svečias gali per `/rpc/is_admin` paklausti, ar duotas vartotojas yra adminas. Grąžinkite jį tik rolėms, kurioms jo reikia, arba laikykite tokias pagalbines funkcijas schemoje, kurios API neatveria.

Nevykdykite vien `alter function public.is_admin(uuid) set search_path = ''`. Tai nutildo Advisor ir sugadina funkciją: mano paleidime kitas iškvietimas nepavyko su `42P01: relation "admins" does not exist`. Plėtinių operatoriams taip pat reikia jų schemos, pavyzdžiui, `operator(extensions.<=>)` pgvector.

Sąrašas to, ką lint pažymės `public`, su definer funkcijomis pirmiausia:

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

## Ar pataisymas ką nors kainuoja?

Kartais. Bet kuri `set` sąlyga neleidžia Postgres įterpti paprastos SQL funkcijos į ją kviečiančią užklausą, kaip [nurodyta GitHub](https://github.com/supabase/supabase/issues/33131). Išmatavau abi formas su 200 000 eilučių ir indeksu filtruojamame stulpelyje (7 paleidimų mediana):

| SQL funkcija, paskui filtruojama pagal savininką     | Be `set`                    | `set search_path = ''` |
| ---------------------------------------------------- | --------------------------- | ---------------------- |
| Filtras išorėje: `visible_items() where owner = ...` | 0,053 ms, indeksas naudotas | 36,7 ms, full scan     |
| Filtras viduje: `items_of(owner)`                    | 0,056 ms                    | 0,082 ms               |

Jei aibę grąžinanti SQL funkcija užklausiama su filtru už jos ribų, prieš fiksuodami kelią perkelkite filtrą į vidų kaip parametrą.

## Patikrinkite savo projektą

- Advisor sąraše pirmiausia ištaisykite `SECURITY DEFINER` funkcijas; invoker funkcijos vis tiek veikia su iškvietėjo teisėmis.
- Nemokamas [Supabase saugos tikrintuvas](https://madsalt.tech/lt/tools/supabase-security-check/) rodo Advisor radinius šalia savo taisyklių, įskaitant definer funkcijas, iškviečiamas per API, ir definer funkcijas, kurios tikrina `current_user`.
- Daugiau apie tai, ką definer funkcijos gali praleisti, rasite pastaboje [NULL PL/pgSQL IF NOT praleido patikrą 3683 užduotims](https://madsalt.tech/lt/notes/plpgsql-if-not-null-fails-open/), o politikos, kviečiančios tokias pagalbines funkcijas, aprašytos straipsnyje [Supabase RLS policy: pavyzdžiai, išbandyti per API](https://madsalt.tech/lt/blog/supabase-rls-policy-examples/).
