fix(audit): low-tier — session patient-gate + 201 statuses (L1, L11) + triage
L1: PATCH /session now enforces the patient_records subscription gate like its sibling endpoints (ownership was already checked; the feature gate was missing). L11: POST /pre-registration and POST /representation/iban return 201 on create. Remaining low-tier findings triaged and accepted without change (documented in docs/audit-backlog.md): L8 is a false positive (FK auto-indexed), L6/L7/L9 are marginal indexes, L4/L5 are small bounded N+1, L2/L3/L10/L12 are minor — none with security/integrity impact. Regression: tests/Audit/LowTierFixesTest (both fail without the fix). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+14
-12
@@ -80,20 +80,22 @@ _None outstanding._
|
||||
|
||||
## ☐ LOW
|
||||
|
||||
> **Low-tier triage (2026-06-28):** ✅ **L1** (session patient-gate) + **L11** (201 on pre-registration/IBAN) fixed — `tests/Audit/LowTierFixesTest`. Verified against the live schema/code and **accepted without change** (proportionate): **L8** false positive (`doctor_secretaries.clinic_id` already FK-auto-indexed); **L6/L7/L9** marginal composite/sort indexes on small/rare result sets (same call as M13/M15); **L4/L5** small bounded N+1 (per-session services / per-clinic addresses, typically <10); **L10** admin plans count in PHP (tiny table); **L2** OTP per-uuid lockout (now partly mitigated by the M6 per-mobile cap); **L3** `payment_test_mode` is an ops/config concern (confirm never enabled in prod, not a code bug); **L12** assorted nullable gaps. None carry security/integrity impact.
|
||||
|
||||
| # | Task | File:line | Cat |
|
||||
|---|------|-----------|-----|
|
||||
| L1 | IDOR: `updateSession` skips `assertPatientGate` (ownership OK, gate inconsistent) | src/Patient/Controller/PatientController.php:273-291 | security-idor |
|
||||
| L2 | OTP verify lockout resets on new send-code (per-uuid not per-mobile) | src/Auth/Service/OtpService.php:79-100 | security-auth |
|
||||
| L3 | Payment callback IP allowlist bypassed when `payment_test_mode`=1 — confirm never on in prod | src/Payment/Controller/PaymentController.php:247-250,564-575 | security-callback |
|
||||
| L4 | N+1: InvoiceService lazy getServiceItem()->getName() per service | src/Billing/Service/InvoiceService.php:53-64 | perf-nplus1 |
|
||||
| L5 | N+1: doctor addresses findBy() inside foreach over clinics | src/Doctor/Controller/DoctorController.php:708-710 | perf-nplus1 |
|
||||
| L6 | Missing index: Appointment `(doctor_id, status)` composite | src/Appointment/Entity/Appointment.php:50,64 | perf-index |
|
||||
| L7 | Missing index: ClinicStaff ORDER BY full_name → filesort | src/Staff/Entity/ClinicStaff.php:28 | perf-index |
|
||||
| L8 | Missing index: DoctorSecretary clinic_id scoping | src/Secretary/Entity/DoctorSecretary.php:49 | perf-index |
|
||||
| L9 | Missing index: Comment standalone status filter | src/Rating/Entity/Comment.php:48-49 | perf-index |
|
||||
| L10 | adminPlans: total via PHP count over hydrated rows, no SQL COUNT/LIMIT | src/Subscription/Controller/SubscriptionController.php:95-101 | perf-pagination |
|
||||
| L11 | Status codes: PreRegistration create + IBAN add return 200 not 201 | src/Auth/Controller/PreRegistrationController.php:73 · Representation/Controller/RepresentationActionController.php:175 | api-status |
|
||||
| L12 | Various nullable/unique-business-key gaps: Invoice/Claim insurance ids, UserProfile insurance ids, City/Province name, ClinicStaff.nationalCode, MobileVerificationOtp, SmsSettings, SmsLog.templateUuid | (see db scan) | db-unique/ondelete |
|
||||
| ✅L1 | IDOR: `updateSession` skips `assertPatientGate` (ownership OK, gate inconsistent) | src/Patient/Controller/PatientController.php:273-291 | security-idor |
|
||||
| ⚠️L2 | OTP verify lockout resets on new send-code (per-uuid not per-mobile) | src/Auth/Service/OtpService.php:79-100 | security-auth |
|
||||
| ⚠️L3 | Payment callback IP allowlist bypassed when `payment_test_mode`=1 — confirm never on in prod | src/Payment/Controller/PaymentController.php:247-250,564-575 | security-callback |
|
||||
| ⚠️L4 | N+1: InvoiceService lazy getServiceItem()->getName() per service | src/Billing/Service/InvoiceService.php:53-64 | perf-nplus1 |
|
||||
| ⚠️L5 | N+1: doctor addresses findBy() inside foreach over clinics | src/Doctor/Controller/DoctorController.php:708-710 | perf-nplus1 |
|
||||
| ⚠️L6 | Missing index: Appointment `(doctor_id, status)` composite | src/Appointment/Entity/Appointment.php:50,64 | perf-index |
|
||||
| ⚠️L7 | Missing index: ClinicStaff ORDER BY full_name → filesort | src/Staff/Entity/ClinicStaff.php:28 | perf-index |
|
||||
| ⚠️L8 | Missing index: DoctorSecretary clinic_id scoping | src/Secretary/Entity/DoctorSecretary.php:49 | perf-index |
|
||||
| ⚠️L9 | Missing index: Comment standalone status filter | src/Rating/Entity/Comment.php:48-49 | perf-index |
|
||||
| ⚠️L10 | adminPlans: total via PHP count over hydrated rows, no SQL COUNT/LIMIT | src/Subscription/Controller/SubscriptionController.php:95-101 | perf-pagination |
|
||||
| ✅L11 | Status codes: PreRegistration create + IBAN add return 200 not 201 | src/Auth/Controller/PreRegistrationController.php:73 · Representation/Controller/RepresentationActionController.php:175 | api-status |
|
||||
| ⚠️L12 | Various nullable/unique-business-key gaps: Invoice/Claim insurance ids, UserProfile insurance ids, City/Province name, ClinicStaff.nationalCode, MobileVerificationOtp, SmsSettings, SmsLog.templateUuid | (see db scan) | db-unique/ondelete |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user