Raport code review dla platformy monitoringu cyberapis.pl — 10 poprawek bezpieczeństwa i czystości kodu w Laravel 12

Cyberapis v0.16.1 — code review: 10 poprawek bezpieczeństwa i czystości kodu

10 poprawek po code review — bezpieczeństwo, niezawodność i porządki w kodzie

Wersja v0.16.1 przechodzi gruntowny audyt kodu przez narzędzie app.devin.ai. W efekcie wprowadzamy 10 poprawek, które zwiększają bezpieczeństwo publicznego API, poprawiają niezawodność pipeline'u monitoringu i usuwają zaległy martwy kod. To nie są zmiany wizualne — to wzmocnienie fundamentów, na których działa cyberapis.pl.

Bezpieczeństwo

Rate limiting publicznego API

Endpoint /api/monitorings nie miał żadnego ograniczenia liczby zapytań ani walidacji parametrów. Atakujący mógł wysyłać nieograniczoną liczbę requestów i skanować dane przez dowolnie długie stringi w filtrze LIKE. Dodaliśmy throttle:60,1 (60 zapytań na minutę) oraz walidację max:255 na parametrach url i site_name. 61. request w ciągu minuty dostaje HTTP 429.

Zaufanie do proxy tylko na produkcji

trustProxies(at: '*') ufało wszystkim adresom IP jako proxy. Za Cloudflare to bezpieczne, ale gdyby aplikacja kiedykolwiek działała poza Cloudflare (staging, dev, bezpośredni dostęp po IPv6), nagłówki X-Forwarded-For mogły być sfałszowane. Ograniczyliśmy zaufanie tylko do środowiska produkcyjnego.

Autoryzacja przez Gate zamiast inline

Endpoint /monitoring/{monitoring}/status miał 10 linii logiki autoryzacyjnej bezpośrednio w closure routy — sprawdzanie właściciela, roli publicznej, locale. Ta logika trafiła do dedykowanego Gate'a view-monitoring-status w AppServiceProvider. Route skrócił się do jednej linii abort_unless(Gate::allows(...), 404). Czytelniej, testowalniej, bez rozsypanej logiki.

Niezawodność

Propagacja wyjątków z joba monitoringu

RunMonitoringCheck wywoływał Artisan::call('monitor:check-websites'), który łapał wyjątki wewnątrz komendy i zwracał exit code. Queue worker widział udany job nawet gdy monitoring się wysypał — zero retry, zero alertów. Teraz job wstrzykuje MonitoringService i MonitoringEmailService bezpośrednio, a wyjątki propagują się naturalnie do queue. Failed job = retry, logi, monitoring.

Izolacja profilu Chrome przy sprawdzaniu tekstu

findTextWithBrowsershot() używało hardcoded /tmp/chrome-browsershot jako katalogu profilu Chromium. Przy dwóch równoległych sprawdzeniach ten sam --user-data-dir powodował konflikt ("browser is already running"). Teraz każdy bieg dostaje unikalny katalog /tmp/cyberapis-browsershot-{uniqid} z pełnym cleanupem po zakończeniu — dokładnie tak samo jak render probe.

Dynamiczny SHA256 digest dla agent skills

AgentSkillsController miał hardcoded hash SHA256 pliku SKILL.md. Zmiana pliku = nieaktualny digest = klienci AI odrzucają skilla. Teraz digest jest liczony dynamicznie przez hash_file('sha256', $path) — zawsze zgodny z rzeczywistą zawartością.

Deduplikacja generacji sitemapy

Post i DocsPage wywoływały Artisan::call('sitemap:generate') synchronicznie przy każdym saved/deleted evencie. Przy masowym imporcie 100 stron — 100 osobnych przebudowań sitemapy. Nowy job RegenerateSitemap z ShouldBeUnique i 10-sekundowym delayem kolejkuje tylko jedną generację niezależnie od liczby zapisów. Kolejne dispatchowanie są ignorowane dopóki job nie zostanie przetworzony.

Czystość kodu

Usunięty duplikat trasy /

W routes/web.php istniały dwie definicje trasy / — pierwsza z anonimową funkcją, druga przez HomeController::index. Laravel używał drugiej, pierwsza była martwym kodem. Usunięta.

Pliki backupowe poza repozytorium

Sześć plików (MonitoringService.php.#1, .gemini, .przedwdrozeniem-spatie-browsershot, MonitoringResource.php.#1, Post.php.back, PostController.php.back) zalegało w katalogach app/Services/, app/Filament/Resources/, app/Models/ i app/Http/Controllers/. Usunięte z repo. Dodane wzorce *.back, *.#*, *.gemini, *.przedwdrozeniem* do .gitignore.

Usunięty martwy komponent MonitorWebsite

Livewire'owy MonitorWebsite był porzuconym prototypem — brak widoku, błędny model (MonitoringResult::first() zamiast Monitoring), sync HTTP w mount(), zero autoryzacji. Nigdy nie był nigdzie renderowany. Usunięty w całości.

Wszystkie 10 poprawek jest już na produkcji. Kolejny krok: wdrożenie rekomendacji z pełnego audytu bezpieczeństwa.