Skip to content

v0.26.0 - Audyt bezpieczenstwa przed v1.0.0

Choose a tag to compare

@mtsle mtsle released this 26 Jul 17:01
· 107 commits to main since this release

v0.26.0 — Audyt bezpieczenstwa przed v1.0.0

Mini-krok wciskniety przed Krokiem 21 (v1.0.0), na wyrazna prosbe usera: pelny
audyt bezpieczenstwa calej wtyczki (rozpoznanie -> audyt -> poprawki -> testy ->
mutacje -> raport). Zero zmian schematu bazy, zero usunietych plikow, zero nowych
zaleznosci, zero bajtow zmian w assets/.

Najpowazniejsze znalezisko: publiczny /ask pisal do bazy bez limitu

Endpoint POST /aifaq/v1/ask jest z definicji publiczny — gosc pyta bez
logowania. Dwie sciezki wyjscia omijaly wszystkie bramki kosztu, a mimo to
zapisywaly wiersz do wp_aifaq_qa_log:

  • trafienie w cache — ta galaz stoi PRZED limiterem gosia (kolejnosc
    potoku RagService), wiec bot powtarzajacy jedno pytanie place zero i pisze
    w nieskonczonosc;
  • odbicie o limit/sufit — samo odrzucenie tez wykonywalo INSERT.

Naprawa: tlumik RagService::log_once()/log_marked() — wpis powstaje przy
PIERWSZYM wystapieniu w oknie (1 h), potem cisza. Gosc dalej dostaje identyczna
odpowiedz (200 z cache albo 429); wlasciciel nie traci widocznosci realnie
roznych pytan (kubelek jest per gosc + per pytanie dla cache, per gosc dla
odbic).

Klucz API byl autoladowany

aifaq_settings (niesie klucz Gemini) powstawala przez update_option() bez
trzeciego argumentu, czyli z autoload = yes — sekret ladowal sie do
alloptions przy KAZDYM zadaniu witryny, takze zadaniu gosia. Naprawa: zapis
z autoload = false plus jednorazowa migracja istniejacych instalacji
(Plugin::maybe_harden_options()), ktora rusza WYLACZNIE kolumne autoload
wartosc opcji nie jest ani kasowana, ani przepisywana, wiec nie ma sciezki
utraty klucza klienta.

Pozostale poprawki

  • Naglowek X-AIFAQ-Crawl byl nieuwierzytelniona stala "1" i wylaczal
    WP-Cron dowolnemu gosciowi (duszenie zadan w tle na witrynie o malym ruchu).
    Teraz token z wp_salt(), porownanie hash_equals().
  • IDOR — rola narzedzia (publish_posts) mogla opublikowac cudza generacje
    po samym id, obchodzac admin-only historie. Galaz id zawezona do
    manage_options; UI zawsze wysyla pairs, wiec zero skutku dla produktu.
  • Temat generatora nie mial serwerowego limitu dlugosci (opis mial od K20).
    Dolozone MAX_TOPIC_CHARS = 500.
  • Pytanie gosia szlo do promptu surowe — bez neutralizacji granic sekcji,
    ktora chroni temat/opis od K20. Dolozona ta sama obrona (harden_question()).
  • Eksport Elementora nie escapowal tresci modelu (Elementor renderuje
    tab_content jako HTML, wiec kodowanie JSON tam nie jest ucieczka).

Retencja dziennika pytan gosci

Dolozona osobno, opt-in jak przy historii generowan: qa_log_keep_rows
(0-20000) i qa_log_keep_days (0-3650), oba domyslnie 0 = nic nie kasuj.
wp_aifaq_qa_log rosnie z kazdym pytaniem gosia (nie tylko dzialaniem
wlasciciela), wiec na ruchliwej witrynie bez sprzatania rosnie szybciej niz
historia generacji, ktora retencje mial juz od Kroku 20.

Odbior

runner 42 zestawy — 0 niezaliczonych   (nowy segment audyt-bezpieczenstwa-test.php, 53 asercje)
mutacje 8/8 czerwienia                 (M1-M8: kazda poprawka realnie wykonana)
budzet API: 0 wywolan                  cala tura statyczna

Pelny raport (10 sekcji, macierz 15 tras REST, LLM security, remaining risks):
plany/AUDYT-BEZPIECZENSTWA-K21.md.

Pozostale ryzyka — decyzje usera

  • readme.txt/LICENSE wciaz brak — twarda blokada WordPress.org, swiadomie
    odlozone do Kroku 21.
  • Limiter jest kluczowany po IP — gosc z pula adresow dostaje swiezy kubelek
    na kazdy adres; zaakceptowane, koszt naprawy przewyzsza zysk przy zamrozonym
    kontrakcie K20.

NASTEPNY KROK: Krok 21 — v1.0.0 (audyt juz zrobiony w tym wydaniu, zostaje
RWA i instrukcja wdrozenia + LICENSE/readme.txt).