docs: complete code-reality alignment for remaining docs + reconcile issue set
Remaining docs updated to match code (the docs that the first pass had not covered):
- Flows: Chat, Referral, Rating, Registration, Google OAuth, Negotiation, Payout,
Trezor Safekeeping — corrected endpoints, socket events, status enums, auth gaps
- API Reference: User API, Trezor API — admin route prefix/verb/status corrections,
added undocumented endpoints (ton-proof challenge, profile email verify,
GET /trezor/account, POST /trezor/verify-operation)
- Data Models: Chat, Notification, Payment, PointTransaction, User — corrected
enums (PaymentProvider, escrowState, PointTransaction.type, User.status),
90-day notification TTL, soft-delete semantics, wallet fields
Trezor "zero frontend" finding (audit C31/C32) corrected as STALE:
- Verified current code HAS a full frontend Trezor implementation (admin/trezor
page, TrezorSettingsView, trezorConnector via @trezor/connect-web,
TrezorSignDialog, actions/trezor.ts building the {message,signature} object)
- Fixed Trezor Safekeeping Flow doc (removed false "no frontend" warnings)
- Reclassified ISSUE-012 as invalid/superseded with explanation
Issue set reconciled to a single canonical numbering (ISSUE-001..054):
- Adopted the comprehensive 51-issue set (long-slug, fully indexed)
- Removed 35 superseded short-slug duplicates from the first pass
- Removed a duplicate ISSUE-046 file
- Added 3 issues the 51-set lacked: ISSUE-052 (completed-not-counted-in-stats),
ISSUE-053 (axios 401-only interceptor), ISSUE-054 (rate limiter counts all attempts)
- Regenerated Issues Index: 53 open (14 critical, 39 major) + 1 invalid
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,50 +0,0 @@
|
||||
---
|
||||
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]]
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
issue: 001
|
||||
title: "PATCH /api/disputes/:id/status and POST /api/disputes/:id/resolve have no role guard — privilege escalation"
|
||||
severity: critical
|
||||
domain: Dispute
|
||||
labels: [security, bug, backend, privilege-escalation]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 PATCH /api/disputes/:id/status and POST /api/disputes/:id/resolve have no role guard — privilege escalation
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** Dispute
|
||||
**Labels:** security, bug, backend, privilege-escalation
|
||||
|
||||
## Description
|
||||
|
||||
Any authenticated buyer or seller can change dispute status to 'resolved', 'closed', or 'rejected', and can post a dispute resolution including action=ban_seller. Neither the dashboard updateStatus controller nor the resolveDispute controller call authorizeRoles('admin'). Only authenticateToken is applied on the router.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Any authenticated user with the dispute ID can call PATCH /api/disputes/:id/status or POST /api/disputes/:id/resolve and receive 200 with the mutation applied.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Both endpoints should return 403 for non-admin users. authorizeRoles('admin') middleware should be applied at the route level.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/disputeRoutes.ts`
|
||||
- `backend/src/controllers/disputeController.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
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]]
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
issue: 002
|
||||
title: "POST /api/disputes/:id/assign has no role guard — any user can self-assign as admin"
|
||||
severity: critical
|
||||
domain: Dispute
|
||||
labels: [security, bug, backend, privilege-escalation]
|
||||
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 admin
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** Dispute
|
||||
**Labels:** security, bug, backend, privilege-escalation
|
||||
|
||||
## Description
|
||||
|
||||
The POST /api/disputes/:id/assign endpoint registers only authenticateToken. Any authenticated user can assign themselves or anyone else as the admin handler for a dispute. The admin check is absent at both the middleware and controller level.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Any authenticated buyer or seller can call POST /api/disputes/:id/assign and become the assigned admin for the dispute.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Return 403 for non-admin tokens. Apply authorizeRoles('admin') at the route level.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/disputeRoutes.ts`
|
||||
- `backend/src/controllers/disputeController.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
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]]
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
issue: 003
|
||||
title: "Route shadowing: POST /api/disputes/:purchaseRequestId/resolve matches dashboard router first and executes wrong handler"
|
||||
severity: critical
|
||||
domain: Dispute
|
||||
labels: [bug, backend, critical, escrow]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 Route shadowing: POST /api/disputes/:purchaseRequestId/resolve matches dashboard router first and executes wrong handler
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** Dispute
|
||||
**Labels:** bug, backend, critical, escrow
|
||||
|
||||
## Description
|
||||
|
||||
Both the dashboard disputeRoutes and the releaseHold disputeRoutes are mounted at /api/disputes in app.ts. The dashboard router is mounted first (line 521). A POST /api/disputes/{purchaseRequestId}/resolve with a valid purchaseRequestId will match the dashboard router's POST /:id/resolve (Dispute CRUD resolve) before reaching the releaseHold router's escrow-unblocking resolve. The escrow hold is never cleared.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
The dashboard router intercepts the request and executes Dispute model CRUD resolve only. Escrow hold is not cleared. Outcome is non-deterministic depending on whether the ID matches a Dispute _id.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
POST /api/disputes/:purchaseRequestId/resolve should reach the releaseHold handler and clear the escrow hold. Route registration order must be corrected or paths made unambiguous.
|
||||
|
||||
## Reproduction Steps
|
||||
|
||||
POST /api/disputes/{validPurchaseRequestId}/resolve with admin token — observe that escrow hold is NOT released, only the Dispute document is updated.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/app.ts`
|
||||
- `backend/src/routes/disputeRoutes.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,46 +0,0 @@
|
||||
---
|
||||
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]]
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
issue: 004
|
||||
title: "POST /api/disputes/:id/resolve (dashboard) does not trigger escrow release — only updates Dispute model"
|
||||
severity: critical
|
||||
domain: Dispute
|
||||
labels: [bug, backend, escrow, major]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 POST /api/disputes/:id/resolve (dashboard) does not trigger escrow release — only updates Dispute model
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** Dispute
|
||||
**Labels:** bug, backend, escrow, major
|
||||
|
||||
## Description
|
||||
|
||||
The API claims resolveDispute 'triggers refund/release/split escrow action.' DisputeService.resolveDispute only updates the Dispute document. The separate POST /api/disputes/:purchaseRequestId/resolve (releaseHold router) is required to actually unblock escrow. Due to the route shadowing bug, the correct handler may never be reached.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
DisputeService.resolveDispute only updates the Dispute document. Escrow remains blocked until a separate correct API call is made to the releaseHold router.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Dispute resolution should atomically update the Dispute record AND release/refund the escrow as indicated by the action field.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/disputeService.ts`
|
||||
- `backend/src/controllers/disputeController.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
issue: 005
|
||||
title: "POST /api/payment/payments/:id/fetch-tx, POST /api/payment/payments/auto-fetch-missing, and GET /api/payment/payments/:id/debug have no authentication middleware"
|
||||
severity: critical
|
||||
domain: Payment
|
||||
labels: [security, bug, backend, critical, missing-auth]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 POST /api/payment/payments/:id/fetch-tx, POST /api/payment/payments/auto-fetch-missing, and GET /api/payment/payments/:id/debug have no authentication middleware
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** Payment
|
||||
**Labels:** security, bug, backend, critical, missing-auth
|
||||
|
||||
## Description
|
||||
|
||||
Three payment utility/debug endpoints are mounted with zero authentication. Any unauthenticated caller can read full payment internals (including blockchain metadata and wallet monitor state) or trigger on-chain fetches and state writes. These are exploitable without credentials in production.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
All three return 200 with full data when called without any Authorization header.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
All three endpoints should require at minimum authenticateToken, and ideally authorizeRoles('admin').
|
||||
|
||||
## Reproduction Steps
|
||||
|
||||
curl -X POST https://api.example.com/api/payment/payments/test123/fetch-tx — expect 401, currently returns 200.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/paymentRoutes.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,40 +0,0 @@
|
||||
---
|
||||
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]]
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
issue: 006
|
||||
title: "GET /api/admin/scanner/status has no authentication middleware despite /api/admin/ prefix"
|
||||
severity: critical
|
||||
domain: Admin
|
||||
labels: [security, bug, backend, critical, missing-auth]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 GET /api/admin/scanner/status has no authentication middleware despite /api/admin/ prefix
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** Admin
|
||||
**Labels:** security, bug, backend, critical, missing-auth
|
||||
|
||||
## Description
|
||||
|
||||
The scanner status proxy endpoint at GET /api/admin/scanner/status proxies directly to AMN_SCANNER_URL without any authentication check, despite sitting under the /api/admin/ route prefix which conventionally requires admin auth.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Returns scanner data (200) to any unauthenticated request.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Return 401 without Authorization header, 403 for non-admin token. Apply authenticateToken + authorizeRoles('admin').
|
||||
|
||||
## Reproduction Steps
|
||||
|
||||
curl https://api.example.com/api/admin/scanner/status — should return 401, currently returns scanner data.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/adminRoutes.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
issue: 007
|
||||
title: "Frontend deleteAccount action calls DELETE /user/profile which has no backend route — account deletion is broken"
|
||||
severity: critical
|
||||
domain: Authentication
|
||||
labels: [bug, frontend, critical, broken-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 Frontend deleteAccount action calls DELETE /user/profile which has no backend route — account deletion is broken
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** Authentication
|
||||
**Labels:** bug, frontend, critical, broken-feature
|
||||
|
||||
## Description
|
||||
|
||||
The frontend deleteAccount action in src/actions/account.ts (line 144) calls axiosInstance.delete(endpoints.users.profile) which resolves to DELETE /user/profile. The actual soft-delete route is DELETE /api/auth/account (requires password in body, runs deleteAccountValidation). Account deletion silently returns 404 from every UI path.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
DELETE /user/profile returns 404. Users cannot delete their accounts from the UI.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
deleteAccount action should call DELETE /api/auth/account with the user's password in the request body.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/account.ts`
|
||||
- `frontend/src/lib/axios.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
issue: 008
|
||||
title: "sendFileMessage posts to wrong endpoint — file uploads silently fail or corrupt text-message handler"
|
||||
severity: critical
|
||||
domain: Chat
|
||||
labels: [bug, frontend, critical, broken-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 sendFileMessage posts to wrong endpoint — file uploads silently fail or corrupt text-message handler
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** Chat
|
||||
**Labels:** bug, frontend, critical, broken-feature
|
||||
|
||||
## Description
|
||||
|
||||
The frontend sendFileMessage action in src/actions/chat.ts (line 386) sends multipart form data to endpoints.chat.sendMessage which resolves to POST /api/chat/:id/messages. The actual file upload endpoint is POST /api/chat/:id/messages/file. The file payload hits the text-message handler which expects JSON with a string content field.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
File uploads hit the text-message handler, which cannot process multipart payloads. File attachments are silently discarded or the request errors.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
sendFileMessage should POST multipart/form-data to /api/chat/:id/messages/file.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/chat.ts`
|
||||
- `frontend/src/lib/axios.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 009
|
||||
title: "archiveConversation sends PUT but backend only accepts PATCH — all archive attempts fail"
|
||||
severity: critical
|
||||
domain: Chat
|
||||
labels: [bug, frontend, critical, broken-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 archiveConversation sends PUT but backend only accepts PATCH — all archive attempts fail
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** Chat
|
||||
**Labels:** bug, frontend, critical, broken-feature
|
||||
|
||||
## Description
|
||||
|
||||
The frontend archiveConversation action (src/actions/chat.ts line 289) calls axiosInstance.put(). The backend registers PATCH /api/chat/:id/archive. HTTP method mismatch causes 404 or 405 on every archive attempt.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Every archive attempt returns 404/405. Chat archiving is non-functional.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
archiveConversation should call axiosInstance.patch().
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/chat.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 010
|
||||
title: "Frontend admin updateUserStatus and updateUserRole use PUT but backend only accepts PATCH"
|
||||
severity: critical
|
||||
domain: User Management
|
||||
labels: [bug, frontend, critical, admin, broken-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 Frontend admin updateUserStatus and updateUserRole use PUT but backend only accepts PATCH
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** User Management
|
||||
**Labels:** bug, frontend, critical, admin, broken-feature
|
||||
|
||||
## Description
|
||||
|
||||
user.ts line 162 calls axiosInstance.put() for updateUserStatus and line 175 calls axiosInstance.put() for updateUserRole. Backend registers these as PATCH /api/users/admin/:userId/status and PATCH /api/users/admin/:userId/role. PUT is not registered; calls return 404 or 405.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Admin status and role update actions fail with 404/405 silently.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Both actions should use axiosInstance.patch().
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/user.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
issue: 011
|
||||
title: "Frontend updateUserStatus sends 'inactive'/'pending' status values that backend does not accept"
|
||||
severity: critical
|
||||
domain: User Management
|
||||
labels: [bug, frontend, critical, admin, type-mismatch]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 Frontend updateUserStatus sends 'inactive'/'pending' status values that backend does not accept
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** User Management
|
||||
**Labels:** bug, frontend, critical, admin, type-mismatch
|
||||
|
||||
## Description
|
||||
|
||||
TypeScript union type in user.ts line 159 is 'active' | 'inactive' | 'pending'. Backend User.status enum is active | suspended | deleted. Values 'inactive' and 'pending' are not valid on the backend and will be rejected or silently ignored. 'suspended' is absent from the frontend type.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Attempting to set user status to 'inactive' or 'pending' via the admin UI sends invalid values. The user's status is not actually updated.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Frontend type should be 'active' | 'suspended' | 'deleted' to match the backend enum. Admin UI should offer 'suspended' as an option.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/user.ts`
|
||||
- `frontend/src/types/user.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 012
|
||||
title: "Trezor Safekeeping frontend — audit 'zero frontend' finding was STALE (feature exists)"
|
||||
severity: info
|
||||
domain: Trezor
|
||||
labels: [invalid, stale-audit, trezor, frontend]
|
||||
status: invalid
|
||||
created: 2026-05-29
|
||||
resolved: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# ⚪ INVALID — Trezor Safekeeping frontend DOES exist (audit finding was stale)
|
||||
|
||||
**Severity:** info (was: critical)
|
||||
**Domain:** Trezor
|
||||
**Status:** INVALID — the audit's "zero frontend implementation" claim (findings C31/C32) was generated from an older code snapshot. The frontend Trezor implementation exists in current code.
|
||||
|
||||
## Why this is not a bug
|
||||
|
||||
A direct re-check of the current frontend on 2026-05-29 confirmed a complete Trezor implementation:
|
||||
|
||||
- `frontend/src/app/dashboard/admin/trezor/page.tsx` → renders `TrezorSettingsView` (registration + re-register UI)
|
||||
- `frontend/src/sections/admin/trezor/trezor-settings-view.tsx` → settings/registration view (~14KB)
|
||||
- `frontend/src/web3/trezor/trezorConnector.ts` → lazy-imports `@trezor/connect-web`; implements `trezorGetXpub`, `trezorGetAddress`, `trezorSignMessage`
|
||||
- `frontend/src/components/trezor-sign-dialog/TrezorSignDialog.tsx` → full stepper: build instruction → sign on Trezor → enter txHash → confirm
|
||||
- `frontend/src/actions/trezor.ts` → complete API client: `getTrezorAccount`, `getTrezorRegistrationMessage`, `registerTrezor`, `getTrezorOperationMessage`, `confirmRelease`, `confirmRefund` — and it **builds the `trezor: { message, signature }` object** in the confirmation body
|
||||
|
||||
The active admin release/refund path goes through `TrezorSignDialog` → `actions/trezor.ts`, which **does** satisfy the backend `assertTrezorSignatureForOperation` guard when `TREZOR_SAFEKEEPING_REQUIRED=true`.
|
||||
|
||||
## Residual note (not a blocker)
|
||||
|
||||
The legacy helpers `confirmReleaseTx` / `confirmRefundTx` in `frontend/src/actions/payment.ts` post only `{ txHash }` with no `trezor` field — but they have **no UI callers** and are dead code. Consider removing them to avoid confusion. Tracked as a minor cleanup, not a release blocker.
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — findings C31, C32 (now superseded)
|
||||
- Corrected doc: `04 - Flows/Trezor Safekeeping Flow.md`
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
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,40 @@
|
||||
---
|
||||
issue: 013
|
||||
title: "createProviderPaymentIntent always routes to request-network/intents regardless of provider argument"
|
||||
severity: critical
|
||||
domain: Payment
|
||||
labels: [bug, frontend, critical, payment, routing]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 createProviderPaymentIntent always routes to request-network/intents regardless of provider argument
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** Payment
|
||||
**Labels:** bug, frontend, critical, payment, routing
|
||||
|
||||
## Description
|
||||
|
||||
src/actions/payment.ts getProviderIntentEndpoint() ignores the provider parameter and always returns endpoints.payments.requestNetwork.intents ('/payment/request-network/intents'). Any checkout using provider='shkeeper' silently POSTs to the wrong backend service.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
SHKeeper checkout silently POSTs to /payment/request-network/intents instead of /payment/shkeeper/intents, causing payment intent creation to fail or create a wrong-provider payment record.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
getProviderIntentEndpoint() should return the correct provider-specific endpoint based on the provider argument (e.g., endpoints.payments.shkeeper.intents for 'shkeeper').
|
||||
|
||||
## Reproduction Steps
|
||||
|
||||
Initiate a SHKeeper checkout and intercept network — observe the POST goes to /payment/request-network/intents not /payment/shkeeper/intents.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/payment.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 014
|
||||
title: "PaymentProvider TypeScript type excludes 'shkeeper' and 'decentralized' causing UI fallthrough for main payment providers"
|
||||
severity: critical
|
||||
domain: Payment
|
||||
labels: [bug, frontend, critical, payment, type-mismatch]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 PaymentProvider TypeScript type excludes 'shkeeper' and 'decentralized' causing UI fallthrough for main payment providers
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** Payment
|
||||
**Labels:** bug, frontend, critical, payment, type-mismatch
|
||||
|
||||
## Description
|
||||
|
||||
src/types/payment.ts defines PaymentProvider as 'request.network' | 'test' | 'other'. The two primary production payment providers ('shkeeper' and 'decentralized') are absent from this union type. Frontend code that switches on PaymentProvider falls through to unknown/default state for the majority of production payments.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Provider-based conditional rendering, labels, and routing logic silently falls through to unknown state for SHKeeper and DePay payments.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
PaymentProvider type should include 'shkeeper' and 'decentralized' variants.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/types/payment.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,43 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
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,40 @@
|
||||
---
|
||||
issue: 015
|
||||
title: "Simulated transaction SIM_ bypass has no environment guard — can fire in production on wallet connection failure"
|
||||
severity: critical
|
||||
domain: Payment
|
||||
labels: [security, bug, critical, payment, frontend, bypass]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🔴 Simulated transaction SIM_ bypass has no environment guard — can fire in production on wallet connection failure
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** Payment
|
||||
**Labels:** security, bug, critical, payment, frontend, bypass
|
||||
|
||||
## Description
|
||||
|
||||
src/web3/context/web3-provider.tsx lines 225 and 232 generate SIM_ prefixed transaction hashes when wallet connection fails. The backend skips on-chain verification for any paymentHash starting with 'SIM_' — controlled only by hash prefix, not an environment flag. The frontend generates SIM_ hashes in an error fallback path that can trigger in production.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
In production, if a wallet connection times out or throws, the fallback generates a SIM_ hash that passes backend verification and creates a completed payment record without any real on-chain transaction.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
SIM_ hash generation should be guarded by process.env.NODE_ENV !== 'production' check. Backend SIM_ bypass should also be gated by NODE_ENV.
|
||||
|
||||
## Reproduction Steps
|
||||
|
||||
Simulate a wallet connection failure in staging — observe that a SIM_ hash is generated and check if a completed payment record is created in the database.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/web3/context/web3-provider.tsx`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
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]]
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 016
|
||||
title: "updatePurchaseRequest uses PUT but backend only registers PATCH — all purchase request edits fail"
|
||||
severity: major
|
||||
domain: Purchase Request
|
||||
labels: [bug, frontend, major, broken-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 updatePurchaseRequest uses PUT but backend only registers PATCH — all purchase request edits fail
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Purchase Request
|
||||
**Labels:** bug, frontend, major, broken-feature
|
||||
|
||||
## Description
|
||||
|
||||
The frontend updatePurchaseRequest action (marketplace.ts line 71) calls axiosInstance.put against '/marketplace/purchase-requests/:id'. Backend registers PATCH (not PUT) on /purchase-requests/:id. PUT returns 404 from the controller router.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Purchase request edits from the buyer edit view fail with 404/405.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
updatePurchaseRequest should call axiosInstance.patch().
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/marketplace.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,46 +0,0 @@
|
||||
---
|
||||
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]]
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 017
|
||||
title: "updateOffer uses PUT /marketplace/offers/:id but backend registers PATCH /offers/:id — offer edits fail"
|
||||
severity: major
|
||||
domain: Seller Offer
|
||||
labels: [bug, frontend, major, broken-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 updateOffer uses PUT /marketplace/offers/:id but backend registers PATCH /offers/:id — offer edits fail
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Seller Offer
|
||||
**Labels:** bug, frontend, major, broken-feature
|
||||
|
||||
## Description
|
||||
|
||||
Frontend updateOffer action (src/actions/marketplace.ts line 289) uses axiosInstance.put() against /marketplace/offers/:id. Backend registers router.patch('/offers/:id') at routes.ts line 1260. Method mismatch. step-1-send-proposal.tsx actively calls updateOffer() for existing offer edits.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Offer price/ETA/notes edits from the seller proposal form fail silently or 404.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
updateOffer should use axiosInstance.patch().
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/marketplace.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
issue: 018
|
||||
title: "select-offer updateMany has no status filter — overwrites withdrawn/rejected offers back to 'rejected' corrupting status history"
|
||||
severity: major
|
||||
domain: Seller Offer
|
||||
labels: [bug, backend, major, data-integrity]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 select-offer updateMany has no status filter — overwrites withdrawn/rejected offers back to 'rejected' corrupting status history
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Seller Offer
|
||||
**Labels:** bug, backend, major, data-integrity
|
||||
|
||||
## Description
|
||||
|
||||
The POST /purchase-requests/:id/select-offer route handler (routes.ts lines 1386-1395) uses updateMany with only {purchaseRequestId, _id: {$ne: offerId}} — no status filter. This can overwrite already-withdrawn or previously-rejected offers' status back to 'rejected', corrupting their status history. SellerOfferService.acceptOffer() correctly filters by status: {$in: ['pending', 'active']}.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Selecting an offer via the select-offer endpoint corrupts previously-withdrawn offer records by setting their status back to 'rejected'.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
The select-offer updateMany call should include a status filter: {$in: ['pending']} to only reject pending offers, matching the service-layer behavior.
|
||||
|
||||
## Reproduction Steps
|
||||
|
||||
Create a request with one withdrawn offer and one pending offer. Select the pending offer via POST /purchase-requests/:id/select-offer. Verify the withdrawn offer's status is now 'rejected'.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/routes.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,53 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -1,46 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 019
|
||||
title: "SellerOffer.status 'active' does not exist in schema enum but is referenced in docs and code comments"
|
||||
severity: major
|
||||
domain: Seller Offer
|
||||
labels: [bug, backend, major, data-model]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 SellerOffer.status 'active' does not exist in schema enum but is referenced in docs and code comments
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Seller Offer
|
||||
**Labels:** bug, backend, major, data-model
|
||||
|
||||
## Description
|
||||
|
||||
SellerOffer Mongoose schema (SellerOffer.ts line 80) and TypeScript interface (line 17) enumerate only 'pending | accepted | rejected | withdrawn'. Attempting to save status='active' on a SellerOffer will throw a Mongoose ValidationError. Any code path that sets status='active' on a SellerOffer will fail at runtime.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Any attempt to set a SellerOffer to 'active' throws Mongoose ValidationError.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Either add 'active' to the SellerOffer status enum if it is a real state, or remove all references to it from code comments and remove the documented state machine entry.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/models/SellerOffer.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
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]]
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 020
|
||||
title: "select-offer does not send per-seller socket events or notifications to winning or losing sellers"
|
||||
severity: major
|
||||
domain: Seller Offer
|
||||
labels: [bug, backend, major, notifications, socket]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 select-offer does not send per-seller socket events or notifications to winning or losing sellers
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Seller Offer
|
||||
**Labels:** bug, backend, major, notifications, socket
|
||||
|
||||
## Description
|
||||
|
||||
POST /purchase-requests/:id/select-offer route (routes.ts lines 1300-1438) emits only a single purchase-request-update event to the request room. It does NOT call notifyOfferAccepted, does NOT call notifyOfferRejected for losing sellers, and does NOT emit seller-offer-update events. Those notifications only fire via SellerOfferService.updateOfferStatus().
|
||||
|
||||
## Current Behavior
|
||||
|
||||
After a buyer selects an offer via select-offer, the winning seller receives no notification and losing sellers receive no rejection notification.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
The select-offer path should emit per-seller socket events and notifications equivalent to what SellerOfferService.acceptOffer() does — notify the winning seller and each losing seller.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/routes.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
issue: 021
|
||||
title: "POST /api/marketplace/offers/:id/withdraw HTTP route does not exist — seller withdraw is dead code"
|
||||
severity: major
|
||||
domain: Seller Offer
|
||||
labels: [missing-feature, backend, frontend, major]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 POST /api/marketplace/offers/:id/withdraw HTTP route does not exist — seller withdraw is dead code
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Seller Offer
|
||||
**Labels:** missing-feature, backend, frontend, major
|
||||
|
||||
## Description
|
||||
|
||||
SellerOfferService.withdrawOffer() method exists (lines 427-443) but no HTTP route calls it. The only way to withdraw is via PUT /offers/:id/status with {status:'withdrawn'} which applies no pending-only guard. No frontend withdraw button or action exists.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Sellers have no UI path to withdraw an offer. withdrawOffer() service method is unreachable via HTTP. The route-level withdrawal via PUT /status has no transition guard.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
A dedicated withdraw endpoint should be registered, calling withdrawOffer() which enforces the pending-only guard. Or the PUT /offers/:id/status path should enforce status transition guards.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/routes.ts`
|
||||
- `frontend/src/actions/marketplace.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 022
|
||||
title: "GET /api/payment/payments/:id/debug has no authentication — full payment data exposed without credentials"
|
||||
severity: major
|
||||
domain: Payment
|
||||
labels: [security, bug, backend, major, missing-auth]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 GET /api/payment/payments/:id/debug has no authentication — full payment data exposed without credentials
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Payment
|
||||
**Labels:** security, bug, backend, major, missing-auth
|
||||
|
||||
## Description
|
||||
|
||||
GET /api/payment/payments/:id/debug returns payment document plus walletMonitor status without any authentication middleware. Backend code explicitly flags this as a security issue.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Any unauthenticated caller can read full payment data including blockchain metadata.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Should require authenticateToken + authorizeRoles('admin').
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/paymentRoutes.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -1,37 +0,0 @@
|
||||
---
|
||||
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,36 @@
|
||||
---
|
||||
issue: 023
|
||||
title: "GET /api/payment/export has no admin role guard at route level — any authenticated user can export all payment data"
|
||||
severity: major
|
||||
domain: Payment
|
||||
labels: [security, bug, backend, major, privilege-escalation]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 GET /api/payment/export has no admin role guard at route level — any authenticated user can export all payment data
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Payment
|
||||
**Labels:** security, bug, backend, major, privilege-escalation
|
||||
|
||||
## Description
|
||||
|
||||
GET /api/payment/export (controller-pattern route) has only authenticateToken — no admin guard at the router level. The parallel /api/payment/payments/export route has an admin role guard. The frontend hits the non-admin-gated path. Any authenticated buyer can export all payment records.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Non-admin buyers can call GET /api/payment/export and receive payment export data for all users.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
GET /api/payment/export should apply authorizeRoles('admin') at the route level.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/paymentRoutes.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 024
|
||||
title: "GET /api/payment/stats has no admin role guard — any authenticated user can read aggregate payment stats"
|
||||
severity: major
|
||||
domain: Payment
|
||||
labels: [security, bug, backend, major, privilege-escalation]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 GET /api/payment/stats has no admin role guard — any authenticated user can read aggregate payment stats
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Payment
|
||||
**Labels:** security, bug, backend, major, privilege-escalation
|
||||
|
||||
## Description
|
||||
|
||||
GET /api/payment/stats (controller-pattern route) requires only authenticateToken. The /api/payment/payments/stats route requires admin role. Frontend uses the non-admin-gated path.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Any authenticated buyer can read aggregate payment platform statistics.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Stats endpoint should be admin-only or return only caller-scoped data for non-admins.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/paymentRoutes.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -1,46 +0,0 @@
|
||||
---
|
||||
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)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 025
|
||||
title: "GET /api/disputes/statistics has no admin role guard — any authenticated user can access aggregate dispute KPIs"
|
||||
severity: major
|
||||
domain: Dispute
|
||||
labels: [security, bug, backend, major, privilege-escalation]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 GET /api/disputes/statistics has no admin role guard — any authenticated user can access aggregate dispute KPIs
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Dispute
|
||||
**Labels:** security, bug, backend, major, privilege-escalation
|
||||
|
||||
## Description
|
||||
|
||||
Backend registers GET /api/disputes/statistics with authenticateToken only. No authorizeRoles(admin) guard is applied at the route or controller level. Any authenticated non-admin user can access aggregate dispute platform data.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Non-admin authenticated users can call GET /api/disputes/statistics and receive platform-wide KPI data.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Return 403 for non-admin tokens. Apply authorizeRoles('admin') at the route level.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/disputeRoutes.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 026
|
||||
title: "GET /notifications/:id only returns user's most-recent notification — all others return 404 erroneously"
|
||||
severity: major
|
||||
domain: Notification
|
||||
labels: [bug, backend, major, broken-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 GET /notifications/:id only returns user's most-recent notification — all others return 404 erroneously
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Notification
|
||||
**Labels:** bug, backend, major, broken-feature
|
||||
|
||||
## Description
|
||||
|
||||
The backend getNotificationById controller calls getUserNotifications(userId, 1, 1) — fetching page 1 with limit 1 — then does an in-memory _id string match. Any notification that is not the single most-recent record for that user always returns 404, regardless of ownership.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
GET /notifications/:id returns 404 for all notifications except the user's most recently created one.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
getNotificationById should perform a direct MongoDB query by _id and userId: Notification.findOne({_id, userId}).
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/controllers/notificationController.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 027
|
||||
title: "confirm-delivery endpoint has no ownership check — any authenticated user can confirm delivery on any request"
|
||||
severity: major
|
||||
domain: Delivery
|
||||
labels: [security, bug, backend, major, authorization]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 confirm-delivery endpoint has no ownership check — any authenticated user can confirm delivery on any request
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Delivery
|
||||
**Labels:** security, bug, backend, major, authorization
|
||||
|
||||
## Description
|
||||
|
||||
marketplaceController.confirmDelivery (line 782) checks dispute gate and status === 'delivery' but does NOT verify the caller is the buyer of the request. Any authenticated user who knows a purchaseRequestId in 'delivery' status can call PATCH /confirm-delivery and advance it to 'delivered'.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Sellers, admins, or any authenticated third party can call confirm-delivery and mark a request as delivered without the buyer's involvement.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
confirmDelivery should verify req.user.id === purchaseRequest.buyerId before proceeding.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/controllers/marketplaceController.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 028
|
||||
title: "delivery-code-generated socket event broadcasts raw 6-digit code to entire request room including seller"
|
||||
severity: major
|
||||
domain: Delivery
|
||||
labels: [security, bug, backend, major, delivery]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 delivery-code-generated socket event broadcasts raw 6-digit code to entire request room including seller
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Delivery
|
||||
**Labels:** security, bug, backend, major, delivery
|
||||
|
||||
## Description
|
||||
|
||||
DeliveryService.generateDeliveryCode emits 'delivery-code-generated' with the raw 6-digit code to the room request-{id}. Both buyer and seller are subscribers of this room. A seller with socket access can intercept the code before physical handoff, defeating the security purpose of the code-based handoff verification.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
DeliveryService.ts line 55 broadcasts {requestId, code, expiresAt, timestamp} to all room subscribers. Seller receives the code via socket before physically receiving the goods.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
The code should only be emitted to the buyer's personal room (user-{buyerId}), not the shared request room.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/deliveryService.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -1,46 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
issue: 029
|
||||
title: "No brute-force protection on delivery code verification endpoint — 900,000 combinations are enumerable"
|
||||
severity: major
|
||||
domain: Delivery
|
||||
labels: [security, bug, backend, major, brute-force]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 No brute-force protection on delivery code verification endpoint — 900,000 combinations are enumerable
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Delivery
|
||||
**Labels:** security, bug, backend, major, brute-force
|
||||
|
||||
## Description
|
||||
|
||||
The 6-digit delivery code verify endpoint (routes.ts lines 2790-2847) has no rate limiting, lockout counter, or attempt count maximum. Failed attempts are recorded to deliveryInfo.deliveryAttempts[] but no enforcement exists. A malicious actor could attempt all 900,000 combinations without being blocked.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Unlimited guesses are permitted. No rate limiting or lockout is applied to the verify endpoint.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
After N failed attempts (e.g., 5), the endpoint should return 429 or lock the code for a period. The deliveryAttempts[] array already tracks attempts — enforcement just needs to be added.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/routes.ts`
|
||||
- `backend/src/services/deliveryService.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
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)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 030
|
||||
title: "POST /api/payment/payments/cleanup-pending admin check is inside handler only — no middleware-level enforcement"
|
||||
severity: major
|
||||
domain: Admin
|
||||
labels: [security, bug, backend, major, missing-auth]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 POST /api/payment/payments/cleanup-pending admin check is inside handler only — no middleware-level enforcement
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Admin
|
||||
**Labels:** security, bug, backend, major, missing-auth
|
||||
|
||||
## Description
|
||||
|
||||
POST /api/payment/payments/cleanup-pending registers only authenticateToken at the route level. Admin check is inside the handler. Any authenticated non-admin who discovers this endpoint can attempt to call it; the in-handler check is the only defense against unauthorized bulk deletion of pending payments.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Non-admin authenticated users can call the endpoint; admin gate fires inside handler code rather than at middleware level.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Apply authorizeRoles('admin') middleware at the route level before the handler runs.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/paymentRoutes.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,47 +0,0 @@
|
||||
---
|
||||
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)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 031
|
||||
title: "POST /api/points/admin/add admin check is inside handler only — no middleware-level enforcement"
|
||||
severity: major
|
||||
domain: Admin
|
||||
labels: [security, bug, backend, major, missing-auth]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 POST /api/points/admin/add admin check is inside handler only — no middleware-level enforcement
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Admin
|
||||
**Labels:** security, bug, backend, major, missing-auth
|
||||
|
||||
## Description
|
||||
|
||||
POST /api/points/admin/add registers authenticateToken only at the route level. Admin role check runs inside the handler. This means the handler code runs before the role is verified, creating potential for edge-case bypass.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
The handler code begins executing for any authenticated user before the role check fires.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Apply authorizeRoles('admin') middleware at the route level.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/pointsRoutes.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
issue: 032
|
||||
title: "Admin delete user via legacy endpoint performs hard delete (findByIdAndDelete) instead of soft delete"
|
||||
severity: major
|
||||
domain: User Management
|
||||
labels: [bug, frontend, backend, major, data-integrity]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Admin delete user via legacy endpoint performs hard delete (findByIdAndDelete) instead of soft delete
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** User Management
|
||||
**Labels:** bug, frontend, backend, major, data-integrity
|
||||
|
||||
## Description
|
||||
|
||||
Frontend deleteUser function calls the legacy /users/admin/:id DELETE route which performs findByIdAndDelete (hard delete). The new controller at /api/user/admin/:userId performs a soft delete (status='deleted'). The frontend comment says 'soft delete' but calls the hard-delete route. User records and all associated data are permanently destroyed.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Admin 'delete user' action permanently destroys the user record from the database via findByIdAndDelete.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Frontend should call the new controller endpoint /api/user/admin/:userId for soft delete, or the legacy route should be updated to perform a soft delete.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/user.ts`
|
||||
- `frontend/src/lib/axios.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 033
|
||||
title: "Admin can delete other admin accounts via new controller — legacy admin-on-admin protection does not apply"
|
||||
severity: major
|
||||
domain: User Management
|
||||
labels: [security, bug, backend, major, privilege-escalation]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Admin can delete other admin accounts via new controller — legacy admin-on-admin protection does not apply
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** User Management
|
||||
**Labels:** security, bug, backend, major, privilege-escalation
|
||||
|
||||
## Description
|
||||
|
||||
The new controller (DELETE /api/user/admin/:userId) only blocks self-deletion. It does not prevent an admin from deleting other admin accounts. The legacy route (DELETE /api/users/admin/:userId) blocks admin-on-admin deletion. The two routes have divergent authorization logic.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
An admin can delete other admin accounts via the new controller endpoint without a 403 error.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
DELETE /api/user/admin/:userId should check if target user has role=admin and return 403 (matching legacy route behavior).
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/controllers/userController.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 034
|
||||
title: "All dispute socket.io emit blocks are TODO stubs — no real-time updates fire for any dispute event"
|
||||
severity: major
|
||||
domain: Dispute
|
||||
labels: [missing-feature, backend, major, socket, dispute]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 All dispute socket.io emit blocks are TODO stubs — no real-time updates fire for any dispute event
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Dispute
|
||||
**Labels:** missing-feature, backend, major, socket, dispute
|
||||
|
||||
## Description
|
||||
|
||||
Every socket.io emit block in DisputeService is commented out as TODO. No real-time updates fire for dispute creation, admin assignment, status changes, evidence uploads, or resolution. The flow doc describes real-time presence as a working feature.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Zero socket events are emitted from DisputeService. All real-time dispute notifications are silent.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Socket events should be emitted for dispute lifecycle events to keep all parties informed in real time.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/disputeService.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
issue: 035
|
||||
title: "Frontend getPaymentStatus and confirmPayment call non-existent endpoints GET /payment/:id/status and POST /payment/:id/confirm"
|
||||
severity: major
|
||||
domain: Payment
|
||||
labels: [bug, frontend, major, broken-feature, dispute]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Frontend getPaymentStatus and confirmPayment call non-existent endpoints GET /payment/:id/status and POST /payment/:id/confirm
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Payment
|
||||
**Labels:** bug, frontend, major, broken-feature, dispute
|
||||
|
||||
## Description
|
||||
|
||||
Frontend getPaymentStatus() builds URL as /payment/:id/status and confirmPayment() builds /payment/:id/confirm. Neither endpoint is registered in the backend. getPaymentStatus is actively called from dispute/payment-details-card.tsx line 101 — the 'Verify' button always returns 404.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
The 'Verify' button in the dispute payment panel always returns 404. confirmPayment() is broken.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Either implement /payment/:id/status and /payment/:id/confirm backend routes, or fix the frontend to use the correct existing payment detail endpoint.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/payment.ts`
|
||||
- `frontend/src/sections/dispute/components/payment-details-card.tsx`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 036
|
||||
title: "cancelPayment action sends DELETE /payment/:id but no DELETE route exists on any payment endpoint"
|
||||
severity: major
|
||||
domain: Payment
|
||||
labels: [bug, frontend, major, broken-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 cancelPayment action sends DELETE /payment/:id but no DELETE route exists on any payment endpoint
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Payment
|
||||
**Labels:** bug, frontend, major, broken-feature
|
||||
|
||||
## Description
|
||||
|
||||
cancelPayment() in src/actions/payment.ts sends DELETE /payment/:id. Backend has no DELETE method on any payment route. The web3 context version is a local state reset, but the action-layer version makes a real HTTP DELETE that will 404.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
cancelPayment() via the action layer returns 404.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Either implement a DELETE /payment/:id backend route for cancellation, or remove/replace the action-layer cancelPayment with correct API call.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/payment.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 037
|
||||
title: "Frontend initiateRequestNetworkPayout, confirmRequestNetworkPayout, confirmRequestNetworkRelease, confirmRequestNetworkRefund call non-existent backend routes"
|
||||
severity: major
|
||||
domain: Payment
|
||||
labels: [missing-feature, bug, frontend, major, payment, request-network]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Frontend initiateRequestNetworkPayout, confirmRequestNetworkPayout, confirmRequestNetworkRelease, confirmRequestNetworkRefund call non-existent backend routes
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Payment
|
||||
**Labels:** missing-feature, bug, frontend, major, payment, request-network
|
||||
|
||||
## Description
|
||||
|
||||
Four frontend actions in src/actions/payment.ts call /api/payment/request-network/:id/payout/initiate, /payout/confirm, /release/confirm, and /refund/confirm. None of these sub-paths exist in the backend. Admin Request Network payout/release/refund operations are completely broken.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
All four RN admin payout/release/refund actions return 404.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Backend should implement the four Request Network admin payout/release/refund endpoints, or the frontend actions should be updated to match existing backend routes.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/payment.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
issue: 038
|
||||
title: "Multiple frontend payment stub actions call non-existent backend endpoints: /payment/history, /payment/methods, /payment/validate, /payment/transactions, /payment/escrow/balance"
|
||||
severity: major
|
||||
domain: Payment
|
||||
labels: [missing-feature, major, frontend, payment]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Multiple frontend payment stub actions call non-existent backend endpoints: /payment/history, /payment/methods, /payment/validate, /payment/transactions, /payment/escrow/balance
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Payment
|
||||
**Labels:** missing-feature, major, frontend, payment
|
||||
|
||||
## Description
|
||||
|
||||
Frontend defines getPaymentHistory, getPaymentMethods, validatePayment, getTransactionHistory, getEscrowBalance — all calling endpoints that have no backend implementation. Any dashboard widget invoking these actions will receive 404 and silently fail or show empty state.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
All five actions return 404 when called.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Either implement backend routes for these endpoints or remove the stub actions. At minimum, verify no production UI calls them.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/payment.ts`
|
||||
- `frontend/src/lib/axios.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 039
|
||||
title: "reset-password-with-code endpoint has no password complexity validation — accepts weak passwords rejected by token-based reset"
|
||||
severity: major
|
||||
domain: Authentication
|
||||
labels: [security, bug, backend, major, auth]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 reset-password-with-code endpoint has no password complexity validation — accepts weak passwords rejected by token-based reset
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Authentication
|
||||
**Labels:** security, bug, backend, major, auth
|
||||
|
||||
## Description
|
||||
|
||||
POST /api/auth/reset-password-with-code has no validation middleware (authRoutes.ts:54-56). A new password of '123456' or 'aaaaaa' is accepted. POST /api/auth/reset-password uses passwordResetValidation enforcing uppercase+lowercase+digit. Inconsistent security between the two reset paths.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Code-based password reset accepts any non-empty password without complexity requirements.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
POST /api/auth/reset-password-with-code should apply the same passwordResetValidation middleware as the token-based reset.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/authRoutes.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 040
|
||||
title: "changePassword action has no UI component — change password feature is untestable from the UI"
|
||||
severity: major
|
||||
domain: Authentication
|
||||
labels: [missing-feature, frontend, major]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 changePassword action has no UI component — change password feature is untestable from the UI
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Authentication
|
||||
**Labels:** missing-feature, frontend, major
|
||||
|
||||
## Description
|
||||
|
||||
The changePassword action is implemented in action.ts (line 263) and POST /api/auth/change-password exists on the backend, but no dashboard page or view component calls it. There is no 'Change Password' UI anywhere under /dashboard.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Users cannot change their password through the UI. The feature only exists at the API level.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
A change-password form should exist in the user dashboard settings.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/account.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
issue: 041
|
||||
title: "Frontend searchPurchaseRequests calls /marketplace/purchase-requests/search which does not exist in backend"
|
||||
severity: major
|
||||
domain: Purchase Request
|
||||
labels: [bug, frontend, major, broken-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Frontend searchPurchaseRequests calls /marketplace/purchase-requests/search which does not exist in backend
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Purchase Request
|
||||
**Labels:** bug, frontend, major, broken-feature
|
||||
|
||||
## Description
|
||||
|
||||
Frontend defines searchPurchaseRequests pointing to /marketplace/purchase-requests/search. No /search sub-path is registered in backend. Search/filter should be handled via query parameters on the list endpoint GET /purchase-requests.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Calling searchPurchaseRequests produces a 404.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
searchPurchaseRequests should use GET /marketplace/purchase-requests with filter query parameters instead of a /search sub-path.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/marketplace.ts`
|
||||
- `frontend/src/lib/axios.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
issue: 042
|
||||
title: "Frontend getMarketplaceStats calls /marketplace/purchase-requests/stats which has no backend handler"
|
||||
severity: major
|
||||
domain: Purchase Request
|
||||
labels: [missing-feature, frontend, major]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Frontend getMarketplaceStats calls /marketplace/purchase-requests/stats which has no backend handler
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Purchase Request
|
||||
**Labels:** missing-feature, frontend, major
|
||||
|
||||
## Description
|
||||
|
||||
Frontend defines getMarketplaceStats calling /marketplace/purchase-requests/stats. No /stats sub-path under purchase-requests is registered in backend. Any dashboard page calling this will receive a 404.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
getMarketplaceStats always returns 404.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Backend should implement GET /marketplace/purchase-requests/stats, or the frontend action should be removed and any UI using it should use an alternative.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/marketplace.ts`
|
||||
- `frontend/src/lib/axios.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 043
|
||||
title: "Frontend getDeliveryAttempts and getDeliveryStats call non-existent backend endpoints"
|
||||
severity: major
|
||||
domain: Delivery
|
||||
labels: [missing-feature, frontend, major]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Frontend getDeliveryAttempts and getDeliveryStats call non-existent backend endpoints
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Delivery
|
||||
**Labels:** missing-feature, frontend, major
|
||||
|
||||
## Description
|
||||
|
||||
getDeliveryAttempts calls /delivery-code/attempts and getDeliveryStats calls /delivery/stats. Neither path is registered in backend. Delivery attempt data exists in deliveryInfo.deliveryAttempts[] but no HTTP route exposes it.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Both actions return 404. Any UI calling them silently fails.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Either implement the backend routes or remove the frontend actions and any UI depending on them.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/delivery.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 044
|
||||
title: "POST /api/marketplace/purchase-requests/:id/final-approval creates dummy payment for testing if no real payment exists — testing backdoor in production code"
|
||||
severity: major
|
||||
domain: Purchase Request
|
||||
labels: [security, bug, backend, major, escrow, bypass]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 POST /api/marketplace/purchase-requests/:id/final-approval creates dummy payment for testing if no real payment exists — testing backdoor in production code
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Purchase Request
|
||||
**Labels:** security, bug, backend, major, escrow, bypass
|
||||
|
||||
## Description
|
||||
|
||||
The final-approval endpoint in routes.ts (lines 1561-1592) contains logic that creates a dummy Payment document when no real payment is found and the request is in 'delivered' or 'delivery' status. This testing backdoor is undocumented and bypasses the payment integrity check in production.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Any request in delivered/delivery status can be final-approved without a real payment by triggering this code path, effectively releasing escrow for unpaid orders.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
The dummy payment creation should be guarded by NODE_ENV !== 'production' or removed entirely from production code.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/routes.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 045
|
||||
title: "addParticipants frontend sends { participants: string[] } array but backend expects { userId: string } single user"
|
||||
severity: major
|
||||
domain: Chat
|
||||
labels: [bug, frontend, major, chat]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 addParticipants frontend sends { participants: string[] } array but backend expects { userId: string } single user
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Chat
|
||||
**Labels:** bug, frontend, major, chat
|
||||
|
||||
## Description
|
||||
|
||||
The frontend addParticipants action (chat.ts line 425) sends { participants: string[] } as the body. The API documents POST /api/chat/:id/participants with body { userId: string } — a single user. Backend expects a single userId, not an array. Bulk participant addition will be silently handled incorrectly.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
addParticipants sends an array payload that the backend does not expect. Participant addition may fail or be ignored.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Frontend should send { userId: string } and call the endpoint once per participant, or backend should be updated to accept an array.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/chat.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
issue: 046
|
||||
title: "Frontend getSellerOfferHistory / seller offer history page does not exist — notification links to /dashboard/seller/marketplace/offers are broken"
|
||||
severity: major
|
||||
domain: Seller Offer
|
||||
labels: [missing-feature, frontend, backend, major]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Frontend getSellerOfferHistory / seller offer history page does not exist — notification links to /dashboard/seller/marketplace/offers are broken
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Seller Offer
|
||||
**Labels:** missing-feature, frontend, backend, major
|
||||
|
||||
## Description
|
||||
|
||||
No frontend page exists at /dashboard/seller/marketplace/offers. No getSellerOffers() action exists. The backend route GET /api/marketplace/offers/seller/:sellerId also does not exist (getOffersBySeller() service method is dead code via HTTP). Backend notification actionUrls pointing to this path produce broken links.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Notification links to the seller offer history are broken. Sellers have no way to view their offer history.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
A seller offer history page should exist at /dashboard/seller/marketplace/offers, backed by a proper backend list endpoint for the seller's own offers.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/routes.ts`
|
||||
- `backend/src/services/sellerOfferService.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 047
|
||||
title: "Frontend cron management and per-id token sweep endpoints for derived-destinations are not in backend inventory"
|
||||
severity: major
|
||||
domain: Admin
|
||||
labels: [missing-feature, backend, major, admin]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Frontend cron management and per-id token sweep endpoints for derived-destinations are not in backend inventory
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Admin
|
||||
**Labels:** missing-feature, backend, major, admin
|
||||
|
||||
## Description
|
||||
|
||||
Frontend derived-destinations actions call GET /api/payment/derived-destinations/cron/status, POST /cron/start, POST /cron/stop, and POST /:id/sweep. Backend lists only the bulk sweep and /:id/sweep-native. The cron management and per-id token sweep may be unimplemented. The UI page calls getSweepCronStatus on mount.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Opening /dashboard/admin/derived-destinations likely triggers 404 on cron status request on mount.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Backend should implement cron status, start, stop, and per-destination token sweep endpoints, or the frontend should be updated to match what is implemented.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/derived-destinations.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 048
|
||||
title: "Frontend reloadNetworkRegistry and probeChain call backend endpoints that do not exist"
|
||||
severity: major
|
||||
domain: Admin
|
||||
labels: [missing-feature, backend, major, admin]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Frontend reloadNetworkRegistry and probeChain call backend endpoints that do not exist
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Admin
|
||||
**Labels:** missing-feature, backend, major, admin
|
||||
|
||||
## Description
|
||||
|
||||
Frontend network-registry actions call POST /api/admin/rn/networks/reload and POST /api/admin/rn/networks/probe/:chainId. Backend only has GET /api/admin/rn/networks. Reload and probe buttons in the network registry UI silently fail.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Reload Registry and Probe Chain UI buttons return 404.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Backend should implement reload and probe endpoints, or the frontend buttons should be removed/disabled.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/network-registry.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 049
|
||||
title: "Frontend getConfirmationThresholdHistory calls GET /api/admin/settings/confirmation-thresholds/history which does not exist in backend"
|
||||
severity: major
|
||||
domain: Admin
|
||||
labels: [missing-feature, backend, major, admin]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Frontend getConfirmationThresholdHistory calls GET /api/admin/settings/confirmation-thresholds/history which does not exist in backend
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Admin
|
||||
**Labels:** missing-feature, backend, major, admin
|
||||
|
||||
## Description
|
||||
|
||||
Frontend confirmation-thresholds action defines getConfirmationThresholdHistory() calling /admin/settings/confirmation-thresholds/history. Backend only lists GET (current values) and PATCH per-chain. No history endpoint is registered.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
getConfirmationThresholdHistory() returns 404.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Backend should implement a history endpoint for threshold changes, or the frontend action should be removed.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/confirmation-thresholds.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 050
|
||||
title: "Points/Referral: five frontend pages do not exist — redemption, levels, referrals, transactions, admin-add all untestable via UI"
|
||||
severity: major
|
||||
domain: Points
|
||||
labels: [missing-feature, frontend, major, points]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Points/Referral: five frontend pages do not exist — redemption, levels, referrals, transactions, admin-add all untestable via UI
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Points
|
||||
**Labels:** missing-feature, frontend, major, points
|
||||
|
||||
## Description
|
||||
|
||||
The following routes 404: /dashboard/points/referrals, /dashboard/points/transactions, /dashboard/points/levels. redeemPoints is never called from any component. generateReferralCode is never called. adminAddPoints has no admin UI page.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
All five features are untestable via UI. Backend endpoints exist but are inaccessible through the product.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Frontend pages should be implemented for the above routes and wired to the corresponding backend endpoints.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/app/dashboard/points/`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 051
|
||||
title: "Self-referral prevention is absent — users can refer themselves for points"
|
||||
severity: major
|
||||
domain: Points
|
||||
labels: [security, bug, backend, major, points]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
# 🟠 Self-referral prevention is absent — users can refer themselves for points
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Points
|
||||
**Labels:** security, bug, backend, major, points
|
||||
|
||||
## Description
|
||||
|
||||
authController.ts referral attribution logic at lines 704 and 1132 has no self-referral check. Any user who obtains their own referral code and uses it during sign-up will receive a referral reward on their own account.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Self-referral is possible. Users can earn referral rewards by using their own code.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Before applying referral attribution, verify that the referrer's userId !== the new user's userId. If they match, skip the reward.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/controllers/authController.ts`
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 052
|
||||
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. Most SHKeeper/DePay payments follow the terminal path `pending → processing → completed`, so the bulk of successful payments are invisible in the success count.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Admin dashboard `successfulPayments` count excludes all `'completed'` payments. For a platform where SHKeeper is the primary provider, this count reads close to zero even after many successful payments.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
`successfulPayments` should count both `'confirmed'` and `'completed'` (the terminal success states), or the stat should be clearly documented as confirmed-only.
|
||||
|
||||
## 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
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
issue: 053
|
||||
title: "Axios interceptor only retriggers token refresh for 401, not 403"
|
||||
severity: major
|
||||
domain: Authentication
|
||||
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:** Authentication
|
||||
**Labels:** frontend, bug
|
||||
|
||||
## Description
|
||||
|
||||
`frontend/src/lib/axios.ts` (line ~105) only triggers the token-refresh flow for `status === 401`. A `403` response (e.g. `EMAIL_NOT_VERIFIED`, blocked account, under-privileged action) is not intercepted — it propagates as an unhandled error and some components may not handle it gracefully.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
Backend returns `403` → interceptor neither refreshes nor surfaces a meaningful state → error propagates raw to the calling component.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
A `403` should NOT trigger a token refresh (it is an authorization failure, not an expired token), but it should be surfaced clearly — e.g. redirect to the verify-email page for `EMAIL_NOT_VERIFIED`. The doc's claim that the interceptor "handles 401/403" should match the code.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/lib/axios.ts` — response interceptor (~line 105)
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M1
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
issue: 054
|
||||
title: "Login rate limiter counts all attempts (not just failures) — users locked out after correct logins"
|
||||
severity: major
|
||||
domain: Authentication
|
||||
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 locked out after correct logins
|
||||
|
||||
**Severity:** major
|
||||
**Domain:** Authentication
|
||||
**Labels:** backend, bug
|
||||
|
||||
## Description
|
||||
|
||||
`rateLimitService.checkLoginAttempts()` calls `checkLimit()` → `redisService.incr`, incrementing the counter on **every** login invocation, before password comparison. The counter only resets after a fully successful login. So 5 total attempts within 15 min (any mix of correct/incorrect passwords) triggers the lockout — not 5 failures as the docs imply.
|
||||
|
||||
## Current Behavior
|
||||
|
||||
5 total login attempts within 15 minutes → `429 TOO_MANY_ATTEMPTS`, even if some attempts used the correct password.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
The counter should increment only on a **failed** password comparison, not on every attempt. Otherwise document the actual behaviour so UX warns users appropriately.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/auth/rateLimitService.ts` — `checkLoginAttempts` / `checkLimit`
|
||||
- `backend/src/controllers/authController.ts` — move the increment to after password comparison
|
||||
|
||||
## References
|
||||
|
||||
- [Doc vs Code Audit Report](../09%20-%20Audits/Doc%20vs%20Code%20Audit%20Report%20-%202026-05-29.md) — Finding M3
|
||||
@@ -1,59 +1,70 @@
|
||||
# Issues Index
|
||||
|
||||
> Generated from Doc vs Code Audit — 2026-05-29
|
||||
> **35 open issues** | 🔴 14 critical · 🟠 19 major · 🟡 2 minor
|
||||
> Generated from Doc vs Code Audit — 2026-05-29 · last reconciled 2026-05-29
|
||||
> **53 open issues** | 🔴 14 critical · 🟠 39 major · 🟡 0 minor · ⚪ 1 invalid (stale audit)
|
||||
|
||||
## 🔴 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`
|
||||
- [[ISSUE-001-patch-api-disputes-id-status-and-post-api-disputes-id-resolv|PATCH /api/disputes/:id/status and POST /api/disputes/:id/resolve have no role guard — privilege escalation]] — `Dispute`
|
||||
- [[ISSUE-002-post-api-disputes-id-assign-has-no-role-guard-any-user-can-s|POST /api/disputes/:id/assign has no role guard — any user can self-assign as admin]] — `Dispute`
|
||||
- [[ISSUE-003-route-shadowing-post-api-disputes-purchaserequestid-resolve-|Route shadowing: POST /api/disputes/:purchaseRequestId/resolve matches dashboard router first and executes wrong handler]] — `Dispute`
|
||||
- [[ISSUE-004-post-api-disputes-id-resolve-dashboard-does-not-trigger-escr|POST /api/disputes/:id/resolve (dashboard) does not trigger escrow release — only updates Dispute model]] — `Dispute`
|
||||
- [[ISSUE-005-post-api-payment-payments-id-fetch-tx-post-api-payment-payme|POST /api/payment/payments/:id/fetch-tx, POST /api/payment/payments/auto-fetch-missing, and GET /api/payment/payments/:id/debug have no authentication middleware]] — `Payment`
|
||||
- [[ISSUE-006-get-api-admin-scanner-status-has-no-authentication-middlewar|GET /api/admin/scanner/status has no authentication middleware despite /api/admin/ prefix]] — `Admin`
|
||||
- [[ISSUE-007-frontend-deleteaccount-action-calls-delete-user-profile-whic|Frontend deleteAccount action calls DELETE /user/profile which has no backend route — account deletion is broken]] — `Authentication`
|
||||
- [[ISSUE-008-sendfilemessage-posts-to-wrong-endpoint-file-uploads-silentl|sendFileMessage posts to wrong endpoint — file uploads silently fail or corrupt text-message handler]] — `Chat`
|
||||
- [[ISSUE-009-archiveconversation-sends-put-but-backend-only-accepts-patch|archiveConversation sends PUT but backend only accepts PATCH — all archive attempts fail]] — `Chat`
|
||||
- [[ISSUE-010-frontend-admin-updateuserstatus-and-updateuserrole-use-put-b|Frontend admin updateUserStatus and updateUserRole use PUT but backend only accepts PATCH]] — `User Management`
|
||||
- [[ISSUE-011-frontend-updateuserstatus-sends-inactive-pending-status-valu|Frontend updateUserStatus sends 'inactive'/'pending' status values that backend does not accept]] — `User Management`
|
||||
- [[ISSUE-013-createproviderpaymentintent-always-routes-to-request-network|createProviderPaymentIntent always routes to request-network/intents regardless of provider argument]] — `Payment`
|
||||
- [[ISSUE-014-paymentprovider-typescript-type-excludes-shkeeper-and-decent|PaymentProvider TypeScript type excludes 'shkeeper' and 'decentralized' causing UI fallthrough for main payment providers]] — `Payment`
|
||||
- [[ISSUE-015-simulated-transaction-sim-bypass-has-no-environment-guard-ca|Simulated transaction SIM_ bypass has no environment guard — can fire in production on wallet connection failure]] — `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`
|
||||
- [[ISSUE-016-updatepurchaserequest-uses-put-but-backend-only-registers-pa|updatePurchaseRequest uses PUT but backend only registers PATCH — all purchase request edits fail]] — `Purchase Request`
|
||||
- [[ISSUE-017-updateoffer-uses-put-marketplace-offers-id-but-backend-regis|updateOffer uses PUT /marketplace/offers/:id but backend registers PATCH /offers/:id — offer edits fail]] — `Seller Offer`
|
||||
- [[ISSUE-018-select-offer-updatemany-has-no-status-filter-overwrites-with|select-offer updateMany has no status filter — overwrites withdrawn/rejected offers back to 'rejected' corrupting status history]] — `Seller Offer`
|
||||
- [[ISSUE-019-selleroffer-status-active-does-not-exist-in-schema-enum-but-|SellerOffer.status 'active' does not exist in schema enum but is referenced in docs and code comments]] — `Seller Offer`
|
||||
- [[ISSUE-020-select-offer-does-not-send-per-seller-socket-events-or-notif|select-offer does not send per-seller socket events or notifications to winning or losing sellers]] — `Seller Offer`
|
||||
- [[ISSUE-021-post-api-marketplace-offers-id-withdraw-http-route-does-not-|POST /api/marketplace/offers/:id/withdraw HTTP route does not exist — seller withdraw is dead code]] — `Seller Offer`
|
||||
- [[ISSUE-022-get-api-payment-payments-id-debug-has-no-authentication-full|GET /api/payment/payments/:id/debug has no authentication — full payment data exposed without credentials]] — `Payment`
|
||||
- [[ISSUE-023-get-api-payment-export-has-no-admin-role-guard-at-route-leve|GET /api/payment/export has no admin role guard at route level — any authenticated user can export all payment data]] — `Payment`
|
||||
- [[ISSUE-024-get-api-payment-stats-has-no-admin-role-guard-any-authentica|GET /api/payment/stats has no admin role guard — any authenticated user can read aggregate payment stats]] — `Payment`
|
||||
- [[ISSUE-025-get-api-disputes-statistics-has-no-admin-role-guard-any-auth|GET /api/disputes/statistics has no admin role guard — any authenticated user can access aggregate dispute KPIs]] — `Dispute`
|
||||
- [[ISSUE-026-get-notifications-id-only-returns-user-s-most-recent-notific|GET /notifications/:id only returns user's most-recent notification — all others return 404 erroneously]] — `Notification`
|
||||
- [[ISSUE-027-confirm-delivery-endpoint-has-no-ownership-check-any-authent|confirm-delivery endpoint has no ownership check — any authenticated user can confirm delivery on any request]] — `Delivery`
|
||||
- [[ISSUE-028-delivery-code-generated-socket-event-broadcasts-raw-6-digit-|delivery-code-generated socket event broadcasts raw 6-digit code to entire request room including seller]] — `Delivery`
|
||||
- [[ISSUE-029-no-brute-force-protection-on-delivery-code-verification-endp|No brute-force protection on delivery code verification endpoint — 900,000 combinations are enumerable]] — `Delivery`
|
||||
- [[ISSUE-030-post-api-payment-payments-cleanup-pending-admin-check-is-ins|POST /api/payment/payments/cleanup-pending admin check is inside handler only — no middleware-level enforcement]] — `Admin`
|
||||
- [[ISSUE-031-post-api-points-admin-add-admin-check-is-inside-handler-only|POST /api/points/admin/add admin check is inside handler only — no middleware-level enforcement]] — `Admin`
|
||||
- [[ISSUE-032-admin-delete-user-via-legacy-endpoint-performs-hard-delete-f|Admin delete user via legacy endpoint performs hard delete (findByIdAndDelete) instead of soft delete]] — `User Management`
|
||||
- [[ISSUE-033-admin-can-delete-other-admin-accounts-via-new-controller-leg|Admin can delete other admin accounts via new controller — legacy admin-on-admin protection does not apply]] — `User Management`
|
||||
- [[ISSUE-034-all-dispute-socket-io-emit-blocks-are-todo-stubs-no-real-tim|All dispute socket.io emit blocks are TODO stubs — no real-time updates fire for any dispute event]] — `Dispute`
|
||||
- [[ISSUE-035-frontend-getpaymentstatus-and-confirmpayment-call-non-existe|Frontend getPaymentStatus and confirmPayment call non-existent endpoints GET /payment/:id/status and POST /payment/:id/confirm]] — `Payment`
|
||||
- [[ISSUE-036-cancelpayment-action-sends-delete-payment-id-but-no-delete-r|cancelPayment action sends DELETE /payment/:id but no DELETE route exists on any payment endpoint]] — `Payment`
|
||||
- [[ISSUE-037-frontend-initiaterequestnetworkpayout-confirmrequestnetworkp|Frontend initiateRequestNetworkPayout, confirmRequestNetworkPayout, confirmRequestNetworkRelease, confirmRequestNetworkRefund call non-existent backend routes]] — `Payment`
|
||||
- [[ISSUE-038-multiple-frontend-payment-stub-actions-call-non-existent-bac|Multiple frontend payment stub actions call non-existent backend endpoints: /payment/history, /payment/methods, /payment/validate, /payment/transactions, /payment/escrow/balance]] — `Payment`
|
||||
- [[ISSUE-039-reset-password-with-code-endpoint-has-no-password-complexity|reset-password-with-code endpoint has no password complexity validation — accepts weak passwords rejected by token-based reset]] — `Authentication`
|
||||
- [[ISSUE-040-changepassword-action-has-no-ui-component-change-password-fe|changePassword action has no UI component — change password feature is untestable from the UI]] — `Authentication`
|
||||
- [[ISSUE-041-frontend-searchpurchaserequests-calls-marketplace-purchase-r|Frontend searchPurchaseRequests calls /marketplace/purchase-requests/search which does not exist in backend]] — `Purchase Request`
|
||||
- [[ISSUE-042-frontend-getmarketplacestats-calls-marketplace-purchase-requ|Frontend getMarketplaceStats calls /marketplace/purchase-requests/stats which has no backend handler]] — `Purchase Request`
|
||||
- [[ISSUE-043-frontend-getdeliveryattempts-and-getdeliverystats-call-non-e|Frontend getDeliveryAttempts and getDeliveryStats call non-existent backend endpoints]] — `Delivery`
|
||||
- [[ISSUE-044-post-api-marketplace-purchase-requests-id-final-approval-cre|POST /api/marketplace/purchase-requests/:id/final-approval creates dummy payment for testing if no real payment exists — testing backdoor in production code]] — `Purchase Request`
|
||||
- [[ISSUE-045-addparticipants-frontend-sends-participants-string-array-but|addParticipants frontend sends { participants: string[] } array but backend expects { userId: string } single user]] — `Chat`
|
||||
- [[ISSUE-046-frontend-getsellerofferhistory-seller-offer-history-page-doe|Frontend getSellerOfferHistory / seller offer history page does not exist — notification links to /dashboard/seller/marketplace/offers are broken]] — `Seller Offer`
|
||||
- [[ISSUE-047-frontend-cron-management-and-per-id-token-sweep-endpoints-fo|Frontend cron management and per-id token sweep endpoints for derived-destinations are not in backend inventory]] — `Admin`
|
||||
- [[ISSUE-048-frontend-reloadnetworkregistry-and-probechain-call-backend-e|Frontend reloadNetworkRegistry and probeChain call backend endpoints that do not exist]] — `Admin`
|
||||
- [[ISSUE-049-frontend-getconfirmationthresholdhistory-calls-get-api-admin|Frontend getConfirmationThresholdHistory calls GET /api/admin/settings/confirmation-thresholds/history which does not exist in backend]] — `Admin`
|
||||
- [[ISSUE-050-points-referral-five-frontend-pages-do-not-exist-redemption-|Points/Referral: five frontend pages do not exist — redemption, levels, referrals, transactions, admin-add all untestable via UI]] — `Points`
|
||||
- [[ISSUE-051-self-referral-prevention-is-absent-users-can-refer-themselve|Self-referral prevention is absent — users can refer themselves for points]] — `Points`
|
||||
- [[ISSUE-052-payment-completed-status-not-counted-in-successful-payments-stats|'completed' payment status not counted in successfulPayments stats — admin dashboard undercounts]] — `Payment`
|
||||
- [[ISSUE-053-axios-interceptor-only-handles-401-not-403-for-token-refresh|Axios interceptor only retriggers token refresh for 401, not 403]] — `Authentication`
|
||||
- [[ISSUE-054-login-rate-limiter-counts-all-attempts-not-only-failures|Login rate limiter counts all attempts (not just failures) — users locked out after correct logins]] — `Authentication`
|
||||
|
||||
## Security Issues Summary
|
||||
## ⚪ Invalid / Superseded (audit was stale vs current code)
|
||||
|
||||
- [[ISSUE-012-trezor-safekeeping-zero-frontend-implementation-all-admin-re|Trezor Safekeeping "zero frontend" — INVALID: the frontend Trezor implementation exists in current code (TrezorSettingsView, trezorConnector, TrezorSignDialog, actions/trezor.ts). Audit findings C31/C32 were from an older snapshot.]] — `Trezor`
|
||||
|
||||
## 🟡 Minor
|
||||
|
||||
| # | 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