Skip to content

CodeWithEugene/Chama-Connect

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ChamaConnect

ChamaConnect Hackathon Submission — ChamaPay

Entry for: MUIAA Ltd × Salamander Community — ChamaConnect Virtual Hackathon
Theme: Reimagining Digital Chamas for the Future
Deadline: Friday, 2026-04-24 at 23:59 EAT
Entrant: Eugene Mutembei (eugenegabriel.ke@gmail.com) & Sidney Muriuki (sidneybarakamuriuki1@gmail.com)

What we built: an M-Pesa-native contribution auto-reconciliation module that closes the single biggest gap chamaconnect.io has today — the feature its own features page marks "Coming Soon." Drop-in Next.js, double-entry ledger, idempotent Daraja callbacks, USSD access for feature phones, on-chain anchoring of daily settlements.


Repository layout

Chama-Connect/
├── README.md                   ← you are here
├── CONTRIBUTING.md             ← how to contribute, PR checks, bug workflow
├── LICENSE
├── .env                        ← login creds to chamaconnect.io (gitignored)
├── .env.example                ← root env template (recon)
│
├── recon/                      ← Playwright recon of the live platform
│   ├── tests/explore.spec.ts   ← logs in, crawls dashboard, records every XHR
│   └── artifacts/<timestamp>/  ← screenshots, HTML, network logs per run
│
├── bugs/                       ← bug register (evidence + root cause + fix)
│   ├── README.md               ← index + severity scale
│   ├── _template.md            ← filing template
│   └── BUG-NNN-*.md            ← one file per bug (001–076 today)
│
├── chamapay/                   ← the deliverable (standalone Next.js app)
│   ├── src/
│   │   ├── app/                ← Next.js App Router: UI + API routes
│   │   │   ├── chamas/[code]/  ← live dashboard
│   │   │   └── api/mpesa/...   ← Daraja webhooks
│   │   ├── lib/
│   │   │   ├── daraja/         ← STK Push, C2B register, B2C, TxStatus
│   │   │   ├── reconciliation/ ← deterministic matching engine + tests
│   │   │   ├── anchor/         ← Merkle tree + Base Sepolia anchor CLI
│   │   │   ├── sms/            ← async outbox
│   │   │   └── db/             ← SQLite schema, migrate, seed
│   │   └── ...
│   ├── var/chamapay.sqlite     ← local DB (created by migrate)
│   └── .env.example
│
└── docs/
    ├── TECHNICAL-PROPOSAL.md   ← judges' technical write-up
    ├── DEMO.md                 ← 90-second demo script
    └── Anchor.sol              ← reference on-chain contract

Quick start — see it run in 90 seconds

git clone <this-repo>
cd Chama-Connect/chamapay
cp .env.example .env.local       # Daraja creds optional for local demo
npm install
npm run db:migrate && npm run db:seed
npm run dev                      # http://localhost:3100

Open http://localhost:3100/chamas/ACME.

Step-by-step narration for judges: docs/DEMO.md.

In another shell, fire a simulated M-Pesa payment through the real reconciliation engine:

curl -X POST http://localhost:3100/api/dev/simulate-c2b \
     -H 'content-type: application/json' \
     -d '{"msisdn":"254711223344","amount":500,"billRef":"ACME-202604"}'

The dashboard updates within a few seconds; the payment shows as matched at 100% confidence to member Brian Otieno for cycle 2026-04.

Run the test suite:

npm test
# 6 reconciliation tests: exact match, idempotency, MSISDN fallback,
# unmatched path, double-entry balance, mixed-format period parsing.

The headline bug we are solving (BUG-007)

From chamaconnect.io/features:

M-pesa Blockchain Integration — M-pesa and bank integration (Coming Soon) will enable seamless deposits, withdrawals, and loan repayments.

In Kenya, ~99% of chama money moves on M-Pesa. Without reconciliation, every chama admin still reads their M-Pesa SMS inbox line-by-line and types amounts into the platform manually — which is exactly the mechanism behind FSD Kenya's documented 13% chama embezzlement rate.

We built the fix in this repo. See docs/TECHNICAL-PROPOSAL.md for the full write-up.

Bugs identified on the live site (chamaconnect.io)

Each row links to a standalone report (evidence, impact, root cause, proposed fix, verification). The canonical index and filing workflow live in bugs/README.md.

