Files
tehran_taj/docs/parity/ISSUES.md
T
s.esmaeiliandClaude Opus 5 71192ba2ba parity(docs): Phase 0 inventory, mapping and draft PROTECTED list
Read-only inventory of all 7 projects (3 reference + 4 target) produced by
parallel subagents, every claim cited path:line. No source code changed.

- 00-INVENTORY.md  consolidated mapping: stack, backend, admin, user apps
- PROTECTED.md     draft protected list (awaiting user confirmation)
- ISSUES.md        26 logged issues (security, money/stock, validation, dead code)
- CHANGELOG.md     change log scaffold
- PROGRESS.md      resumable state + Q1-Q5 blocking Phase 1
- _inv-*.md        7 raw per-project inventories (8111 lines)

Key finding: the reference is a gold-jewellery B2B/B2C e-commerce system, not a
gold bank. Buy/sell, deposit/withdraw, transfer, KYC levels, payment callbacks,
cron and websockets do not exist in it, so they cannot be parity gaps.

[ref: henza_server, henza_admin, henza_pwa]

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 18:09:46 +03:30

10 KiB
Raw Blame History

ISSUES — logged, NOT fixed during parity

Anything found during the parity audit that is a bug, risk, smell or improvement opportunity goes here. Do not fix these while parity work is in progress (see CLAUDE.md §3). They are worked through in Phase 3, only when the user asks.

Severity: Critical (money loss / security / data loss) · High · Medium · Low

Paths are relative to the workspace root (gold bank/). "REF" = reference project (read-only). "TGT" = gold-banking-monorepo. Items marked [replicated] exist in the reference and were faithfully ported — fixing them is a product decision, not a parity fix.

Security & auth

