---
title: "NULL PL/pgSQL IF NOT izlaida pārbaudi 3683 uzdevumiem"
description: "SECURITY DEFINER funkcijā false OR NULL ir NULL, un IF NOT izlaiž RAISE. Kāpēc tā pati pārbaude RLS ir droša, coalesce labojums un kā meklēt līdzīgas kļūdas."
url: "https://madsalt.tech/lv/notes/plpgsql-if-not-null-fails-open/"
author: "Denys Zaichenko"
language: "lv"
published: 2026-10-18
---

# NULL PL/pgSQL IF NOT izlaida pārbaudi 3683 uzdevumiem

Īsumā

Atļaujas pārbaude `SECURITY DEFINER` funkcijā bija `if not (has_perm or created_by = me or assignee_id = me) then raise`. 2026. gada jūnijā visiem 3683 lietotnes uzdevumiem nebija ne izveidotāja, ne atbildīgā. `false OR NULL OR NULL` ir `NULL`, `NOT NULL` joprojām ir `NULL`, un `IF` izlaiž savu pamatdaļu, ja nosacījums ir `NULL`. Tāpēc kļūda netika izmesta nekad, un jebkurš pieteicies lietotājs varēja klonēt uzdevumus. Labojums ir `coalesce(..., false)` ap visu nosacījumu.

Tas notika [revīzijas uzņēmuma darba laika uzskaites](https://madsalt.tech/lv/work/audit-firm-time-tracking/) lietotnē, funkcijā, kas stāv aiz "klonēt uzdevumu".

## Kā izskatījās pārbaude?

```sql
if not ( has_perm('tasks', 'create')
         or v_task.created_by = auth.uid()
         or v_task.assignee_id = auth.uid() ) then
  raise exception 'forbidden';
end if;
```

Tā skan kā "atsaki, ja vien tu nedrīksti veidot uzdevumus, neizveidoji šo vai nepieder tev". 3683 uzdevumi bija iesēti automātiski, tāpēc `created_by` un `assignee_id` visiem bija `NULL`. Lomai ar izslēgtām visām atļaujām nosacījums bija `NOT (false OR NULL OR NULL)`, kas ir `NULL`, un `raise` nekad neizpildījās.

## Kāpēc tas pats nosacījums RLS ir drošs?

Row level security visu, kas nav `true`, uztver kā atteikumu, tāpēc `NULL` nozīmē nē. PL/pgSQL `IF` visu, kas nav `true`, uztver kā "izlaid pamatdaļu", un šeit pamatdaļa bija tieši atteikums. PostgreSQL dokumentācija nosaka abas puses, par [row security](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) un par [IF](https://www.postgresql.org/docs/current/plpgsql-control-structures.html); kopā tās to pašu izteiksmi vienā vietā pārvērš aizliegumā, bet otrā atļaujā.

![Tas pats NULL, divas pretējas atbildes. has_perm ir false, created_by un assignee_id ir NULL. RLS politikā: false OR NULL → NULL → Rinda atteikta; IF NOT (...) THEN RAISE gadījumā: false OR NULL → NOT NULL ir NULL → RAISE izlaists → Klons atļauts.](https://madsalt.tech/charts/lv/plpgsql-if-not-null-fails-open.svg)

*Trīsvērtību loģika, kā to apraksta PostgreSQL 18 dokumentācija, sadaļas 5.9 (row security) un 41.6.4 (IF).*

`SECURITY DEFINER` funkcija darbojas arī apejot RLS, tāpēc spēkā ir tikai tās pašas pārbaude. Pietiek ar vienu `NULL` zaru: visi pārējie var būt `false`.

## Kas to izlaboja?

Ietiniet visu nosacījumu tā, lai `NULL` kļūtu par atteikumu, un uztveriet klonēšanu kā uzdevuma izveidi, nevis avota rediģēšanu:

```sql
if not coalesce((select has_perm('tasks', 'create')), false) then
  raise exception 'forbidden' using errcode = '42501';
end if;
```

## Vai tas atkārtojās?

Jā, apmēram pēc trim mēnešiem, vēl trijās funkcijās: uzdevuma arhivēšana, tā tipa maiņa un pārbaude, vai uz tā drīkst reģistrēt laiku. SQL lasīšana tās nenotvēra. Notvēra [RLS pierādījums pa lomām](https://madsalt.tech/lv/blog/how-to-test-supabase-rls/), jo tas izslēdz katru atļauju un pārbauda, vai darbība tiek noraidīta.

## Kā meklēt?

`grep 'if not ('` atrod sintaksi, nevis kļūdu. Kas strādāja:

1. Ar vienu vaicājumu izvelciet visu funkciju pamatdaļas ar `pg_get_functiondef`.
2. Katrā `if not (...) then` un `if a <> b then` noņemiet `coalesce(...)`, `exists(...)` un `(select ...)`. Kailā `or` ķēde vai salīdzinājums ar kolonnu, kas var būt NULL, ir kandidāts.
3. Meklējiet arī `if not <variable> then` un izsekojiet, kur mainīgajam piešķir vērtību. `_ok := has_perm(...) or created_by = me;` paslēpj to pašu kļūdu divās rindās.
4. Katru palīgfunkciju, ko lieto kā `if not helper(...)`, izsauciet trīs veidos atceltā transakcijā: nezināms id, `NULL` arguments un īsts lietotājs, kuram gaidītās rindas nav. Palīgfunkcijai, kas jebkurā no tiem atgriež `NULL`, vajadzīgs `coalesce` izsaukuma vietā vai iekšpusē.

Tajā lietotnē pārbaude aptvēra 14 palīgfunkcijas, visas jau bija drošas. Kļūda aprobežojās ar klonēšanas funkciju un trim vēlāk atrastajām. Saistīta vairāku nomnieku nepilnība: [ko RLS izlaiž lietotājam divās org.](https://madsalt.tech/lv/notes/supabase-rls-user-in-two-organizations/). Lietotne bija revīzijas uzņēmuma darba laika uzskaites rīks; tā citas klusās kļūdas ir rakstā [darba laika uzskaite revīzijas uzņēmumam](https://madsalt.tech/lv/blog/time-tracking-for-audit-firms/). [Supabase drošības pārbaude](https://madsalt.tech/lv/tools/supabase-security-check/) savos 46 noteikumos uzskaita SECURITY DEFINER funkcijas, bet nevar pateikt, vai IF tajās izlaiž pārbaudi (fails open), tāpēc šī pārlūkošana paliek manuāla.
