---
title: "Kaip testuoti Supabase RLS: 30 iš 87 leidimų nieko nedarė"
description: "Supabase RLS testai pagal politikas nemato to, ką mato vartotojas. Patikra pagal roles produkcijos kopijoje rado 30 iš 87 leidimų, kurie nieko nedarė."
url: "https://madsalt.tech/lt/blog/how-to-test-supabase-rls/"
author: "Denys Zaichenko"
language: "lt"
published: 2026-10-14
---

# Kaip testuoti Supabase RLS: 30 iš 87 leidimų nieko nedarė

Trumpai

Supabase row level security (eilučių lygio saugą) testuokite taip, kaip ją sutinka narys: prisijunkite kiekviena role, atlikite tikrą veiksmą su išjungtu, o paskui įjungtu leidimu ir patikrinkite, ar rezultatas pasikeičia. Produkcinėje programoje su penkiomis redaguojamomis rolėmis taip paaiškėjo, kad 30 iš 87 leidimų langelių, kuriuos administratorius galėjo išsaugoti, duomenų bazėje nieko nekeitė. Testas pagal atskiras politikas to nepamato, nes jis tikrina jūsų parašytą politiką, o ne langelį ekrane.

Esu Denys, full-stack ir AI inžinierius. Šiais metais Supabase pagrindu sukūriau projektų ir laiko apskaitos sistemą audito įmonei, kurioje kiekviena prieigos taisyklė gyvena duomenų bazėje. Toliau aprašau metodą, kuriuo tas taisykles įrodau, ką jis rado ir kiek kainuoja jį paleisti.

## Kodėl politikų testų nepakanka Supabase RLS patikrinti?

Politikos testas atsako į klausimą „ar ši politika daro tai, ką parašiau?". Jūsų vartotojai klausia ko kito: „jei šiai rolei nuimsiu šią varnelę, ar ji vis tiek galės tai padaryti?". Mažoje programoje atsakymai sutampa. Programoje su nustatymų puslapiu, kuriame administratoriai redaguoja leidimų matricą, jie išsiskiria.

Programa turėjo 41 lentelę su įjungtu RLS ir 402 `CREATE POLICY` sakinius 255 migracijose. Administratoriaus ekrane buvo matrica: sritys (projektai, užduotys, darbo laiko žiniaraščiai, failai ir t. t.) prieš veiksmus (peržiūrėti, kurti, redaguoti, archyvuoti), po vieną stulpelį kiekvienai rolei. Kiekvienas išsaugotas langelis buvo pažadas administratoriui. Niekas netikrino, ar duomenų bazė jį laiko.

