From e1eae1099c559a3a568962087d67d0906fea7b80 Mon Sep 17 00:00:00 2001 From: hamed <15238-genius.ha@users.noreply.drupalcode.org> Date: Wed, 1 Jul 2026 12:26:02 +0330 Subject: [PATCH] feat(audit): add comprehensive QA audit report for ClinicPro with findings and recommendations --- .claude/prompt/qa-full-system-audit.md | 197 +++++++++++++++++ docs/qa/full-system-audit-report.md | 205 ++++++++++++++++++ .../Controller/DashboardController.php | 3 +- src/Sms/Controller/SmsWalletController.php | 2 +- 4 files changed, 405 insertions(+), 2 deletions(-) create mode 100644 .claude/prompt/qa-full-system-audit.md create mode 100644 docs/qa/full-system-audit-report.md diff --git a/.claude/prompt/qa-full-system-audit.md b/.claude/prompt/qa-full-system-audit.md new file mode 100644 index 00000000..8620fae9 --- /dev/null +++ b/.claude/prompt/qa-full-system-audit.md @@ -0,0 +1,197 @@ +# ممیزی کامل QA سیستم — بک‌اند Symfony API + پنل ادمین React + +## پروژه + +`clinicpro` (Backend Symfony 7.4 + پنل ادمین React 19 داخل همان repo) + +## نقش اجراکننده + +Senior QA Engineer. خروجی این پرامپت **کد جدید محصول نیست**؛ یک **گزارش باگ/ممیزی واقعی و قابل‌بازتولید** است، به‌علاوهٔ در صورت لزوم افزودن/اصلاح تست‌های خودکار (phpunit / vitest) برای اثبات باگ‌ها. **حدس ممنوع** — هر یافته باید با خروجی واقعی دستور/تست یا ارجاع `file:line` مستند شود. + +## زمینه + +سیستم منبع واحد داده و auth برای سه کلاینت است (پنل ادمین خودش، `nobat724_front`، `clinic-pro-tauri`). ۲۷۷ روت `api/v1`، ۷ دامنه اصلی، احراز هویت JWT بدون‌حالت (stateless). زیرساخت تست موجود است و باید استفاده شود، نه بازنویسی: + +- **Backend:** PHPUnit — پایه `tests/ApiTestCase.php`، ۳۸ فایل تست در `tests//`. اجرا: `ddev exec php bin/phpunit` +- **Frontend:** Vitest — ۹ فایل `*.test.tsx`/`*.test.ts` در `assets/admin/`. اجرا: `ddev exec yarn test` +- **Static analysis:** `ddev exec php vendor/bin/phpstan analyse` (level 5) + +## هدف + +اجرای واقعی تست روی کل سیستم و تولید گزارش ساختاریافته از باگ‌ها، مشکلات طراحی، ناسازگاری قرارداد API با کلاینت، و ضعف‌های امنیتی. تمرکز روی **رفتار واقعی مشاهده‌شده**، نه بازبینی نظری کد. + +--- + +## فایل‌های مرتبط (نقشهٔ سیستم برای تست) + +| ناحیه | مسیر | نقش | +|------|------|-----| +| پایه پاسخ‌ها | `src/Shared/Controller/BaseController.php` | قرارداد `success`/`paginated`/`error`/`validationError` | +| کدهای خطا | `src/Shared/Constant/ErrorCodes.php` | همه error codeها + پیام فارسی | +| Exception handler | `src/**/ExceptionSubscriber*` | تبدیل `AppException` به پاسخ خطا | +| امنیت | `config/packages/security.yaml` | firewalls، `public_endpoints`، `access_control` | +| احراز هویت | `src/Auth/` | `PasswordAuthenticator`، `oauth/token`، refresh، otp-login | +| کنترلرهای admin | `src/Admin/Controller/AdminApiController.php` | لیست/آمار admin (DQL array hydration) | +| دامنه‌ها | `src/{Appointment,Doctor,Clinic,Payment,Rating,Blog,Sms,Representation,Secretary,Settlement,Category,Billing,Subscription}/` | business logic | +| تست‌های موجود | `tests//`، `tests/ApiTestCase.php` | نقطهٔ شروع افزودن تست | +| پنل ادمین | `assets/admin/pages/` (۴۹ صفحه)، `assets/admin/App.tsx` | routing + صفحات | +| API client فرانت | `assets/admin/lib/api.ts` | fetch wrapper، خواندن JWT از `localStorage['clinicpro-auth']` | +| state | `assets/admin/stores/authStore.ts`، `uiStore.ts` | Zustand | +| مستندات قرارداد | `docs/api/*.md` | مرجع مقایسهٔ رفتار واقعی با مستند | +| کاربران تست | `TEST_USERS.md` | credential همهٔ نقش‌ها | + +--- + +## پیش‌نیازها (قبل از شروع تست) + +```bash +# سرویس‌ها بالا باشند +ddev exec php bin/console doctrine:migrations:migrate --no-interaction +ddev exec php create_test_users.php # بازساخت کاربران تست در صورت نیاز +ddev exec php bin/console messenger:consume async --limit=0 & # برای تست SMS/queue + +# کاربران تست (رمز همه: Test@1234) +# ادمین: 09100000001 ROLE_ADMIN +# کلینیک: 09100100000 ROLE_CLINIC (uuid: 9ac73318-d313-4772-bf1e-418d47f8f4bc) +# دکتر: 09100100001 ROLE_DOCTOR (uuid: 2a3a7ab9-8d34-4118-862f-b458bcd6d77f) +# منشی: 09100100002 ROLE_SECRETARY +``` + +**دریافت توکن برای تست زندهٔ API** — پارامترهای دقیق grant را از `docs/api/auth.md` بخوان (حدس نزن)، سپس: + +```bash +# قالب — پارامترهای دقیق (grant_type/client_id/scope) را از docs/api/auth.md تأیید کن +curl -sk -X POST https://clinic-pro.ddev.site/oauth/token \ + -H 'Content-Type: application/json' \ + -d '{"grant_type":"password","username":"09100000001","password":"Test@1234","scope":"clinicpro"}' +# توکن خروجی را در $TOKEN بگذار و برای endpointهای محافظت‌شده: +# -H "Authorization: Bearer $TOKEN" +``` + +--- + +## وظایف + +اجرا **فاز‌به‌فاز**؛ هر فاز یک آیتم todo. در هر فاز اول تست خودکار موجود را اجرا کن، سپس تست زندهٔ هدفمند. یافته‌ها را در گزارش (بخش «قالب گزارش») ثبت کن. + +### فاز ۰ — Baseline + +```bash +ddev exec php bin/phpunit 2>&1 | tail -30 # وضعیت فعلی سبز/قرمز +ddev exec yarn test 2>&1 | tail -30 +ddev exec php vendor/bin/phpstan analyse 2>&1 | tail -30 +``` + +هر تست شکست‌خورده یا خطای phpstan = یافتهٔ فاز ۰ (قبل از هر تغییری وضعیت پایه ثبت شود). + +### فاز ۱ — قرارداد پاسخ و Envelope + +از `BaseController` قرارداد را استخراج کن و روی نمونهٔ واقعی از هر شکل تأیید کن: + +- `success` → `{ success:true, data }` +- `paginated` → `{ success, data:[], meta:{ totalRecords, totalPages, currentPage } }` (تخت، نه nested) +- `error` → `{ success:false, errors:[{code,message}] }` +- `validationError` → HTTP 422 با `errors:[{code,field,message}]` + +تست زنده روی حداقل یک endpoint از هر نوع: + +```bash +curl -sk "https://clinic-pro.ddev.site/api/v1/admin/doctors?page=1&limit=5" -H "Authorization: Bearer $TOKEN" | head +curl -sk "https://clinic-pro.ddev.site/api/v1/doctor/INVALID-UUID" -H "Authorization: Bearer $TOKEN" +``` + +**دام شناخته‌شده (`double-nested`):** endpointهایی که `$this->success(['data' => ...])` می‌زنند خروجی `data.data.data` می‌دهند. هر endpoint را که این الگو را دارد پیدا کن و بررسی کن کلاینت (`assets/admin/` و `docs/api/`) درست استخراج می‌کند یا نه. ناسازگاری = یافته. + +### فاز ۲ — احراز هویت و مجوزها (Auth / RBAC) + +نقش‌ها: `ROLE_ADMIN`, `ROLE_CLINIC`, `ROLE_DOCTOR`, `ROLE_SECRETARY`, `ROLE_REPRESENTATION`. + +- **login flows:** `POST /oauth/token`، `/oauth/token/refresh`، `/api/v1/user/login`، `/api/v1/user/otp-login`، `/oauth/logout`. تست: توکن معتبر، توکن منقضی، refresh با توکن باطل، رمز اشتباه (نباید ۵۰۰ بدهد، باید خطای ساخت‌یافته). +- **ماتریس دسترسی:** برای نمونه‌ای از endpointهای هر دامنه، با توکنِ نقشِ *نامجاز* درخواست بزن و انتظار `403` (نه `200`، نه `500`). مثال بحرانی: + +```bash +# دکتر نباید به endpointهای admin دسترسی داشته باشد +curl -sk -o /dev/null -w "%{http_code}\n" \ + "https://clinic-pro.ddev.site/api/v1/admin/users" -H "Authorization: Bearer $DOCTOR_TOKEN" # انتظار 403 +``` + +- **IDOR / افقی:** با توکن دکتر A به منبع دکتر B دسترسی بگیر (`/api/v1/appointment/{uuid}`، `/api/v1/patient/{uuid}`، `/api/v1/settlement/...`). دسترسی موفق = یافتهٔ Critical. +- **public_endpoints:** الگوی regex در `security.yaml` را با روت‌های واقعی تطبیق بده؛ endpointی که باید محافظت‌شده باشد ولی زیر public افتاده = یافتهٔ Critical. برعکس، endpoint عمومی که ۴۰۱ می‌دهد = یافتهٔ High. + +### فاز ۳ — اعتبارسنجی ورودی و مدیریت خطا + +برای endpointهای نوشتنی (POST/PUT/PATCH/DELETE — ۳۹ در admin، بقیه در دامنه‌ها): + +- بدنهٔ خالی، فیلد اجباری غایب، نوع اشتباه (string به‌جای int)، مقدار خارج از بازه، UUID نامعتبر، تاریخ نامعتبر. +- انتظار: `422` با `validationError` ساخت‌یافته، نه `500` و نه پذیرش خاموش. +- **timestamp:** تاریخ‌ها باید Unix timestamp صحیح باشند نه DateTime؛ ورودی/خروجی تاریخ را روی یک نمونهٔ واقعی (مثلاً ساخت نوبت) بررسی کن. + +### فاز ۴ — امنیت API + +- **SQL Injection:** روی endpointهای دارای فیلتر/جستجو (`/api/v1/patient/search-user`, `/api/v1/admin/*?search=`, `categorys/{bundle}`) payload تزریق بفرست؛ چون لیست‌های admin از DQL array hydration استفاده می‌کنند انتظار امن‌بودن است — اما تأیید عملی کن. +- **XSS ذخیره‌شده:** در فیلدهای متنی (نام دکتر، متن بلاگ، کامنت، متن پیامک post-visit) `