08.02 — CURA Healthcare (SUT)#
La première victime d’Ocarina#
- Hébergement : https://katalon-demo-cura.herokuapp.com/
- Source : https://github.com/katalon-studio/katalon-demo-cura (PHP, open source, MIT)
- Hébergeur : Heroku eco-dyno (qui s’endort après inactivité)
- Credentials :
John Doe/ThisIsNotAPassword(publics, hardcodés sur la page login)
Périmètre#
| Page | Fonction |
|---|---|
/ | Marketing homepage |
/profile.php#login | Login |
/appointment.php | Form de booking (facility, programs, hospital_readmission, visit_date, comment) |
/confirmation.php | Confirmation du booking |
/history.php | Historique des bookings du user |
/profile.php#profile | Page Profil (placeholder vide — gap §G-SPEC-2) |
Pourquoi cibler CURA#
- Open source. On peut lire le PHP pour comprendre pourquoi tel comportement.
- Beaucoup de gaps. Riche terrain de findings.
- Public, démo. Pas d’autorisation à demander. Stable (maintenu par Katalon).
Plus une raison tacite : Katalon est un éditeur de framework de test concurrent (Katalon Studio).
C’est joliment poétique de tester leur app démo avec Ocarina.
CURA_FRD.md#
À propos de
CURA_FRD.md. CURA est une app démo publique — aucune documentation fonctionnelle n’existe nulle part.CURA_FRD.mda été écrit de zéro, entièrement par IA, en explorant l’app live et en lisant son PHP open source. C’est une spec reconstruite — du reverse engineering par IA de ce que CURA fait et de ce qu’un système de santé comme lui devrait faire — ce document n’est néanmoins pas une source d’autorité.
- Sessions d’exploration manuelle de l’app live.
- Lecture du PHP via
gh api repos/katalon-studio/katalon-demo-cura/contents/.... - Hypothèses sur ce qu’un système de santé devrait faire (§9).
La SFD est l’arborescence sémantique sur laquelle les tests sont basés.
Chaque test cite un REQ-X-N ou un §9.x (gap).
Périmètre des SFD#
| Section | Contenu |
|---|---|
| 1-3 | Executive summary, system overview, roles |
| 4 | Functional requirements (Auth, Appointment, History, Profile) |
| 5 | Element IDs (un par champ de formulaire) |
| 6 | URL map (/profile.php#login, etc.) |
| 7 | Business rules |
| 8 | Error handling |
| 9 | Known bugs / gaps (CSRF, validation, session, BFCache, etc.) |
Chaque gap a un ID stable (§9.1, §9.2, …) référencé par les tests.
IDENTIFIED_GAPS.md#
| Document | Contenu | Cible |
|---|---|---|
CURA_FRD.md §9 | Description du gap (impact, recommandation, test ref) | Stakeholders |
IDENTIFIED_GAPS.md | Inventaire technique : citations PHP file:line, recettes de reproduction | Mainteneurs |
### G-SEC-1 — No CSRF token on deployed forms
- **Where:** `views/page_login.php` (login form), `views/page_appointment.php` (appointment form) on https://katalon-demo-cura.herokuapp.com/.
- **Github source vs deployment:** `page_appointment.php` in the github repo calls `$antiCSRF->insertHiddenToken()` before the submit button. The
**deployed** Heroku app does not render a hidden token input — verified by reading the live `<form>` HTML; the only fields are
`facility / hospital_readmission / programs / visit_date / comment`. Reproduce by driving a Selenium browser through login → appointment and reading
`document.querySelector('form').outerHTML`.
- **Authentication side:** `authenticate.php` performs no CSRF validation on login either — it directly calls
`_f::login(_f::post("username"), _f::post("password"))` without consulting `$antiCSRF`.
- **Bonus bug:** Even if the form _did_ include a token, `appointment.php` checks `$antiCSRF->isValidRequest()` where `$antiCSRF` is never defined in
that file (`security.php`'s class isn't instantiated there). The expression evaluates against an undefined variable; the check is no-op.
- **Impact:** Cross-origin POSTs to `appointment.php` and `authenticate.php` with the right field names succeed unconditionally for the demo account.
- **SFD ref:** §9.9.→ Précision file:line, recipe de reproduction, SFD cross-ref.
C’est de la documentation forensique.
Heroku dyno#
CURA tourne sur un Heroku eco-dyno :
- Sleep après ~30 min sans trafic.
- Cold start.
D’où l’étape « warm-up Heroku dyno » dans ai_proof_e2e.yml :
curl -sf --retry 6 --retry-delay 5 --retry-all-errors \
--max-time 30 https://katalon-demo-cura.herokuapp.com/ > /dev/null
echo "Dyno is warm"# CURA runs on a Heroku dyno that sleeps after inactivity.
# Tests hitting a cold dyno produce timeouts (false fails) and slow
# confirmations (false passes on gap tests). Wake it explicitly
# before any browser opens.A-ENV-1 : contention dyno sous --workers 3#
A-ENV-1 — Rapid-POST drop under
--workers 3against the shared dyno#
- Symptom: Under
--workers 3, a rapid second-in-a-row POST from one of the three parallel workers intermittently fails to redirect within 10 s.- Matrix-wide, not browser-specific: the trigger is three browser instances hammering one Heroku eco dyno, not any one browser.
- Not a CSRF issue: per G-SEC-1, the deployed forms have no token.
- Not a SUT bug at single-worker: solo (one worker) books two consecutive appointments cleanly in ~200–600 ms each.
- Cause: Heroku eco-dyno concurrency limit hit by near-simultaneous POSTs from parallel workers.
- Handling:
TimeoutException ⊆ WebDriverExceptionis intransient_errors, so a single occurrence auto-retries. A failure that survives all retries is this artifact at full contention, not a regression.
A-ENV-2 : Chrome password breach detection#
A-ENV-2 — Chrome password-breach modal swallows all input after login (resolved)#
- Symptom (historical): On chrome only, a test that logged in and then did anything — click,
send_keys, evenKeys.ENTERon a focused submit button — saw the input silently do nothing.- Cause: the demo password
ThisIsNotAPasswordis a publicly-leaked credential, so Chrome’s password manager raised a native breach-detection modal on every successful login. The modal is browser chrome — invisible toelementFromPoint, doesn’t block JS — but it captures every real input event.- Resolution:
src/lib/ext/ocarina/adapters/selenium/create_drivers_pool.pybuilds chrome with the consumer password manager off.- Why it stays documented: the entry preserves why the chrome driver adapter exists — delete the adapter and this artifact returns.
Lectures connexes#
- Les gaps détaillés :
05-security-gaps.md,06-data-gaps.md,07-spec-gaps.md,08-bfcache.md - Le
create_drivers_poolcustom (Chrome clean) :09-ci-matrix.md