This directory is the authoritative log of issues discovered on chamaconnect.io during the MUIAA x Salamander ChamaConnect Virtual Hackathon (deadline 2026-04-24).
Each issue lives in its own file as BUG-NNN-slug.md and contains:
- Evidence — where we observed it (URL, screenshot path, request/response if applicable).
- Severity — Critical / High / Medium / Low.
- User impact — what breaks for a real chama admin or member.
- Root cause — what in the code is likely wrong.
- Proposed fix — the smallest diff that resolves it, with code.
- Verification — how to prove the fix works.
Update this index when a new bug is filed.
| ID | Title | Severity | Surface | Status |
|---|---|---|---|---|
| BUG-001 | Every public page ships with default Next.js boilerplate <title> + <meta description> |
High | Public site / SEO | Open |
| BUG-002 | Footer Features, Pricing, Resources, Blog, Community, Events all point to # |
Medium | Public site / UX | Open |
| BUG-003 | Contact page phone number does not match footer / hackathon-brief contact number | Medium | Public site / trust | Open |
| BUG-004 | Contact page renders literal [email protected] instead of an email address |
Medium | Public site / UX | Fixed (2026-04-20) |
| BUG-005 | Login has no 2FA, no phone OTP, no social login — for a money platform | High | Auth / security | Open |
| BUG-006 | Register country selector defaults to International despite Kenya focus |
Low | UX / signup conversion | Open |
| BUG-007 | M-Pesa integration marked "Coming Soon" — the #1 Kenyan chama requirement | Critical | Core product / revenue | Open → fixed by ChamaPay module |
| BUG-008 | POST /users/signin returns "message": "User Created" on every login |
High | Auth / API | Open |
| BUG-009 | MERRRY_GO_AROUND typo (triple-R, wrong phrase) in both group-types endpoints and the Create Chama dropdown |
High | UI + API + data | Open |
| BUG-010 | Two different group-types endpoints with inconsistent (swapped) schemas |
High | API / data integrity | Open |
| BUG-011 | Notifications page tries to open ws://localhost:3080 in production — real-time notifications broken |
Critical | Real-time / prod config | Open |
| BUG-012 | /admin/chamas throws TypeError: Failed to fetch and shows "create your first chama" on network errors |
High | Dashboard / reliability | Open |
| BUG-013 | Signin returns the raw JWT in the response body (also in httpOnly cookie) — XSS-to-takeover path | High (security) | Auth / API | Open |
| BUG-014 | /contact throws React error #418 (hydration mismatch) |
Medium | Public site | Open (not reproduced 2026-04-20) |
| BUG-015 | Every role record has permissions: [] — authz likely enforced by role name only |
High (authz) | Backend | Open |
| BUG-016 | Signin JWT has no exp/nbf/jti/iss/aud — tokens never expire, can't be revoked |
Critical (security) | Auth / API | Open |
| BUG-017 | No Content-Security-Policy; HTML documents missing HSTS / X-Frame-Options / X-Content-Type-Options / Referrer-Policy / Permissions-Policy |
High (security) | Every HTML response | Open |
| BUG-018 | Signin rate limit is 1000 req / 15 min per IP, no per-account lockout — brute-force viable | High (security) | Auth / API | Open |
| BUG-019 | /api/auth/token returns 401 on every public page load (should be 200 with {token:null}) |
Medium | Public site / API | Open |
| BUG-020 | /api/proxy/users/current-user returns message "Successfully retrieved logged in user" (double space) |
Low | API / copy | Open |
| BUG-021 | /api/proxy/roles omits permissions; /users/current-user.role includes permissions:[] — same resource, two shapes |
Medium | API contract | Open |
| BUG-022 | Login form inputs have no name and no autocomplete — password managers break, WCAG 1.3.5 fails |
Medium | Auth / UX / a11y | Open |
| BUG-023 | /contact "Send Us a Message" inputs have no name, no id, no aria-label |
Medium | Public site / a11y | Open |
| BUG-024 | /about has 3 <h1> tags; /contact and /faqs each have 2 — SEO + a11y |
Low | Public site / SEO | Open |
| BUG-025 | Every admin/dashboard page ships with <title>Create Next App</title> |
Medium | Admin / UX | Open |
| BUG-026 | X-Powered-By: Next.js + x-nextjs-* headers leak backend stack on every HTML response |
Low (security) | Every HTML response | Open |
| BUG-027 | Any authenticated User can PUT /api/proxy/settings/:id — platform-wide loanFee, withDrawalFee, fineDelayPercentageIncrement (and likely M-Pesa callback URLs) are attacker-controlled |
Critical | API / authz | Open |
| BUG-028 | GET /api/proxy/settings returns M-Pesa Daraja ConsumerKey / ConsumerSecret / LipaNaMpesaShortPass + all C2B/B2C/B2B callback URLs to every signed-in user |
Critical | API / secrets | Open |
| BUG-029 | BOLA: GET /api/proxy/groups/:id returns any chama's full data (members' names, emails, phones, blockchain address, schedule) to any authenticated user |
Critical | API / authz / PII | Open |
| BUG-030 | BOLA: GET /api/proxy/transactions returns every chama's transactions (amounts, approvals, crypto hashes) to every signed-in user |
Critical | API / authz | Open |
| BUG-031 | Signin reveals which emails/phones are registered ("Incorrect password" vs "Invalid email or phone number" + 22-byte size + ~500 ms timing delta) |
High (security) | Auth / API | Open |
| BUG-032 | Signup leaks registration status via differential error ("Error creating user…" fires only on already-registered email) |
High (security) | Auth / API | Open |
| BUG-033 | Internal backend reachable from the internet at /backend/api/v1/* — doubles the attack surface and bypasses any future proxy-layer mitigations |
High (security) | Infrastructure | Open |
| BUG-034 | /api/proxy/users/request-password-reset has no per-account rate limit — enables mail bombing, SMS-cost attack, and OTP brute-force prep |
High | Auth / API | Open |
| BUG-035 | GET /api/proxy/permissions returns 201 Created with an empty role-list payload (routing bug + status-code misuse) |
Medium | API | Open |
| BUG-036 | GET /api/proxy/notifications/all returns 500 Internal Server Error on every call |
Medium | API / stability | Open |
| BUG-037 | Authorization failures return 400 Bad Request (should be 401/403) across signin, group PATCH/DELETE, user DELETE |
Medium | API / REST semantics | Open |
| BUG-038 | Signup response contains contradictory status fields (isActive:false + accountStatus:"ACTIVE" + activatedAt populated) |
Medium | Data model | Open |
| BUG-039 | Signin/password-reset accept object-valued email/password (MongoDB operators) and return 500 — latent NoSQL injection surface |
High | Auth / API | Open |
| BUG-040 | Any authenticated User can POST /api/proxy/roles (create roles) and PATCH /api/proxy/roles/:id (rename/modify ANY role, including SuperAdmin) |
Critical | API / authz | Open |
| BUG-041 | GET /api/proxy/transactions?userId=<other> returns another user's full transaction history (IDOR); no server-side pagination |
Critical | API / authz | Open |
| BUG-042 | DELETE /api/proxy/groups/:id response embeds full M-Pesa Daraja credentials in GroupSettings — a second exfiltration path independent of BUG-028 |
Critical | API / secrets | Open |
| BUG-043 | POST /api/proxy/notifications returns 500 Internal Server Error on every call; no role guard means any user could reach the broken create-notification handler |
Medium | API / stability | Open |
| BUG-044 | Path traversal: GET /api/proxy/groups/../settings resolves to /api/proxy/settings, bypassing route guards — all 10+ cross-path combinations confirmed working including M-Pesa credential exfiltration |
Critical | API / routing | Open |
| BUG-045 | CORS misconfiguration: localhost:3000 receives Access-Control-Allow-Origin: http://localhost:3000, https://chamaconnect.io + duplicate ACAC: true, true — any localhost JS context can make credentialed cross-origin requests |
High | API / headers | Open |
| BUG-046 | JWT not invalidated on logout: DELETE /api/auth/session clears the cookie but the Bearer token remains valid indefinitely (confirmed 200 after "logout") — no revocation mechanism exists |
High | Auth / session management | Open |
| BUG-047 | Password-reset OTP brute force: 15+ attempts accepted without lockout or 429, combined with BUG-034 (no reset rate limit) enables full account takeover |
High | Auth / API | Open |
| BUG-048 | Transaction approval endpoint leaks internal state: "Only undefined can approve this transaction" (null dereference); rejection endpoint returns 500 even when reason is provided |
Medium | API / stability | Open |
| BUG-049 | GET /api/proxy/groups/types and /groups/group-types return 500 — routing collision where "types" is passed as a Mongoose ObjectId |
Medium | API / routing | Open |
| BUG-050 | Stored XSS: group name accepts raw HTML including <script> tags without any sanitization — payload persists in DB, returned in API responses, exploitable in email/PDF/SSR contexts |
High | API / input validation | Open |
| BUG-051 | GET /api/proxy/roles/permissions and /roles/assign return 500 — same routing collision pattern as BUG-049 |
Medium | API / routing | Open |
| BUG-052 | GET /api/proxy/notifications/mark-all-read, /clear, /all return 500 (routing collision + unimplemented handler); clear broken on all methods |
Medium | API / routing | Open |
| BUG-053 | BOLA: PATCH /api/proxy/groups/:id/members/:memberId — any authenticated user can change the role of ANY member in ANY chama (confirmed on multiple real members) |
Critical | API / authz | Open |
| BUG-054 | M-Pesa STK callback endpoint publicly accessible with no auth, no IP allowlist, no Safaricom signature — forged success callbacks accepted, enabling fake contribution credits | Critical | API / M-Pesa / financial | Open |
| BUG-055 | ?limit=99999 dumps all 29 platform transactions from 7 chamas and 11 users in one request — no server-side pagination cap (amplifies BUG-030) |
High | API | Open |
| BUG-056 | NaN / Infinity / -Infinity in any numeric field crashes the handler with 500 Internal Server Error — DoS viable |
Medium | API / input validation | Open |
| BUG-057 | withDrawalFee (capital D) field name inconsistency causes silent update failure; sending the canonical name via PUT can null the field |
Medium | API / data model | Open |
| BUG-058 | OTP tokens stored in plaintext AND returned in current-user + signin API responses — any JWT holder can read all pending OTPs and reset the victim's password without email access |
Critical | Auth / API | Open |
| BUG-059 | Each OTP request creates a new record without invalidating previous ones — 34+ concurrent valid OTPs observed, compounding BUG-047 brute-force and BUG-058 plaintext exposure | High | Auth / API | Open |
| BUG-060 | Null bytes (\u0000) accepted and stored verbatim in group names and other string fields — enables filter bypass, log injection, and downstream null-termination truncation |
Medium | API / input validation | Open |
| BUG-061 | Strict-Transport-Security header present on API responses but absent from all HTML pages (homepage, login, admin) — SSL-strip downgrade attack viable on shared Wi-Fi |
High | Web / HTTP headers | Open |
| BUG-062 | SPF uses ~all (softfail) and DMARC uses p=quarantine — spoofed @chamaconnect.io emails delivered to recipient spam/junk, enabling phishing and credential harvesting |
Medium | Infrastructure / DNS | Open |
| BUG-063 | PATCH /api/proxy/users/update-profile changes email/firstName/lastName with no password re-entry, no OTP, no re-verification — one-shot ATO primitive for any leaked JWT; reproduced twice on live site |
Critical | Auth / Profile API | Open |
| BUG-064 | Password policy accepts "password", "password123", and eight-space strings; 6-char minimum, no blacklist, no complexity, no HIBP check |
High | Auth / signup | Open |
| BUG-065 | Signin performs exact-match on email — uppercase + whitespace variants all return 400 Invalid email or phone number; silent lockout + enumeration amplifier |
High | Auth / signin | Open |
| BUG-066 | Signin accepts ≥ 100 KB request bodies with no 413; bandwidth amplifier for credential-stuffing + log bloat |
Medium | Auth / infrastructure | Open |
| BUG-067 | POST /api/auth/session sets auth_token cookie to any supplied value without verifying JWT signature — session-fixation / XSS-amplifier |
Medium | Auth / Next.js bridge | Open |
| BUG-068 | No CAA DNS record on chamaconnect.io — any publicly-trusted CA in the world may issue certs for the domain |
Medium | DNS / TLS | Open |
| BUG-069 | Every unmatched path (including /.env, /.git/HEAD, /swagger, /api/health) returns a 26 KB HTML clone of the homepage — 130× bandwidth amplifier + soft-404 SEO |
Low | Public site / config | Open |
| BUG-070 | /users/signin accepts application/x-www-form-urlencoded — CORS-preflight bypass that becomes a full CSRF amplifier if any /api/proxy/* mutation endpoint also accepts it |
Medium | Auth / API content-type | Open |
| BUG-071 | ?from, ?to, ?since, ?createdAt filters accepted but silently ignored on /transactions, /notifications, /groups — dashboard widgets silently show all-time data instead of the filtered range |
High | API / data model | Open |
| BUG-072 | /api/proxy/users/admin uses ad-hoc role-name strings ("super admins" vs "admins") across methods — none match the canonical role taxonomy; 400 instead of 403 on authz failure |
Medium | API / authorisation | Open |
| BUG-073 | Email verification is enforced at signup (400 "Please verify your email before signing in") but bypassable via PATCH /users/update-profile — strengthens BUG-063 ATO chain |
High | Auth / profile API | Open |
| BUG-074 | /users/update-profile uses a correct write-allowlist (roleId/isSuperadmin/accountStatus can't be hijacked) but returns 200 on unknown keys — no 400 hard-reject, so client typos silently fail to persist |
Low | API contract | Open |
| BUG-075 | Every /api/proxy/* mutation endpoint accepts application/x-www-form-urlencoded and multipart/form-data — platform-wide CSRF surface (supersedes / escalates BUG-070 from "signin only" to "all mutations") |
High | API / content-type / CSRF | Open |
| BUG-076 | Live evidence of BUG-040: a BackendHack role created during probing still exists in the production roles table, visible to every signed-in user and surfaced in the Create-Chama wizard dropdown |
Critical | API / authz / data integrity | Open |
- Critical — a real user cannot achieve the core job of the product (e.g. collecting contributions).
- High — core trust/security/SEO issue visible to everyone.
- Medium — noticeable inconsistency or dead UX.
- Low — cosmetic / polish.
- Discover a bug (manual or via
/reconPlaywright run). - Create
BUG-NNN-slug.mdusing_template.md. - Add a row to the table above.
- Write a failing test (if feasible) in
/recon/tests. - Ship the fix in
/chamapay(or as a patch in/bugs/patches/BUG-NNN.patch). - Flip status to Fixed and link the PR / commit.