08.02 — CURA Healthcare (SUT)#
Ocarina’s first victim#
- Hosting: https://katalon-demo-cura.herokuapp.com/
- Source: https://github.com/katalon-studio/katalon-demo-cura (PHP, open source, MIT)
- Host: Heroku eco-dyno (sleeps after inactivity)
- Credentials:
John Doe/ThisIsNotAPassword(public, hardcoded on the login page)
Perimeter#
| Page | Function |
|---|---|
/ | Marketing homepage |
/profile.php#login | Login |
/appointment.php | Booking form (facility, programs, hospital_readmission, visit_date, comment) |
/confirmation.php | Booking confirmation |
/history.php | The user’s booking history |
/profile.php#profile | Profile page (empty placeholder — gap §G-SPEC-2) |
Why target CURA#
- Open source — we can read the PHP to understand why a given behavior exists.
- Plenty of gaps — rich territory for findings.
- Public demo — no authorization needed. Stable (maintained by Katalon).
There’s also a tacit reason: Katalon is a competing test-framework vendor (Katalon Studio). It’s poetic to test their demo app with Ocarina.
CURA_FRD.md#
About
CURA_FRD.md. CURA is a public demo app — no functional documentation exists anywhere.CURA_FRD.mdwas written from scratch, entirely by AI, by exploring the live app and reading its open-source PHP. It’s a reconstructed spec — AI reverse engineering of what CURA does and of what a healthcare system like it should do — this document, however, is not a source of authority.
- Manual exploration sessions of the live app.
- PHP reading via
gh api repos/katalon-studio/katalon-demo-cura/contents/.... - Hypotheses about what a healthcare system should do (§9).
The FRD is the semantic tree the tests are grounded in. Each test cites a REQ-X-N or a §9.x (gap).
FRD scope#
| Section | Content |
|---|---|
| 1-3 | Executive summary, system overview, roles |
| 4 | Functional requirements (Auth, Appointment, History, Profile) |
| 5 | Element IDs (one per form field) |
| 6 | URL map (/profile.php#login, etc.) |
| 7 | Business rules |
| 8 | Error handling |
| 9 | Known bugs / gaps (CSRF, validation, session, BFCache, etc.) |
Each gap has a stable ID (§9.1, §9.2, …) referenced by the tests.
IDENTIFIED_GAPS.md#
| Document | Content | Audience |
|---|---|---|
CURA_FRD.md §9 | Gap description (impact, recommendation, test ref) | Stakeholders |
IDENTIFIED_GAPS.md | Technical inventory: PHP citations file:line, reproduction recipes | Maintainers |
### 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.Precise file:line references, reproduction recipe, FRD cross-ref. Forensic documentation.
Heroku dyno#
CURA runs on a Heroku eco-dyno: sleeps after ~30 min of inactivity, cold-starts on the next request.
Hence the Heroku warm-up step in 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: dyno contention under --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.
Related reading#
- The detailed gaps:
05-security-gaps.md,06-data-gaps.md,07-spec-gaps.md,08-bfcache.md - The custom
create_drivers_pool(Chrome clean):09-ci-matrix.md