7. Rizika
Stav k exportu z 19. 8. 2026: rizika R1, R2, R4, R14 platí beze změny. R7 (duplicitní route) bylo v novém exportu opraveno. Přibyla rizika R16–R20 kolem účetní vrstvy, exportu do Pohody a přihlášení personálu.
Bezpečnostní rizika
R1 — OTP kód odhalen v response (KRITICKÉ)
Ověřený fakt — z clientAuth/entry.ts:
return Response.json({
demo_code: contact_type === 'phone' ? code : undefined,
message: contact_type === 'phone' ? `Demo kód: ${code}` : '...'
});
OTP kód pro přihlášení telefonním číslem je vrácen přímo v HTTP response (nikoliv odesláním SMS). Toto je funkce "demo módu", ale pokud by byl systém nasazen v produkci bez SMS gateway, kdokoliv může přihlásit libovolného klienta bez přístupu k jeho telefonu.
Dopad: Neoprávněný přístup k účtům klientů.
Zmírnění: Před nasazením implementovat SMS gateway, nebo zakázat phone OTP v produkci.
R2 — Automatická tvorba klientů přes OTP (STŘEDNÍ)
Ověřený fakt — z clientAuth/entry.ts:
if (activeClients.length === 0) {
const newClient = await base44.asServiceRole.entities.Client.create({
first_name: 'Nový', last_name: 'Klient', email: contact, ...
});
}
Kdokoliv s platnou e-mailovou adresou může přes OTP flow vytvořit nového klienta v systému. Neexistuje whitelisting ani schvalování.
Dopad: Zahlcení databáze falešnými klienty, potenciální GDPR implikace.
R3 — Platební odkaz bez autentizace (STŘEDNÍ)
Ověřený fakt — z sendNotification/entry.ts:
const paymentPath = `/client?payment=1&booking_id=${booking.id}&date=...`;
Platební odkaz je přímé URL s booking_id. Bez dalšího ověření může ten, kdo URL zná, zaplatit za libovolnou rezervaci (nebo vidět její detail).
Odvozeno — může to být záměrné (jednorázový magic link), ale není to dokumentováno.
R4 — Chybí rate limiting na OTP endpointu (STŘEDNÍ)
Ověřený fakt — žádný rate limiting není viditelný v clientAuth/entry.ts. Útočník může generovat neomezené množství OTP kódů pro libovolný e-mail, čímž zahltí Resend API účet nebo obtěžuje uživatele.
R5 — Session klienta v localStorage (NÍZKÉ)
Odvozeno — z ClientAuthContext je patrné, že session klienta je uložena v localStorage. To je zranitelné vůči XSS útokům. Pro citlivý zdravotnický systém by bylo vhodnější HttpOnly cookie.
R14 — Uplatnění voucheru bez serverové validace (STŘEDNÍ)
Ověřený fakt — VoucherRedeem.jsx označuje voucher jako redeemed přímo z prohlížeče klienta (Voucher.update), bez serverové kontroly aktuálního stavu.
Dopad: Souběžné otevření stejného odkazu /redeem?code=… na dvou zařízeních může vést k dvojímu uplatnění (race condition). Kontrola platnosti probíhá jen na frontendu. Při migraci přesunout uplatnění do serverové funkce (jako clientBooking) s atomickou kontrolou stavu. Viz O18.
R15 — Ruční PDF generátor pro VOP (NÍZKÉ)
Ověřený fakt — sendNotification sestavuje VOP jako PDF ručně psaným %PDF-1.4 řetězcem (generateSimplePdf), bez podpory diakritiky a formátování. Pro archivaci právně relevantního dokumentu nedostatečné — v PHP nahradit knihovnou (mPDF/TCPDF). Viz O19.
R6 — Meet link je deterministický (NÍZKÉ)
Ověřený fakt — z sendNotification/entry.ts:
function generateMeetLink(bookingId) {
const seed = bookingId.replace(/-/g, '').substring(0, 9);
// Deterministický algoritmus...
}
Google Meet link je generován deterministicky z booking_id. Pokud někdo zná booking_id, může odvodit meet link. Toto není skutečný Google Meet link — jde o náhodně vygenerovaný URL ve formátu Meet, který nemusí být funkční.
Nezjištěno: Jsou tyto Meet linky funkční? Nebo se počítá s budoucí integrací skutečného Google Meet API?
Technická rizika
R7 — Duplicitní route /PaymentSettingsPage v App.jsx (VYŘEŠENO)
Ověřený fakt — v exportu z 19. 8. 2026 je route registrována jen jednou. Riziko zaniklo.
R8 — N+1 query pattern v některých místech (STŘEDNÍ)
Odvozeno — ze zdrojového kódu clientBooking get_therapists_with_slots je viditelné, že funkce načítá až 1000 bloků a 2000 událostí najednou jako workaround pro N+1 problém. Toto může být pomalé s rostoucím objemem dat.
R9 — NoSQL bez referenční integrity (STŘEDNÍ)
Ověřený fakt — Base44 entity jsou NoSQL. Vztahy jsou udržovány pouze pomocí ID referencí. Při mazání entity (terapeut, místnost) neproběhne kaskádové mazání — zanechá se orphan data.
R10 — Verze SDK se liší mezi funkcemi (NÍZKÉ)
Ověřený fakt — různé funkce importují různé verze SDK:
0.8.25—activityLogger,reminderDispatcher,sendNotification0.8.31—clientAuth,clientBooking,checkAvailabilityConflicts,onPaymentConfirmed,processExpiredPayments,syncToOutlook0.8.40— všech 8 nových účetních funkcí
Rozptyl verzí se s novým exportem zvětšil (tři verze místo dvou). Nekonzistence může způsobit chyby při aktualizaci.
Procesní rizika migrace
R11 — Vendor lock-in (KRITICKÉ)
Ověřený fakt — veškerá data jsou uložena v Base44 proprietární databázi. Export dat do standardního formátu (SQL, JSON) je možný pouze přes Base44 admin rozhraní nebo SDK — není garantovaný.
Dopad: Při ukončení Base44 platformy nebo přechodu na vlastní backend by migrace dat mohla být problematická.
R12 — Chybí testy (STŘEDNÍ)
Ověřený fakt — v projektu nejsou žádné unit testy, integrační testy ani e2e testy. Migrace bez testů zvyšuje riziko regresí.
R13 — Komplexita business logiky (STŘEDNÍ)
Ověřený fakt — clientBooking/entry.ts obsahuje komplexní logiku:
- Storno politika s lhůtami a vrácením (credit vs. balíček)
- Výpočet splatnosti (
payment_due_date) - Refund flow pro balíček (LIFO pořadí)
Tato logika musí být přesně replikována při migraci.
Rizika účetní vrstvy (export 19. 8. 2026)
R16 — Klíče k externím službám uložené v databázi (STŘEDNÍ)
Ověřený fakt — NotificationSettings.sms_api_key (SmsManager) a SystemSettings.pohoda_auth_token + telephony_api_key a telephony_webhook_secret jsou běžná pole entit, ne Base44 secrets. Čte je kdokoli s přístupem k entitám přes SDK a zobrazují se v admin UI.
Změna logiky: dříve byl jediný citlivý klíč (RESEND_API_KEY) v secrets s přístupem jen pro backend.
Dopad: kompromitace admin účtu znamená i únos SMS brány a přístupu do účetnictví.
Zmírnění: v novém backendu přesunout do env/secret storu, v UI jen maskovat a zapisovat.
R17 — Účetní zápisy bez transakcí (VYSOKÉ)
Ověřený fakt — doklad, platba i kreditní transakce vznikají jako samostatné entity postupnými voláními (shared/documentGeneration.ts), Base44 NoSQL nemá transakce. Konzistence stojí na event_id a kontrolách v aplikačním kódu.
Dopad: přerušení uprostřed operace (timeout funkce, chyba sítě) nechá rozpracovaný stav — platba bez dokladu nebo doklad bez vazby na platbu. U účetních dat jde o obtížně dohledatelnou chybu.
Zmírnění: v cílovém backendu zabalit vznik dokladu, platby a kreditní transakce do jedné SQL transakce; event_id ponechat jako idempotenční klíč.
R18 — Export do Pohody bez automatického opakování (STŘEDNÍ)
Ověřený fakt — syncPohodaExport označí neúspěšný doklad jako pohoda_export_status = 'failed' a uloží chybu, ale žádná funkce failed doklady znovu nezkouší; funkce se spouští ručně adminem nebo plánovaně a exportuje pouze pending.
Dopad: doklad, který Pohoda odmítne (chybný kód výkonu, nedostupný mServer), tiše zůstane mimo účetnictví.
Zmírnění: přidat frontu s opakováním a upozornění na failed doklady.
R19 — Přihlášení personálu závislé na DemoUser (STŘEDNÍ)
Ověřený fakt — StaffGate pouští do celé aplikace kohokoli, kdo projde OTP na e-mail vedený v entitě DemoUser. Session je v localStorage (neo_demo_user) a při výpadku sítě zůstává v platnosti cached kopie i pro účet, který mezitím mohl být deaktivován.
Změna logiky: entita původně sloužila jen k demo přepínání rolí — nyní je to autentizační zdroj pro celé admin rozhraní. Autorizace přitom stále běží na frontendu (permissions.js); backendové funkce kontrolují jen Base44 roli (admin / finance).
Dopad: oprávnění rolí admin-klientske, klientske, finance jsou vynucována pouze v UI — přímé volání SDK je obejde.
Zmírnění: v novém backendu přenést kontrolu oprávnění na serverovou stranu.
R20 — Veřejná úhrada storno poplatku a doklady na token (NÍZKÉ)
Ověřený fakt — /storno?token=… a /invoice?token=… jsou veřejné stránky bez přihlášení; getPublicInvoice vydá doklad výhradně podle invoice_token.
Ověřený fakt — token vzniká jako crypto.randomUUID() bez pomlček (shared/documentGeneration.ts), entropie je tedy dostatečná. Token ale nemá expiraci a platí trvale.
Zmírnění: doplnit časové omezení platnosti odkazu a rate limiting na veřejné endpointy.