Pāriet uz saturu

Kā testēt Supabase RLS: 30 no 87 atļaujām neko nedarīja

Skatīt kā MarkdownAtvērt pakalpojumā ClaudeAtvērt pakalpojumā ChatGPTAtvērt pakalpojumā Perplexity

Jautājums tiek arī nokopēts. Ja tērzēšana atveras tukša, ielīmējiet to.

Īsumā

Testējiet Supabase row level security (rindu līmeņa drošību) tā, kā ar to saskaras lietotājs: piesakieties kā katras lomas dalībnieks, izpildiet īstu darbību ar vienu atļauju vispirms izslēgtu un tad ieslēgtu, un pārbaudiet, vai rezultāts mainās. Produkcijas lietotnē ar piecām rediģējamām lomām tas atklāja, ka 30 no 87 atļauju rūtiņām, ko administrators varēja saglabāt, datubāzē neko nemainīja. Tests pa politikām to nevar redzēt, jo tas testē jūsu uzrakstīto politiku, nevis rūtiņu ekrānā.

Es esmu Denys, full-stack un AI inženieris. Šī gada sākumā es izveidoju projektu un darba laika uzskaites lietotni revīzijas uzņēmumam uz Supabase, kur katrs piekļuves noteikums dzīvo datubāzē. Zemāk ir metode, ar kuru es šos noteikumus pierādu, ko tā atrada un cik maksā tās palaišana.

Kāpēc ar politiku testiem nepietiek, lai testētu Supabase RLS?

Politikas tests atbild uz jautājumu “vai šī politika dara to, ko es uzrakstīju?”. Jūsu lietotāji jautā ko citu: “ja es šai lomai noņemu ķeksīti šajā rūtiņā, vai viņi joprojām to var izdarīt?”. Mazā lietotnē abiem jautājumiem ir viena atbilde. Lietotnē ar iestatījumu lapu, kur administratori rediģē atļauju matricu, atbildes aiziet katra savā virzienā.

Lietotnē bija 41 tabula ar ieslēgtu RLS un 402 CREATE POLICY priekšraksti 255 migrācijās. Administratora ekrānā bija matrica, kur jomas (projekti, uzdevumi, darba laika uzskaites lapas, faili un tā tālāk) bija pretī darbībām (skatīt, izveidot, rediģēt, arhivēt), pa vienai kolonnai katrai lomai. Katra saglabātā rūtiņa bija solījums administratoram. Nekas nepārbaudīja, vai datubāze to tur.

Supabase labi dokumentē standarta rīkus: pgTAP testi un testu palīgrīki ļauj izveidot lietotājus, pārslēgties uz tiem un pārbaudīt, ko tie var lasīt. Izmantojiet tos. Zemāk aprakstītā metode darbojas virs tiem: to vada matrica, ko redz administrators, nevis politiku saraksts.

Kā darbojas matricas pierādījums?

Katra matricas rūtiņa kļūst par zondi. Zonde piesakās kā lomas dalībnieks, izpilda vienu īstu darbību un pieraksta rezultātu. Tā to dara divreiz: vienreiz ar izslēgtu rūtiņu un vienreiz ar ieslēgtu, viss pārējais paliek nemainīgs. Ja rezultāts mainās, rūtiņa tiek ievērota. Ja nemainās, rūtiņa neko nedara, lai cik pārliecinoši tā izskatītos ekrānā.

  1. Viena rūtiņa matricāPiemērs: Standard Member · Uzdevumi · Izveidot
  2. Rūtiņa izslēgta: rīkojies kā lomas dalībnieksIzveido uzdevumu pa to pašu ceļu, ko izmanto lietotne. Pieraksti rezultātu.
  3. Rūtiņa ieslēgta: atkārto to pašu darbībuTas pats lietotājs, tie paši dati. Mainījās tikai rūtiņa.
  4. Salīdzini abus rezultātusSpriedums ir atkarīgs tikai no tā, ko izdarīja datubāze.
  • Rezultāts mainījāsIevērota. Izslēgta liedz, ieslēgta atļauj.
  • Abos gadījumos vienādsNeko nedara. Ekrāns sola kontroli, kuras nav.
  • Ekrāns un datubāze nesakrītKļūda. Lapa rāda pogu, ko datubāze noraida, vai otrādi.
1. attēls. Katrs spriedums rodas no īstu rezultātu pāra, nekad no koda lasīšanas.

“Rezultāts” šeit nozīmē to, ko dara atļaujas lietotājs: politika atgriež rindas vai noraida ierakstu, datubāzes funkcija izmet kļūdu, trigeris bloķē vai edge function atbild ar 403. Viena rūtiņa var sargāt vairākus no tiem, tāpēc 87 rūtiņām vajadzēja 279 pārbaudes katrai lomai. Katra pārbaude beidzās ar vienu no šiem spriedumiem:

