docs: align flow docs with code reality + create 35 implementation issue files
Flow docs updated (11 files): - Delivery Confirmation: reversed actor roles (buyer generates, seller verifies), fixed endpoint paths (/delivery-code/generate, /delivery-code/verify) - Passkey (WebAuthn): removed stub/simulated-key claims; real @simplewebauthn/server attestation is implemented; refresh tokens are persisted - Dispute: corrected resolve schema (action enum), removed non-existent statuses, documented security gaps (no role guards on status/resolve/assign), route shadowing, all socket events are TODO stubs - Seller Offer: corrected all endpoint paths, removed 'active' status, documented withdraw dead code, missing seller history page, select-offer notification gap - Notification: corrected mark-all-read method+path, fixed GET /:id broken lookup, added unread-count-update socket event - Authentication: corrected rate limiter (counts all attempts), axios 403 not handled, deleteAccount wrong endpoint bug, changePassword no UI - Password Reset: corrected 6-digit code (not 8), documented no-complexity gap on reset-with-code vs token reset - Payment Flow DePay: /create→/save, removed phantom sub-routes, SIM_ bypass risk, PaymentProvider type gap, getProviderIntentEndpoint routing bug - Payment Flow SHKeeper: removed phantom polling endpoint, fixed release/refund paths - Purchase Request: added pending_payment/active statuses, fixed sellers/attachments endpoints, corrected socket events, PUT→PATCH bug - Escrow: documented dispute resolve does not touch escrow, route shadowing, confirm-delivery auth gap Issues created (35 files in Issues/): - 9 security issues (critical) including: dispute privilege escalation ×4, unauthenticated payment/scanner endpoints ×2, SIM_ production bypass, confirm-delivery ownership gap - 26 additional major/critical bugs covering broken endpoints, missing features, data integrity gaps, and frontend-backend mismatches Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
50
Issues/ISSUE-001-dispute-status-no-role-guard.md
Normal file
50
Issues/ISSUE-001-dispute-status-no-role-guard.md
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
issue: "001"
|
||||
title: "PATCH /api/disputes/:id/status has no role guard — privilege escalation"
|
||||
severity: critical
|
||||
domain: dispute
|
||||
labels: [security, backend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 PATCH /api/disputes/:id/status has no role guard — privilege escalation
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** dispute
|
||||
**Labels:** security, backend, bug
|
||||
|
||||
## Description
|
||||
|
||||
`PATCH /api/disputes/:id/status` is mounted with only `authenticateToken` middleware — no `authorizeRoles('admin')` guard. Any authenticated buyer or seller who knows a dispute `_id` can change that dispute's status to `resolved`, `closed`, or any other value including states that release funds or trigger bans.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Any authenticated user (buyer or seller) can call:
|
||||
```
|
||||
PATCH /api/disputes/{disputeId}/status
|
||||
{ "status": "resolved" }
|
||||
```
|
||||
and receive a 200 response. The dispute status is updated in MongoDB.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Only users with `role: admin` should be permitted to change a dispute's status. Non-admin tokens should receive `403 Forbidden`.
|
||||
|
||||
## Reproduction Steps
|
||||
|
||||
1. Log in as a buyer or seller, obtain a JWT.
|
||||
2. Find or create a dispute `_id`.
|
||||
3. `PATCH /api/disputes/{id}/status` with `{ "status": "resolved" }` and the buyer/seller Bearer token.
|
||||
4. Observe 200 and the status change in the DB.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/disputeRoutes.ts` — router missing `authorizeRoles('admin')` before `updateStatus` handler
|
||||
- `backend/src/controllers/disputeController.ts` — `updateStatus` method
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding C16
|
||||
- Related: [[ISSUE-002-dispute-resolve-no-role-guard]]
|
||||
45
Issues/ISSUE-002-dispute-resolve-no-role-guard.md
Normal file
45
Issues/ISSUE-002-dispute-resolve-no-role-guard.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
issue: "002"
|
||||
title: "POST /api/disputes/:id/resolve has no role guard — any user can resolve disputes and ban sellers"
|
||||
severity: critical
|
||||
domain: dispute
|
||||
labels: [security, backend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 POST /api/disputes/:id/resolve has no role guard — any user can resolve disputes and ban sellers
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** dispute
|
||||
**Labels:** security, backend, bug
|
||||
|
||||
## Description
|
||||
|
||||
The dashboard dispute router's `POST /api/disputes/:id/resolve` handler applies only `authenticateToken`. No `authorizeRoles('admin')` guard exists. Any authenticated user can post any resolution action including `action: 'ban_seller'`, `action: 'refund'`, or `action: 'no_action'`, bypassing all admin authority.
|
||||
|
||||
Note: the *releaseHold* router's `POST /api/disputes/:purchaseRequestId/resolve` correctly uses `authorizeRoles('admin')`, but the dashboard router does not.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
A buyer or seller can call:
|
||||
```
|
||||
POST /api/disputes/{disputeId}/resolve
|
||||
{ "action": "ban_seller", "notes": "malicious" }
|
||||
```
|
||||
The resolution is persisted with a 200 response.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
`POST /api/disputes/:id/resolve` must be protected by `authorizeRoles('admin')`. Non-admin tokens should receive `403`.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/disputeRoutes.ts` (dashboard router, mounted at `/api/disputes` first)
|
||||
- `backend/src/controllers/disputeController.ts` — `resolveDispute` method
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding C17
|
||||
- Related: [[ISSUE-001-dispute-status-no-role-guard]], [[ISSUE-003-dispute-route-shadowing]]
|
||||
41
Issues/ISSUE-003-dispute-route-shadowing.md
Normal file
41
Issues/ISSUE-003-dispute-route-shadowing.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
issue: "003"
|
||||
title: "Route shadowing: two dispute routers mounted at /api/disputes cause non-deterministic handler dispatch"
|
||||
severity: critical
|
||||
domain: dispute
|
||||
labels: [backend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 Route shadowing: two dispute routers mounted at /api/disputes cause non-deterministic handler dispatch
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** dispute
|
||||
**Labels:** backend, bug
|
||||
|
||||
## Description
|
||||
|
||||
In `backend/src/app.ts`, two separate dispute routers are mounted on the same path `/api/disputes`:
|
||||
- Line ~521: `dashboardDisputeRoutes` (first — unguarded `POST /:id/resolve`, `PATCH /:id/status`)
|
||||
- Line ~585: `releaseHold disputeRoutes` (second — admin-guarded `POST /:purchaseRequestId/resolve`, also `GET /:purchaseRequestId/status`)
|
||||
|
||||
Express evaluates in registration order. A `POST /api/disputes/{purchaseRequestId}/resolve` request will match the **dashboard router's** `POST /:id/resolve` handler first (since `:id` and `:purchaseRequestId` are identical route patterns). This executes the unguarded Dispute CRUD resolve instead of the admin-guarded escrow release-hold logic.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
`POST /api/disputes/{purchaseRequestId}/resolve` executes the dashboard `resolveDispute` controller (updates the Dispute document only, no role guard) rather than the intended `releaseHold` handler (admin-only, clears escrow).
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
The escrow-release resolve handler should be reachable at a distinct, unambiguous path (e.g., `/api/disputes/hold/:purchaseRequestId/resolve` or mounted at a different prefix).
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/app.ts` — two `app.use('/api/disputes', ...)` mount points
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding C18
|
||||
- Related: [[ISSUE-002-dispute-resolve-no-role-guard]]
|
||||
46
Issues/ISSUE-004-payment-endpoints-no-auth.md
Normal file
46
Issues/ISSUE-004-payment-endpoints-no-auth.md
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
issue: "004"
|
||||
title: "fetch-tx, auto-fetch-missing, and debug payment endpoints have no authentication"
|
||||
severity: critical
|
||||
domain: payment
|
||||
labels: [security, backend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 fetch-tx, auto-fetch-missing, and debug payment endpoints have no authentication
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** payment
|
||||
**Labels:** security, backend, bug
|
||||
|
||||
## Description
|
||||
|
||||
Three backend payment endpoints are mounted with **no `authenticateToken` middleware**, despite being documented as admin-only:
|
||||
|
||||
1. `POST /api/payment/payments/:id/fetch-tx` — triggers on-chain transaction fetch for a payment
|
||||
2. `POST /api/payment/payments/auto-fetch-missing` — triggers bulk on-chain fetch for all pending payments
|
||||
3. `GET /api/payment/payments/:id/debug` — returns full payment document including blockchain metadata and wallet monitor state
|
||||
|
||||
Any unauthenticated caller (no Authorization header needed) can call all three endpoints.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
```bash
|
||||
curl -X POST https://api.example.com/api/payment/payments/anyId/fetch-tx
|
||||
# Returns 200 and triggers on-chain state write
|
||||
```
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
All three endpoints should require `authenticateToken` + `authorizeRoles('admin')` and return `401` without credentials.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/paymentRoutes.js` — route definitions for `fetch-tx`, `auto-fetch-missing`, `debug`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Findings C28, M40
|
||||
- Related: [[ISSUE-005-scanner-status-no-auth]]
|
||||
40
Issues/ISSUE-005-scanner-status-no-auth.md
Normal file
40
Issues/ISSUE-005-scanner-status-no-auth.md
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
issue: "005"
|
||||
title: "GET /api/admin/scanner/status has no authentication despite /api/admin/ prefix"
|
||||
severity: critical
|
||||
domain: admin
|
||||
labels: [security, backend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 GET /api/admin/scanner/status has no authentication despite /api/admin/ prefix
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** admin
|
||||
**Labels:** security, backend, bug
|
||||
|
||||
## Description
|
||||
|
||||
`GET /api/admin/scanner/status` proxies to `AMN_SCANNER_URL` and returns scanner status data. Despite sitting under the `/api/admin/` prefix (which conventionally implies admin auth), this endpoint has **no `authenticateToken` middleware**. Any unauthenticated request returns scanner data.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
```bash
|
||||
curl https://api.example.com/api/admin/scanner/status
|
||||
# Returns scanner data with 200, no credentials needed
|
||||
```
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Should return `401` without a valid admin JWT.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/adminRoutes.js` — scanner proxy route definition
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding C29
|
||||
- Related: [[ISSUE-004-payment-endpoints-no-auth]]
|
||||
49
Issues/ISSUE-006-delete-account-wrong-endpoint.md
Normal file
49
Issues/ISSUE-006-delete-account-wrong-endpoint.md
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
issue: "006"
|
||||
title: "Frontend deleteAccount action calls DELETE /user/profile which does not exist"
|
||||
severity: critical
|
||||
domain: auth
|
||||
labels: [frontend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 Frontend deleteAccount action calls DELETE /user/profile which does not exist
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** auth
|
||||
**Labels:** frontend, bug
|
||||
|
||||
## Description
|
||||
|
||||
`frontend/src/actions/account.ts` (line ~144) calls:
|
||||
```ts
|
||||
axiosInstance.delete(endpoints.users.profile)
|
||||
// resolves to DELETE /user/profile
|
||||
```
|
||||
|
||||
There is no `DELETE` handler on `/user/profile` in the backend. The actual soft-delete endpoint is:
|
||||
```
|
||||
DELETE /api/auth/account
|
||||
```
|
||||
which requires a `password` field in the request body and runs `deleteAccountValidation`.
|
||||
|
||||
**Result:** Account deletion silently 404s from every UI path. Users cannot delete their accounts.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Clicking the delete account button in the dashboard sends `DELETE /user/profile` → 404. The account is not deleted.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
The action should send `DELETE /api/auth/account` with `{ password }` in the body. On success, the account status is set to `'deleted'` (soft delete) in MongoDB.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/account.ts` — `deleteAccount` function
|
||||
- `frontend/src/lib/axios.ts` — `endpoints.users.profile` key used for the path
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding C3
|
||||
42
Issues/ISSUE-007-sim-bypass-no-env-guard.md
Normal file
42
Issues/ISSUE-007-sim-bypass-no-env-guard.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
issue: "007"
|
||||
title: "SIM_ transaction bypass active in production — no NODE_ENV guard on wallet connection fallback"
|
||||
severity: critical
|
||||
domain: payment
|
||||
labels: [security, frontend, backend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 SIM_ transaction bypass active in production — no NODE_ENV guard on wallet connection fallback
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** payment
|
||||
**Labels:** security, frontend, backend, bug
|
||||
|
||||
## Description
|
||||
|
||||
`frontend/src/web3/context/web3-provider.tsx` (lines ~225 and ~232) generates `SIM_` prefixed transaction hashes when wallet connection fails, and passes these to the backend as real transaction hashes.
|
||||
|
||||
The backend's payment service skips all on-chain verification for any `paymentHash` starting with `SIM_`. This bypass is controlled **only by the hash prefix** — there is no `process.env.NODE_ENV === 'development'` check in either the frontend or backend.
|
||||
|
||||
In production, if a user's wallet connection times out or throws (e.g., network error, MetaMask not responding), the frontend will submit a `SIM_` hash. This can result in a payment record being created as `completed` without any actual on-chain transaction.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Wallet connection failure → frontend generates `SIM_xxxxxxxx` hash → sends to backend → backend skips on-chain verification → payment created as completed.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
- Frontend: `SIM_` hash generation should be gated on `process.env.NODE_ENV !== 'production'`
|
||||
- Backend: `SIM_` bypass should additionally check an environment flag (e.g., `process.env.ALLOW_SIM_PAYMENTS !== 'true'`)
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/web3/context/web3-provider.tsx` — lines ~225, ~232
|
||||
- `backend/src/services/payment/` — SIM_ prefix check in payment verification logic
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M39
|
||||
41
Issues/ISSUE-008-chat-file-upload-wrong-endpoint.md
Normal file
41
Issues/ISSUE-008-chat-file-upload-wrong-endpoint.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
issue: "008"
|
||||
title: "sendFileMessage posts to wrong endpoint — file uploads always fail in chat"
|
||||
severity: critical
|
||||
domain: chat
|
||||
labels: [frontend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 sendFileMessage posts to wrong endpoint — file uploads always fail in chat
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** chat
|
||||
**Labels:** frontend, bug
|
||||
|
||||
## Description
|
||||
|
||||
`frontend/src/actions/chat.ts` (line ~386) sends file upload multipart form data to `endpoints.chat.sendMessage` which resolves to `POST /api/chat/:id/messages` — the text message endpoint.
|
||||
|
||||
The actual backend file upload endpoint is `POST /api/chat/:id/messages/file`.
|
||||
|
||||
The text-message handler expects a JSON body with a `content` string field, not a multipart payload. The file upload either fails or the attachment is silently discarded.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
User picks a file in the chat input → `sendFileMessage` POSTs multipart to `/chat/:id/messages` → backend text handler rejects or ignores the multipart payload → file is never uploaded or stored.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
`sendFileMessage` should POST to `/api/chat/:id/messages/file` with the multipart form data. The response should include a message with an `attachments` array.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/chat.ts` — `sendFileMessage` function uses `endpoints.chat.sendMessage`
|
||||
- `frontend/src/lib/axios.ts` — no `endpoints.chat.sendFileMessage` entry exists; needs to be added as `/chat/:id/messages/file`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding C19
|
||||
36
Issues/ISSUE-009-archive-chat-wrong-method.md
Normal file
36
Issues/ISSUE-009-archive-chat-wrong-method.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: "009"
|
||||
title: "archiveConversation uses PUT but backend only accepts PATCH"
|
||||
severity: major
|
||||
domain: chat
|
||||
labels: [frontend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 archiveConversation uses PUT but backend only accepts PATCH
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** chat
|
||||
**Labels:** frontend, bug
|
||||
|
||||
## Description
|
||||
|
||||
`frontend/src/actions/chat.ts` (line ~289) calls `axiosInstance.put(endpoints.chat.archive, ...)`. The backend registers this route as `PATCH /api/chat/:id/archive`. Express treats PUT and PATCH as distinct methods; PUT will not match the PATCH handler and returns 404/405.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Attempting to archive a conversation from the UI sends `PUT /api/chat/:id/archive` → 404. The chat is not archived.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
`archiveConversation` should use `axiosInstance.patch(...)` to match the backend's PATCH registration. The endpoint also has toggle semantics — calling it on an archived chat unarchives it.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/chat.ts` — `archiveConversation` method verb (`put` → `patch`)
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding C20
|
||||
49
Issues/ISSUE-010-admin-user-status-wrong-values-and-verb.md
Normal file
49
Issues/ISSUE-010-admin-user-status-wrong-values-and-verb.md
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
issue: "010"
|
||||
title: "Admin user status/role actions broken: wrong HTTP verb (PUT vs PATCH) and wrong status values"
|
||||
severity: critical
|
||||
domain: admin
|
||||
labels: [frontend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 Admin user status/role actions broken: wrong HTTP verb (PUT vs PATCH) and wrong status values
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** admin
|
||||
**Labels:** frontend, bug
|
||||
|
||||
## Description
|
||||
|
||||
Two separate bugs on the admin user management actions:
|
||||
|
||||
**Bug 1 — Wrong HTTP verb:**
|
||||
`frontend/src/actions/user.ts`:
|
||||
- `updateUserStatus` calls `axiosInstance.put(...)` — backend registers `PATCH`
|
||||
- `updateUserRole` calls `axiosInstance.put(...)` — backend registers `PATCH`
|
||||
|
||||
Both will 404/405 in production since Express doesn't alias PUT to PATCH.
|
||||
|
||||
**Bug 2 — Wrong status values:**
|
||||
`updateUserStatus` accepts and sends `'active' | 'inactive' | 'pending'`. The backend `User.status` enum only accepts `'active' | 'suspended' | 'deleted'`. Sending `'inactive'` or `'pending'` is silently rejected or ignored. `'suspended'` is completely absent from the frontend type.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
- Clicking "Suspend user" in admin panel sends `PUT /api/users/admin/:userId/status` with `{ status: 'inactive' }` → 404 and wrong value
|
||||
- Clicking "Update role" sends `PUT /api/users/admin/:userId/role` → 404
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
- Use `axiosInstance.patch(...)` for both actions
|
||||
- Status values should be `'active' | 'suspended' | 'deleted'` to match the backend enum
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/user.ts` — `updateUserStatus` (line ~162), `updateUserRole` (line ~175)
|
||||
- `frontend/src/types/user.ts` (line ~159) — status union type needs to include `'suspended'` and remove `'inactive'`/`'pending'`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Findings C26, C27
|
||||
36
Issues/ISSUE-011-update-purchase-request-put-vs-patch.md
Normal file
36
Issues/ISSUE-011-update-purchase-request-put-vs-patch.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: "011"
|
||||
title: "updatePurchaseRequest sends PUT but backend only accepts PATCH"
|
||||
severity: major
|
||||
domain: purchase-request
|
||||
labels: [frontend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 updatePurchaseRequest sends PUT but backend only accepts PATCH
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** purchase-request
|
||||
**Labels:** frontend, bug
|
||||
|
||||
## Description
|
||||
|
||||
`frontend/src/actions/marketplace.ts` (line ~71) calls `axiosInstance.put(endpoints.marketplace.requests.update)`. The backend registers `PATCH /marketplace/purchase-requests/:id` (routes.ts). Sending PUT results in 404/405 — edits to purchase requests silently fail.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Editing a purchase request from the buyer edit view sends `PUT /marketplace/purchase-requests/:id` → 404. The request is not updated.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
The action should use `axiosInstance.patch(...)`.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/marketplace.ts` — `updatePurchaseRequest` function (verb: `put` → `patch`)
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M18
|
||||
36
Issues/ISSUE-012-update-offer-put-vs-patch.md
Normal file
36
Issues/ISSUE-012-update-offer-put-vs-patch.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: "012"
|
||||
title: "updateOffer sends PUT but backend registers PATCH — offer edits fail"
|
||||
severity: major
|
||||
domain: seller-offer
|
||||
labels: [frontend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 updateOffer sends PUT but backend registers PATCH — offer edits fail
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** seller-offer
|
||||
**Labels:** frontend, bug
|
||||
|
||||
## Description
|
||||
|
||||
`frontend/src/actions/marketplace.ts` (line ~289) calls `axiosInstance.put(endpoints.marketplace.offers.update)` mapping to `PUT /marketplace/offers/:id`. The backend registers `PATCH /offers/:id` (routes.ts line ~1260). Method mismatch → 404 or matched wrong route. `step-1-send-proposal.tsx` calls `updateOffer()` for proposal edits, so this path is actively exercised.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
A seller editing an existing proposal sends `PUT /marketplace/offers/:id` which does not match the registered `PATCH` handler.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
`updateOffer` should use `axiosInstance.patch(...)`.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/marketplace.ts` — `updateOffer` function
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M28
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
issue: "013"
|
||||
title: "select-offer cascade overwrites withdrawn/rejected offers — missing status filter in updateMany"
|
||||
severity: major
|
||||
domain: seller-offer
|
||||
labels: [backend, bug, data-integrity]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 select-offer cascade overwrites withdrawn/rejected offers — missing status filter in updateMany
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** seller-offer
|
||||
**Labels:** backend, bug, data-integrity
|
||||
|
||||
## Description
|
||||
|
||||
`POST /api/marketplace/purchase-requests/:id/select-offer` (routes.ts lines ~1386-1395) calls `SellerOffer.updateMany({ purchaseRequestId, _id: { $ne: offerId } }, { status: 'rejected' })` with **no status filter**. This overwrites offers that are already `'withdrawn'` or previously `'rejected'`, corrupting their status history.
|
||||
|
||||
By contrast, `SellerOfferService.acceptOffer()` (the service method used by `PUT /offers/:id/accept`) correctly filters with `status: { $in: ['pending', 'active'] }` before bulk-rejecting competitors.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
1. Seller A submits offer → pending
|
||||
2. Seller B submits offer → pending
|
||||
3. Seller B withdraws offer → withdrawn
|
||||
4. Buyer selects Seller A's offer via `POST .../select-offer`
|
||||
5. Seller B's withdrawn offer is **overwritten to 'rejected'** — status history corrupted
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
The `updateMany` in the `select-offer` route handler should add `status: { $in: ['pending'] }` to only reject currently-pending competing offers. Already-withdrawn or rejected offers should be left untouched.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/routes.ts` (or marketplaceController.ts) — `select-offer` route handler's `updateMany` call
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M23
|
||||
43
Issues/ISSUE-014-select-offer-no-seller-notifications.md
Normal file
43
Issues/ISSUE-014-select-offer-no-seller-notifications.md
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
issue: "014"
|
||||
title: "select-offer sends no per-seller socket events or notifications to winning/losing sellers"
|
||||
severity: major
|
||||
domain: seller-offer
|
||||
labels: [backend, missing-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 select-offer sends no per-seller socket events or notifications to winning/losing sellers
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** seller-offer
|
||||
**Labels:** backend, missing-feature
|
||||
|
||||
## Description
|
||||
|
||||
`POST /api/marketplace/purchase-requests/:id/select-offer` (routes.ts lines ~1300-1438) emits only a single `purchase-request-update` event to the request room with `eventType: 'offer-selected'`. It does NOT:
|
||||
- Call `notifyOfferAccepted` for the winning seller
|
||||
- Call `notifyOfferRejected` for losing sellers
|
||||
- Emit `seller-offer-update` events to individual seller rooms
|
||||
|
||||
These notifications only fire when using `PUT /offers/:id/accept` or `PUT /offers/:id/status` (via `SellerOfferService.updateOfferStatus`), not via the `select-offer` path used by the frontend.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Buyer selects an offer → winning seller gets no real-time notification → losing sellers get no notification.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
When a buyer selects an offer:
|
||||
1. Winning seller receives a `seller-offer-update` event and a push notification
|
||||
2. Losing sellers receive a `seller-offer-update` event and a notification
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/routes.ts` — `select-offer` route handler, missing `notifyOfferAccepted` and `notifyOfferRejected` calls
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M25
|
||||
44
Issues/ISSUE-015-seller-offer-withdraw-no-http-route.md
Normal file
44
Issues/ISSUE-015-seller-offer-withdraw-no-http-route.md
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
issue: "015"
|
||||
title: "Seller offer withdraw has no HTTP route — withdrawOffer() service method is dead code"
|
||||
severity: major
|
||||
domain: seller-offer
|
||||
labels: [backend, missing-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Seller offer withdraw has no HTTP route — withdrawOffer() service method is dead code
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** seller-offer
|
||||
**Labels:** backend, missing-feature
|
||||
|
||||
## Description
|
||||
|
||||
`SellerOfferService.withdrawOffer()` (SellerOfferService.ts lines ~427-443) exists and implements withdrawal logic, but no HTTP route calls it. The documented `POST /api/marketplace/offers/:id/withdraw` endpoint does not exist in `routes.ts` or `marketplaceController.ts`.
|
||||
|
||||
There is also no frontend `withdrawOffer()` action, no withdraw button in any seller step component, and no seller offers history page at `/dashboard/seller/marketplace/offers`.
|
||||
|
||||
The only workaround is `PUT /api/marketplace/offers/:id/status` with `{ status: 'withdrawn' }`, which has no guard ensuring the requester is the offer's seller.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Sellers cannot withdraw their pending offers through any UI path. Withdrawing via `PUT /offers/:id/status` is the only API path and has no ownership guard.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
1. Wire a `POST /api/marketplace/offers/:id/withdraw` route to `SellerOfferService.withdrawOffer()`
|
||||
2. Add an ownership guard (only the offer's seller can withdraw)
|
||||
3. Add a frontend withdraw button and action
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/routes.ts` — missing `POST /offers/:id/withdraw` route
|
||||
- `frontend/src/actions/marketplace.ts` — missing `withdrawOffer` action
|
||||
- Frontend seller dashboard — missing offers list page
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Findings C9, M26
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: "016"
|
||||
title: "createProviderPaymentIntent always routes to request-network regardless of provider — SHKeeper checkout broken"
|
||||
severity: critical
|
||||
domain: payment
|
||||
labels: [frontend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 createProviderPaymentIntent always routes to request-network regardless of provider — SHKeeper checkout broken
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** payment
|
||||
**Labels:** frontend, bug
|
||||
|
||||
## Description
|
||||
|
||||
`frontend/src/actions/payment.ts` — `getProviderIntentEndpoint()` ignores its `provider` argument and always returns `endpoints.payments.requestNetwork.intents` (`/payment/request-network/intents`).
|
||||
|
||||
If any UI component passes `provider='shkeeper'` to `createProviderPaymentIntent()`, the intent creation silently POSTs to the Request Network endpoint instead of `/payment/shkeeper/intents`. The SHKeeper intents endpoint is defined in `axios.ts` but is never reached by this factory.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
A SHKeeper checkout call to `createProviderPaymentIntent('shkeeper', ...)` POSTs to `/payment/request-network/intents`. The RN endpoint creates a Request Network intent, not a SHKeeper intent. The payment provider is silently misrouted.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
`getProviderIntentEndpoint('shkeeper')` should return `endpoints.payments.shkeeper.intents`. The function should switch on the provider argument.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/payment.ts` — `getProviderIntentEndpoint()` function (~line 444)
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M38
|
||||
- Related: [[ISSUE-017-payment-provider-type-missing-values]]
|
||||
46
Issues/ISSUE-017-payment-provider-type-missing-values.md
Normal file
46
Issues/ISSUE-017-payment-provider-type-missing-values.md
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
issue: "017"
|
||||
title: "PaymentProvider TypeScript type missing 'shkeeper' and 'decentralized' values"
|
||||
severity: major
|
||||
domain: payment
|
||||
labels: [frontend, bug, typescript]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 PaymentProvider TypeScript type missing 'shkeeper' and 'decentralized' values
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** payment
|
||||
**Labels:** frontend, bug, typescript
|
||||
|
||||
## Description
|
||||
|
||||
`frontend/src/types/payment.ts` defines:
|
||||
```ts
|
||||
type PaymentProvider = 'request.network' | 'test' | 'other'
|
||||
```
|
||||
|
||||
The backend accepts `'shkeeper'`, `'decentralized'`, and `'other'` as `provider` values on Payment records. The two most-used production providers (`shkeeper`, `decentralized`) are absent from the TypeScript union.
|
||||
|
||||
Any frontend code that switches on `payment.provider` will fall through to a default/unknown branch for all SHKeeper and DePay payments, causing incorrect UI rendering (wrong labels, missing payment method icons, etc.).
|
||||
|
||||
## Current Behavior
|
||||
|
||||
SHKeeper and DePay payments in the payment list and payment detail views may show as "Unknown provider" or trigger TypeScript errors at compile time.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
```ts
|
||||
type PaymentProvider = 'request.network' | 'shkeeper' | 'decentralized' | 'test' | 'other'
|
||||
```
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/types/payment.ts` — `PaymentProvider` type definition (~line 15)
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M37
|
||||
- Related: [[ISSUE-016-payment-provider-routing-always-request-network]]
|
||||
53
Issues/ISSUE-018-trezor-no-frontend-implementation.md
Normal file
53
Issues/ISSUE-018-trezor-no-frontend-implementation.md
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
issue: "018"
|
||||
title: "Trezor Safekeeping has zero frontend implementation — all backend endpoints unreachable from UI"
|
||||
severity: critical
|
||||
domain: trezor
|
||||
labels: [frontend, missing-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 Trezor Safekeeping has zero frontend implementation — all backend endpoints unreachable from UI
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** trezor
|
||||
**Labels:** frontend, missing-feature
|
||||
|
||||
## Description
|
||||
|
||||
A comprehensive search of all `.ts` and `.tsx` files in `frontend/src/` finds **zero calls** to any Trezor backend endpoint. There is no:
|
||||
- Trezor registration page
|
||||
- xpub input UI
|
||||
- Trezor Connect SDK import
|
||||
- Admin Trezor signing panel
|
||||
- Any action calling `/api/trezor/*`
|
||||
|
||||
The only Trezor reference in the entire frontend is a brand logo in `wallet-icons.ts`.
|
||||
|
||||
The documented 12-step challenge-sign-submit flow exists entirely in the backend but has no frontend surface at any step.
|
||||
|
||||
Additionally, `confirmReleaseTx` and `confirmRefundTx` in `frontend/src/actions/payment.ts` post `{ txHash, ...extra }` with **no `trezor` object** (message + signature). With `TREZOR_SAFEKEEPING_REQUIRED=true`, every admin release/refund from the UI will be rejected by the backend's `assertTrezorSignatureForOperation` guard.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
- No UI exists for Trezor registration
|
||||
- Admin release/refund with `TREZOR_SAFEKEEPING_REQUIRED=true` always fails (missing signature payload)
|
||||
- All Trezor API endpoints are only testable via curl/Postman
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
A complete frontend implementation covering:
|
||||
1. Trezor registration page (xpub input, challenge-sign-submit flow)
|
||||
2. Operation signing UI for admin release/refund (call `POST /api/trezor/operation-message`, prompt sign, attach `trezor` object to confirm body)
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/payment.ts` — `confirmReleaseTx`, `confirmRefundTx` missing `trezor` field
|
||||
- Missing: Trezor registration page component
|
||||
- Missing: Admin Trezor signing integration in dispute/payment admin panels
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Findings C31, C32
|
||||
46
Issues/ISSUE-019-rn-payout-release-refund-not-implemented.md
Normal file
46
Issues/ISSUE-019-rn-payout-release-refund-not-implemented.md
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
issue: "019"
|
||||
title: "Request Network admin payout/release/refund sub-routes do not exist in backend"
|
||||
severity: major
|
||||
domain: payment
|
||||
labels: [backend, missing-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Request Network admin payout/release/refund sub-routes do not exist in backend
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** payment
|
||||
**Labels:** backend, missing-feature
|
||||
|
||||
## Description
|
||||
|
||||
`frontend/src/actions/payment.ts` exports four functions that hit non-existent backend endpoints:
|
||||
|
||||
| Function | Calls | Status |
|
||||
|---|---|---|
|
||||
| `initiateRequestNetworkPayout()` | `POST /api/payment/request-network/:id/payout/initiate` | 404 |
|
||||
| `confirmRequestNetworkPayout()` | `POST /api/payment/request-network/:id/payout/confirm` | 404 |
|
||||
| `confirmRequestNetworkRelease()` | `POST /api/payment/request-network/:id/release/confirm` | 404 |
|
||||
| `confirmRequestNetworkRefund()` | `POST /api/payment/request-network/:id/refund/confirm` | 404 |
|
||||
|
||||
The backend only implements: `POST /api/payment/request-network/intents`, `GET /api/payment/request-network/:paymentId/checkout`, `POST /api/payment/request-network/webhook`.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
All four admin RN payout/release/refund actions return 404. Admin has no way to complete or refund a Request Network payment through the UI.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Backend should implement the four sub-routes, or the frontend actions should be mapped to the actual release/refund mechanism.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/payment.ts` — `initiateRequestNetworkPayout`, `confirmRequestNetworkPayout`, `confirmRequestNetworkRelease`, `confirmRequestNetworkRefund`
|
||||
- Backend: missing `request-network/:id/payout/*`, `release/confirm`, `refund/confirm` routes
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M34
|
||||
42
Issues/ISSUE-020-dispute-assign-no-role-guard.md
Normal file
42
Issues/ISSUE-020-dispute-assign-no-role-guard.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
issue: "020"
|
||||
title: "POST /api/disputes/:id/assign has no role guard — any user can self-assign as mediator"
|
||||
severity: major
|
||||
domain: dispute
|
||||
labels: [security, backend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 POST /api/disputes/:id/assign has no role guard — any user can self-assign as mediator
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** dispute
|
||||
**Labels:** security, backend, bug
|
||||
|
||||
## Description
|
||||
|
||||
`POST /api/disputes/:id/assign` is mounted with only `authenticateToken`. Any authenticated buyer or seller can assign themselves as the mediator/admin for any open dispute.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
```bash
|
||||
POST /api/disputes/{disputeId}/assign
|
||||
Authorization: Bearer <buyer-jwt>
|
||||
{ "adminId": "<buyer-user-id>" }
|
||||
```
|
||||
Returns 200 and sets the dispute's assigned mediator to the buyer.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Should require `authorizeRoles('admin')`. Non-admin tokens should receive `403`.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/disputeRoutes.ts` — missing role guard on the assign route
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
- Related: [[ISSUE-001-dispute-status-no-role-guard]], [[ISSUE-002-dispute-resolve-no-role-guard]]
|
||||
45
Issues/ISSUE-021-axios-interceptor-403-not-handled.md
Normal file
45
Issues/ISSUE-021-axios-interceptor-403-not-handled.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
issue: "021"
|
||||
title: "Axios interceptor only retriggers token refresh for 401, not 403"
|
||||
severity: major
|
||||
domain: auth
|
||||
labels: [frontend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Axios interceptor only retriggers token refresh for 401, not 403
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** auth
|
||||
**Labels:** frontend, bug
|
||||
|
||||
## Description
|
||||
|
||||
`frontend/src/lib/axios.ts` (line ~105) only triggers the token refresh flow for `status === 401`:
|
||||
```ts
|
||||
if (status === 401 && !isAuthRoute && !originalRequest?._retry) {
|
||||
// trigger refresh
|
||||
}
|
||||
```
|
||||
|
||||
A `403` response (e.g., `EMAIL_NOT_VERIFIED`, a blocked account, or an under-privileged action) is not intercepted — it propagates as an unhandled error. Depending on how calling components handle errors, this may result in a blank screen or silent failure rather than an appropriate user message.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Backend returns `403 EMAIL_NOT_VERIFIED` → interceptor does not retry or refresh → error propagates to the component. Some components may not handle this gracefully.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
The interceptor (or a separate error handler) should:
|
||||
- On `403`: **not** attempt a token refresh (a 403 is an authorization failure, not an expired token)
|
||||
- But should surface the error clearly to the user (e.g., redirect to verify-email page for `EMAIL_NOT_VERIFIED` errors)
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/lib/axios.ts` — response interceptor
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M1
|
||||
38
Issues/ISSUE-022-rate-limit-counts-all-attempts.md
Normal file
38
Issues/ISSUE-022-rate-limit-counts-all-attempts.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: "022"
|
||||
title: "Login rate limiter counts all attempts (not just failures) — users can be locked out after correct logins"
|
||||
severity: major
|
||||
domain: auth
|
||||
labels: [backend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Login rate limiter counts all attempts (not just failures) — users can be locked out after correct logins
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** auth
|
||||
**Labels:** backend, bug
|
||||
|
||||
## Description
|
||||
|
||||
`rateLimitService.checkLoginAttempts()` calls `checkLimit()` which calls `redisService.incr` — incrementing the counter on **every invocation**, before password comparison. The counter is only reset after a full successful login (password verified + session created).
|
||||
|
||||
With the limit at 5 attempts/15 min, a user who makes 4 correct logins in quick succession (e.g., testing on multiple devices) followed by 1 wrong password will be locked out immediately, even though they never "failed" 5 times in the intended sense.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
5 total login attempts within 15 minutes (any combination of correct/incorrect passwords) triggers `429 TOO_MANY_ATTEMPTS`.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
The counter should only increment on **failed** password comparison, not on every attempt. Alternatively, the behaviour should be clearly documented so UX can warn users appropriately.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/auth/rateLimitService.ts` — `checkLoginAttempts` / `checkLimit` — counter increment should move to after password comparison in `authController.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M3
|
||||
37
Issues/ISSUE-023-change-password-no-ui.md
Normal file
37
Issues/ISSUE-023-change-password-no-ui.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
issue: "023"
|
||||
title: "changePassword action exists but no dashboard UI page exposes it"
|
||||
severity: major
|
||||
domain: auth
|
||||
labels: [frontend, missing-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 changePassword action exists but no dashboard UI page exposes it
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** auth
|
||||
**Labels:** frontend, missing-feature
|
||||
|
||||
## Description
|
||||
|
||||
`frontend/src/actions/account.ts` (line ~263) defines `changePassword()` which calls `POST /api/auth/change-password`. The backend endpoint exists and `changePasswordValidation` enforces password complexity (uppercase + lowercase + digit). However, **no dashboard page or component renders a change-password form**. The feature is API-only.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Users have no UI path to change their password after login. The only password reset mechanism is the email-based reset flow.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
A "Change Password" section in the account settings dashboard (e.g., under `/dashboard/account`) that calls `changePassword()` with `{ currentPassword, newPassword }`.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- Missing: Change password form component in `/dashboard/account` or `/dashboard/account/security`
|
||||
- `frontend/src/actions/account.ts` — `changePassword` function (implemented, no callers)
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M4
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
issue: "024"
|
||||
title: "POST /api/auth/reset-password-with-code accepts weak passwords — no complexity validation"
|
||||
severity: major
|
||||
domain: auth
|
||||
labels: [backend, security, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 POST /api/auth/reset-password-with-code accepts weak passwords — no complexity validation
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** auth
|
||||
**Labels:** backend, security, bug
|
||||
|
||||
## Description
|
||||
|
||||
`POST /api/auth/reset-password-with-code` has **no `passwordResetValidation` middleware** (`authRoutes.ts` line ~54-57). The controller only validates that email, code, and password fields are present, and that the code is 6 digits.
|
||||
|
||||
Passwords like `'123456'`, `'aaaaaa'`, or `'password'` are accepted.
|
||||
|
||||
By contrast, the legacy `POST /api/auth/reset-password` (token-based) is wired with `passwordResetValidation` which enforces `/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/` — at least one uppercase, one lowercase, one digit.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
`POST /api/auth/reset-password-with-code` with `{ email, code: "123456", password: "aaaaaa" }` → 200, password reset to weak value.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Apply `passwordResetValidation` (or equivalent inline validation) to `reset-password-with-code` as well.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/authRoutes.ts` — line ~54-57, add `passwordResetValidation` middleware
|
||||
- `backend/src/shared/middleware/authValidation.ts` — `passwordResetValidation` definition
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M6
|
||||
46
Issues/ISSUE-025-dispute-socket-events-all-stubs.md
Normal file
46
Issues/ISSUE-025-dispute-socket-events-all-stubs.md
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
issue: "025"
|
||||
title: "All dispute socket events are commented-out TODO stubs — no real-time updates in dispute flow"
|
||||
severity: major
|
||||
domain: dispute
|
||||
labels: [backend, missing-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 All dispute socket events are commented-out TODO stubs — no real-time updates in dispute flow
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** dispute
|
||||
**Labels:** backend, missing-feature
|
||||
|
||||
## Description
|
||||
|
||||
Every `socket.io` emit block in `DisputeService` is currently commented out as a TODO. No real-time updates fire for any dispute lifecycle event:
|
||||
- Dispute created
|
||||
- Admin assigned
|
||||
- Status changed
|
||||
- Evidence uploaded
|
||||
- Resolution posted
|
||||
|
||||
The dispute flow is CRUD-only. Any UI component that relies on socket events for real-time dispute state will never receive updates.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
All dispute state changes are only visible after a manual page refresh.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Implement the socket emit calls for key dispute events:
|
||||
- `dispute-created` → to buyer, seller, and admin rooms
|
||||
- `dispute-status-changed` → to involved parties
|
||||
- `dispute-resolved` → to buyer and seller rooms
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/dispute/disputeService.ts` — all commented-out `io.to(...).emit(...)` blocks
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
38
Issues/ISSUE-026-payment-completed-not-counted-in-stats.md
Normal file
38
Issues/ISSUE-026-payment-completed-not-counted-in-stats.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: "026"
|
||||
title: "'completed' payment status not counted in successfulPayments stats — admin dashboard undercounts"
|
||||
severity: major
|
||||
domain: payment
|
||||
labels: [backend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 'completed' payment status not counted in successfulPayments stats — admin dashboard undercounts
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** payment
|
||||
**Labels:** backend, bug
|
||||
|
||||
## Description
|
||||
|
||||
`paymentService.getPaymentStats()` aggregate counts only `'confirmed'` as `successfulPayments`. `'completed'` is excluded from this count.
|
||||
|
||||
Most SHKeeper payments follow the terminal path: `pending → processing → completed`. `'confirmed'` is a separate RN-specific intermediate state. This means the vast majority of successfully completed payments (SHKeeper + DePay) are **invisible in the `successfulPayments` count** in the admin stats endpoint.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Admin dashboard shows a `successfulPayments` count that excludes all `'completed'` status payments. For a platform where SHKeeper is the primary payment provider, this count is close to 0 even when hundreds of payments have succeeded.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
`successfulPayments` should count payments in both `'confirmed'` and `'completed'` status, or the aggregate should be documented with a clear note about which statuses are terminal success states.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/payment/paymentService.ts` — `getPaymentStats()` aggregate pipeline
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M36
|
||||
38
Issues/ISSUE-027-get-notification-by-id-broken.md
Normal file
38
Issues/ISSUE-027-get-notification-by-id-broken.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: "027"
|
||||
title: "GET /api/notifications/:id always 404s for non-latest notifications — broken in-memory lookup"
|
||||
severity: major
|
||||
domain: notification
|
||||
labels: [backend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 GET /api/notifications/:id always 404s for non-latest notifications — broken in-memory lookup
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** notification
|
||||
**Labels:** backend, bug
|
||||
|
||||
## Description
|
||||
|
||||
The `getNotificationById` controller does NOT perform a direct MongoDB `findById` lookup. Instead it calls `getUserNotifications(userId, 1, 1)` — fetching only the user's single most-recent notification — and then does an **in-memory `_id` string comparison**.
|
||||
|
||||
Any notification that is not the user's absolute latest record returns `404`, regardless of ownership. This makes the endpoint completely unreliable for any consumer that tries to fetch a specific notification by ID.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
`GET /api/notifications/abc123` returns the notification only if `abc123` happens to be the user's most recently created notification. For all others: 404.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
`getNotificationById` should do a direct `Notification.findOne({ _id: id, userId })` query.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/notification/notificationService.ts` (or controller) — `getNotificationById` / `getUserNotifications` call
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding C22
|
||||
41
Issues/ISSUE-028-payment-export-no-admin-guard.md
Normal file
41
Issues/ISSUE-028-payment-export-no-admin-guard.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
issue: "028"
|
||||
title: "GET /api/payment/export has no admin role guard — any authenticated user can export payment data"
|
||||
severity: major
|
||||
domain: payment
|
||||
labels: [security, backend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 GET /api/payment/export has no admin role guard — any authenticated user can export payment data
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** payment
|
||||
**Labels:** security, backend, bug
|
||||
|
||||
## Description
|
||||
|
||||
Two parallel export endpoints exist:
|
||||
- `GET /api/payment/payments/export` — has `authorizeRoles('admin')` guard (correct)
|
||||
- `GET /api/payment/export` (controller-pattern route) — only has `authenticateToken`, **no admin guard**
|
||||
|
||||
The frontend hits `/payment/export` (the controller-pattern route without the admin guard). Any authenticated buyer can export payment records.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
`GET /api/payment/export` with any valid user JWT → 200 with payment export data.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
`GET /api/payment/export` should require `authorizeRoles('admin')`, or the frontend should be pointed at `/api/payment/payments/export`.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- Backend: controller-pattern route for `GET /payment/export` — missing `authorizeRoles('admin')`
|
||||
- `frontend/src/lib/axios.ts` — `endpoints.payments.export` maps to the wrong route
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M31
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
issue: "029"
|
||||
title: "Frontend delivery actions regenerate/attempts/stats call non-existent backend endpoints"
|
||||
severity: major
|
||||
domain: delivery
|
||||
labels: [frontend, missing-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Frontend delivery actions regenerate/attempts/stats call non-existent backend endpoints
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** delivery
|
||||
**Labels:** frontend, missing-feature
|
||||
|
||||
## Description
|
||||
|
||||
Three frontend delivery actions hit non-existent backend routes:
|
||||
|
||||
| Action | Calls | Status |
|
||||
|---|---|---|
|
||||
| `regenerateDeliveryCode` | `POST /delivery-code/regenerate` | 404 (falls back to `/generate`) |
|
||||
| `getDeliveryAttempts` | `GET /delivery-code/attempts` | 404, throws |
|
||||
| `getDeliveryStats` | `GET /delivery/stats` | 404, throws |
|
||||
|
||||
`regenerateDeliveryCode` silently falls back to the generate endpoint on 404. The other two throw unhandled errors if any component calls them.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
- Code "regeneration" actually calls generate (new code, ignores regenerate semantic)
|
||||
- Any UI showing delivery attempt count or stats shows nothing or throws
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Either implement the backend routes, or remove the phantom actions and handle their use cases differently.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/delivery.ts` — `regenerateDeliveryCode`, `getDeliveryAttempts`, `getDeliveryStats`
|
||||
- Backend: missing routes for `/delivery-code/regenerate`, `/delivery-code/attempts`, `/delivery/stats`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M15
|
||||
36
Issues/ISSUE-030-confirm-delivery-no-auth-guard.md
Normal file
36
Issues/ISSUE-030-confirm-delivery-no-auth-guard.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: "030"
|
||||
title: "PATCH /confirm-delivery has no ownership check — any authenticated user can confirm delivery"
|
||||
severity: major
|
||||
domain: delivery
|
||||
labels: [backend, security, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 PATCH /confirm-delivery has no ownership check — any authenticated user can confirm delivery
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** delivery
|
||||
**Labels:** backend, security, bug
|
||||
|
||||
## Description
|
||||
|
||||
`PATCH /api/marketplace/purchase-requests/:id/confirm-delivery` (the buyer fast-track path to `'delivered'` status) has no ownership or role check. Any authenticated user who knows a purchase request ID can mark it as delivered without possessing the delivery code.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
`PATCH /purchase-requests/{anyId}/confirm-delivery` with any valid JWT → 200, status set to `'delivered'`.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Should verify `req.user.id === request.buyerId` — only the buyer of that specific request should be able to confirm delivery via this fast-track path.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/controllerRoutes.ts` or `routes.ts` — `confirm-delivery` handler missing ownership guard
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
47
Issues/ISSUE-031-points-missing-frontend-pages.md
Normal file
47
Issues/ISSUE-031-points-missing-frontend-pages.md
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
issue: "031"
|
||||
title: "Points/referral system missing 5 frontend pages — redemption, levels, referrals, transactions, admin"
|
||||
severity: major
|
||||
domain: points
|
||||
labels: [frontend, missing-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Points/referral system missing 5 frontend pages — redemption, levels, referrals, transactions, admin
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** points
|
||||
**Labels:** frontend, missing-feature
|
||||
|
||||
## Description
|
||||
|
||||
The following routes return 404 because no frontend pages exist:
|
||||
|
||||
| Route | Backend Endpoint | Status |
|
||||
|---|---|---|
|
||||
| `/dashboard/points/referrals` | `GET /api/points/referrals` | Page missing |
|
||||
| `/dashboard/points/transactions` | `GET /api/points/transactions` | Page missing |
|
||||
| `/dashboard/points/levels` | `GET /api/points/levels` | Page missing |
|
||||
| `/dashboard/points/redeem` (or any UI) | `POST /api/points/redeem` | No redemption UI anywhere |
|
||||
| Admin points management | `POST /api/points/admin/add` | No admin page |
|
||||
|
||||
`redeemPoints()` and `generateReferralCode()` actions are defined but have no call sites in any component.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
All points features beyond the basic balance display are inaccessible from the UI.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Implement frontend pages for: referral history, transaction history, levels display, points redemption flow, and admin points management.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- Missing pages in `frontend/src/app/dashboard/points/`
|
||||
- `frontend/src/actions/points.ts` — `redeemPoints`, `generateReferralCode` (defined, no callers)
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
45
Issues/ISSUE-032-shkeeper-release-refund-wrong-paths.md
Normal file
45
Issues/ISSUE-032-shkeeper-release-refund-wrong-paths.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
issue: "032"
|
||||
title: "SHKeeper release/refund doc paths include erroneous /shkeeper/ segment"
|
||||
severity: major
|
||||
domain: payment
|
||||
labels: [backend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 SHKeeper release/refund doc paths include erroneous /shkeeper/ segment
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** payment
|
||||
**Labels:** backend, bug
|
||||
|
||||
## Description
|
||||
|
||||
The SHKeeper Payment Flow was documented with `/shkeeper/` in the release/refund paths. The actual backend routes are:
|
||||
|
||||
| Documented (wrong) | Actual (correct) |
|
||||
|---|---|
|
||||
| `POST /api/payment/shkeeper/:id/release` | `POST /api/payment/:id/release` |
|
||||
| `POST /api/payment/shkeeper/:id/release/confirm` | `POST /api/payment/:id/release/confirm` |
|
||||
| `POST /api/payment/shkeeper/:id/refund` | `POST /api/payment/:id/refund` |
|
||||
| `POST /api/payment/shkeeper/:id/refund/confirm` | `POST /api/payment/:id/refund/confirm` |
|
||||
|
||||
The frontend `endpoints.payments.details` maps to `/payment/:id` (correct), so the frontend is unaffected. The issue is in the documentation and any external integration or test harness built from the docs.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Calling any `/shkeeper/` path returns 404.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Documentation and any test harnesses should use paths without the `/shkeeper/` segment.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- Doc file updated: `04 - Flows/Payment Flow - SHKeeper.md`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding C30
|
||||
42
Issues/ISSUE-033-seller-offer-history-route-missing.md
Normal file
42
Issues/ISSUE-033-seller-offer-history-route-missing.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
issue: "033"
|
||||
title: "GET seller offer history has no HTTP route — getOffersBySeller() is unreachable dead code"
|
||||
severity: major
|
||||
domain: seller-offer
|
||||
labels: [backend, missing-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 GET seller offer history has no HTTP route — getOffersBySeller() is unreachable dead code
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** seller-offer
|
||||
**Labels:** backend, missing-feature
|
||||
|
||||
## Description
|
||||
|
||||
`SellerOfferService.getOffersBySeller()` exists in the service layer but no HTTP route exposes it. The documented endpoint `GET /api/marketplace/offers/seller/:sellerId` does not exist in `routes.ts` or `marketplaceController.ts`.
|
||||
|
||||
Notification action URLs that point to `/dashboard/seller/marketplace/offers` are also broken — that frontend page does not exist.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
- Sellers have no way to view their own offer history via the API
|
||||
- Notification deep-links to the offers page return 404
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
1. Register `GET /api/marketplace/offers/seller/:sellerId` (or equivalent scoped route) calling `getOffersBySeller()`
|
||||
2. Create the frontend page at `/dashboard/seller/marketplace/offers`
|
||||
3. Fix notification `actionUrl` to point to the real page
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/routes.ts` — missing `GET /offers/seller/:sellerId` route
|
||||
- Missing: `frontend/src/app/dashboard/shops/` or similar seller offers list page
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M27
|
||||
41
Issues/ISSUE-034-seller-offer-active-status-invalid.md
Normal file
41
Issues/ISSUE-034-seller-offer-active-status-invalid.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
issue: "034"
|
||||
title: "SellerOffer 'active' status does not exist in schema — saves with this value throw ValidationError"
|
||||
severity: major
|
||||
domain: seller-offer
|
||||
labels: [backend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 SellerOffer 'active' status does not exist in schema — saves with this value throw ValidationError
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** seller-offer
|
||||
**Labels:** backend, bug
|
||||
|
||||
## Description
|
||||
|
||||
The Seller Offer Flow doc lists `'active'` as a valid `SellerOffer.status`. The Mongoose schema and TypeScript interface only enumerate:
|
||||
```
|
||||
'pending' | 'accepted' | 'rejected' | 'withdrawn'
|
||||
```
|
||||
|
||||
Any code path that attempts to set `SellerOffer.status = 'active'` will throw a Mongoose `ValidationError`. The `createOffer()` service correctly checks `PurchaseRequest.status === 'active'` (a different model's status), but `SellerOffer.status = 'active'` is never valid.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
`SellerOffer.save()` with `status: 'active'` → Mongoose ValidationError. (Currently no code path actually tries to do this — the bug is latent but would be triggered by misreading the documentation.)
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Remove `'active'` from all `SellerOffer` status documentation. The valid states are `pending | accepted | rejected | withdrawn`.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- Doc file updated: `04 - Flows/Seller Offer Flow.md` and `02 - Data Models/SellerOffer.md`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M22
|
||||
41
Issues/ISSUE-035-payment-dispute-verify-button-404.md
Normal file
41
Issues/ISSUE-035-payment-dispute-verify-button-404.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
issue: "035"
|
||||
title: "Dispute payment card 'Verify' button always 404s — getPaymentStatus calls non-existent endpoint"
|
||||
severity: major
|
||||
domain: payment
|
||||
labels: [frontend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Dispute payment card 'Verify' button always 404s — getPaymentStatus calls non-existent endpoint
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** payment
|
||||
**Labels:** frontend, bug
|
||||
|
||||
## Description
|
||||
|
||||
`frontend/src/sections/dispute/components/payment-details-card.tsx` (line ~101) calls `getPaymentStatus()` which builds URL as `GET /payment/:id/status`. No `/status` sub-route exists on any payment route in the backend.
|
||||
|
||||
The 'Verify' button in the dispute panel is permanently broken in production.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Clicking 'Verify' on the dispute payment card → `GET /payment/{id}/status` → 404.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Either:
|
||||
1. Implement `GET /api/payment/:id/status` on the backend, or
|
||||
2. Update the component to use the existing `GET /api/payment/:id` endpoint for payment detail fetching
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/sections/dispute/components/payment-details-card.tsx` — line ~101
|
||||
- `frontend/src/actions/payment.ts` — `getPaymentStatus` function
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding C13
|
||||
59
Issues/Issues Index.md
Normal file
59
Issues/Issues Index.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Issues Index
|
||||
|
||||
> Generated from Doc vs Code Audit — 2026-05-29
|
||||
> **35 open issues** | 🔴 14 critical · 🟠 19 major · 🟡 2 minor
|
||||
|
||||
## 🔴 Critical
|
||||
|
||||
- [[ISSUE-001-dispute-status-no-role-guard|PATCH /api/disputes/:id/status no role guard — privilege escalation]] — `dispute` · security
|
||||
- [[ISSUE-002-dispute-resolve-no-role-guard|POST /api/disputes/:id/resolve no role guard — any user can resolve + ban sellers]] — `dispute` · security
|
||||
- [[ISSUE-003-dispute-route-shadowing|Route shadowing: two dispute routers at /api/disputes — wrong handler fires]] — `dispute`
|
||||
- [[ISSUE-004-payment-endpoints-no-auth|fetch-tx, auto-fetch-missing, debug payment endpoints have no authentication]] — `payment` · security
|
||||
- [[ISSUE-005-scanner-status-no-auth|GET /api/admin/scanner/status has no authentication]] — `admin` · security
|
||||
- [[ISSUE-006-delete-account-wrong-endpoint|Frontend deleteAccount calls DELETE /user/profile — endpoint doesn't exist]] — `auth`
|
||||
- [[ISSUE-007-sim-bypass-no-env-guard|SIM_ transaction bypass active in production — no NODE_ENV guard]] — `payment` · security
|
||||
- [[ISSUE-008-chat-file-upload-wrong-endpoint|sendFileMessage posts to wrong endpoint — chat file uploads always fail]] — `chat`
|
||||
- [[ISSUE-010-admin-user-status-wrong-values-and-verb|Admin user status/role broken: wrong HTTP verb + wrong status values]] — `admin`
|
||||
- [[ISSUE-016-payment-provider-routing-always-request-network|createProviderPaymentIntent always routes to request-network — SHKeeper broken]] — `payment`
|
||||
- [[ISSUE-018-trezor-no-frontend-implementation|Trezor Safekeeping has zero frontend implementation]] — `trezor`
|
||||
- [[ISSUE-020-dispute-assign-no-role-guard|POST /api/disputes/:id/assign no role guard — any user can self-assign mediator]] — `dispute` · security
|
||||
- [[ISSUE-030-confirm-delivery-no-auth-guard|PATCH /confirm-delivery no ownership check — any user can confirm delivery]] — `delivery` · security
|
||||
- [[ISSUE-035-payment-dispute-verify-button-404|Dispute 'Verify' button always 404s — getPaymentStatus hits non-existent endpoint]] — `payment`
|
||||
|
||||
## 🟠 Major
|
||||
|
||||
- [[ISSUE-009-archive-chat-wrong-method|archiveConversation uses PUT but backend only accepts PATCH]] — `chat`
|
||||
- [[ISSUE-011-update-purchase-request-put-vs-patch|updatePurchaseRequest sends PUT but backend only accepts PATCH]] — `purchase-request`
|
||||
- [[ISSUE-012-update-offer-put-vs-patch|updateOffer sends PUT but backend registers PATCH]] — `seller-offer`
|
||||
- [[ISSUE-013-select-offer-no-status-filter-corrupts-withdrawn|select-offer cascade overwrites withdrawn offers — missing status filter]] — `seller-offer` · data-integrity
|
||||
- [[ISSUE-014-select-offer-no-seller-notifications|select-offer sends no per-seller notifications to winning/losing sellers]] — `seller-offer`
|
||||
- [[ISSUE-015-seller-offer-withdraw-no-http-route|Seller offer withdraw has no HTTP route — withdrawOffer() is dead code]] — `seller-offer`
|
||||
- [[ISSUE-017-payment-provider-type-missing-values|PaymentProvider TypeScript type missing 'shkeeper' and 'decentralized']] — `payment`
|
||||
- [[ISSUE-019-rn-payout-release-refund-not-implemented|Request Network admin payout/release/refund sub-routes do not exist]] — `payment`
|
||||
- [[ISSUE-021-axios-interceptor-403-not-handled|Axios interceptor only retriggers token refresh for 401, not 403]] — `auth`
|
||||
- [[ISSUE-022-rate-limit-counts-all-attempts|Login rate limiter counts all attempts — users locked out after correct logins]] — `auth`
|
||||
- [[ISSUE-023-change-password-no-ui|changePassword action exists but no dashboard UI page]] — `auth`
|
||||
- [[ISSUE-024-reset-password-with-code-no-complexity-check|POST /api/auth/reset-password-with-code accepts weak passwords]] — `auth` · security
|
||||
- [[ISSUE-025-dispute-socket-events-all-stubs|All dispute socket events are TODO stubs — no real-time updates]] — `dispute`
|
||||
- [[ISSUE-026-payment-completed-not-counted-in-stats|'completed' payment not counted in successfulPayments — admin dashboard undercounts]] — `payment`
|
||||
- [[ISSUE-027-get-notification-by-id-broken|GET /api/notifications/:id always 404s for non-latest notifications]] — `notification`
|
||||
- [[ISSUE-028-payment-export-no-admin-guard|GET /api/payment/export has no admin guard — any user can export payments]] — `payment` · security
|
||||
- [[ISSUE-029-delivery-attempts-stats-phantom-endpoints|Frontend delivery actions regenerate/attempts/stats hit non-existent endpoints]] — `delivery`
|
||||
- [[ISSUE-031-points-missing-frontend-pages|Points/referral missing 5 frontend pages — redemption, levels, referrals, transactions, admin]] — `points`
|
||||
- [[ISSUE-032-shkeeper-release-refund-wrong-paths|SHKeeper release/refund doc paths include erroneous /shkeeper/ segment]] — `payment`
|
||||
- [[ISSUE-033-seller-offer-history-route-missing|GET seller offer history has no HTTP route — getOffersBySeller() is dead code]] — `seller-offer`
|
||||
- [[ISSUE-034-seller-offer-active-status-invalid|SellerOffer 'active' status invalid — saves throw ValidationError]] — `seller-offer`
|
||||
|
||||
## Security Issues Summary
|
||||
|
||||
| # | Issue | Severity |
|
||||
|---|---|---|
|
||||
| 001 | Dispute status PATCH — no role guard (privilege escalation) | 🔴 Critical |
|
||||
| 002 | Dispute resolve POST — no role guard (ban_seller without auth) | 🔴 Critical |
|
||||
| 004 | Payment fetch-tx/auto-fetch/debug — no authentication | 🔴 Critical |
|
||||
| 005 | Admin scanner status — no authentication | 🔴 Critical |
|
||||
| 007 | SIM_ bypass active in production | 🔴 Critical |
|
||||
| 020 | Dispute assign — no role guard | 🔴 Critical |
|
||||
| 030 | confirm-delivery — no ownership check | 🔴 Critical |
|
||||
| 024 | reset-password-with-code — no complexity validation | 🟠 Major |
|
||||
| 028 | Payment export — no admin guard | 🟠 Major |
|
||||
Reference in New Issue
Block a user