Supabase gerai dokumentuoja standartinius įrankius: [pgTAP testai](https://supabase.com/docs/guides/database/extensions/pgtap) ir [testų pagalbinės funkcijos](https://supabase.com/docs/guides/local-development/testing/pgtap-extended) leidžia sukurti vartotojus, persijungti į juos ir tikrinti, ką jie gali skaityti. Naudokite juos. Žemiau aprašytas metodas veikia virš jų: jį valdo administratoriaus matoma matrica, o ne politikų sąrašas.

## Kaip veikia matricos įrodymas?

Kiekvienas matricos langelis tampa zondu. Zondas prisijungia kaip tos rolės narys, atlieka vieną tikrą veiksmą ir užfiksuoja rezultatą. Tai daroma du kartus: kartą su išjungtu langeliu, kartą su įjungtu, visa kita vienoda. Jei rezultatas pasikeičia, langelis veikia. Jei ne, langelis nieko nedaro, kad ir kaip įtikinamai jis atrodytų ekrane.

1. **Vienas matricos langelis**. Pavyzdys: Standard Member · Užduotys · Kurti
2. **Langelis išjungtas: veikiame kaip rolės narys**. Sukuriame užduotį tuo pačiu keliu, kuriuo tai daro programa. Užfiksuojame rezultatą.
3. **Langelis įjungtas: kartojame tą patį veiksmą**. Tas pats vartotojas, tie patys duomenys. Pasikeitė tik langelis.
4. **Palyginame du rezultatus**. Verdiktas priklauso tik nuo to, ką padarė duomenų bazė.
- **Rezultatas pasikeitė**. Veikia. Išjungus atmetama, įjungus leidžiama.
- **Abiem atvejais tas pats**. Nieko nedaro. Ekranas žada kontrolę, kurios nėra.
- **Ekranas ir duomenų bazė nesutaria**. Klaida. Puslapis rodo mygtuką, kurį duomenų bazė atmeta, arba atvirkščiai.

*1 pav. Kiekvienas verdiktas kyla iš tikrų rezultatų poros, niekada ne iš kodo skaitymo.*

„Rezultatas" čia reiškia tai, ką daro leidimo vartotojas: politika grąžina eilutes arba atmeta įrašymą, duomenų bazės funkcija iškelia klaidą, trigeris blokuoja arba edge funkcija atsako 403. Vienas langelis gali saugoti kelis iš jų, todėl 87 langeliams prireikė 279 patikrų kiekvienai rolei. Kiekviena patikra baigėsi vienu iš šių verdiktų:

| Verdiktas                       | Patikrų rolei | Ką tai reiškia                                                |
| ------------------------------- | ------------- | ------------------------------------------------------------- |
| Veikia                          | 227           | Rezultatas pasikeitė kartu su langeliu                        |
| Neveikia                        | 30            | Rezultatas liko toks pat su išjungtu ir įjungtu langeliu      |
| Nepasiekiama                    | 19            | Šiai rolei veiksmas apskritai nepasiekiamas, įrodyta atskirai |
| Edge funkcija                   | 1             | Užtikrina edge funkcija, patikrinta per HTTP                  |
| Tik ekrane                      | 1             | Keičia tik tai, ką rodo puslapis, padengta front-end testu    |
| Ekranas ir duomenų bazė sutaria | 1             | Tai, ką siūlo puslapis, atitinka tai, ką leidžia duomenų bazė |
| Nepavyko                        | 0             | Patikra, kurios rezultatas neatitiko lūkesčio                 |

## Ką įrodymas rado?

2026 m. rugsėjo 15 d. paleidimas apėmė visas penkias roles, kurias administratorius gali redaguoti (Admin, Senior, Standard, Restricted ir nukopijuotą pasirinktinę rolę). Rezultatai visoms rolėms buvo vienodi.

![87 leidimų langeliai, kuriuos administratorius galėjo išsaugoti. Pagal tai, kas iš tikrųjų užtikrino kiekvieną langelį. Tas pats visoms penkioms rolėms. Užtikrina duomenų bazė: 55; Užtikrinama kitur: 1 edge funkcija, 1 ekranas: 2; Nieko nekeitė: 30.](https://madsalt.tech/charts/lt/how-to-test-supabase-rls.svg)

*2 pav. 87 leidimų langeliai, kuriuos administratorius galėjo išsaugoti, pagal tai, kas juos iš tikrųjų užtikrino. Tas pats rezultatas kiekvienai iš penkių rolių.*

Nė vienas iš 30 nieko neįleido. 29 iš jų neskaitė jokia politika, funkcija ar trigeris; paskutinis nustojo turėti reikšmę, kai buvo perrašyta politika. Tai vis tiek tikra problema: administratorius, nuėmęs varnelę „Ištrinti" kuriai nors rolei, tiki, kad rolė negali trinti. Visus 30 paslėpėme, o ne prijungėme, nes kiekvienam būtų reikėję produkto sprendimo, o nieko nedarantis langelis neturėtų būti ekrane, kol tą sprendimą priimate.

Tas pats darbas atskleidė dar keturis defektus:

| Kas buvo blogai                                                                                    | Kaip tai pasirodė                                             | Pataisa                                                         |
| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------- |
| 30 langelių nieko nedarė                                                                           | Verdiktas „Neveikia", tas pats rezultatas išjungus ir įjungus | Paslėpti iš matricos                                            |
| Trys duomenų bazės funkcijos praleisdavo veiksmą, kai užduotis neturėjo vykdytojo                  | Zondas, kuris turėjo būti atmestas, buvo praleistas           | Funkcijos dabar atmeta, kai reikšmės nėra                       |
| Žiniaraščio grąžinimas į juodraštį priklausė nuo netinkamo langelio                                | Vienai rolei ekranas ir duomenų bazė nesutarė                 | Susieta su pakartotinio atidarymo leidimu, kuriam jis priklauso |
| Narys naujus leidimus matė tik perkrovęs puslapį                                                   | Duomenų bazė pasikeitė, puslapis ne                           | Leidimai atnaujinami kas 60 sekundžių ir sufokusavus langą      |
| Stulpelio numatytoji reikšmė pažeidė jo paties check apribojimą, todėl įrašai be to stulpelio lūžo | Rado pataisos saugumo peržiūra, to paties darbo metu          | Numatytoji reikšmė pakeista į tokią, kurią apribojimas leidžia  |

Svarbiausia buvo trūkstamo vykdytojo klaida. PL/pgSQL apsaugos buvo parašytos taip:

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

Kai `assignee_id` yra null, `assignee_id = auth.uid()` yra null, `false or null` yra null, `not null` vis dar null, o `if` null traktuoja kaip false. Išimtis niekada neiškeliama. Produkcijoje nė viena užduotis neturėjo nustatyto vykdytojo, todėl tai nebuvo kraštutinis atvejis: tai galiojo kiekvienai aprėpties užduočiai. Pataisa apgaubia sąlygą `coalesce(…, false)`, kad trūkstama reikšmė būtų atmesta, ir ji buvo išleista pirmoji, atskirai, prieš likusį paketą.

## Kaip tai paleisti prieš produkcijos kopiją?

Įrodymas prieš schemą, kuri skiriasi nuo produkcijos, apie produkciją nieko neįrodo. Testų sistema veikia vietiniame Supabase steke, atskiroje duomenų bazėje, kuri yra produkcijos schemos kopija plius peržiūrimos migracijos. Prieš kiekvieną paleidimą ji palygina abiejų vietinių duomenų bazių kontrolinį atspaudą: 799 eilutes, aprašančias lenteles, politikas, funkcijas ir teises. Bet koks skirtumas sustabdo paleidimą.

1. **Produkcijos schema**. Nukopijuota į vieną patikimą kompiuterį. Ji niekada nepatenka į talpinamą CI vykdytoją.
2. **Vietinis stekas: programos duomenų bazė**. Aptarnaujama vietinio API ir autentifikacijos, naudojama end-to-end patikrų.
3. **Vietinis stekas: izoliuota įrodymo duomenų bazė**. Ta pati schema, naudojama tik matricos įrodymui.
4. **Atspaudo patikra**. 799 eilutės kiekvienoje duomenų bazėje. Bet koks skirtumas sustabdo paleidimą dar prieš pirmą zondą.
5. **Zondai veikia kiekviena role**. Kaip tikri prisijungę nariai, tais pačiais keliais, kuriais naudojasi programa.
6. **Nelieka pėdsakų**. Eilučių skaičius pabaigoje turi sutapti su eilučių skaičiumi pradžioje.

*3 pav. Įrodymas galioja tik tada, kai duomenų bazė, kurioje jis veikė, atitinka produkciją.*

Dvi detalės padarė testų sistemą patikimą:

- **Pirmiausia sugadinkite tyčia.** Prieš pasitikėdamas žaliu paleidimu, vieną įterpimo politiką pakeičiau tokia, kuri priima viską, ir paleidau įrodymą dar kartą. Jis turėjo parausti ties langeliais, kuriuos ta politika saugo. Testų sistema, kuri lieka žalia su sugadinta politika, yra dekoracija.
- **Patikrinkite ir išsaugotą būseną.** Pirmas praėjimas kiekvieną rolę pradeda nuo švarios bazinės būsenos. Antras pradeda nuo dabartinės kiekvienos rolės išsaugotos matricos, nes langelis gali elgtis kitaip šalia langelių, kuriuos administratorius iš tikrųjų pažymėjo.

## Kaip išlaikyti jį žalią po išleidimo?

Kartą paleistas įrodymas pasensta su kita migracija. Dabar įrodymas veikia prieš kiekvieną push, kuris paliečia migracijas, autentifikacijos kodą ar pačią matricą.

1. **git push**. Pakeitimas paliečia migracijas, autentifikaciją ar matricą.
2. **Pre-push hook: matricos įrodymas**. Veikia vietoje prieš įrodymo duomenų bazę. Raudona blokuoja push.
3. **Push pasiekia main**. Diegimas vyksta kaip įprasta.
4. **Savarankiškai talpinamas CI paleidimas**. Tas pats įrodymas dar kartą, kompiuteryje, kuriame laikoma produkcijos schemos kopija.

*4 pav. Blokuojantys vartai yra vietiniai, nes produkcijos schemos kopija lieka viename kompiuteryje.*

Įrodymą sudaro 135 testai 11 failų, šalia maždaug 3 100 įprastų unit testų tame pačiame projekte.

## Kas neveikė?

- **Talpinamas CI negalėjo jo paleisti.** Įrodymui reikia produkcijos schemos kopijos, o jos nenorėjau laikyti talpinamame vykdytojuje. Todėl blokuojantys vartai yra pre-push hook, o CI savarankiškai talpinamame kompiuteryje yra antra patikra, kurios diegimas nelaukia.
- **Fiksuoti eilučių skaičiai lūžo.** Pirmoji „nelieka pėdsakų" patikra lygino su fiksuotais skaičiais. Kita teisėta migracija pridėjo leidimo eilutę ir patikra paraudo. Dabar ji lygina skaičius pabaigoje su skaičiais pradžioje.
- **Praleista nėra praėjusi.** End-to-end patikroms reikia vietinio edge runtime. Kai jis neveikia, jos praleidžiamos, o praleidimą lengva perskaityti kaip žalią.
- **Užimtame nešiojamame kompiuteryje jis lėtas.** Apkrautame kompiuteryje praėjimams pagal roles reikėjo ilgesnių laiko limitų.
- **Jis įrodo tai, ką aprašo zondai.** Langelis, kuriam niekas neparašė zondo, pateikiamas kaip aprėpties spraga, o ne kaip saugus.

## Kaip tai pakartoti savo Supabase programoje

1. Surašykite kiekvieną leidimą, kurį ekranas leidžia išsaugoti: rolė × sritis × veiksmas. Tas sąrašas yra specifikacija, o ne jūsų politikos.
2. Kiekvienam langeliui užrašykite tikrą veiksmą, kurį jis turėtų valdyti, ir kaip pasireiškia atmetimas (jokių eilučių, klaida, 403).
3. Sukurkite duomenų bazę, atitinkančią produkciją, ir padarykite jos atspaudą, kad schemos nukrypimas sustabdytų paleidimą.
4. Kiekvieną veiksmą atlikite kaip prisijungęs narys, langelis išjungtas, tada įjungtas, ir palyginkite.
5. Tyčia sugadinkite vieną politiką ir įsitikinkite, kad įrodymas parausta.
6. Paslėpkite arba prijunkite kiekvieną nieko nedarantį langelį, tada paverskite įrodymą vartais prieš push.

Jei jūsų programa jau turi RLS, bet nesate tikri, ar jis daro tai, ką sako nustatymų ekranas, tas pats metodas tinka esamos kodo bazės auditui, pirmam [projekto gelbėjimo](https://madsalt.tech/lt/services/project-rescue/) žingsniui. Apie projektą, iš kurio yra šie skaičiai, rašiau [audito įmonės laiko apskaitos atvejyje](https://madsalt.tech/lt/work/audit-firm-time-tracking/).

Nesate tikri, ar jūsų RLS atitinka nustatymų ekraną?

Mielai palyginsiu, kaip jūs tai testuojate. Be pardavimo.

[Susisiekti](https://madsalt.tech/lt/#contact)