Spriedums Pārbaudes katrai lomai Ko tas nozīmē
Iedarbība 227 Rezultāts mainījās līdz ar rūtiņu
Mirusi 30 Rezultāts palika tāds pats ar izslēgtu un ieslēgtu rūtiņu
Nesasniedzama 19 Šai lomai darbība vispār nav pieejama, pierādīts atsevišķi
Edge function 1 To nodrošina edge function, pārbaudīts caur HTTP
Tikai ekrānā 1 Maina tikai to, ko rāda lapa, sedz front-end tests
Ekrāns un datubāze sakrīt 1 Tas, ko piedāvā lapa, atbilst tam, ko atļauj datubāze
Neizdevās 0 Pārbaude, kuras rezultāts neatbilda gaidītajam

Ko pierādījums atrada?

Palaišana 2026. gada 15. septembrī aptvēra visas piecas lomas, ko administrators var rediģēt (Admin, Senior, Standard, Restricted un klonētu pielāgotu lomu). Rezultāti visām lomām bija vienādi.

87 atļauju rūtiņas, ko administrators varēja saglabāt. Pēc tā, kas katru rūtiņu patiesībā nodrošināja. Tas pats visām piecām lomām. Nodrošina datubāze: 55; Nodrošina citur: 1 edge function, 1 ekrāns: 2; Neko nemainīja: 30.
2. attēls. 87 atļauju rūtiņas, ko administrators varēja saglabāt, pēc tā, kas tās patiesībā nodrošināja. Tāds pats rezultāts katrai no piecām lomām.

Neviena no šīm 30 nevienu neielaida. 29 no tām nekad nenolasīja neviena politika, funkcija vai trigeris; pēdējā zaudēja nozīmi pēc tam, kad kāda politika tika pārrakstīta. Tā tomēr ir īsta problēma: administrators, kas lomai noņem ķeksīti pie “Dzēst”, tic, ka loma nevar dzēst. Mēs paslēpām visas 30, nevis tās pieslēdzām, jo katrai būtu vajadzīgs produkta lēmums, un rūtiņai, kas neko nedara, nav jābūt ekrānā, kamēr šo lēmumu pieņem.

Tas pats darbs atklāja vēl četrus defektus:

Kas bija nepareizi Kā tas izpaudās Labojums
30 rūtiņas neko nedarīja Spriedums “mirusi”, vienāds rezultāts izslēgtai un ieslēgtai Paslēptas no matricas
Trīs datubāzes funkcijas palaida darbību cauri, ja uzdevumam nebija atbildīgā Zonde, kurai vajadzēja tikt noraidītai, tika atļauta Funkcijas tagad noraida, ja vērtības nav
Darba laika lapas atiestatīšana uz melnrakstu bija atkarīga no nepareizās rūtiņas Vienai lomai ekrāns un datubāze nesakrita Piesaistīta atkārtotas atvēršanas atļaujai, kurai tā pieder
Dalībnieks redzēja jaunās atļaujas tikai pēc lapas pārlādes Datubāze bija mainījusies, lapa nē Atļaujas atjaunojas ik pēc 60 sekundēm un loga fokusā
Kolonnas noklusējuma vērtība pārkāpa pašas savu check ierobežojumu, tāpēc ieraksti bez šīs kolonnas neizdevās To atrada labojuma drošības pārskats tā paša darba laikā Noklusējums nomainīts uz vērtību, ko ierobežojums atļauj

Svarīgākā bija kļūda ar trūkstošo atbildīgo. Aizsardzības PL/pgSQL bija uzrakstītas šādi:

if not (has_permission or assignee_id = auth.uid()) then
  raise exception 'forbidden';
end if;

Ja assignee_id ir null, tad assignee_id = auth.uid() ir null, false or null ir null, not null joprojām ir null, un if uztver null kā nepatiesu. Izņēmums nekad neiedarbojas. Produkcijā nevienam uzdevumam nebija norādīts atbildīgais, tāpēc tas nebija robežgadījums: tas attiecās uz katru uzdevumu tvērumā. Labojums ietin nosacījumu coalesce(…, false), lai trūkstoša vērtība noraidītu, un tas tika izlaists pirmais, atsevišķi, pirms pārējās partijas.

Kā to palaist pret produkcijas kopiju?

Pierādījums uz shēmas, kas atšķiras no produkcijas, neko nepierāda par produkciju. Testu vide darbojas lokālajā Supabase stekā, atsevišķā datubāzē, kas ir produkcijas shēmas kopija plus pārskatāmās migrācijas. Pirms katras palaišanas tā salīdzina abu lokālo datubāzu nospiedumu: 799 rindas, kas apraksta tabulas, politikas, funkcijas un piešķirtās tiesības. Jebkura atšķirība aptur palaišanu.

  1. Produkcijas shēmaNokopēta uz vienu uzticamu datoru. Tā nekad nenonāk mitinātā CI izpildītājā.
  2. Lokālais steks: lietotnes datubāzeTo apkalpo lokālais API un auth, to izmanto end-to-end pārbaudes.
  3. Lokālais steks: izolēta pierādījuma datubāzeTā pati shēma, to izmanto tikai matricas pierādījums.
  4. Nospieduma pārbaude799 rindas katrā datubāzē. Jebkura atšķirība aptur palaišanu pirms pirmās zondes.
  5. Zondes darbojas katras lomas vārdāKā īsti pieteikušies dalībnieki, pa tiem pašiem ceļiem, ko izmanto lietotne.
  6. Nekādu pēduRindu skaitam beigās jāsakrīt ar rindu skaitu sākumā.
