parity(api,apps): security hotfix stage 1 - close unauthenticated write paths
Authorised by rulings D7 and T1-T3 in docs/parity/DECISIONS.md. This is the CLAUDE.md section 3 carve-out: matching the reference here would keep a money write path open to anonymous callers. Closed, each behind the rid the legacy vocabulary already had: GET accounting/customers/:id/balance -> wallet.list POST accounting/customers/:id/reconcile -> wallet.charge POST accounting/repayments/gold -> document.create POST accounting/repayments/rial -> document.create POST accounting/entries/:id/confirm -> document.list.confirm PATCH orders/:id/status -> order.list.sts.submit Four of the accounting routes WRITE to the ledger. The order-status route was worse in kind: its DTO takes a client-supplied actorAdminUserId, so an anonymous caller could move any order and pin it on any admin. It had no caller - the admin panel posts to admin/orders/status. GET /customers kept its public status (both apps' login still matches the phone number in the browser) but now selects only id/mobile/isActive/ isArchived. It previously returned every active customer's full name, national code, both debts, credit limit and the URLs of their identity-document scans. CustomerSummary narrowed in BOTH user apps in the same commit, per the v1/v2 parity rule. Not changed: /uploads stays public per ruling T2. The filename check requested alongside it passed unchanged - md5(time + name + uuid), no directory listing. Tests: apps/api/scripts/auth-smoke.sh asserts 401 without a token, 403 with a low-privilege token (ut 88, 5 unrelated rids) and pass-through for a super admin on every closed route, plus that the public routes still answer 200 and that GET /customers exposes only the four login fields. 23 passed, 0 failed against a live server. Typecheck clean on api, user-app and user-app-v2. [ref: henza_server/routes/admin.php:12,272; ApiMobileController.php:39-78] Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
004a3945c2
commit
fdf833565f
+21
-6
@@ -7,12 +7,27 @@
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| **Phase** | Phase 0 ✅ passed → **Phase 1 starting** |
|
||||
| **Branch** | `parity/phase-0` (next: `parity/phase-1-admin`) |
|
||||
| **Status** | Gate passed. Decisions D0–D12 recorded in `DECISIONS.md`. Urgent traces (percent, @Public, login leak) done and delivered. |
|
||||
| **Next step** | Show the user the `percent` trace + `@Public` list (done), get approval on (a) protecting the 5 ledger routes and (b) the `percent` schema change → then build `01-ADMIN-MATRIX.md` **field-by-field** (D12) |
|
||||
| **Blocked on** | user approval for: the 5 `@Public` ledger routes (D7), the `percent` column (D9), the D0 migration file (approval required before running) |
|
||||
| **Last updated** | 2026-09-21 |
|
||||
| **Phase** | Security hotfix (out-of-band, authorised by D7/T1–T3) — **stage 1 ✅ done**, stage 2 next |
|
||||
| **Branch** | `parity/security-hotfix` |
|
||||
| **Status** | Stage 1 complete and tested live: 23/23 smoke assertions pass. Decisions D0–D12 + T1–T3 recorded. |
|
||||
| **Next step** | Stage 2 — customer JWT (`typ` claim, separate secret) + real OTP + migrate both apps' login; then remove `GET /customers` from the public surface |
|
||||
| **Blocked on** | nothing for stage 2. Still pending later: D0 migration + `percent` migration must be **shown and approved before running** |
|
||||
| **Last updated** | 2026-09-22 |
|
||||
|
||||
### Security hotfix — stage 1 result
|
||||
|
||||
| Route | Was | Now | Rid |
|
||||
|---|---|---|---|
|
||||
| `GET accounting/customers/:id/balance` | public | admin | `wallet.list` |
|
||||
| `POST accounting/customers/:id/reconcile` | public **write** | admin | `wallet.charge` |
|
||||
| `POST accounting/repayments/gold` | public **write** | admin | `document.create` |
|
||||
| `POST accounting/repayments/rial` | public **write** | admin | `document.create` |
|
||||
| `POST accounting/entries/:id/confirm` | public **write** | admin | `document.list.confirm` |
|
||||
| `PATCH orders/:id/status` | public **write** | admin | `order.list.sts.submit` |
|
||||
| `GET customers` | 18 fields incl. national ID, debts, doc URLs | 4 fields | still public until stage 2 |
|
||||
|
||||
Test: `apps/api/scripts/auth-smoke.sh` — **23 passed, 0 failed**.
|
||||
Typecheck: api ✅ · user-app ✅ · user-app-v2 ✅.
|
||||
|
||||
## ⚠️ Read `DECISIONS.md` before doing anything
|
||||
|
||||
|
||||
Reference in New Issue
Block a user