08.02 — CURA Healthcare (SUT)#

Ocarina’s first victim#

Perimeter#

PageFunction
/Marketing homepage
/profile.php#loginLogin
/appointment.phpBooking form (facility, programs, hospital_readmission, visit_date, comment)
/confirmation.phpBooking confirmation
/history.phpThe user’s booking history
/profile.php#profileProfile page (empty placeholder — gap §G-SPEC-2)

Why target CURA#

  1. Open source — we can read the PHP to understand why a given behavior exists.
  2. Plenty of gaps — rich territory for findings.
  3. 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.md was 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.

  1. Manual exploration sessions of the live app.
  2. PHP reading via gh api repos/katalon-studio/katalon-demo-cura/contents/....
  3. 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#

SectionContent
1-3Executive summary, system overview, roles
4Functional requirements (Auth, Appointment, History, Profile)
5Element IDs (one per form field)
6URL map (/profile.php#login, etc.)
7Business rules
8Error handling
9Known bugs / gaps (CSRF, validation, session, BFCache, etc.)

Each gap has a stable ID (§9.1, §9.2, …) referenced by the tests.

IDENTIFIED_GAPS.md#

DocumentContentAudience
CURA_FRD.md §9Gap description (impact, recommendation, test ref)Stakeholders
IDENTIFIED_GAPS.mdTechnical inventory: PHP citations file:line, reproduction recipesMaintainers
### 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 3 against 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 ⊆ WebDriverException is in transient_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, even Keys.ENTER on a focused submit button — saw the input silently do nothing.
  • Cause: the demo password ThisIsNotAPassword is 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 to elementFromPoint, doesn’t block JS — but it captures every real input event.
  • Resolution: src/lib/ext/ocarina/adapters/selenium/create_drivers_pool.py builds 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.