3. attēls. Pierādījums skaitās tikai tad, ja datubāze, uz kuras tas darbojās, atbilst produkcijai.

Divas detaļas padarīja testu vidi uzticamu:

  • Vispirms salauziet to apzināti. Pirms uzticēties zaļai palaišanai, es nomainīju vienu insert politiku pret tādu, kas pieņem visu, un palaidu pierādījumu vēlreiz. Tam bija jākļūst sarkanam uz tām rūtiņām, ko sargā šī politika. Testu vide, kas paliek zaļa ar salauztu politiku, ir dekorācija.
  • Pārbaudiet arī saglabāto stāvokli. Pirmā kārta katru lomu sāk no tīra sākumstāvokļa. Otrā kārta sāk no katras lomas pašreizējās saglabātās matricas, jo rūtiņa var uzvesties citādi blakus tām rūtiņām, ko administrators tiešām atzīmējis.

Kā to uzturēt zaļu pēc izlaides?

Vienreiz palaists pierādījums noveco ar nākamo migrāciju. Tagad pierādījums darbojas pirms katra push, kas skar migrācijas, auth kodu vai pašu matricu.

  1. git pushIzmaiņa skar migrācijas, auth vai matricu.
  2. Pre-push hook: matricas pierādījumsDarbojas lokāli pret pierādījuma datubāzi. Sarkans bloķē push.
  3. Push nonāk mainIzvietošana notiek kā parasti.
  4. Pašmitināta CI palaišanaTas pats pierādījums vēlreiz, uz datora, kurā ir produkcijas shēmas kopija.
4. attēls. Bloķējošie vārti ir lokāli, jo produkcijas shēmas kopija paliek uz viena datora.

Pierādījums ir 135 testi 11 failos, blakus aptuveni 3100 parastiem vienībtestiem tajā pašā projektā.

Kas nedarbojās?

  • Mitinātais CI to nevarēja palaist. Pierādījumam vajadzīga produkcijas shēmas kopija, un es negribēju to likt uz mitināta izpildītāja. Tāpēc bloķējošie vārti ir pre-push hook, un CI uz pašmitināta datora ir otrā pārbaude, kuru izvietošana negaida.
  • Fiksēti rindu skaiti salūza. Pirmā “nekādu pēdu” pārbaude salīdzināja ar fiksētiem skaitļiem. Nākamā pilnīgi likumīgā migrācija pievienoja atļaujas rindu un padarīja to sarkanu. Tagad tā salīdzina rindu skaitu beigās ar rindu skaitu sākumā.
  • Izlaists nenozīmē izturēts. End-to-end pārbaudēm vajadzīga lokālā edge izpildes vide. Kad tā nedarbojas, tās tiek izlaistas, un izlaišanu ir viegli nolasīt kā zaļu.
  • Noslogotā klēpjdatorā tas ir lēns. Kārtām pa lomām noslogotā datorā vajadzēja garākus taimautus.
  • Tas pierāda to, ko apraksta zondes. Rūtiņa, kurai neviens neuzrakstīja zondi, tiek norādīta kā pārklājuma robs, nevis kā droša.

Kā to atkārtot savā Supabase lietotnē

  1. Uzskaitiet katru atļauju, ko ekrāns ļauj kādam saglabāt: loma × joma × darbība. Šis saraksts ir specifikācija, nevis jūsu politikas.
  2. Katrai rūtiņai pierakstiet īsto darbību, kas tai būtu jākontrolē, un kā izpaužas atteikums (nav rindu, kļūda, 403).
  3. Izveidojiet datubāzi, kas atbilst produkcijai, un noņemiet tās nospiedumu, lai shēmas novirze apturētu palaišanu.
  4. Izpildiet katru darbību kā pieteicies dalībnieks, rūtiņu vispirms izslēdzot, tad ieslēdzot, un salīdziniet.
  5. Apzināti salauziet vienu politiku un pārliecinieties, ka pierādījums kļūst sarkans.
  6. Paslēpiet vai pieslēdziet katru rūtiņu, kas neko nedara, un padariet pierādījumu par vārtiem pirms push.

Ja jūsu lietotnē jau ir RLS, bet neesat pārliecināti, ka tā dara to, ko saka iestatījumu ekrāns, tā pati metode darbojas kā esošas koda bāzes audits, pirmais solis projekta glābšanā. Par projektu, kas ir aiz šiem skaitļiem, rakstīju revīzijas uzņēmuma darba laika uzskaites gadījumā.

Neesat pārliecināti, ka jūsu RLS atbilst iestatījumu ekrānam?

Labprāt salīdzināšu pieredzi par to, kā jūs to testējat. Bez pārdošanas.

Sazināties