ID Title Severity Status
BUG-001 Every public page ships with default Next.js boilerplate <title> + <meta description> High Open
BUG-002 Footer Features, Pricing, Resources, Blog, Community, Events all point to # Medium Open
BUG-003 Contact page phone number does not match footer / hackathon-brief contact number Medium Open
BUG-004 Contact page renders literal [email protected] instead of an email address Medium Fixed (2026-04-20)
BUG-005 Login has no 2FA, no phone OTP, no social login — for a money platform High Open
BUG-006 Register country selector defaults to International despite Kenya focus Low Open
BUG-007 M-Pesa integration marked "Coming Soon" — the #1 Kenyan chama requirement Critical Open → fixed by ChamaPay module
BUG-008 POST /users/signin returns "message": "User Created" on every login High Open
BUG-009 MERRRY_GO_AROUND typo (triple-R, wrong phrase) in group-types endpoints and Create Chama dropdown High Open
BUG-010 Two different group-types endpoints with inconsistent (swapped) schemas High Open
BUG-011 Notifications page opens ws://localhost:3080 in production — real-time notifications broken Critical Open
BUG-012 /admin/chamas throws TypeError: Failed to fetch and shows "create your first chama" on network errors High Open
BUG-013 Signin returns the raw JWT in the response body (also in httpOnly cookie) — XSS-to-takeover path High Open
BUG-014 /contact throws React error #418 (hydration mismatch) Medium Open (not reproduced 2026-04-20)
BUG-015 Every role record has permissions: [] — authz likely enforced by role name only High Open
BUG-016 Signin JWT has no exp/nbf/jti/iss/aud — tokens never expire, can't be revoked Critical Open
BUG-017 No Content-Security-Policy; HTML documents ship without HSTS / X-Frame-Options / X-Content-Type-Options / Referrer-Policy / Permissions-Policy High Open
BUG-018 Signin rate limit is 1000 req / 15 min per IP, no per-account lockout — brute-force viable High Open
BUG-019 /api/auth/token returns 401 on every public page load (should be 200 with {token:null}) Medium Open
BUG-020 /api/proxy/users/current-user message says "Successfully retrieved logged in user" (double space) Low Open
BUG-021 /api/proxy/roles omits permissions; /users/current-user.role includes permissions:[] — same resource, two shapes Medium Open
BUG-022 Login form inputs have no name and no autocomplete — password managers break, WCAG 1.3.5 fails Medium Open
BUG-023 /contact "Send Us a Message" inputs have no name, no id, no aria-label Medium Open
BUG-024 /about has 3 <h1> tags; /contact and /faqs each have 2 — SEO + a11y Low Open
BUG-025 Every admin/dashboard page ships with <title>Create Next App</title> — tab labels unusable Medium Open
BUG-026 X-Powered-By: Next.js + x-nextjs-* headers leak backend stack on every HTML response Low Open
BUG-027 Any authenticated User can PUT /api/proxy/settings/:id — platform-wide fees and likely M-Pesa callback URLs are attacker-controlled Critical Open
BUG-028 GET /api/proxy/settings returns M-Pesa Daraja ConsumerKey / ConsumerSecret / LipaNaMpesaShortPass to every signed-in user Critical Open
BUG-029 BOLA: GET /api/proxy/groups/:id returns any chama's full data + members' PII (names, emails, phones) to any authenticated user Critical Open
BUG-030 BOLA: GET /api/proxy/transactions returns every chama's transactions (amounts, approvals, crypto hashes) to every signed-in user Critical Open
BUG-031 Signin reveals which emails/phones are registered (differential error + size + timing) High Open
BUG-032 Signup leaks registration status via "Error creating user…" on existing emails High Open
BUG-033 Internal backend reachable from the internet at /backend/api/v1/* — doubles attack surface High Open
BUG-034 /api/proxy/users/request-password-reset has no per-account rate limit — mail bombing + SMS cost attack High Open
BUG-035 GET /api/proxy/permissions returns 201 Created with a role-list payload (routing + status bug) Medium Open
BUG-036 GET /api/proxy/notifications/all returns 500 Internal Server Error on every call Medium Open
BUG-037 Authorization failures return 400 Bad Request instead of 401/403 across signin + group + user endpoints Medium Open
BUG-038 Signup response contradictory status fields (isActive:false + accountStatus:"ACTIVE" + activatedAt populated) Medium Open
BUG-039 Signin/password-reset accept MongoDB operator objects → 500 crash (latent NoSQL injection) High Open
BUG-040 Any User can POST /api/proxy/roles (create roles) + PATCH /api/proxy/roles/:id (rename SuperAdmin) Critical Open
BUG-041 GET /api/proxy/transactions?userId=<victim> returns that user's full financial history (IDOR); no pagination Critical Open
BUG-042 DELETE /api/proxy/groups/:id response embeds full M-Pesa Daraja credentials in GroupSettings Critical Open
BUG-043 POST /api/proxy/notifications returns 500 on every call; no role guard Medium Open
BUG-044 Path traversal: GET /api/proxy/groups/../settings resolves to /settings, bypassing route guards — 10+ cross-path variants confirmed including M-Pesa credential exfiltration Critical Open
BUG-045 CORS: localhost:3000 gets duplicate ACAO: localhost, chamaconnect.io + ACAC: true, true — any localhost JS can make authenticated cross-origin requests High Open
BUG-046 JWT remains valid after logout — DELETE /api/auth/session only clears cookie; Bearer token confirmed working post-"logout" with no expiry High Open
BUG-047 Password-reset OTP brute force: 15+ attempts, zero rate limit or lockout — enables full account takeover via OTP exhaustion High Open
BUG-048 Transaction approve leaks "Only undefined can approve" (null dereference); reject returns 500 even with reason Medium Open
BUG-049 GET /api/proxy/groups/types returns 500 — routing collision, "types" treated as MongoDB ObjectId Medium Open
BUG-050 Stored XSS: group name stores raw <script> tags without sanitization — persists in DB, returned in API responses High Open
BUG-051 GET /api/proxy/roles/permissions + /roles/assign return 500 (routing collision) Medium Open
BUG-052 /notifications/mark-all-read (wrong method), /clear (all methods), /all all return 500 Medium Open
BUG-053 BOLA: any user can PATCH /groups/:id/members/:memberId to change ANY chama member's role — including promoting strangers to Treasurer/ChamaAdmin Critical Open
BUG-054 M-Pesa callback endpoint publicly reachable, no auth/IP/signature validation — forged STK callbacks can fake contribution payments Critical Open
BUG-055 ?limit=99999 dumps entire platform transaction ledger in one request (29 transactions, 7 chamas, 11 users) High Open
BUG-056 NaN / Infinity in any numeric field → 500 crash on every affected endpoint (DoS) Medium Open
BUG-057 withDrawalFee field casing inconsistency silently drops updates; sending canonical name nulls the field Medium Open
BUG-058 OTP tokens stored in plaintext and returned in current-user/signin responses — JWT holder can read & use any pending OTP without email access Critical Open
BUG-059 New password-reset OTP request does not invalidate prior OTPs — 34+ concurrent valid codes observed High Open
BUG-060 Null bytes (\u0000) stored verbatim in group names — enables filter bypass and downstream truncation Medium Open
BUG-061 HSTS header present on API responses but absent on all HTML pages (login, homepage, admin) High Open
BUG-062 SPF ~all softfail + DMARC p=quarantine allow spoofed @chamaconnect.io emails to reach spam Medium 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 account takeover for any leaked JWT; reproduced twice on live site Critical Open
BUG-064 Password policy accepts "password", "password123", and eight-space strings; 6-char minimum, no blacklist, no complexity, no HIBP check High Open
BUG-065 Signin is email-case-sensitive + does not trim whitespace — EUGENEGABRIEL.KE@GMAIL.COM returns 400 Invalid email; silent lockout + enumeration amplifier High Open
BUG-066 Signin accepts ≥ 100 KB request bodies with no 413 cap — bandwidth / log-bloat amplifier for credential-stuffing Medium Open
BUG-067 POST /api/auth/session sets the auth_token cookie to any supplied string without verifying the JWT signature — session-fixation / XSS amplifier (SameSite=Strict mitigates direct browser-CSRF but not the design flaw) Medium Open
BUG-068 No CAA DNS record on chamaconnect.io — any publicly-trusted CA in the world may issue certs for the domain Medium Open
BUG-069 Every unmatched path (/.env, /.git/HEAD, /swagger, /api/health, …) returns a 26 KB HTML clone of the homepage — 130× bandwidth amplifier + soft-404 SEO Low 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 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 Open
BUG-072 /api/proxy/users/admin uses ad-hoc role-name strings ("super admins" on GET vs "admins" on DELETE) — neither matches the canonical role taxonomy; 400 returned instead of 403 Medium Open
BUG-073 Email verification enforced at signup ("Please verify your email before signing in") but bypassable via PATCH /users/update-profile — strengthens BUG-063 ATO chain High 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 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 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 Open

Severity (short): Critical → core job blocked or security-critical data exposure/modification; High → trust, security, or major product surface; Medium → clear UX or consistency break; Low → polish / conversion nits.

Totals — 76 bugs on file (1 fixed, 75 open)

  • 16 Critical — direct financial harm, secrets exposure, one-shot ATO, or persisted attacker artefacts (BUG-007 · 011 · 016 · 027 · 028 · 029 · 030 · 040 · 041 · 042 · 044 · 053 · 054 · 058 · 063 · 076).
  • 27 High — auth hardening, BOLA reads, session lifetime, rate limits, stored XSS, filters silently ignored, weak password policy, HSTS, email-verification bypass, platform-wide CSRF surface, SEO metadata, other exploit amplifiers.
  • 27 Medium — consistency, routing collisions, API shape, accessibility, DNS / TLS posture.
  • 6 Low — metadata polish, copy typos, stack-leak headers, silent API mis-writes.

Audit methodology — how those bugs were found

The register is backed by three distinct, reproducible evidence trails — all under recon/:

  1. Authenticated platform crawlrecon/tests/explore.spec.ts + recon/tests/deep-interact.spec.ts log in with the .env credentials, crawl every admin / chama / profile / settings route, and persist every XHR + response body to recon/artifacts/deep-*/network/. This is the source for BOLA (BUG-029/030/041/053), path traversal (BUG-044), credential leaks (BUG-028/042), roles CRUD (BUG-040), M-Pesa callback exposure (BUG-054) and most routing bugs.
  2. Extended audit probes — three passes under recon/tests/audit-extended*.spec.ts cover the OWASP API Top-10 surface:
    • Pass 1 (20 probes) — static exposure, HTTP method tampering, content-type bypass, CSRF / cookies / logout, numeric edges, payload size, prototype pollution, open redirect, clickjacking, password strength, email-change / verification, cache-control, wallet-repair flag, CORS preflights, HEAD vs GET.
    • Pass 2 (8 probes) — mass assignment, file upload, form-urlencoded mutations, Daraja callback forgery, emailVerified reset, WebSocket auth, /users/admin, date-filter parsing.
    • Pass 3 (5 probes) — mass-assignment with valid base fields (to bypass field validation), profile-picture upload variants, form-urlencoded across all mutation endpoints, Create-Chama wizard walk-through with network capture, supply-chain fingerprints from captured bundles. Each probe writes its raw artifact to recon/artifacts/audit*/NN_*.json so every bug cites a specific capture. Mutation probes include explicit revert paths so repeat runs are idempotent.
  3. Static analysis — captured client bundles in recon/probes/bundles/*.js plus DNS / TLS data in recon/artifacts/dns-tls-audit/ were searched offline for hardcoded localhost URLs (BUG-011), exposed source maps, dead API paths, /superadmin/dashboard references, SPF / DMARC / CAA posture (BUG-062 / BUG-068) and similar.

Re-run any pass end-to-end with:

cd recon && npm install && npx playwright install chromium
npm test                                            # explore + deep-interact
npx playwright test tests/audit-extended.spec.ts    # pass 1 (20 probes)
npx playwright test tests/audit-extended-2.spec.ts  # pass 2 (8 probes)
npx playwright test tests/audit-extended-3.spec.ts  # pass 3 (5 probes)

Pass-2 probes that mutate data (21 mass-assignment, 22 file upload, 23 form-urlencoded mutations, 25 emailVerified reset) gracefully skip unless a fresh probe account with a completed email-verification OTP is available. Pass-3 mutation probes run against Eugene's account and include explicit reverts — profile picture, first/last name, and email are restored to their original values at the end of each test so repeat runs are idempotent and side-effect-free against the live platform. One-shot side-effects from earlier probe runs (a handful of @probe.local accounts, +25470… phone slots, and the BackendHack role) are listed in BUG-064, BUG-073, and BUG-076 for MUIAA to purge.

Reproducing the authenticated recon

Login creds live in ./.env at the repo root (gitignored). Copy from .env.example. The Playwright recon logs in, crawls every authenticated route, screenshots each, records every XHR, and dumps JSON to recon/artifacts/<timestamp>/:

cd recon
npm install
npx playwright install chromium
npm test

Every run produces:

  • screenshots/ — full-page screenshots of every route visited
  • html/ — full rendered HTML of every route
  • network/requests.json — every XHR / fetch / document with request + response body
  • summary.json — one-line overview

Large artifact trees stay gitignored; retain representative runs for submission evidence if needed.

Contributing

See CONTRIBUTING.md for local setup in chamapay/ and recon/, commands to run before a PR (lint, typecheck, test, build), and how to file new bugs under bugs/.

License

MIT. See LICENSE.

About

A comprehensive security audit and technical optimization suite for ChamaConnect: resolving logic vulnerabilities and enhancing transactional integrity.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages