audit: 2026-05-30 full-codebase audit — report, issues, docs, runbooks
Full-codebase-audit 2026-05-30 outputs: - Audit report: 09 - Audits/Full Codebase Audit - 2026-05-30.md - 81 issue files ISSUE-055..135 (decisions + 1 skipped no-brainer). - Scanner docs from scratch (was zero): architecture, data model, API ref, payment flow, operations runbook + repo README. - Doc-sync updates across API reference, data models, flows, design system. - Secret Rotation Runbook (08 - Operations) for the exposed credentials. - Reusable workflow guide (07 - Development) + .claude/workflows/full-codebase-audit.js. Issues remain status:open intentionally — the code fixes are uncommitted-then-committed working-tree changes per repo and aren't "resolved" until merged/deployed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,9 @@ issue: 007
|
||||
title: "Frontend deleteAccount action calls DELETE /user/profile which has no backend route — account deletion is broken"
|
||||
severity: critical
|
||||
domain: Authentication
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Updated deleteAccount in account.ts to call DELETE /auth/account (endpoints.auth.deleteAccount) — the route that exists in authRoutes.ts. Added deleteAccount entry to endpoints.auth in axios.ts."
|
||||
labels: [bug, frontend, critical, broken-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 008
|
||||
title: "sendFileMessage posts to wrong endpoint — file uploads silently fail or corrupt text-message handler"
|
||||
severity: critical
|
||||
domain: Chat
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Added sendFileMessage: '/chat/:id/messages/file' to endpoints.chat in axios.ts. Updated sendFileMessage in chat.ts to use endpoints.chat.sendFileMessage instead of sendMessage."
|
||||
labels: [bug, frontend, critical, broken-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 009
|
||||
title: "archiveConversation sends PUT but backend only accepts PATCH — all archive attempts fail"
|
||||
severity: critical
|
||||
domain: Chat
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Changed archiveConversation in chat.ts from axiosInstance.put to axiosInstance.patch — matches backend PATCH /:id/archive route."
|
||||
labels: [bug, frontend, critical, broken-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 010
|
||||
title: "Frontend admin updateUserStatus and updateUserRole use PUT but backend only accepts PATCH"
|
||||
severity: critical
|
||||
domain: User Management
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Changed updateUserStatus and updateUserRole in user.ts from axiosInstance.put to axiosInstance.patch — matches backend PATCH /admin/:userId/status and PATCH /admin/:userId/role routes."
|
||||
labels: [bug, frontend, critical, admin, broken-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 011
|
||||
title: "Frontend updateUserStatus sends 'inactive'/'pending' status values that backend does not accept"
|
||||
severity: critical
|
||||
domain: User Management
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Updated updateUserStatus type signature in user.ts from 'active' | 'inactive' | 'pending' to 'active' | 'suspended' | 'deleted' — matching backend's ['active', 'suspended', 'deleted'] validation."
|
||||
labels: [bug, frontend, critical, admin, type-mismatch]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 013
|
||||
title: "createProviderPaymentIntent always routes to request-network/intents regardless of provider argument"
|
||||
severity: critical
|
||||
domain: Payment
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Replaced stub getProviderIntentEndpoint in payment.ts with a switch on provider: shkeeper → shkeeper.intents, decentralized → decentralized.save, default → requestNetwork.intents."
|
||||
labels: [bug, frontend, critical, payment, routing]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 014
|
||||
title: "PaymentProvider TypeScript type excludes 'shkeeper' and 'decentralized' causing UI fallthrough for main payment providers"
|
||||
severity: critical
|
||||
domain: Payment
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Added 'shkeeper' and 'decentralized' to PaymentProvider union type in types/payment.ts."
|
||||
labels: [bug, frontend, critical, payment, type-mismatch]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 015
|
||||
title: "Simulated transaction SIM_ bypass has no environment guard — can fire in production on wallet connection failure"
|
||||
severity: critical
|
||||
domain: Payment
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Backend: wrapped SIM_ bypass in both paymentRoutes.ts and marketplace/routes.ts with process.env.NODE_ENV !== 'production' guard. Frontend: web3-provider.tsx and web3-payment.tsx now throw in production instead of silently returning a fake SIM_ hash."
|
||||
labels: [security, bug, critical, payment, frontend, bypass]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -4,6 +4,9 @@ title: "updatePurchaseRequest uses PUT but backend only registers PATCH — all
|
||||
severity: major
|
||||
domain: Purchase Request
|
||||
labels: [bug, frontend, major, broken-feature]
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Changed axiosInstance.put to axiosInstance.patch in updatePurchaseRequest in marketplace.ts — matches backend PATCH /purchase-requests/:id."
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 017
|
||||
title: "updateOffer uses PUT /marketplace/offers/:id but backend registers PATCH /offers/:id — offer edits fail"
|
||||
severity: major
|
||||
domain: Seller Offer
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Changed axiosInstance.put to axiosInstance.patch in updateOffer in marketplace.ts — matches backend PATCH /offers/:id."
|
||||
labels: [bug, frontend, major, broken-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -4,6 +4,9 @@ title: "select-offer updateMany has no status filter — overwrites withdrawn/re
|
||||
severity: major
|
||||
domain: Seller Offer
|
||||
labels: [bug, backend, major, data-integrity]
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Added status: { $nin: ['withdrawn', 'rejected'] } to the updateMany filter in select-offer handler — preserves existing terminal statuses."
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
|
||||
@@ -4,6 +4,9 @@ title: "POST /api/marketplace/offers/:id/withdraw HTTP route does not exist —
|
||||
severity: major
|
||||
domain: Seller Offer
|
||||
labels: [missing-feature, backend, frontend, major]
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Added POST /offers/:id/withdraw route in marketplace/routes.ts. Calls sellerOfferService.withdrawOffer with ownership check (seller or admin only)."
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
|
||||
@@ -4,7 +4,9 @@ title: "GET /api/payment/payments/:id/debug has no authentication — full payme
|
||||
severity: major
|
||||
domain: Payment
|
||||
labels: [security, bug, backend, major, missing-auth]
|
||||
status: open
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Already guarded by authenticateToken + authorizeRoles('admin') at paymentRoutes.ts line 285 (applied as part of ISSUE-005 fix). Confirmed in current code."
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
@@ -4,6 +4,9 @@ title: "GET /api/payment/export has no admin role guard at route level — any a
|
||||
severity: major
|
||||
domain: Payment
|
||||
labels: [security, bug, backend, major, privilege-escalation]
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Already guarded by authenticateToken + authorizeRoles('admin') at paymentRoutes.ts line 79. Confirmed in current code."
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 024
|
||||
title: "GET /api/payment/stats has no admin role guard — any authenticated user can read aggregate payment stats"
|
||||
severity: major
|
||||
domain: Payment
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Already guarded by authenticateToken + authorizeRoles('admin') at paymentRoutes.ts line 56. Confirmed in current code."
|
||||
labels: [security, bug, backend, major, privilege-escalation]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 025
|
||||
title: "GET /api/disputes/statistics has no admin role guard — any authenticated user can access aggregate dispute KPIs"
|
||||
severity: major
|
||||
domain: Dispute
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Added authorizeRoles('admin', 'resolver') to GET /statistics in disputeRoutes.ts."
|
||||
labels: [security, bug, backend, major, privilege-escalation]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 026
|
||||
title: "GET /notifications/:id only returns user's most-recent notification — all others return 404 erroneously"
|
||||
severity: major
|
||||
domain: Notification
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Fixed getNotificationById in notificationController.ts to use Notification.findOne({ _id: id, userId }) instead of fetching page 1 of user notifications."
|
||||
labels: [bug, backend, major, broken-feature]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -4,6 +4,9 @@ title: "confirm-delivery endpoint has no ownership check — any authenticated u
|
||||
severity: major
|
||||
domain: Delivery
|
||||
labels: [security, bug, backend, major, authorization]
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Added buyer ownership check in marketplaceController.confirmDelivery — rejects with 403 if caller is not the request's buyerId and not admin."
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
|
||||
@@ -4,6 +4,9 @@ title: "delivery-code-generated socket event broadcasts raw 6-digit code to enti
|
||||
severity: major
|
||||
domain: Delivery
|
||||
labels: [security, bug, backend, major, delivery]
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Removed 'code' field from the delivery-code-generated socket payload in deliveryService.ts — only metadata (requestId, expiresAt, timestamp) is now broadcast to the room."
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
|
||||
@@ -4,6 +4,9 @@ title: "No brute-force protection on delivery code verification endpoint — 900
|
||||
severity: major
|
||||
domain: Delivery
|
||||
labels: [security, bug, backend, major, brute-force]
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Added deliveryCodeVerifyLimiter (express-rate-limit: 10 attempts per 15 min per requestId+userId) to POST /delivery-code/verify in marketplace/routes.ts."
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 030
|
||||
title: "POST /api/payment/payments/cleanup-pending admin check is inside handler only — no middleware-level enforcement"
|
||||
severity: major
|
||||
domain: Admin
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Moved admin check to middleware: added authorizeRoles('admin') to cleanup-pending route in paymentRoutes.ts and removed inline role check."
|
||||
labels: [security, bug, backend, major, missing-auth]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 031
|
||||
title: "POST /api/points/admin/add admin check is inside handler only — no middleware-level enforcement"
|
||||
severity: major
|
||||
domain: Admin
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Replaced inline admin check with authorizeRoles('admin') middleware on POST /admin/add in pointsRoutes.ts."
|
||||
labels: [security, bug, backend, major, missing-auth]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 032
|
||||
title: "Admin delete user via legacy endpoint performs hard delete (findByIdAndDelete) instead of soft delete"
|
||||
severity: major
|
||||
domain: User Management
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Changed findByIdAndDelete to findByIdAndUpdate({ status: 'deleted' }) in legacy admin delete route in userRoutes.ts."
|
||||
labels: [bug, frontend, backend, major, data-integrity]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -4,6 +4,9 @@ title: "Admin can delete other admin accounts via new controller — legacy admi
|
||||
severity: major
|
||||
domain: User Management
|
||||
labels: [security, bug, backend, major, privilege-escalation]
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Added pre-flight check in userController.deleteUser — looks up target user and returns 403 CANNOT_DELETE_ADMIN if role is 'admin'."
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 035
|
||||
title: "Frontend getPaymentStatus and confirmPayment call non-existent endpoints GET /payment/:id/status and POST /payment/:id/confirm"
|
||||
severity: major
|
||||
domain: Payment
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Added GET /payments/:id/status and POST /payments/:id/confirm routes to paymentRoutes.ts. Updated getPaymentStatus and confirmPayment in payment.ts to use /payment/payments/:id/status and /payment/payments/:id/confirm."
|
||||
labels: [bug, frontend, major, broken-feature, dispute]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ 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
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Added complexity validation in authController.resetPasswordWithCode: min 8 chars, requires uppercase, lowercase, and digit."
|
||||
labels: [security, bug, backend, major, auth]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -4,6 +4,9 @@ title: "POST /api/marketplace/purchase-requests/:id/final-approval creates dummy
|
||||
severity: major
|
||||
domain: Purchase Request
|
||||
labels: [security, bug, backend, major, escrow, bypass]
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Wrapped dummy payment creation in process.env.NODE_ENV !== 'production' guard in marketplace/routes.ts — in production the route returns 404 when no real payment exists."
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 045
|
||||
title: "addParticipants frontend sends { participants: string[] } array but backend expects { userId: string } single user"
|
||||
severity: major
|
||||
domain: Chat
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Fixed addParticipants in chat.ts to send { participantIds: participants } instead of { participants } — matches backend's expected body shape."
|
||||
labels: [bug, frontend, major, chat]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -4,8 +4,10 @@ title: "Frontend reloadNetworkRegistry and probeChain call backend endpoints tha
|
||||
severity: major
|
||||
domain: Admin
|
||||
labels: [missing-feature, backend, major, admin]
|
||||
status: open
|
||||
status: resolved
|
||||
created: 2026-05-29
|
||||
resolved: 2026-05-30
|
||||
fix: "POST /api/admin/rn/networks/reload and POST /api/admin/rn/networks/probe/:chainId implemented in commit 5681abf (task #8)"
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@ title: "Frontend getConfirmationThresholdHistory calls GET /api/admin/settings/c
|
||||
severity: major
|
||||
domain: Admin
|
||||
labels: [missing-feature, backend, major, admin]
|
||||
status: open
|
||||
status: resolved
|
||||
created: 2026-05-29
|
||||
resolved: 2026-05-30
|
||||
fix: "GET /api/admin/settings/confirmation-thresholds/history implemented in commit 27fb15a (task #9) using ConfigSettingHistory model"
|
||||
source: Doc vs Code Audit 2026-05-29
|
||||
---
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 052
|
||||
title: "'completed' payment status not counted in successfulPayments stats — admin dashboard undercounts"
|
||||
severity: major
|
||||
domain: Payment
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Added case 'completed' to the successfulPayments switch in paymentService.ts getPaymentStats."
|
||||
labels: [backend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 053
|
||||
title: "Axios interceptor only retriggers token refresh for 401, not 403"
|
||||
severity: major
|
||||
domain: Authentication
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Extended axios response interceptor condition from status === 401 to (status === 401 || status === 403) in axios.ts."
|
||||
labels: [frontend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -3,6 +3,9 @@ issue: 054
|
||||
title: "Login rate limiter counts all attempts (not just failures) — users locked out after correct logins"
|
||||
severity: major
|
||||
domain: Authentication
|
||||
status: resolved
|
||||
resolved: 2026-05-29
|
||||
fix: "Split checkLoginAttempts into read-only check and new incrementFailedLoginAttempt. authController now only calls increment on failed login paths, not on all attempts."
|
||||
labels: [backend, bug]
|
||||
status: open
|
||||
created: 2026-05-29
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 055
|
||||
title: "DELETE /api/files/delete has no ownership check — requires new persistence layer (NB-27 skipped)"
|
||||
severity: high
|
||||
domain: File Management
|
||||
labels: [security, backend, idor, skipped-nobrainer]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# DELETE /api/files/delete has no ownership check — requires new persistence layer (NB-27 skipped)
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** File Management
|
||||
**Labels:** security, backend, idor, skipped-nobrainer
|
||||
|
||||
## Description
|
||||
|
||||
`fileService.deleteFile()` is a pure filesystem path operation — there is no `File` model and no `createdBy`/`owner` field stored anywhere in the database. Any authenticated user who knows (or guesses) another user's filename can delete that file via `DELETE /api/files/delete?filename=...`.
|
||||
|
||||
This was triaged as NB-27 but skipped because adding an ownership check requires first creating a new File persistence layer (model + write-on-upload path), which is a larger-than-mechanical change that risks introducing new bugs.
|
||||
|
||||
## What is Needed
|
||||
|
||||
1. Create a `File` model (or add an `uploads` sub-document to the User model) that records `{ filename, uploadedBy: ObjectId, createdAt }` when a file is stored.
|
||||
2. Add a middleware or controller check in `fileController.deleteFile` that looks up the record and requires `req.user.id === file.uploadedBy` (or admin).
|
||||
3. Back-fill the upload handler to write the record on every `POST /api/files/upload`.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/file/fileController.ts` — add ownership check
|
||||
- `backend/src/services/file/fileRoutes.ts` — (route already protected by `authenticateToken`)
|
||||
- New: `backend/src/models/File.ts` (or equivalent) — persistence layer
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md)
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
issue: 056
|
||||
title: "Backend: verifyPayment and paymentCallback routes unauthenticated — payment completion exploitable"
|
||||
severity: critical
|
||||
domain: Payment
|
||||
labels: [security, backend, authentication, webhook]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: verifyPayment and paymentCallback routes unauthenticated — payment completion exploitable
|
||||
|
||||
**Severity:** critical
|
||||
**Domain:** Payment
|
||||
**Labels:** security, backend, authentication, webhook
|
||||
|
||||
## Description
|
||||
|
||||
`POST /payments/verify` and `POST /payments/callback` are registered without `authenticateToken` middleware. Additionally, a non-web3 bypass path (`isWeb3Payment === false`) allows marking a payment completed without any verifiable on-chain or provider proof.
|
||||
|
||||
An unauthenticated actor can call `/payments/verify` for any payment ID and trigger the completion side-effects (status change, offer acceptance, escrow release) without owning that payment. The callback endpoint is similarly unguarded, allowing fake webhook injection.
|
||||
|
||||
## Options
|
||||
|
||||
1. Require `authenticateToken` + ownership check on `/verify`; enforce HMAC signature verification on `/callback` as a provider webhook; remove the `isWeb3Payment=false` bypass so completion always requires verifiable proof.
|
||||
2. Treat `/callback` as a provider webhook with HMAC only; add auth+ownership for `/verify`.
|
||||
3. Remove the non-web3 bypass so payments without a verifiable tx cannot be marked completed.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Add `authenticateToken` + ownership to `/verify`, enforce HMAC/on-chain verification on `/callback` as a webhook endpoint, and remove the `isWeb3Payment=false` bypass so completion always requires verifiable proof.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/payment/paymentControllerRoutes.ts` — lines 20–21
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-23
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 057
|
||||
title: "Frontend admin UI routes lack role-based authorization guard"
|
||||
severity: high
|
||||
domain: Admin
|
||||
labels: [security, frontend, authorization]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend admin UI routes lack role-based authorization guard
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Admin
|
||||
**Labels:** security, frontend, authorization
|
||||
|
||||
## Description
|
||||
|
||||
The `/dashboard/admin/*` route tree has no `RoleBasedGuard` at the layout level. Any authenticated user who knows the URL can access and interact with admin pages (trezor, payments-awaiting-confirmation, etc.) without any frontend role enforcement.
|
||||
|
||||
## Options
|
||||
|
||||
1. Wrap the admin route segment in a single `RoleBasedGuard(admin)` at the layout level — minimal surface, one place to maintain.
|
||||
2. Add `useRole` checks inside each section view — more granular but repetitive and error-prone.
|
||||
3. Server-side redirect in Next.js middleware for `/dashboard/admin/*` based on a decoded role claim — strongest but needs role in token/cookie.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Add a `RoleBasedGuard(admin)` at the admin route-group layout (single chokepoint), and confirm the backend independently enforces admin on every admin API. Defense in depth, low blast radius.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/app/dashboard/admin/trezor/page.tsx` and all sibling admin pages
|
||||
- Admin route-group layout file
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-1
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 058
|
||||
title: "Frontend test payment mode enablable in production via NEXT_PUBLIC env var"
|
||||
severity: high
|
||||
domain: Payment
|
||||
labels: [security, frontend, test-bypass]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend test payment mode enablable in production via NEXT_PUBLIC env var
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Payment
|
||||
**Labels:** security, frontend, test-bypass
|
||||
|
||||
## Description
|
||||
|
||||
`isTestPaymentEnabled()` in `src/web3/services/test-payment-service.ts` is gated only on `NEXT_PUBLIC_ENABLE_TEST_PAYMENT` env flag. Setting this flag in a production deployment (intentionally or by misconfiguration) activates test-payment mode, which bypasses real payment flows.
|
||||
|
||||
## Options
|
||||
|
||||
1. Gate `isTestPaymentEnabled()` on `(process.env.NODE_ENV !== 'production') AND` the env flag — code-level hard stop.
|
||||
2. Strip the test-payment code path entirely from production via a build-time define/dead-code elimination.
|
||||
3. Both: NODE_ENV guard plus CI assertion that the flag is unset in prod env.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Require `NODE_ENV !== 'production'` in addition to the flag, and add a CI check that `NEXT_PUBLIC_ENABLE_TEST_PAYMENT` is absent in production secrets.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/web3/services/test-payment-service.ts:131`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-3
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 059
|
||||
title: "Frontend auth provider clears tokens on any non-403 error including network failures"
|
||||
severity: high
|
||||
domain: Authentication
|
||||
labels: [bug, frontend, session]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend auth provider clears tokens on any non-403 error including network failures
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Authentication
|
||||
**Labels:** bug, frontend, session
|
||||
|
||||
## Description
|
||||
|
||||
`src/auth/context/jwt/auth-provider.tsx:85` clears tokens and logs out the user on any error from the session-check call, including transient network failures and 5xx server errors. A momentary connectivity issue or backend restart silently logs out all active users.
|
||||
|
||||
## Options
|
||||
|
||||
1. Clear only on 401/403; treat 5xx and network errors as transient (keep tokens, retry).
|
||||
2. Clear on 401/403 plus explicit invalid-token responses; keep tokens for everything else.
|
||||
3. Add retry/backoff before deciding to clear.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Clear tokens only on 401/403; on network/5xx errors keep the session and retry. Confirm acceptable retry behavior and UX with owner.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/auth/context/jwt/auth-provider.tsx:85`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-12
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 060
|
||||
title: "Frontend contacts-popover reads userId from non-existent localStorage 'user' key"
|
||||
severity: high
|
||||
domain: Chat
|
||||
labels: [bug, frontend]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend contacts-popover reads userId from non-existent localStorage 'user' key
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Chat
|
||||
**Labels:** bug, frontend
|
||||
|
||||
## Description
|
||||
|
||||
`src/layouts/components/contacts-popover.tsx:61` reads `currentUserId` from `localStorage.getItem('user')`, but no part of the auth flow writes a `'user'` key to localStorage. The result is always `null`, breaking any per-user contact filtering in the popover.
|
||||
|
||||
## Options
|
||||
|
||||
1. Use the auth context (`useAuthContext`) to get the real user id.
|
||||
2. Decode the user id from the access token claims.
|
||||
3. Add a real `'user'` object to storage on login and read it here.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Pull `currentUserId` from the live auth context rather than a non-existent storage key. Requires confirming the canonical user-id field name in the auth context.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/layouts/components/contacts-popover.tsx:61`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-13
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 061
|
||||
title: "Frontend socket context helpers accumulate listeners without dedup — memory/event leaks"
|
||||
severity: high
|
||||
domain: Realtime
|
||||
labels: [bug, frontend, memory-leak]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend socket context helpers accumulate listeners without dedup — memory/event leaks
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Realtime
|
||||
**Labels:** bug, frontend, memory-leak
|
||||
|
||||
## Description
|
||||
|
||||
Socket subscription helpers in `src/socket/contexts/socket-context.tsx:244` do not return unsubscribe functions and do not call `socket.off` when consumers unmount. Each React effect re-registration adds a new listener without removing the old one, causing duplicate event callbacks and memory leaks.
|
||||
|
||||
## Options
|
||||
|
||||
1. Have each `on*` helper return an unsubscribe function and require consumers to call it in effect cleanup.
|
||||
2. Make helpers stable (`useCallback`) and internally `off()` the previous handler before `on()`.
|
||||
3. Centralize event handling in the provider and expose state, not raw subscriptions.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Return an unsubscribe from each helper and call `socket.off` in cleanup; also memoize the helpers. This touches many consumers — coordinate as a single refactor pass.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/socket/contexts/socket-context.tsx:244`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-19
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 062
|
||||
title: "Backend: payment update routes lack ownership/role guards (cluster)"
|
||||
severity: high
|
||||
domain: Payment
|
||||
labels: [security, backend, authorization, idor]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: payment update routes lack ownership/role guards (cluster)
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Payment
|
||||
**Labels:** security, backend, authorization, idor
|
||||
|
||||
## Description
|
||||
|
||||
`PUT /:id updatePayment`, `PATCH /marketplace/payments/:id`, and status-change routes in `paymentControllerRoutes.ts` require only `authenticateToken` — no role check, no ownership check, no status-transition whitelist. Any authenticated user can change any payment's status to any value.
|
||||
|
||||
## Options
|
||||
|
||||
1. Add an admin-role middleware to all payment status-mutating routes and a status whitelist.
|
||||
2. Add ownership checks (`req.user.id === buyerId/sellerId`) plus a strict allowed-status-transition validator shared across routes.
|
||||
3. Both: admin-only for arbitrary status writes; constrained self-service transitions for owners.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Introduce a shared `requireAdmin` middleware for arbitrary status writes and a centralized transition validator; owners may only trigger whitelisted transitions. This is a business-logic and authZ change across multiple routes.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/payment/paymentControllerRoutes.ts:17`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-22
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 063
|
||||
title: "Backend: legacy marketplace PATCH /payments/:id lets buyer/seller set any status"
|
||||
severity: high
|
||||
domain: Payment
|
||||
labels: [security, backend, authorization]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: legacy marketplace PATCH /payments/:id lets buyer/seller set any status
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Payment
|
||||
**Labels:** security, backend, authorization
|
||||
|
||||
## Description
|
||||
|
||||
`backend/src/services/marketplace/routes.ts:237` registers a legacy PATCH endpoint for payment status that has no admin guard and no status whitelist. Buyers or sellers can set any status value directly.
|
||||
|
||||
## Options
|
||||
|
||||
1. Add admin role guard + status whitelist.
|
||||
2. Deprecate/remove the legacy route if superseded by the new payment controller.
|
||||
3. Restrict to system/internal callers only.
|
||||
|
||||
## Recommendation
|
||||
|
||||
If the route is legacy and superseded by the new payment controller, remove it. Otherwise gate with admin + whitelist. Needs confirmation that it is unused before removal.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/marketplace/routes.ts:237`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-24
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 064
|
||||
title: "Backend: REQUEST_NETWORK_ALLOW_TEST_WEBHOOKS bypasses signature verification"
|
||||
severity: high
|
||||
domain: Payment
|
||||
labels: [security, backend, webhook, test-bypass]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: REQUEST_NETWORK_ALLOW_TEST_WEBHOOKS bypasses signature verification
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Payment
|
||||
**Labels:** security, backend, webhook, test-bypass
|
||||
|
||||
## Description
|
||||
|
||||
`REQUEST_NETWORK_ALLOW_TEST_WEBHOOKS` env flag disables HMAC signature verification on Request Network webhooks at `requestNetworkRoutes.ts:104` and `requestNetworkAdapter.ts:77`. If this flag is set in production (or if `NODE_ENV` is not production), any unauthenticated actor can forge a webhook and trigger payment completion.
|
||||
|
||||
## Options
|
||||
|
||||
1. Gate the bypass on `NODE_ENV === 'test'` only and ignore the env flag in production.
|
||||
2. Require both `NODE_ENV !== 'production'` AND the flag.
|
||||
3. Remove the env-flag bypass entirely; use a dedicated test harness.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Allow the bypass only when `NODE_ENV === 'test'`; ignore `REQUEST_NETWORK_ALLOW_TEST_WEBHOOKS` in production. Apply the same fix in `requestNetworkAdapter.ts:77`.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/payment/requestNetwork/requestNetworkRoutes.ts:104`
|
||||
- `backend/src/services/payment/requestNetwork/requestNetworkAdapter.ts:77`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-25
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 065
|
||||
title: "Backend: RN webhook advances PurchaseRequest to non-existent 'funded' status"
|
||||
severity: high
|
||||
domain: Payment
|
||||
labels: [bug, backend, state-machine]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: RN webhook advances PurchaseRequest to non-existent 'funded' status
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Payment
|
||||
**Labels:** bug, backend, state-machine
|
||||
|
||||
## Description
|
||||
|
||||
`src/services/payment/request-network/requestNetworkWebhook.ts:123` sets `PurchaseRequest.status = 'funded'` when a payment is confirmed. `'funded'` does not exist in `STATUS_PROGRESSION_ORDER` or the status enum. The update is silently dropped by Mongoose, leaving the purchase request in its old status and breaking downstream state transitions.
|
||||
|
||||
## Options
|
||||
|
||||
1. Use the canonical `'processing'` status that exists in `STATUS_PROGRESSION_ORDER`.
|
||||
2. Add `'funded'` as a real status across progression and transition maps.
|
||||
3. Route through the same `propagatePaymentCompletion` path the new flow uses.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Replace `'funded'` with the canonical status used by the current completion flow (likely `'processing'`), keeping the `escrow.funded` flag if needed. This is a state-machine decision — verify the intended state after on-chain funding confirmation.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/payment/request-network/requestNetworkWebhook.ts:123`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-31
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 066
|
||||
title: "Backend: payout/confirm and release/confirm need canonical terminal status — DEC-32"
|
||||
severity: high
|
||||
domain: Payment
|
||||
labels: [bug, backend, state-machine]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: payout/confirm and release/confirm need canonical terminal status — DEC-32
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Payment
|
||||
**Labels:** bug, backend, state-machine
|
||||
|
||||
## Description
|
||||
|
||||
NB-29 was applied as an interim fix (setting `status:'completed'`), but the correct terminal status for a released/paid-out payment has not been decided. The enum currently has no `'released'` value. Until DEC-32 is resolved, the interim `'completed'` value may be incorrect for payments that need to be distinguished from non-released completions.
|
||||
|
||||
## Options
|
||||
|
||||
1. Add `'released'` to the Payment status enum and update all status-dependent logic (dashboards, filters, cleanup).
|
||||
2. Map release/payout to the existing `'completed'` status plus a separate `releasedAt`/payout flag.
|
||||
3. Introduce a dedicated escrow/payout sub-state field instead of overloading `status`.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Decide the canonical representation (likely add `'released'` to the enum or a dedicated payout state) and update dashboards/filters consistently. The NB-29 interim fix uses `'completed'` — verify this does not cause dashboard overcounting or misclassification.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/payment/requestNetwork/requestNetworkRoutes.ts:526`
|
||||
- `backend/src/models/Payment.ts` — enum definition
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-32
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 067
|
||||
title: "Backend: amount-mismatch check runs after payment saved and offers accepted"
|
||||
severity: medium
|
||||
domain: Payment
|
||||
labels: [bug, backend, logic]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: amount-mismatch check runs after payment saved and offers accepted
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Payment
|
||||
**Labels:** bug, backend, logic
|
||||
|
||||
## Description
|
||||
|
||||
In `paymentController.ts:886-889`, the check comparing `storedAmount` vs `amount` is executed after the payment has already been saved and offers accepted. If there is a mismatch, those side-effects cannot be rolled back, potentially leaving the system in an inconsistent state.
|
||||
|
||||
## Options
|
||||
|
||||
1. Move the `storedAmount` vs `amount` check before saving/advancing/accepting offers.
|
||||
2. Wrap the verify flow in a transaction and roll back on mismatch.
|
||||
3. Validate amount at intent-creation and re-check before completion.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Reorder so the amount-mismatch check (and ideally a transaction) gates all side-effects. This is a control-flow/business-logic change.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/payment/paymentController.ts:886-889`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-33
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
issue: 068
|
||||
title: "Backend: dataCleanupService deletes Payments without provider scoping — risk of destroying escrow records"
|
||||
severity: high
|
||||
domain: Admin
|
||||
labels: [security, backend, data-loss, escrow]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: dataCleanupService deletes Payments without provider scoping — risk of destroying escrow records
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Admin
|
||||
**Labels:** security, backend, data-loss, escrow
|
||||
|
||||
## Description
|
||||
|
||||
`dataCleanupService.ts:121` deletes Payment documents without filtering by `provider`. Request Network and SHKeeper escrow payments are webhook-driven and can take hours to confirm. Sweeping them deletes the ledger records that webhooks need to reconcile, silently destroying multi-seller cart records.
|
||||
|
||||
This matches the project memory note: "Any Payment-collection cleanup/orphan query MUST scope by `provider:`."
|
||||
|
||||
## Options
|
||||
|
||||
1. Scope all payment deletes by provider (exclude `request.network`/`shkeeper` escrow records).
|
||||
2. Soft-delete instead of hard delete for payments.
|
||||
3. Disallow payment-collection cleanup entirely from this tool.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Require provider scoping on every payment delete and prefer soft-delete; never sweep escrow-driven records. This is a data-loss risk.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/admin/dataCleanupService.ts:121`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-38
|
||||
- Project memory: `feedback_payment_cleanup_provider_filter.md`
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 069
|
||||
title: "Backend: cleanupOldPendingPayments deletes pending RN payments mid-flow"
|
||||
severity: high
|
||||
domain: Payment
|
||||
labels: [bug, backend, data-loss, escrow]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: cleanupOldPendingPayments deletes pending RN payments mid-flow
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Payment
|
||||
**Labels:** bug, backend, data-loss, escrow
|
||||
|
||||
## Description
|
||||
|
||||
`cleanupPendingPayments.ts:42` deletes pending payments after a TTL without excluding webhook-driven providers. Request Network flows can take hours or days to receive on-chain confirmation. A pending RN payment deleted by this cleanup will never be reconciled when the late webhook arrives.
|
||||
|
||||
## Options
|
||||
|
||||
1. Exclude provider `request.network`/`shkeeper` from the cleanup, or greatly extend the TTL for them.
|
||||
2. Mark as `expired` instead of deleting, so a late webhook can reconcile.
|
||||
3. Only delete pending payments that have no associated active purchase request.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Exclude webhook-driven providers (or use a long TTL) and prefer expire-over-delete so late webhooks can reconcile. This is a data-loss risk.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/payment/cleanupPendingPayments.ts:42`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-39
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 070
|
||||
title: "Backend: notifyAllSellersAboutNewRequest unbounded fan-out — N DB writes + N socket emits per new request"
|
||||
severity: high
|
||||
domain: Marketplace
|
||||
labels: [performance, backend, scalability]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: notifyAllSellersAboutNewRequest unbounded fan-out — N DB writes + N socket emits per new request
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Marketplace
|
||||
**Labels:** performance, backend, scalability
|
||||
|
||||
## Description
|
||||
|
||||
`PurchaseRequestService.ts:196` loops over every seller and issues one `Notification.insertOne` and one `socket.emit` per seller, wrapped in `setTimeout`. With hundreds of sellers this creates hundreds of sequential DB writes and socket emits, blocking the event loop and risking OOM.
|
||||
|
||||
## Options
|
||||
|
||||
1. Batch with `insertMany` for notifications and a single room/broadcast emit instead of per-seller `setTimeout`.
|
||||
2. Move to a queue/worker that processes seller notifications asynchronously with concurrency limits.
|
||||
3. Fan out via a topic/room subscription rather than per-seller writes.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Use `insertMany` + a single broadcast/room emit, or offload to a queue with bounded concurrency. This is an architectural change.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/marketplace/PurchaseRequestService.ts:196`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-40
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 071
|
||||
title: "Backend: getReferrals N+1 — PurchaseRequest + PointTransaction per referral"
|
||||
severity: high
|
||||
domain: Points
|
||||
labels: [performance, backend, n-plus-1]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: getReferrals N+1 — PurchaseRequest + PointTransaction per referral
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Points
|
||||
**Labels:** performance, backend, n-plus-1
|
||||
|
||||
## Description
|
||||
|
||||
`PointsService.ts:312` issues two sequential DB queries per referred user: one `PurchaseRequest.findOne` and one `PointTransaction.find`. With N referrals, this results in 2×N queries. Under load this will cause slow responses and high DB load.
|
||||
|
||||
## Options
|
||||
|
||||
1. Batch with `$in` queries and aggregate grouped by referred user.
|
||||
2. Precompute referral stats in a maintained summary doc.
|
||||
3. Add pagination plus batched lookups.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Replace per-referral queries with batched `$in` queries and an aggregation grouped by user; add pagination.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/points/PointsService.ts:312`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-41
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 072
|
||||
title: "Backend: chat messages stored as embedded array — unbounded document growth, 16MB ceiling"
|
||||
severity: high
|
||||
domain: Chat
|
||||
labels: [performance, backend, data-model]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: chat messages stored as embedded array — unbounded document growth, 16MB ceiling
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Chat
|
||||
**Labels:** performance, backend, data-model
|
||||
|
||||
## Description
|
||||
|
||||
`backend/src/models/Chat.ts:173` stores all messages as an embedded array in the Chat document. MongoDB's 16MB document size limit will be hit for active long-running chats. Reads also load the full message history into memory even when only the latest page is needed.
|
||||
|
||||
## Options
|
||||
|
||||
1. Migrate messages to a `Messages` collection keyed by `chatId` with pagination.
|
||||
2. Cap embedded messages and archive older ones.
|
||||
3. Keep embedded but project only needed messages (slice) on reads.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Plan migration to a dedicated `Messages` collection. This is a large data-model migration that needs careful coordination.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/models/Chat.ts:173`
|
||||
- `backend/src/services/chat/ChatService.ts` — all read paths
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-42
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 073
|
||||
title: "Backend: Payment provider enum missing 'shkeeper' — records silently dropped"
|
||||
severity: high
|
||||
domain: Payment
|
||||
labels: [bug, backend, data-model]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: Payment provider enum missing 'shkeeper' — records silently dropped
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Payment
|
||||
**Labels:** bug, backend, data-model
|
||||
|
||||
## Description
|
||||
|
||||
`backend/src/models/Payment.ts:46` defines the `provider` enum without including `'shkeeper'`. Any Payment document saved with `provider: 'shkeeper'` will fail Mongoose validation or be silently dropped. All downstream `Payment.find({provider: 'shkeeper'})` filters also return empty results.
|
||||
|
||||
## Options
|
||||
|
||||
1. Add `'shkeeper'` to the enum and audit all provider filters/migrations.
|
||||
2. Migrate existing shkeeper records and standardize the provider taxonomy.
|
||||
3. Add enum value plus a data migration to repair any silently-dropped values.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Add `'shkeeper'` to the enum AND run a data audit/migration to repair records and verify every `Payment.find({provider})` filter.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/models/Payment.ts:46`
|
||||
- All `Payment.find({ provider: ... })` call sites
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-30
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 074
|
||||
title: "Backend: Telegram bot token + SMTP key (and others) committed in .env.development"
|
||||
severity: high
|
||||
domain: Security
|
||||
labels: [security, backend, secrets, rotation-required]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: Telegram bot token + SMTP key (and others) committed in .env.development
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Security
|
||||
**Labels:** security, backend, secrets, rotation-required
|
||||
|
||||
## Description
|
||||
|
||||
`backend/.env.development` contains live production secrets including the Telegram bot token and Resend SMTP API key (and potentially others). NB-33 replaced the `.env.example` placeholders, but `.env.development` itself contains the live values and is tracked in git.
|
||||
|
||||
The `.dockerignore` whitelist (see ISSUE-075) also copies this file into production images.
|
||||
|
||||
## What Must Happen
|
||||
|
||||
1. Rotate the Telegram bot token immediately.
|
||||
2. Rotate the Resend SMTP API key immediately.
|
||||
3. Untrack `.env.development` from git and scrub it from history.
|
||||
4. Inject secrets at runtime via CI/vault rather than committed env files.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/.env.development:31` (and potentially other lines)
|
||||
- `backend/.dockerignore:14` (whitelist — see ISSUE-075)
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-56
|
||||
- [[ISSUE-075-backend-dockerignore-whitelists-env-development-into-prod-image|ISSUE-075]]
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
issue: 075
|
||||
title: "Backend: .dockerignore whitelists .env.development into production image"
|
||||
severity: high
|
||||
domain: Security
|
||||
labels: [security, backend, secrets, ci-cd]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: .dockerignore whitelists .env.development into production image
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Security
|
||||
**Labels:** security, backend, secrets, ci-cd
|
||||
|
||||
## Description
|
||||
|
||||
`backend/.dockerignore:14` contains `!.env.development`, which negates the `.env*` ignore rule and causes `.env.development` (with live secrets) to be copied into every production Docker image. Any container pull or image inspection exposes the credentials.
|
||||
|
||||
## Options
|
||||
|
||||
1. Remove the `!.env.development` whitelist so no env file is copied into images.
|
||||
2. Use a dedicated `.env.production` injected at runtime only.
|
||||
3. Both: strip env files from image and inject secrets via runtime env.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Remove the whitelist and never copy env files into images; inject secrets at runtime. Pair with rotating the leaked secrets (see ISSUE-074) and fixing backend config to not load `.env.development` unconditionally (see ISSUE-101).
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/.dockerignore:14`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-50
|
||||
- [[ISSUE-074-backend-env-development-committed-with-live-telegram-and-smtp-s|ISSUE-074]]
|
||||
- [[ISSUE-101-backend-config-loads-env-development-unconditionally|ISSUE-101]]
|
||||
39
Issues/ISSUE-076-scanner-ssrf-via-unvalidated-callbackurl.md
Normal file
39
Issues/ISSUE-076-scanner-ssrf-via-unvalidated-callbackurl.md
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 076
|
||||
title: "Scanner: SSRF via unvalidated callbackUrl on intent creation"
|
||||
severity: high
|
||||
domain: Scanner
|
||||
labels: [security, scanner, ssrf]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Scanner: SSRF via unvalidated callbackUrl on intent creation
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Scanner
|
||||
**Labels:** security, scanner, ssrf
|
||||
|
||||
## Description
|
||||
|
||||
`scanner/api.go:143` accepts a caller-supplied `callbackUrl` without validating the scheme, host, or whether it points to an internal/RFC-1918 address. A caller can set `callbackUrl` to any internal service URL and receive webhook deliveries from the scanner, enabling server-side request forgery.
|
||||
|
||||
## Options
|
||||
|
||||
1. Allowlist schemes (`https` only) and reject RFC-1918/link-local/loopback hosts at create time and at dial time via a custom `DialContext`.
|
||||
2. Restrict callbacks to a configured set of backend hostnames.
|
||||
3. Route webhooks through an egress proxy that blocks internal ranges.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Enforce `https`-only + block private/loopback/link-local at both validation and dial time (custom `DialContext`), ideally plus a host allowlist.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `scanner/api.go:143`
|
||||
- `scanner/webhook.go` — dial path
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-57
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 077
|
||||
title: "Scanner: caller can override confirmation threshold down to 1 — reorg safety bypass"
|
||||
severity: high
|
||||
domain: Scanner
|
||||
labels: [security, scanner, reorg]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Scanner: caller can override confirmation threshold down to 1 — reorg safety bypass
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Scanner
|
||||
**Labels:** security, scanner, reorg
|
||||
|
||||
## Description
|
||||
|
||||
`scanner/api.go:170` accepts a caller-supplied `confirmations` value and uses it as-is without enforcing the chain-config threshold as a floor. A caller can set `confirmations: 1` on a chain that requires 12 confirmations, bypassing reorg safety and causing premature payment confirmation.
|
||||
|
||||
## Options
|
||||
|
||||
1. Clamp confirmations to `max(callerValue, chainConfigThreshold)` — config is a floor.
|
||||
2. Ignore caller value entirely; always use chain config.
|
||||
3. Allow override only above the chain threshold.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Treat the chain config threshold as a hard floor (`max` of caller and config). Changes reorg-safety semantics.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `scanner/api.go:170`
|
||||
- `scanner/config.go` — chain threshold definition
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-58
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 078
|
||||
title: "Scanner: idempotency path ignores mismatched parameters — silent collision"
|
||||
severity: high
|
||||
domain: Scanner
|
||||
labels: [bug, scanner, idempotency]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Scanner: idempotency path ignores mismatched parameters — silent collision
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Scanner
|
||||
**Labels:** bug, scanner, idempotency
|
||||
|
||||
## Description
|
||||
|
||||
`scanner/api.go:191` returns the existing intent when an `intentId` collision is detected, but does not compare the stored parameters to the incoming request. If a caller reuses an `intentId` with different `amount`, `tokenAddress`, or `callbackUrl`, the scanner silently returns the old intent and monitors the wrong payment parameters.
|
||||
|
||||
## Options
|
||||
|
||||
1. Return `409 Conflict` if stored params differ from request.
|
||||
2. Return existing intent only if params match; else error.
|
||||
3. Treat any reuse as conflict regardless of params.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Compare stored vs incoming params and return `409 Conflict` on mismatch (return existing only on exact match). Changes API contract.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `scanner/api.go:191`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-62
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 079
|
||||
title: "Frontend: Telegram bot token committed in .gitleaks.toml allowlist — must rotate"
|
||||
severity: high
|
||||
domain: Security
|
||||
labels: [security, frontend, secrets, rotation-required]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: Telegram bot token committed in .gitleaks.toml allowlist — must rotate
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Security
|
||||
**Labels:** security, frontend, secrets, rotation-required
|
||||
|
||||
## Description
|
||||
|
||||
`frontend/.gitleaks.toml:15` contains a value-based allowlist entry with the plaintext Telegram bot token. Value-based allowlist entries in gitleaks effectively publish the secret in the allowlist itself. The same token appears in the backend `.env.development` (see ISSUE-074).
|
||||
|
||||
## Options
|
||||
|
||||
1. Replace the value-based allowlist with a path/commit-hash allowlist and rotate the token.
|
||||
2. Remove the allowlist entry entirely after scrubbing the secret from source.
|
||||
3. Use the handle-gitleaks workflow to triage and remediate.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Rotate the token, switch to a non-value-based allowlist (path/fingerprint), and scrub history. Coordinate with backend ISSUE-074 since the same token appears there.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/.gitleaks.toml:15`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-78
|
||||
- [[ISSUE-074-backend-env-development-committed-with-live-telegram-and-smtp-s|ISSUE-074]]
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 080
|
||||
title: "Frontend: open redirect via unvalidated returnTo in GuestGuard"
|
||||
severity: medium
|
||||
domain: Authentication
|
||||
labels: [security, frontend, open-redirect]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: open redirect via unvalidated returnTo in GuestGuard
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Authentication
|
||||
**Labels:** security, frontend, open-redirect
|
||||
|
||||
## Description
|
||||
|
||||
`src/auth/guard/guest-guard.tsx:27` passes `returnTo` directly to `router.replace()` without validating that it is a same-origin relative path. An attacker can craft a link with `returnTo=//evil.com` or `returnTo=https://evil.com` to redirect users after login.
|
||||
|
||||
## Options
|
||||
|
||||
1. Allow only same-origin relative paths starting with a single `/` and not `//` — strict, safe default.
|
||||
2. Allowlist of known internal path prefixes — safest but must be maintained.
|
||||
3. Parse with `URL()` against `window.origin` and reject cross-origin — robust but slightly more code.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Reject any `returnTo` that does not match `^/(?!/)` (single leading slash, not protocol-relative), else fall back to default landing route. One small helper, applied everywhere `returnTo` is consumed.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/auth/guard/guest-guard.tsx:27`
|
||||
- Any other component that reads and acts on `returnTo`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-2
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
issue: 081
|
||||
title: "Frontend: auth tokens stored in localStorage — XSS-accessible"
|
||||
severity: medium
|
||||
domain: Authentication
|
||||
labels: [security, frontend, session]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: auth tokens stored in localStorage — XSS-accessible
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Authentication
|
||||
**Labels:** security, frontend, session
|
||||
|
||||
## Description
|
||||
|
||||
`src/auth/context/jwt/action.ts:100` stores access and refresh tokens in `localStorage`. Any XSS vulnerability can steal these tokens and impersonate the user. The risk is compounded by the lack of a Content-Security-Policy (see ISSUE-083).
|
||||
|
||||
## Options
|
||||
|
||||
1. Move refresh token to HttpOnly cookie, keep short-lived access token in memory — strong, but requires backend cookie + CSRF work.
|
||||
2. Keep localStorage but add strict CSP + sanitization to reduce XSS surface — cheaper, weaker.
|
||||
3. Full cookie-based session with `SameSite=strict` — strongest, largest change to axios/socket auth.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Plan a migration to HttpOnly refresh cookie + in-memory access token, coordinated with backend. This is a large, cross-cutting change that breaks many call sites — treat as a deliberate project.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/auth/context/jwt/action.ts:100`
|
||||
- `frontend/src/lib/axios.ts` — auth header injection
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-4
|
||||
- [[ISSUE-083-frontend-no-content-security-policy-header-in-next-config|ISSUE-083]]
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 082
|
||||
title: "Frontend: wallet ownership signature verification is a no-op"
|
||||
severity: medium
|
||||
domain: Web3
|
||||
labels: [security, frontend, wallet]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: wallet ownership signature verification is a no-op
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Web3
|
||||
**Labels:** security, frontend, wallet
|
||||
|
||||
## Description
|
||||
|
||||
`src/sections/account/account-wallet-connection.tsx:425` has a `verifySignature` stub that always passes. The frontend does not actually verify that the signature matches the claimed wallet address, meaning any wallet address can be submitted without proof of ownership.
|
||||
|
||||
## Options
|
||||
|
||||
1. Implement real client-side verification with `ethers.verifyMessage(message, signature) === wallet.address` as a UX pre-check, keep backend authoritative.
|
||||
2. Remove the misleading `verifySignature` stub and rely solely on backend (document this).
|
||||
3. Both: client pre-check and confirm backend enforcement exists.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Implement `ethers.verifyMessage` as a UX gate AND verify the backend enforces ownership. The stub is actively misleading.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/sections/account/account-wallet-connection.tsx:425`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-6
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 083
|
||||
title: "Frontend: no Content-Security-Policy header in Next.js config"
|
||||
severity: medium
|
||||
domain: Security
|
||||
labels: [security, frontend, csp]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: no Content-Security-Policy header in Next.js config
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Security
|
||||
**Labels:** security, frontend, csp
|
||||
|
||||
## Description
|
||||
|
||||
`next.config.ts:29` does not set a `Content-Security-Policy` header. Without CSP, XSS attacks have unrestricted script execution, making token theft (localStorage) and DOM-based attacks much easier.
|
||||
|
||||
## Options
|
||||
|
||||
1. Ship `Content-Security-Policy-Report-Only` first to collect violations, then enforce — safe rollout.
|
||||
2. Enforce a moderate CSP allowing required hosts (Telegram, WalletConnect, Mapbox, Sentry) with nonces for inline scripts.
|
||||
3. Strict CSP with nonces and removal of all inline scripts — strongest but requires refactoring `layout.tsx` inline scripts.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Ship `Content-Security-Policy-Report-Only` first, gather violations for a week, then enforce. Inline scripts in `layout.tsx` must move to nonces. Non-trivial rollout.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/next.config.ts:29`
|
||||
- `frontend/src/app/layout.tsx` — inline scripts that need nonces
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-8
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 084
|
||||
title: "Frontend: console.error/warn suppression masks production errors"
|
||||
severity: medium
|
||||
domain: Observability
|
||||
labels: [bug, frontend, logging]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: console.error/warn suppression masks production errors
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Observability
|
||||
**Labels:** bug, frontend, logging
|
||||
|
||||
## Description
|
||||
|
||||
`src/app/layout.tsx:95` overrides `console.error` and `console.warn` globally in all environments. In production this suppresses real errors from reaching Sentry or developer tools, making production issues invisible. See also ISSUE-120 (the polling suppression interval that triggers this).
|
||||
|
||||
## Options
|
||||
|
||||
1. Remove the global override in production entirely (allow real errors through to Sentry).
|
||||
2. Scope suppression to the specific known Emotion/MUI warning string only.
|
||||
3. Keep dev-only suppression, none in production.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Remove global suppression in production and, at most, filter the one known benign warning by message substring in development. Coordinate with ISSUE-120 since it is the same script.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/app/layout.tsx:95`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-10
|
||||
- [[ISSUE-120-frontend-50ms-setinterval-console-suppression-script-in-root-l|ISSUE-120]]
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 085
|
||||
title: "Frontend: token refresh queue dispatches with undefined Authorization header"
|
||||
severity: medium
|
||||
domain: Authentication
|
||||
labels: [bug, frontend, session]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: token refresh queue dispatches with undefined Authorization header
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Authentication
|
||||
**Labels:** bug, frontend, session
|
||||
|
||||
## Description
|
||||
|
||||
`src/lib/axios.ts:136` flushes queued requests after a refresh attempt unconditionally. When the refresh yields no token (expired session, network error), queued requests are dispatched with `Authorization: Bearer undefined`, which backend middleware treats as an invalid token, causing all queued requests to fail with 401 — but no logout or error surfacing occurs.
|
||||
|
||||
## Options
|
||||
|
||||
1. On no token: reject queued requests (fail fast) and trigger logout/redirect.
|
||||
2. Skip the `forEach` when `newAccessToken` is falsy and let requests retry later.
|
||||
3. Move the `forEach` inside the `if(newAccessToken)` guard and reject the queue in the `else` branch.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Move flush inside the token guard and explicitly reject queued callbacks so they error rather than retry with `'Bearer undefined'`.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/lib/axios.ts:136`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-11
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 086
|
||||
title: "Frontend: PaymentDetailsView status dropdown exposed to all users"
|
||||
severity: medium
|
||||
domain: Payment
|
||||
labels: [security, frontend, authorization]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: PaymentDetailsView status dropdown exposed to all users
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Payment
|
||||
**Labels:** security, frontend, authorization
|
||||
|
||||
## Description
|
||||
|
||||
`src/sections/payment/view/payment-details-view.tsx:312` renders a status-change dropdown without an `isAdmin` check. `PaymentDetailsCard` already gates this correctly with `isAdmin`, but the view-level dropdown bypasses that check, allowing any authenticated user to attempt a status change from the UI.
|
||||
|
||||
## Options
|
||||
|
||||
1. Wrap the status `TextField` in an `isAdmin` check mirroring `PaymentDetailsCard`.
|
||||
2. Hide the control for non-admins and rely on backend role enforcement too.
|
||||
3. Move status changes to an admin-only view.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Gate the control behind `isAdmin` (as `PaymentDetailsCard` already does) AND ensure backend enforces admin for the underlying route (see ISSUE-062). UI gating alone is insufficient.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/sections/payment/view/payment-details-view.tsx:312`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-15
|
||||
- [[ISSUE-062-backend-payment-update-routes-lack-ownership-role-guards|ISSUE-062]]
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 087
|
||||
title: "Frontend: getPaymentStatus and checkPaymentStatus hit different endpoints"
|
||||
severity: medium
|
||||
domain: Payment
|
||||
labels: [bug, frontend]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: getPaymentStatus and checkPaymentStatus hit different endpoints
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Payment
|
||||
**Labels:** bug, frontend
|
||||
|
||||
## Description
|
||||
|
||||
`src/actions/payment.ts:62` has two functions — `getPaymentStatus` and `checkPaymentStatus` — that appear to serve the same purpose but call different endpoints (`/payment/:id/status` vs `/payment/payments/:id/status`). Only one of these can be the correct backend path.
|
||||
|
||||
## Options
|
||||
|
||||
1. Point `getPaymentStatus` at the registry-defined `/payment/:id/status` and deduplicate with `checkPaymentStatus`.
|
||||
2. Add `/payment/payments/:id/status` to the endpoints registry if backend truly serves it.
|
||||
3. Remove the redundant `getPaymentStatus` and migrate callers to `checkPaymentStatus`.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Verify the real backend route, then collapse to a single function using the registry path. Could break callers, so verify before removing.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/payment.ts:62`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-16
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 088
|
||||
title: "Frontend: adminWalletPayout falls back to literal 'admin' adminUserId"
|
||||
severity: medium
|
||||
domain: Payment
|
||||
labels: [bug, frontend, authorization]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: adminWalletPayout falls back to literal 'admin' adminUserId
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Payment
|
||||
**Labels:** bug, frontend, authorization
|
||||
|
||||
## Description
|
||||
|
||||
`src/actions/payment.ts:663` sends `adminUserId: adminUserId || 'admin'`. When the admin user ID is not available (e.g. context not loaded), the string literal `'admin'` is sent to the backend. This may match a user record named 'admin' unintentionally or corrupt audit trails.
|
||||
|
||||
## Options
|
||||
|
||||
1. Require `adminUserId` and throw/abort if absent (no fallback).
|
||||
2. Source `adminUserId` from the authenticated admin context automatically.
|
||||
3. Keep a fallback but use the real admin id from token rather than the string `'admin'`.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Remove the `'admin'` literal and require a real admin id from the auth context; abort if unavailable. This affects audit/authorization semantics.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/payment.ts:663`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-17
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 089
|
||||
title: "Frontend: admin payments-awaiting-confirmation polls every 12s unconditionally"
|
||||
severity: medium
|
||||
domain: Admin
|
||||
labels: [performance, frontend, polling]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: admin payments-awaiting-confirmation polls every 12s unconditionally
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Admin
|
||||
**Labels:** performance, frontend, polling
|
||||
|
||||
## Description
|
||||
|
||||
`payments-awaiting-confirmation-list-view.tsx:95` polls the backend every 12 seconds regardless of tab visibility or socket connectivity. NB-49 added visibility-gating as a no-brainer, but the longer-term question of whether to replace polling with socket subscriptions remains.
|
||||
|
||||
## Options
|
||||
|
||||
1. Pause polling when `document.visibilityState === 'hidden'` and increase interval (applied via NB-49).
|
||||
2. Replace polling with a socket subscription for awaiting-confirmation events — best but needs backend events.
|
||||
3. Both: visibility-gated polling now, socket later.
|
||||
|
||||
## Recommendation
|
||||
|
||||
NB-49 applied the visibility gate. Plan a socket subscription for awaiting-confirmation events to eliminate polling entirely. Confirm acceptable notification latency with owner.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/sections/admin/payments-awaiting-confirmation/payments-awaiting-confirmation-list-view.tsx:95`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-18
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
issue: 090
|
||||
title: "Frontend: chat views re-fetch full conversation on every new-message socket event"
|
||||
severity: medium
|
||||
domain: Chat
|
||||
labels: [performance, frontend]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: chat views re-fetch full conversation on every new-message socket event
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Chat
|
||||
**Labels:** performance, frontend
|
||||
|
||||
## Description
|
||||
|
||||
`src/sections/chat/view/buyer-chat-view.tsx:157` calls the full conversation fetch whenever a `new-message` socket event fires. With the chat messages stored as an embedded array (see ISSUE-072), this re-fetches the entire conversation history on every incoming message, causing high network and backend load in active chats.
|
||||
|
||||
## Options
|
||||
|
||||
1. Append the message from the socket payload to local state; only re-fetch on gaps/errors.
|
||||
2. Keep re-fetch but debounce it.
|
||||
3. Hybrid: optimistic append plus periodic reconciliation.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Append the payload message directly and reconcile only on inconsistency. This changes data-flow correctness assumptions.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/sections/chat/view/buyer-chat-view.tsx:157`
|
||||
- `frontend/src/sections/chat/view/seller-chat-view.tsx` (similar pattern)
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-20
|
||||
- [[ISSUE-072-backend-chat-messages-stored-as-embedded-array-unbounded-growth|ISSUE-072]]
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 091
|
||||
title: "Frontend: dual socket connections (SocketProvider + socketService singleton)"
|
||||
severity: medium
|
||||
domain: Realtime
|
||||
labels: [bug, frontend, performance]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: dual socket connections (SocketProvider + socketService singleton)
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Realtime
|
||||
**Labels:** bug, frontend, performance
|
||||
|
||||
## Description
|
||||
|
||||
`src/socket/lib/socket-service.ts:217` creates a standalone socket.io connection separate from the `SocketProvider` context. Both may connect simultaneously, resulting in duplicate connections to the backend, doubled event delivery, and doubled auth overhead.
|
||||
|
||||
## Options
|
||||
|
||||
1. Make `socketService` delegate to the `SocketProvider` connection (single source of truth).
|
||||
2. Migrate all `actions/chat.ts` usages to the context provider and delete `socketService`.
|
||||
3. Keep both but ensure only one actually connects.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Consolidate onto `SocketProvider` and refactor `socketService` callers; remove the duplicate connection. This is a large refactor.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/socket/lib/socket-service.ts:217`
|
||||
- `frontend/src/actions/chat.ts` — socketService callers
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-21
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 092
|
||||
title: "Backend: JWT refresh and access tokens share the same secret; middleware skips token type check"
|
||||
severity: medium
|
||||
domain: Authentication
|
||||
labels: [security, backend, jwt]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: JWT refresh and access tokens share the same secret; middleware skips token type check
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Authentication
|
||||
**Labels:** security, backend, jwt
|
||||
|
||||
## Description
|
||||
|
||||
`src/services/auth/authService.ts:44` signs both access and refresh tokens with the same secret. `authenticateToken` middleware does not check `token.type`, so a refresh token can be presented as an access token and accepted by protected routes.
|
||||
|
||||
## Options
|
||||
|
||||
1. Add a `type:'access'` claim check in `authenticateToken` middleware (reject `type:'refresh'`).
|
||||
2. Use separate secrets for access vs refresh tokens.
|
||||
3. Add audience/issuer claims and verify them in middleware.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Enforce a token-type check in the middleware (reject refresh tokens) and ideally split secrets. Both changes touch core auth verification.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/auth/authService.ts:44`
|
||||
- `backend/src/middleware/authenticateToken.ts` (or equivalent)
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-26
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 093
|
||||
title: "Backend: addEvidence has no participant ownership check on disputes"
|
||||
severity: medium
|
||||
domain: Dispute
|
||||
labels: [security, backend, authorization]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: addEvidence has no participant ownership check on disputes
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Dispute
|
||||
**Labels:** security, backend, authorization
|
||||
|
||||
## Description
|
||||
|
||||
`src/routes/disputeRoutes.ts:32` registers the `addEvidence` route with only `authenticateToken`. Any authenticated user can submit evidence to any dispute, not just the buyer/seller/admin who are participants.
|
||||
|
||||
## Options
|
||||
|
||||
1. Verify `req.user.id` is buyer or seller of the dispute before accepting evidence.
|
||||
2. Allow admins plus participants only.
|
||||
3. Add participant check in controller and reject otherwise.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Add a participant (buyer/seller/admin) check in `addEvidence` before persisting. This is an authorization-logic change.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/routes/disputeRoutes.ts:32`
|
||||
- `backend/src/controllers/disputeController.ts` — `addEvidence` handler
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-27
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 094
|
||||
title: "Backend: selectOffer does not verify buyer owns the purchase request"
|
||||
severity: medium
|
||||
domain: Marketplace
|
||||
labels: [security, backend, idor]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: selectOffer does not verify buyer owns the purchase request
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Marketplace
|
||||
**Labels:** security, backend, idor
|
||||
|
||||
## Description
|
||||
|
||||
`src/services/marketplace/marketplaceController.ts:1029` handles `selectOffer` without checking that `req.user.id` matches the `purchaseRequest.buyerId`. Any authenticated user who knows the purchase request ID can select an offer on someone else's request.
|
||||
|
||||
## Options
|
||||
|
||||
1. Reject when `req.user.id !== purchaseRequest.buyerId`.
|
||||
2. Allow buyer-owner or admin only.
|
||||
3. Atomic `findOneAndUpdate` scoped by `buyerId`.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Enforce `req.user.id === purchaseRequest.buyerId` (admin override allowed). This changes who can accept offers.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/marketplace/marketplaceController.ts:1029`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-28
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 095
|
||||
title: "Backend: getUserStats has no ownership/admin check (IDOR)"
|
||||
severity: medium
|
||||
domain: Payment
|
||||
labels: [security, backend, idor]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: getUserStats has no ownership/admin check (IDOR)
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Payment
|
||||
**Labels:** security, backend, idor
|
||||
|
||||
## Description
|
||||
|
||||
`paymentControllerRoutes.ts:13` serves `GET /api/payment/stats/:userId` without checking that `req.user.id === req.params.userId` or that the caller is an admin. Any authenticated user can retrieve payment statistics for any other user ID.
|
||||
|
||||
## Options
|
||||
|
||||
1. Require `req.user.id === req.params.userId`, or admin.
|
||||
2. Admin-only endpoint.
|
||||
3. Scope query to the authenticated user, ignore param.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Require self-or-admin (`req.user.id === userId || isAdmin`).
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/payment/paymentControllerRoutes.ts:13`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-29
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 096
|
||||
title: "Backend: validateStatusTransition requires escrowState 'funded' never set on completed payments"
|
||||
severity: medium
|
||||
domain: Payment
|
||||
labels: [bug, backend, state-machine]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: validateStatusTransition requires escrowState 'funded' never set on completed payments
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Payment
|
||||
**Labels:** bug, backend, state-machine
|
||||
|
||||
## Description
|
||||
|
||||
`marketplaceController.ts:570-583` guards a status transition by querying for `{ status:'completed', escrowState:'funded' }`. The completion flow never sets `escrowState:'funded'`; it is set earlier (at funding time). A genuinely completed payment may not match this query, causing the guard to reject valid transitions.
|
||||
|
||||
## Options
|
||||
|
||||
1. Query by `status:'completed'` only (drop `escrowState:'funded'`).
|
||||
2. Ensure the completion flow sets `escrowState:'funded'` consistently and keep the guard.
|
||||
3. Match on a documented completed-payment predicate aligned with the actual write path.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Align the guard with what the completion flow actually writes — most safely query `status:'completed'` without the `escrowState` constraint, after confirming no false positives.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/marketplace/marketplaceController.ts:570-583`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-34
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 097
|
||||
title: "Backend: validTransitions map missing 'in_negotiation' key"
|
||||
severity: medium
|
||||
domain: Marketplace
|
||||
labels: [bug, backend, state-machine]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: validTransitions map missing 'in_negotiation' key
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Marketplace
|
||||
**Labels:** bug, backend, state-machine
|
||||
|
||||
## Description
|
||||
|
||||
`marketplaceController.ts:544-555` defines a `validTransitions` map for PurchaseRequest status transitions but has no entry for `'in_negotiation'`. A PurchaseRequest in the `in_negotiation` state cannot transition to any other state via this validator.
|
||||
|
||||
## Options
|
||||
|
||||
1. Add `'in_negotiation'` with its allowed next statuses (e.g. `payment`, `cancelled`).
|
||||
2. Treat missing key as 'allow same-tier transitions' default.
|
||||
3. Derive transitions from `STATUS_PROGRESSION_ORDER` instead of a hand-maintained map.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Add an explicit `'in_negotiation'` entry with the correct next statuses. Requires product/state-machine confirmation of valid transitions.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/marketplace/marketplaceController.ts:544-555`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-35
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 098
|
||||
title: "Backend: in-memory seenDeliveryIds resets on restart — webhook dedup lost"
|
||||
severity: medium
|
||||
domain: Payment
|
||||
labels: [bug, backend, idempotency]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: in-memory seenDeliveryIds resets on restart — webhook dedup lost
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Payment
|
||||
**Labels:** bug, backend, idempotency
|
||||
|
||||
## Description
|
||||
|
||||
`requestNetworkRoutes.ts:16` maintains webhook deduplication via an in-memory `Set` of delivery IDs. This Set is lost on every server restart or pod restart. A redelivered webhook that arrived before the restart will be processed twice, potentially triggering double payment completion.
|
||||
|
||||
## Options
|
||||
|
||||
1. Persist processed delivery IDs in MongoDB (unique index) with TTL.
|
||||
2. Use Redis SET with TTL for delivery-id dedup.
|
||||
3. Make webhook handlers idempotent by keying state transitions on payment status guards.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Persist delivery IDs (Mongo unique index or Redis) AND make handlers idempotent via status guards. This is an infra/state decision.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/payment/requestNetwork/requestNetworkRoutes.ts:16`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-36
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 099
|
||||
title: "Backend: on-demand RN reconciliation in getPaymentById can race — double-processing risk"
|
||||
severity: medium
|
||||
domain: Payment
|
||||
labels: [bug, backend, concurrency]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: on-demand RN reconciliation in getPaymentById can race — double-processing risk
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Payment
|
||||
**Labels:** bug, backend, concurrency
|
||||
|
||||
## Description
|
||||
|
||||
`paymentController.ts:407-466` triggers RN reconciliation on every `GET /payment/:id` call. If two browser tabs or requests call this concurrently on a pending payment, both can read `status:'pending'` and both trigger the completion side-effects before either write commits.
|
||||
|
||||
## Options
|
||||
|
||||
1. Use an atomic `findOneAndUpdate` guarded on `status:'pending'` so only one writer wins.
|
||||
2. Add a distributed lock (Redis) around reconciliation per payment.
|
||||
3. Move reconciliation off the read path into a single-writer background job.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Make the status transition atomic (`findOneAndUpdate` filtering on current status) so only the first concurrent caller advances it; ideally move reconciliation off the GET path.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/payment/paymentController.ts:407-466`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-37
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 100
|
||||
title: "Backend: updatePurchaseRequest does findById then findByIdAndUpdate — non-atomic race"
|
||||
severity: medium
|
||||
domain: Marketplace
|
||||
labels: [bug, backend, concurrency]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: updatePurchaseRequest does findById then findByIdAndUpdate — non-atomic race
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Marketplace
|
||||
**Labels:** bug, backend, concurrency
|
||||
|
||||
## Description
|
||||
|
||||
`PurchaseRequestService.ts:413` reads the document first (`findById`) to check allowed status transitions, then writes it (`findByIdAndUpdate`). Between the read and the write, another request can change the status, defeating the transition guard.
|
||||
|
||||
## Options
|
||||
|
||||
1. Use `findOneAndUpdate` with `status:{$in:allowedCurrentStatuses}` condition — atomic.
|
||||
2. Keep two queries but wrap in a transaction.
|
||||
3. Leave as-is.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Use a single conditional `findOneAndUpdate` to make the transition atomic and halve round-trips.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/services/marketplace/PurchaseRequestService.ts:413`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-46
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 101
|
||||
title: "Backend: config loads .env.development unconditionally regardless of NODE_ENV"
|
||||
severity: medium
|
||||
domain: Security
|
||||
labels: [security, backend, configuration]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: config loads .env.development unconditionally regardless of NODE_ENV
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Security
|
||||
**Labels:** security, backend, configuration
|
||||
|
||||
## Description
|
||||
|
||||
`backend/src/shared/config/index.ts:4` loads `.env.development` unconditionally. In a production environment where `NODE_ENV=production`, this still reads and applies `.env.development` values, overriding injected production secrets with development values. Paired with `.dockerignore` whitelisting this file (ISSUE-075), it means dev secrets are active in prod images.
|
||||
|
||||
## Options
|
||||
|
||||
1. Load `.env.<NODE_ENV>` conditionally, never fall back to dev file in production.
|
||||
2. Only load dotenv when not in production (rely on injected env in prod).
|
||||
3. Load env-specific file and fail fast if required vars are missing.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Load the env-file matching `NODE_ENV` (or none in production) and never default to `.env.development`. Pair with ISSUE-075.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/shared/config/index.ts:4`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-49
|
||||
- [[ISSUE-075-backend-dockerignore-whitelists-env-development-into-prod-image|ISSUE-075]]
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 102
|
||||
title: "Backend: 14 high-severity npm vulnerabilities, no audit step in CI"
|
||||
severity: medium
|
||||
domain: Dependencies
|
||||
labels: [security, backend, dependencies, ci-cd]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: 14 high-severity npm vulnerabilities, no audit step in CI
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Dependencies
|
||||
**Labels:** security, backend, dependencies, ci-cd
|
||||
|
||||
## Description
|
||||
|
||||
`npm audit` reports 14 high-severity vulnerabilities in backend production dependencies (packages include mongoose, multer, axios, and others). No CI pipeline step runs `npm audit`, so new vulnerabilities silently accumulate.
|
||||
|
||||
## Options
|
||||
|
||||
1. Add `npm audit` (or `audit-ci`) as a non-blocking report step first, then make blocking.
|
||||
2. Upgrade the flagged packages and add a blocking audit gate.
|
||||
3. Adopt Renovate/Dependabot plus a CI audit step.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Add an audit step (start as report), prioritize upgrading the 14 highs, then make the gate blocking. Package upgrades risk breakage — test before making the gate mandatory.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/package.json`
|
||||
- `backend/.woodpecker/development.yml` — add audit step
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-51
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 103
|
||||
title: "Backend: react/react-dom in backend production dependencies"
|
||||
severity: medium
|
||||
domain: Dependencies
|
||||
labels: [backend, dependencies, cleanup]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: react/react-dom in backend production dependencies
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Dependencies
|
||||
**Labels:** backend, dependencies, cleanup
|
||||
|
||||
## Description
|
||||
|
||||
`backend/package.json:83` lists `react` and `react-dom` as production dependencies. These are large packages with no apparent usage in the backend (no SSR email templates confirmed). They inflate the production bundle and increase the attack surface.
|
||||
|
||||
## Options
|
||||
|
||||
1. Remove both after confirming zero imports.
|
||||
2. Move to `devDependencies` if only used in tooling.
|
||||
3. Keep if some build step requires them.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Confirm no runtime/SSR usage, then remove. Because removal could break an unseen template render, verify all imports before removing.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/package.json:83`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-52
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 104
|
||||
title: "Backend: native bcrypt addon present alongside bcryptjs — unnecessary build toolchain dependency"
|
||||
severity: medium
|
||||
domain: Dependencies
|
||||
labels: [backend, dependencies, cleanup]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: native bcrypt addon present alongside bcryptjs — unnecessary build toolchain dependency
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Dependencies
|
||||
**Labels:** backend, dependencies, cleanup
|
||||
|
||||
## Description
|
||||
|
||||
`backend/package.json:67` includes `bcrypt` (native C++ addon, requires build toolchain) alongside `bcryptjs` (pure JS). Code uses `bcryptjs`. The native addon adds unnecessary native build complexity and is an unused dependency.
|
||||
|
||||
## Options
|
||||
|
||||
1. Remove `bcrypt` (keep `bcryptjs`) after confirming no imports and no migration need.
|
||||
2. Standardize on native `bcrypt` instead (faster) and migrate hashes-compatible.
|
||||
3. Leave both.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Confirm `bcryptjs` is the sole hasher and remove native `bcrypt` to drop the build toolchain requirement. Hashing libs are sensitive — verify before removing.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/package.json:67`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-53
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 105
|
||||
title: "Backend: no startup validation of required env vars — silent misconfiguration"
|
||||
severity: medium
|
||||
domain: Configuration
|
||||
labels: [backend, reliability, configuration]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: no startup validation of required env vars — silent misconfiguration
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Configuration
|
||||
**Labels:** backend, reliability, configuration
|
||||
|
||||
## Description
|
||||
|
||||
`backend/src/shared/config/index.ts:32` reads env vars without validating they are present or have correct types. A misconfigured deployment (missing `JWT_SECRET`, `MONGODB_URI`, or `SMTP_PORT`) starts silently and fails only at runtime when those vars are first used, making misconfiguration hard to diagnose.
|
||||
|
||||
## Options
|
||||
|
||||
1. Validate required vars with a schema (zod/envalid) and exit on missing/NaN.
|
||||
2. Manual assertions for the critical few (`PORT`, `JWT_SECRET`, `MONGODB_URI`, `SMTP_PORT`).
|
||||
3. Log-and-continue warnings only.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Add schema-based validation that fails fast on missing/invalid required vars. Changes startup behavior.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/src/shared/config/index.ts:32`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-54
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
issue: 106
|
||||
title: "Backend: dual lockfiles (yarn.lock + package-lock.json) diverge"
|
||||
severity: medium
|
||||
domain: Dependencies
|
||||
labels: [backend, ci-cd, dependencies]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Backend: dual lockfiles (yarn.lock + package-lock.json) diverge
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Dependencies
|
||||
**Labels:** backend, ci-cd, dependencies
|
||||
|
||||
## Description
|
||||
|
||||
`backend/package.json:117` has both `yarn.lock` and `package-lock.json` in the repo, and they are not kept in sync. CI and production use npm; the `packageManager` field references yarn. The two lockfiles represent different resolved dependency trees, so local yarn installs and CI npm installs can diverge.
|
||||
|
||||
## Options
|
||||
|
||||
1. Standardize on npm + `package-lock.json` (matches CI/prod), delete `yarn.lock`, fix `Dockerfile.dev`.
|
||||
2. Standardize on yarn (matches `packageManager` field), make CI use yarn.
|
||||
3. Keep both but regenerate and pin.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Pick one (npm matches prod/CI), delete the other lockfile, align Dockerfiles, and regenerate.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `backend/package.json`
|
||||
- `backend/yarn.lock`
|
||||
- `backend/package-lock.json`
|
||||
- `backend/Dockerfile.dev`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-55
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 107
|
||||
title: "Scanner: TronGrid pagination next-URL used unvalidated — SSRF via API response"
|
||||
severity: medium
|
||||
domain: Scanner
|
||||
labels: [security, scanner, ssrf]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Scanner: TronGrid pagination next-URL used unvalidated — SSRF via API response
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Scanner
|
||||
**Labels:** security, scanner, ssrf
|
||||
|
||||
## Description
|
||||
|
||||
`scanner/tron_chain.go:180` follows the `Links.Next` URL from a TronGrid API response without validating that it has the same scheme and host as the configured RPC URL. A compromised or malicious TronGrid response can redirect the scanner to arbitrary internal endpoints.
|
||||
|
||||
## Options
|
||||
|
||||
1. Require next URL to share scheme+host with `chain.RpcURL`.
|
||||
2. Reconstruct pagination params ourselves instead of trusting `Links.Next`.
|
||||
3. Allowlist the TronGrid host.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Validate scheme+host equals the configured RPC URL before following `Links.Next`.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `scanner/tron_chain.go:180`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-61
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 108
|
||||
title: "Scanner: unauthenticated startup when SCANNER_API_KEY unset"
|
||||
severity: medium
|
||||
domain: Scanner
|
||||
labels: [security, scanner, configuration]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Scanner: unauthenticated startup when SCANNER_API_KEY unset
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Scanner
|
||||
**Labels:** security, scanner, configuration
|
||||
|
||||
## Description
|
||||
|
||||
`scanner/config.go:111` logs a warning when `SCANNER_API_KEY` is empty but allows the server to start and accept unauthenticated requests. An operator mistake or CI misconfiguration can deploy a scanner that accepts any intent without an API key.
|
||||
|
||||
## Options
|
||||
|
||||
1. Fail fast in non-dev when `SCANNER_API_KEY` is empty.
|
||||
2. Allow empty key only when bound to localhost; refuse otherwise.
|
||||
3. Keep warning but add a required-in-prod env flag.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Refuse to start (or restrict to loopback) when no API key is set outside local dev.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `scanner/config.go:111`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-60
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 109
|
||||
title: "Scanner: Tron lag metric reported in ms, not blocks — inconsistent with EVM chains"
|
||||
severity: medium
|
||||
domain: Scanner
|
||||
labels: [scanner, observability]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Scanner: Tron lag metric reported in ms, not blocks — inconsistent with EVM chains
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Scanner
|
||||
**Labels:** scanner, observability
|
||||
|
||||
## Description
|
||||
|
||||
`scanner/api.go:55` reports Tron lag in milliseconds while EVM chains report lag in blocks. Monitoring dashboards and alerts that compare lag across chains will produce incorrect comparisons.
|
||||
|
||||
## Options
|
||||
|
||||
1. Convert Tron lag to blocks (divide by ~3s block time) to match EVM semantics.
|
||||
2. Keep ms but relabel the field/units and fix the comment and alerts.
|
||||
3. Report a normalized seconds value across all chains.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Pick a consistent unit (blocks for EVM/Tron, or seconds everywhere), update the struct comment and any alerts. Affects monitoring contracts.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `scanner/api.go:55`
|
||||
- Status struct and any Prometheus/monitoring config
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-64
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 110
|
||||
title: "Scanner: TON worker O(N) HTTP fan-out per scan cycle — one TonCenter call per intent"
|
||||
severity: medium
|
||||
domain: Scanner
|
||||
labels: [performance, scanner, scalability]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Scanner: TON worker O(N) HTTP fan-out per scan cycle — one TonCenter call per intent
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Scanner
|
||||
**Labels:** performance, scanner, scalability
|
||||
|
||||
## Description
|
||||
|
||||
`scanner/ton_chain.go:131` issues one TonCenter API call per pending intent per scan cycle. With N pending intents, this creates N outbound HTTP calls per cycle. Under load or with many intents, this exhausts outbound connection capacity and hits TonCenter rate limits.
|
||||
|
||||
## Options
|
||||
|
||||
1. Batch intents by destination/jetton and query once per group.
|
||||
2. Bounded-concurrency worker pool for per-intent calls.
|
||||
3. Subscribe to TonCenter streaming/index instead of polling.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Batch queries by jetton/destination where the API allows; otherwise bound concurrency. A TODO is already noted in the code.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `scanner/ton_chain.go:131`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-66
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 111
|
||||
title: "Scanner: deliverWebhook goroutines use blocking time.Sleep — goroutine leak under sustained failure"
|
||||
severity: medium
|
||||
domain: Scanner
|
||||
labels: [bug, scanner, goroutine-leak]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Scanner: deliverWebhook goroutines use blocking time.Sleep — goroutine leak under sustained failure
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Scanner
|
||||
**Labels:** bug, scanner, goroutine-leak
|
||||
|
||||
## Description
|
||||
|
||||
`scanner/webhook.go:90` spawns a goroutine per webhook delivery that uses `time.Sleep` for retry backoff. Under sustained backend failure, many goroutines accumulate blocking on sleep with no upper bound on their count or total memory usage.
|
||||
|
||||
## Options
|
||||
|
||||
1. Replace per-delivery sleeping goroutines with a persisted retry queue + ticker (already partially present).
|
||||
2. Use a bounded worker pool + context cancellation instead of `time.Sleep`.
|
||||
3. Cap concurrent in-flight deliveries with a semaphore.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Move retries to the persisted queue/ticker model with a bounded worker pool and context-aware delays. Coordinate with ISSUE-112.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `scanner/webhook.go:90`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-67
|
||||
- [[ISSUE-112-scanner-unbounded-goroutine-fan-out-for-webhook-retries|ISSUE-112]]
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
issue: 112
|
||||
title: "Scanner: unbounded goroutine fan-out for webhook retries and reconciliation"
|
||||
severity: medium
|
||||
domain: Scanner
|
||||
labels: [bug, scanner, goroutine-leak]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Scanner: unbounded goroutine fan-out for webhook retries and reconciliation
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Scanner
|
||||
**Labels:** bug, scanner, goroutine-leak
|
||||
|
||||
## Description
|
||||
|
||||
`scanner/main.go:130` spawns goroutines for retry and reconciliation fan-out without any concurrency bound. Under high load or many failed deliveries, the number of live goroutines is unbounded, risking OOM.
|
||||
|
||||
## Options
|
||||
|
||||
1. Bound with a semaphore/worker pool (e.g. `errgroup` with limit).
|
||||
2. Process retries in batches sequentially.
|
||||
3. Rate-limit outbound webhook calls globally.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Introduce a bounded worker pool (`errgroup.SetLimit` or semaphore) for all retry fan-out paths. Coordinate with ISSUE-111.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `scanner/main.go:130`
|
||||
- `scanner/webhook.go` — retry fan-out
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-68
|
||||
- [[ISSUE-111-scanner-deliverwebhook-goroutines-use-blocking-time-sleep|ISSUE-111]]
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
issue: 113
|
||||
title: "Scanner/backend: RPC response bodies read without size limit — OOM risk"
|
||||
severity: medium
|
||||
domain: Scanner
|
||||
labels: [security, scanner, oom]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Scanner/backend: RPC response bodies read without size limit — OOM risk
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** Scanner
|
||||
**Labels:** security, scanner, oom
|
||||
|
||||
## Description
|
||||
|
||||
NB-42 applied a `LimitReader` as a mechanical guard with a default cap, but the exact byte limit per endpoint was not decided. Choosing the wrong cap (too small) breaks legitimate large responses; too large offers little protection. A malicious RPC node can still exhaust memory if the cap is too generous.
|
||||
|
||||
## Options
|
||||
|
||||
1. Wrap `resp.Body` in `io.LimitReader(resp.Body, maxBytes)` with a generous per-endpoint cap (applied as NB-42).
|
||||
2. Use `http.MaxBytesReader`-style enforcement and error on exceed.
|
||||
3. Stream-parse JSON with a bounded decoder.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Review the default cap applied by NB-42 against actual maximum RPC response sizes for each chain (EVM batch, Tron page, TON jetton response). Adjust per-endpoint caps and error explicitly when the limit is exceeded rather than silently truncating.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `scanner/chain.go:96`
|
||||
- `scanner/tron_chain.go:116`
|
||||
- `scanner/ton_chain.go:106`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-72
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 114
|
||||
title: "Frontend: WalletConnect/Google client IDs hardcoded as Dockerfile ARG defaults"
|
||||
severity: low
|
||||
domain: Security
|
||||
labels: [frontend, configuration, ci-cd]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: WalletConnect/Google client IDs hardcoded as Dockerfile ARG defaults
|
||||
|
||||
**Severity:** low
|
||||
**Domain:** Security
|
||||
**Labels:** frontend, configuration, ci-cd
|
||||
|
||||
## Description
|
||||
|
||||
`frontend/Dockerfile:14` has hardcoded default values for `NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID` and `NEXT_PUBLIC_GOOGLE_CLIENT_ID` in `ARG` defaults. Forks or copies of this repo will silently use production IDs without being aware.
|
||||
|
||||
## Options
|
||||
|
||||
1. Remove defaults; require build-args/CI to supply them.
|
||||
2. Keep defaults since values are public-by-design but document them.
|
||||
3. Move to runtime env only.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Remove the baked defaults and supply via CI build-args to avoid forks reusing prod IDs. These values are public but should be explicit.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/Dockerfile:14`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-74
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 115
|
||||
title: "Frontend: real plaintext credentials in committed scripts/show-credentials.sh"
|
||||
severity: low
|
||||
domain: Security
|
||||
labels: [security, frontend, secrets, rotation-required]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: real plaintext credentials in committed scripts/show-credentials.sh
|
||||
|
||||
**Severity:** low
|
||||
**Domain:** Security
|
||||
**Labels:** security, frontend, secrets, rotation-required
|
||||
|
||||
## Description
|
||||
|
||||
`frontend/scripts/show-credentials.sh:8` contains hardcoded credentials including the password `Moji6364`. If this account exists in any real environment, the password must be rotated.
|
||||
|
||||
## Options
|
||||
|
||||
1. Delete the scripts and rotate the password if the account is real.
|
||||
2. Replace hardcoded creds with env-var prompts.
|
||||
3. Keep scripts but move creds out and rotate.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Remove the hardcoded credentials (use env-var prompts instead) and rotate the account password if it exists in any real environment.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/scripts/show-credentials.sh:8`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-75
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
issue: 116
|
||||
title: "Frontend/scanner/backend: CI pipeline images not pinned to digests — tag-hijack risk"
|
||||
severity: medium
|
||||
domain: CI/CD
|
||||
labels: [security, ci-cd, supply-chain]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend/scanner/backend: CI pipeline images not pinned to digests — tag-hijack risk
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** CI/CD
|
||||
**Labels:** security, ci-cd, supply-chain
|
||||
|
||||
## Description
|
||||
|
||||
CI step images across all three repos use floating version tags or `latest` (node, buildx plugin, curl, alpine). A tag can be replaced with a malicious image that exfiltrates secrets or produces a compromised build artifact.
|
||||
|
||||
NB-40 and NB-41 pinned the scanner `alpine:latest` and buildx plugin. The broader policy of pinning all CI images across all repos remains a decision.
|
||||
|
||||
## Options
|
||||
|
||||
1. Pin all CI images (node, buildx plugin, curl, alpine) to immutable digests — track updates via Renovate.
|
||||
2. Pin to specific version tags only.
|
||||
3. Use a vetted internal mirror with digests.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Pin every CI step image to a digest across all pipelines; track updates via Renovate. Affects all CI files in frontend, backend, and scanner.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/.woodpecker/development.yml:8`
|
||||
- `frontend/.woodpecker/production.yml`
|
||||
- `backend/.woodpecker/development.yml`
|
||||
- `scanner/.woodpecker/development.yml`
|
||||
- (and all other pipeline files)
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-76
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
issue: 117
|
||||
title: "Frontend/scanner/backend: production/manual CI pipelines lack lint/type/test/audit gates"
|
||||
severity: medium
|
||||
domain: CI/CD
|
||||
labels: [ci-cd, quality, supply-chain]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend/scanner/backend: production/manual CI pipelines lack lint/type/test/audit gates
|
||||
|
||||
**Severity:** medium
|
||||
**Domain:** CI/CD
|
||||
**Labels:** ci-cd, quality, supply-chain
|
||||
|
||||
## Description
|
||||
|
||||
Production and manual CI pipelines across all three repos push images without the same lint/type/test gates that development pipelines apply. A broken build can be pushed to production via a manual trigger. NB-37 added a typecheck to the backend manual pipeline; the broader question of enforcing gates on all production/manual pipelines remains.
|
||||
|
||||
## Options
|
||||
|
||||
1. Add tsc/lint/test (and `go vet`/`go test` for scanner) to production and manual pipelines.
|
||||
2. Reuse the development pipeline's gate as a shared step.
|
||||
3. Block manual pipeline pushes unless a gate flag is passed.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Require the same lint/type/test gate on production and manual pipelines across all repos. This is a known project memory item ("verify before push").
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/.woodpecker/production.yml`
|
||||
- `backend/.woodpecker/manual.yml`
|
||||
- `scanner/.woodpecker/manual.yml`
|
||||
- `scanner/.woodpecker/production.yml`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-77
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 118
|
||||
title: "Frontend: notification title rendered via dangerouslySetInnerHTML in .backup drawer"
|
||||
severity: low
|
||||
domain: Security
|
||||
labels: [security, frontend, xss, dead-code]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: notification title rendered via dangerouslySetInnerHTML in .backup drawer
|
||||
|
||||
**Severity:** low
|
||||
**Domain:** Security
|
||||
**Labels:** security, frontend, xss, dead-code
|
||||
|
||||
## Description
|
||||
|
||||
`src/layouts/components/notifications-drawer.backup/notification-item.tsx:32` renders a notification title via `dangerouslySetInnerHTML`, creating an XSS sink. The `.backup` directory is likely dead code but may be imported somewhere or re-enabled in the future.
|
||||
|
||||
## Options
|
||||
|
||||
1. Delete the entire `.backup` directory if unused — removes dead code and the XSS sink.
|
||||
2. Replace `dangerouslySetInnerHTML` with plain text rendering.
|
||||
3. Keep HTML but sanitize via DOMPurify.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Confirm nothing imports the `.backup` directory and delete it. If any live notification rendering uses `dangerouslySetInnerHTML` elsewhere, switch to text or DOMPurify.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/layouts/components/notifications-drawer.backup/notification-item.tsx:32`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-5
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 119
|
||||
title: "Frontend: TelegramDebugPanel exposed in production via URL/localStorage flag"
|
||||
severity: low
|
||||
domain: Security
|
||||
labels: [security, frontend, debug-panel]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: TelegramDebugPanel exposed in production via URL/localStorage flag
|
||||
|
||||
**Severity:** low
|
||||
**Domain:** Security
|
||||
**Labels:** security, frontend, debug-panel
|
||||
|
||||
## Description
|
||||
|
||||
`src/components/debug/telegram-debug-panel.tsx:50` is enabled by a URL param or localStorage flag. In production, any user who discovers this flag can activate the debug panel, which exposes internal state including email, wallet, userId, and Telegram session data.
|
||||
|
||||
## Options
|
||||
|
||||
1. Render the panel only when `NODE_ENV !== 'production'` (compile-time) — removes the enumeration surface.
|
||||
2. Keep runtime flag but redact PII fields (email, wallet, userId).
|
||||
3. Remove the component from account pages entirely.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Guard rendering on `NODE_ENV !== 'production'` so the flag cannot reveal it in prod builds.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/components/debug/telegram-debug-panel.tsx:50`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-7
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
issue: 120
|
||||
title: "Frontend: 50ms setInterval console-suppression script in root layout"
|
||||
severity: high
|
||||
domain: Observability
|
||||
labels: [bug, frontend, logging]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: 50ms setInterval console-suppression script in root layout
|
||||
|
||||
**Severity:** high
|
||||
**Domain:** Observability
|
||||
**Labels:** bug, frontend, logging
|
||||
|
||||
## Description
|
||||
|
||||
`src/app/layout.tsx:139` contains a `setInterval` that repeatedly overrides `console.error`/`console.warn` every 50ms. This creates a recurring CPU microtask throughout the page lifecycle. The goal appears to be silencing an Emotion/MUI SSR warning, but the approach overrides the console globally on every tick.
|
||||
|
||||
## Options
|
||||
|
||||
1. Remove the suppression script entirely and address the underlying Emotion/MUI SSR warning properly.
|
||||
2. Keep one-time suppression (no interval) gated to development only.
|
||||
3. Replace with a single non-polling console override applied once at module load.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Remove the polling entirely. If the SSR warning must be silenced, apply a single non-polling override and only in development. Coordinate with ISSUE-084 (console suppression masks prod errors).
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/app/layout.tsx:139`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-9
|
||||
- [[ISSUE-084-frontend-console-error-warn-suppression-masks-prod-errors|ISSUE-084]]
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
issue: 121
|
||||
title: "Frontend: transferFunds and createPayment POST to the same endpoint"
|
||||
severity: low
|
||||
domain: Payment
|
||||
labels: [bug, frontend]
|
||||
status: open
|
||||
created: 2026-05-30
|
||||
source: Full Codebase Audit 2026-05-30
|
||||
---
|
||||
|
||||
# Frontend: transferFunds and createPayment POST to the same endpoint
|
||||
|
||||
**Severity:** low
|
||||
**Domain:** Payment
|
||||
**Labels:** bug, frontend
|
||||
|
||||
## Description
|
||||
|
||||
`src/actions/payment.ts:186` — `transferFunds` and `createPayment` both POST to the same backend endpoint. It is unclear whether this is intentional (payload-shape disambiguation on the backend) or an error where `transferFunds` should use a dedicated route.
|
||||
|
||||
## Options
|
||||
|
||||
1. Give `transferFunds` a dedicated backend route + frontend endpoint constant.
|
||||
2. Keep shared endpoint but document the backend disambiguation contract.
|
||||
3. Merge the two functions if they are truly the same operation.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Confirm backend routing intent; if distinct operations, introduce a dedicated endpoint.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `frontend/src/actions/payment.ts:186`
|
||||
|
||||
## References
|
||||
|
||||
- [Full Codebase Audit 2026-05-30](../09%20-%20Audits/Full%20Codebase%20Audit%20-%202026-05-30.md) — DEC-14
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user