Skip to content

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ý faktVoucherRedeem.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ý faktsendNotification 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.

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.25activityLogger, reminderDispatcher, sendNotification
  • 0.8.31clientAuth, clientBooking, checkAvailabilityConflicts, onPaymentConfirmed, processExpiredPayments, syncToOutlook
  • 0.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ý faktclientBooking/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ý faktNotificationSettings.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ý faktsyncPohodaExport 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ý faktStaffGate 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.