fix(billing): bound approved/paid amounts on claim transition (M1)

approve/pay accepted any approved_rials/paid_rials with no bounds, so the
claiming tenant could write arbitrary figures into the insurer-debt ledger
(negative, or far above the claimed total). Validate: approved ∈ [0, claimed],
paid ∈ [0, approved] → 422 otherwise. (The "force arbitrary status" half of the
finding was already prevented by Claim::canTransitionTo.)

Regression: tests/Billing/ClaimAmountBoundsTest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
hamed
2026-06-28 20:07:19 +03:30
co-authored by Claude Opus 4.8
parent 131a78343b
commit 23ca56b293
4 changed files with 84 additions and 4 deletions
+1 -1
View File
@@ -54,7 +54,7 @@ _None outstanding._
| # | Task | File:line | Cat | How to test |
|---|------|-----------|-----|-------------|
| M1 | Mass-assign: claim approve/pay lets owning tenant set arbitrary `approved_rials`/`paid_rials` + force status PAID/APPROVED | src/Billing/Controller/BillingController.php:192 | security-massassign | POST claim approve w/ huge amounts → server computes, ignores caller figures |
| M1 | Mass-assign: claim approve/pay lets owning tenant set arbitrary `approved_rials`/`paid_rials` ~~+ force status~~ | src/Billing/Controller/BillingController.php | security-massassign | **DONE** — status-jump part was already guarded by `canTransitionTo` (false alarm). Added bounds: approved ∈ [0, claimed], paid ∈ [0, approved] → 422. `tests/Billing/ClaimAmountBoundsTest`. |
| M2 | IDOR read: `GET /insurance/{id}` (showDoctorInsurance) leaks any doctor's insurance incl. negotiated price by id enumeration | src/Insurance/Controller/InsuranceController.php:499 | security-idor | GET insurance/{id} as non-owner → 403 |
| M3 | IDOR read: `showAddress` loads any DoctorAddress by id, no owner check | src/Doctor/Controller/DoctorController.php:559 | security-idor | GET doctor-address/{id} as non-owner → 403 |
| M4 | IDOR write: ClinicService `createItem`/`updateItem` bind staff via global `staffRepo->findByUuid()`, no entity-scope check (cross-tenant staff binding) | src/ClinicService/Controller/ClinicServiceController.php:157,195 | security-idor | Bind other clinic's staff_uuid → 403/validation |