ID Severity Where Description Evidence Suggested fix
SEC-01 Critical REF henza_server/routes/admin.php:12,272 The whole admin route file is unauthenticated — the Route::middleware('auth:api')->group(...) wrapper is commented out at both ends, leaving all ~215 admin routes public (incl. wallet/charge, users, roles). Verified by reading both lines. n/a in reference (read-only). Drives TGT decision below.
SEC-02 Critical TGT apps/api/src/modules/accounting/accounting.controller.ts:7, orders/orders.controller.ts:7, customers/customers.controller.ts:5, catalog/catalog.controller.ts:5, pricing/pricing.controller.ts:5 15 routes carry @Public(), including ledger writes (POST accounting/repayments/*, entries/:id/confirm) and POST /api/orders + PATCH /api/orders/:id/status. The public status DTO accepts a client-supplied actorAdminUserId, so a caller can attribute an action to any admin. grep -rn "@Public()"; source comments say "pre-existing unauthenticated endpoint". Introduce customer-session auth for the app-facing routes; move ledger/status writes behind the admin guard. [replicated] from SEC-01.
SEC-03 Critical TGT apps/user-app/src/views/user/LoginView.vue, apps/user-app-v2/ same OTP is not verified — any 4-digit code logs the user in. Login also downloads the entire customer list (GET /api/customers) and matches the phone number client-side, exposing every customer's data to any visitor. Both inventory agents verified independently; customers.controller.ts:5 is @Public(). Implement a real server-side OTP issue/verify pair; never ship the customer list to the client.
SEC-04 High TGT apps/api (two files) JWT_SECRET falls back to 'dev-only-insecure-secret' and is absent from .env.example. enableCors() is wide open. Reported by api inventory; see _inv-tgt-api.md §11. Require the env var at boot; restrict CORS origins.
SEC-05 High TGT apps/api/prisma/seed.ts Seed creates a SUPER_ADMIN (09128081392) whose password equals the phone number, holding all 144 rids. Marked DEV-ONLY in comments but lives in the normal seed. docs/CHANGELOG-PORT-HISTORY.md §4 documents this as intentional for dev. Gate behind an explicit env flag so it can never run outside dev.
SEC-06 High REF henza_server Hardcoded secrets committed to the repo: SMS panel password, PayPing bearer token, Tahesab bearer + IP allowlist, wallet hash pepper ("yhoenszeaf"). See _inv-ref-server.md §11.4. Rotate all of them before any deployment that reuses these credentials.
SEC-07 Medium TGT apps/api/src/modules/admin-customers/admin-customers.service.ts setRoles stores arbitrary strings as rids with no validation against ALL_RIDS. api inventory §9. Validate against the rid catalogue.

Money, stock & data integrity

ID Severity Where Description Evidence Suggested fix
MON-01 Critical TGT apps/user-app-v2/src/stores/cartStore.ts v2 has no capQty / maxQty stock ceiling, which user-app does have (apps/user-app/src/stores/cartStore.ts:62,65; components/product/CartItem.vue:82-83). A v2 user can order more units than exist. Verified by grep across both apps — zero hits in v2. Port the ceiling to v2 (this is also a Phase 2 parity gap, row to be added to 02-APPS-MATRIX.md).
MON-02 High REF henza_server wallet package The wallet balance column mixes grams and toman in one field, and money operations are not wrapped in DB transactions. _inv-ref-server.md §7. Separate the units; wrap mutations in transactions.
MON-03 High TGT apps/api/src/modules/accounting/ledger.service.ts:189 GRAMS_PER_MESGHAL = 4.331802 is a JS float used inside an otherwise-Prisma.Decimal CASH repayment formula, so the divisor is pre-rounded before the Decimal division. Read at ledger.service.ts:182-190; the source comment already flags the formula for stakeholder review. Make the constant a Decimal (or a string literal passed to Decimal).
MON-04 High TGT apps/api/src/modules/orders/orders.service.ts Debt is recognised at DELIVERED_TO_AGENT, but the idempotency guard checks deliveredAt, which is only set by DELIVERED_TO_CUSTOMER — so the debt trigger is not reliably idempotent. There is also no status-transition validation anywhere. api inventory §8. Guard on the same field the trigger fires on; add an explicit transition table.
MON-05 Medium TGT apps/api canPlaceOrders and verificationStep are stored and toggled by the admin panel but never enforced on any order path. api inventory §8. Enforce at order creation, or remove the toggles.
MON-06 Medium TGT apps/api Setting rows are never seeded — wallet.config, wallet.birthday, promotion.auto, mobile_main_slider only materialise on first write, so a fresh install has undefined behaviour until an admin saves each one. api inventory §7. Seed the defaults.
MON-07 Medium REF henza_server/app/Models/ProductVariant.php:183-190 vs SingleCustomerOrders.php:67, Order.php:66 Two divergent order-status enums coexist (0–6 and 1–7) for what is conceptually one flow. Read both models. Unify, or document the mapping explicitly.

Validation & contracts

ID Severity Where Description Evidence Suggested fix
VAL-01 High REF henza_server Zero FormRequests; all validation is inline, and setOrder, wallet charge, setConst and file upload have no validation at all. The wallet package's own validator is dead code (it calls failed() instead of fails()). _inv-ref-server.md §12. n/a in reference; informs how much validation the target must add.
VAL-02 High TGT apps/api Only 8 DTO classes exist; most admin request bodies are untyped Record<string, unknown>, so class-validator never runs on them. api inventory §5. Add DTOs for the admin write paths.
VAL-03 Medium TGT both user apps HTTP client is a bare fetch wrapper: no interceptors, no Authorization header, no refresh, no 401 handling, no error toasts. Non-2xx throws a generic Error; call sites mostly console.log(e). Order-submit failure is silent to the user. apps/user-app/src/api/client.ts, apps/user-app-v2/src/api/client.ts:3. Add a shared client with error surfacing; compare against henza_pwa/src/service/* in Phase 2.

Parity-relevant divergence between user-app and user-app-v2

CLAUDE.md §2: the only allowed difference is visual design. These are logic differences, so they are Phase 2 parity gaps as well as issues.

ID Severity Where Description Evidence
V2-01 Critical v2 missing stock ceiling see MON-01 grep
V2-02 Medium v2 missing getHomeSlider() / GET /home/slider admin banner v1 api/client.ts:152-170 defines AdminSlide + getHomeSlider; v2 has neither diff apps/user-app/src/api/client.ts apps/user-app-v2/src/api/client.ts
V2-03 Medium v2 missing per-variant images v1 defines ProductImageSummary (id/url/sortOrder/role) and imageId/image on the variant; v2's images is just Array<{url:string}> same diff
V2-04 Medium v2 missing orderable-first variant defaulting v1 composables/useProduct.ts:26 picks the first orderable variant; v2 has no equivalent grep
V2-05 Low v2 missing variant-chip preview cap + per-chip stock badges v1 components/product/SingleProduct.vue:388-392 grep

Dead code & maintenance

ID Severity Where Description
DEAD-01 Low TGT apps/user-app 8 components with no importers, loadingStore, api.getBalance (zero call sites), unused gsap dependency.
DEAD-02 Low TGT apps/user-app-v2 11 dead components, loadingStore, getBalance, priceUnit/priceUnitRialLess, 12 of 15 validators, unused gsap + @ant-design/icons-vue deps.
DEAD-03 Low TGT gold-banking-monorepo/user-app-v2/ (repo root, outside apps/) Stray leftover folder containing only package.json and an older vite.config.ts (hardcoded port 5179, hardcoded proxy, imports a themeConfig that doesn't exist there). Not matched by pnpm-workspace.yaml, so pnpm ignores it. Left in place — deleting it needs user approval.
DEAD-04 Low REF henza_server/app/Console/Kernel.php schedule() is empty — no cron. Price refresh / expiry / birthday logic must be driven by OS cron or the GET / web route.
DEAD-05 Low REF henza_server PayPing returnUrl points at site/transaction/verify, which is not registered in any route file — the payment callback is unreachable.

Known stubs in the target (not bugs, but not parity either)

ID Where Description
STUB-01 TGT both user apps Registration persists nothing; favourites are in-memory only (lost on reload); notifications is a static empty state; home slides are 3 hardcoded placeholders in v2.
STUB-02 TGT apps/api No OTP, no SMS provider, no payment gateway/callbacks, no cron, no websockets. SMS and notifications are DB-persistence only.
STUB-03 TGT both user apps All list filtering and "pagination" is client-side over one flat GET /catalog/products; no server page/limit params. "Pagination" is a 20-item IntersectionObserver window.
STUB-04 TGT both user apps Live price is pull-on-mount only (GET /pricing/latest?karatPerMille=750) — no socket, no SSE, no polling interval. quotedAt renders as a Jalali date only, so a stale rate looks current.