Strona główna / Blog / pgAdmin: incomplete fix lineage

Blog · case study

pgAdmin 9.17 naprawia trzy incomplete fixy z 9.16: kiedy „patched” nie znaczy „naprawione”

W typowym procesie VM historia kończy się szybko: skaner wykrywa CVE, producent wskazuje fixed version, zespół aktualizuje, finding znika z kolejnego skanu. pgAdmin 4 pokazuje, dlaczego taki model bywa zbyt prosty. Wersja 9.16 naprawiała m.in. SQL injection CVE-2026-12044, bypass read-only w AI Assistant CVE-2026-12045 oraz brak autoryzacji na endpointach SQL Editora CVE-2026-12046. W 9.17 projekt opublikował trzy kolejne CVE i przy każdym wprost zaznaczył, że to incomplete fix wcześniejszego problemu.

CVE-2026-12044 → CVE-2026-17346 CVE-2026-12045 → CVE-2026-17351 CVE-2026-12046 → CVE-2026-17348

To nie jest ciekawostka dla researcherów. Dla Vulnerability Engineera to problem z wiarygodnością stanu Closed.

Pierwszy przypadek: SQL injection naprawione lokalnie, nie klasowo

CVE-2026-12044 obejmowało szereg miejsc, w których pgAdmin generował SQL z wartości wymagających prawidłowego escaping. W 9.16 poprawiono wiele template'ów i utwardzono określone sinki. W 9.17 pojawiło się CVE-2026-17346 — producent wyjaśnia, że wcześniejsza poprawka ominęła kolejne miejsca, które błędnie uznano za bezpieczne (PostgreSQL pozwala na nietypowe znaki w quoted identifiers, więc nazwa obiektu z apostrofem mogła ponownie wejść do nieescapowanej interpolacji). To różnica między naprawą konkretnego wystąpienia a naprawą klasy błędu. Jeżeli finding brzmi „SQL injection w template X”, łatwo poprawić template X — ale VE powinno zadać kolejne pytanie: gdzie jeszcze występuje ten sam sposób budowania zapytania?

Drugi przypadek: dwa parsery, dwie interpretacje bezpieczeństwa

CVE-2026-12045 dotyczyło AI Assistanta i mechanizmu ograniczającego wygenerowany SQL do transakcji read-only. W 9.17 CVE-2026-17351 pokazało lexer differential: sqlparse i PostgreSQL mogły inaczej interpretować ten sam fragment SQL przy określonej konfiguracji. Kontrola istniała — problem w tym, że oceniała inną semantykę danych niż komponent, który dane później wykonywał:

walidator/parser A ↓ „bezpieczne” parser PostgreSQL ↓ inne znaczenie ↓ bypass

Ważna lekcja dla remediation verification: test starego PoC potwierdza tylko, że stary PoC już nie działa. Nie odpowiada, czy właściwość bezpieczeństwa zapewniono dla wszystkich równoważnych reprezentacji wejścia.

Trzeci przypadek: brakujący decorator znaleziono ponownie

CVE-2026-12046 naprawiało dwa endpointy pozbawione @pga_login_required. W 9.17 CVE-2026-17348 wskazało kolejne trasy w Constraints, Preferences, Debugger i Schema Diff z tym samym problemem. Jeżeli po znalezieniu pierwszego endpointu poprawiamy wyłącznie ten endpoint, wykonujemy bug fixing. Jeśli enumerujemy wszystkie endpointy i sprawdzamy je względem polityki autoryzacji, wykonujemy variant analysis — a to jest bliższe Vulnerability Engineering.

Czy administrator, który wdrożył 9.16, popełnił błąd?

Nie. W momencie, w którym vendor publikował 9.16 jako wersję z poprawkami, administrator miał racjonalne podstawy, by wdrożyć aktualizację i zamknąć findings. Problem pojawia się później, gdy nowy evidence zmienia stan wiedzy. Dlatego Fixed nie powinno być interpretowane jako wieczna prawda — lepszy model to fixed according to evidence available at time T. Po publikacji incomplete-fix CVE system powinien umieć odnaleźć assety, których wcześniejsze remediation zakończyło się na wersji obecnie uznanej za podatną.

Remediation lineage

Warto przechowywać relacje między podatnościami:

finding: cve: CVE-2026-17351 relation: type: incomplete_fix_of cve: CVE-2026-12045

Dzięki temu można automatycznie znaleźć populację, w której finding dla parent CVE był zamknięty, asset nadal istnieje, a wersja jest affected przez follow-up CVE — i nadać jej stan REVALIDATION_REQUIRED. Nie trzeba fałszować historii przez zmianę starego Closed na Open: można zachować fakt, że wcześniejsza decyzja była poprawna według ówczesnej wiedzy, a jednocześnie uruchomić nową kampanię.

Trzy statusy zamiast dwóch

PATCH_APPLIED: wdrożono wersję zgodną z aktualnym advisory.
REMEDIATION_VERIFIED: potwierdzono, że security property / attack path został rzeczywiście zamknięty.
REVALIDATION_REQUIRED: pojawiły się nowe informacje podważające wcześniejsze założenie.

Nie każda podatność wymaga laboratoryjnego testu po patchu. Ale dla systemów krytycznych, exploitowanych CVE i przypadków incomplete fix taki model bywa znacznie wartościowszy niż proste Open/Closed.

Co powinien zrobić Vulnerability Engineer?

  1. Zidentyfikować wszystkie instancje poniżej 9.17, ze szczególnym uwzględnieniem 9.16.
  2. Wyszukać findings zamknięte wcześniej dla CVE-2026-12044, -12045 i -12046.
  3. Powiązać je z nowymi CVE (remediation lineage).
  4. Uwzględnić server mode, ekspozycję, model uwierzytelnienia i uprawnienia bazodanowe.
  5. Po aktualizacji potwierdzić nie tylko numer wersji, ale — tam gdzie ma to sens — oczekiwaną właściwość bezpieczeństwa.

Wniosek

pgAdmin 9.16 → 9.17 to bardzo dobry przykład, że Vulnerability Management nie kończy się na dopasowaniu wersji. Dobre VE śledzi historię poprawki, warianty tej samej klasy błędu i nowe dowody, które zmieniają ocenę wcześniejszego zamknięcia. Patch może być wdrożony poprawnie, a remediation nadal może wymagać ponownej walidacji. Liczba zamkniętych ticketów nie mówi jeszcze, czy remediation była trwała.

Powiązane na blogu

Źródła

Nota redakcyjna: stan informacji 1 września 2026. pgAdmin 9.17 opisuje CVE-2026-17346, CVE-2026-17348 i CVE-2026-17351 jako incomplete fixy odpowiednio CVE-2026-12044, CVE-2026-12046 i CVE-2026-12045 z 9.16. Artykuł koncentruje się na remediation lineage, variant analysis i modelu statusów, nie na eksploatacji.

Zapamiętaj jedno

Fixed nie jest wieczną prawdą — to „fixed according to evidence available at time T”. Naprawa konkretnego wystąpienia to nie naprawa klasy błędu; po publikacji incomplete-fix CVE potrzebny jest status REVALIDATION_REQUIRED i remediation lineage, nie zmiana historii.