---
title: "Supabase Function Search Path Mutable: vad det betyder"
description: "Vad Advisor-varningen function_search_path_mutable betyder, vem som kan utnyttja den, varför search_path = public inte räckte i mitt test, och åtgärden."
url: "https://madsalt.tech/sv/notes/supabase-function-search-path-mutable/"
author: "Denys Zaichenko"
author_url: "https://madsalt.tech/sv/about/"
language: "sv"
published: 2026-11-01
---

# Supabase Function Search Path Mutable: vad det betyder

Kort sagt

Varningen betyder att en funktion saknar fast `search_path`, så varje okvalificerat namn i den, som `admins`, slås upp som anroparens session anger. I mitt lokala test returnerade en `SECURITY DEFINER`-funktion `is_admin()` `true` för en icke-admin efter att användaren skapat en temporär tabell som hette `admins`, och att låsa `search_path = public` stoppade det inte. Det som fungerade: `set search_path = ''` med varje tabell skriven som `public.admins`.

Supabases Security Advisor listar detta som lint 0011, "Function Search Path Mutable", nivå WARN, med detaljen "Function `public.x` has a role mutable search_path". Den flaggar varje funktion utan inställningen, även `SECURITY INVOKER`-funktioner, så i ett projekt med många hjälpfunktioner blir listan lång: ett open source-projekt [åtgärdade 99 av dem i en enda migrering](https://github.com/bdougie/contributor.info/commit/3c73dda01df80faccb75278ec0f826a84e01a6d9).

## Vad betyder "role mutable search_path"?

`search_path` är listan över scheman som Postgres söker i efter ett namn utan schema framför. En funktion utan egen inställning använder anroparens lista. Supabases [lint-sida](https://supabase.com/docs/guides/database/database-linter?lint=0011_function_search_path_mutable) rekommenderar att sätta den till en tom sträng, vilket tvingar varje referens i funktionen att vara fullt kvalificerad.

För en `SECURITY DEFINER`-funktion spelar det störst roll: den körs med sin ägares rättigheter, vanligtvis `postgres`, som kringgår RLS. Om anroparen kan styra vilken `admins` den läser, styr anroparen en kontroll som körs som `postgres`.

## Går det att utnyttja i mitt test?

På en lokal Postgres 17.10 skapade jag en tabell `public.admins` med en admin och fyra versioner av samma funktion, `select exists (select 1 from admins where user_id = uid)`, alla `SECURITY DEFINER`. Sedan loggade jag in som en roll med låga rättigheter som kan köra SQL och har standardrätten att skapa temporära tabeller, och skapade `pg_temp.admins` med användare A, som inte är admin:

![is_admin(A) efter en temp-tabell admins. SECURITY DEFINER-funktion, lokal Postgres 17.10. Ingen 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/sv/supabase-function-search-path-mutable.svg)

*Min återskapning på en lokal Postgres, 24 oktober 2026.*

| Funktionens inställning                                  | Lint-villkor | `is_admin(A)` efter den temporära tabellen |
| -------------------------------------------------------- | ------------ | ------------------------------------------ |
| ingen                                                    | flaggad      | `true`                                     |
| `set search_path = public`                               | godkänd      | `true`                                     |
| `set search_path = ''`, kroppen använder `public.admins` | godkänd      | `false`                                    |
| `set search_path = public, pg_temp`                      | godkänd      | `false`                                    |

Den andra raden är den som överraskade mig. En Supabase-maintainer [skrev 2024](https://github.com/supabase/splinter/issues/100) att en fast icke-tom sökväg inte innebär någon säkerhetsrisk, och linten accepterar den. Men Postgres söker först i det temporära schemat om inte `pg_temp` nämns i sökvägen, som dess [dokumentation om att skriva SECURITY DEFINER-funktioner](https://www.postgresql.org/docs/current/sql-createfunction.html#SQL-CREATEFUNCTION-SECURITY) varnar för. Så med `search_path = public` kan en temporär tabell fortfarande skugga ett okvalificerat namn.

## Vem kan faktiskt göra detta på Supabase?

Inte en besökare via din app. Data API:t kan varken skapa temporära tabeller eller ändra sessionens `search_path`, så enbart publishable key tar inte dit. Det krävs en direkt databasanslutning: en andra tjänst med egen inloggning, ett BI- eller rapportverktyg, en konsults skrivskyddade roll, en läckt anslutningssträng. I de flesta små appar är risken låg; i varje projekt där andra personer eller verktyg än du har databasinloggningar, åtgärda definer-funktionerna först.

## Hur åtgärdar du det?

Skapa funktionen på nytt med tom sökväg och schemanamn i kroppen:

```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` är lika viktig som sökvägen: en definer-funktion i `public` kan anropas via Data API:t, så utan den kan vilken gäst som helst fråga `/rpc/is_admin` om en viss användare är admin. Ge tillbaka den bara till de roller som behöver den, eller håll sådana hjälpfunktioner i ett schema som API:t inte exponerar.

Kör inte bara `alter function public.is_admin(uuid) set search_path = ''`. Det tystar Advisor och förstör funktionen: i min körning misslyckades nästa anrop med `42P01: relation "admins" does not exist`. Operatorer från tillägg behöver också sitt schema, som `operator(extensions.<=>)` för pgvector.

Så listar du det linten flaggar i `public`, definer-funktioner först:

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

## Kostar åtgärden något?

Ibland. Varje `set`-klausul hindrar Postgres från att infoga en enkel SQL-funktion i frågan som anropar den, [som rapporterats på GitHub](https://github.com/supabase/supabase/issues/33131). Jag mätte båda formerna på 200 000 rader med ett index på den filtrerade kolumnen (median av 7 körningar):

| SQL-funktion, sedan filtrerad på ägare              | Utan `set`             | `set search_path = ''` |
| --------------------------------------------------- | ---------------------- | ---------------------- |
| Filter utanför: `visible_items() where owner = ...` | 0,053 ms, index använt | 36,7 ms, full skanning |
| Filter innanför: `items_of(owner)`                  | 0,056 ms               | 0,082 ms               |

Om en funktion som returnerar en mängd frågas med ett filter utanför, flytta in filtret som parameter innan du låser sökvägen.

## Kontrollera ditt projekt

- Åtgärda i Advisor först `SECURITY DEFINER`-funktionerna på listan; invoker-funktioner körs ändå med anroparens rättigheter.
- Den kostnadsfria [Supabase-säkerhetskontrollen](https://madsalt.tech/sv/tools/supabase-security-check/) visar Advisor-fynd bredvid sina egna regler, inklusive definer-funktioner som kan anropas via API:t och definer-funktioner som kontrollerar `current_user`.
- Mer om vad definer-funktioner kan hoppa över finns i [NULL i PL/pgSQL IF NOT släppte igenom 3683 uppgifter](https://madsalt.tech/sv/notes/plpgsql-if-not-null-fails-open/), och policyer som anropar sådana hjälpfunktioner i [Supabase RLS policy-exempel, testade via API:t](https://madsalt.tech/sv/blog/supabase-rls-policy-examples/).
