PISA Pakistan is a comprehensive national cyber-security awareness platform built on Next.js with a 4-week campaign (1–31 Oct 2026). It has 50+ working pages, a fully functional CTF/roleplay/leaderboard system, and a rich content library covering scams, courses, events, partnerships, and toolkits.
However, several critical features are broken or missing: certificate verification, the CTF platform, and the AI scam-drill engine (MiniMax is offline). Authentication flows accept demo credentials but do not actually persist a session. The /status page UI mis-reports the database as down when it is actually up. None of these block browse-only traffic, but they break the platform's value proposition for logged-in users — and there is 1 day to launch.
The platform is launch-ready for browse-only users (anonymous public traffic works well — homepage, events, scam-gallery, first-aid, partner, institutes, activity, roleplay UI, courses, leaderboard, pledge, media-kit, gcsap, reports).
However, the logged-in experience is broken in multiple ways: certificates can't be verified, AI drills fail (MiniMax down), login doesn't persist sessions, and /dashboard is publicly accessible without auth. Recommendation: Fix B1–B4 (critical) and B5–B9 (high) before Oct 1 launch. The polish items (B10–B14) can wait until week 2.
Where: https://pisa.maahir.io/verify
Page returns "404 — This page could not be found." This page is linked from every page's footer and is the platform's core promise ("QR-Verified Certificates" marketed on the homepage).
curl -sI https://pisa.maahir.io/verify → HTTP/2 404/verify on every pageBuild /verify — accept a certificate ID or QR scan, show holder name, course, date, score, status (active/revoked). Use existing Next.js App Router pattern.
Where: https://pisa.maahir.io/ctf
CTFd instance is installed (footer reads "Powered by CTFd") but the index page is missing. Linked from dashboard quick-tools, events page, roleplay scenarios, and the homepage "🚩 CTF" track. The Oct 24 "National CTF Qualifiers (youth)" event points users here.
/ctf/users, /ctf/scoreboard, /ctf/challenges may also be missingEither mount CTFd at /ctf path, or create a landing page that redirects to a live CTFd subdomain (e.g. ctfd.pisa.maahir.io). Alternatively update the dead links to point to the working subdomain.
Where: https://pisa.maahir.io/status
UI shows: ✗ Postgres down ✗ AI (MiniMax) down ✓ App online. The underlying API returns the correct values:
curl -s https://pisa.maahir.io/api/status
{"status":"degraded","uptimeSec":226664,"services":{"db":true,"ai":false,"app":true},...}
The UI is inverting or swapping the db/ai booleans. AI IS genuinely down (MiniMax provider offline) — but DB is up.
Inspect the /status page component. Most likely a `services.db` ↔ `services.ai` field swap in the JSX. After fix, only "AI (MiniMax) down" should remain red.
Where: /api/status + https://pisa.maahir.io/roleplay + https://cti.pisa.maahir.io
API confirms services.ai: false. The AI Scam Drill (/roleplay) is the platform's flagship differentiator and is rendered non-functional. OpenCTI lab (cti.pisa.maahir.io) is also AI-dependent.
Check MiniMax provider config + API key. Restart the AI service. Confirm /api/status recovers ai: true. If MiniMax provider is down globally, swap to a backup provider (Groq, Chutes, OpenRouter all available per Hermes provider list).
Where: https://pisa.maahir.io/dashboard + /dashboard/security
Hitting /dashboard directly with no cookie renders the full "Demo" dashboard — name "Demo", "Cyber Recruit" level, 0 XP, recommended course, my activity/exams/certificates/badges sections, "Logout" button. Same content shown to logged-in and anonymous users. The /dashboard/security subpage similarly shows "Delete my account" + "Download my data" buttons to anyone.
document.cookie = "", localStorage = {...} empty of auth tokens/api/me/export → HTTP/2 401 unauthenticatedAdd Next.js middleware on /dashboard and /dashboard/*. Redirect to /login if no session cookie. The /admin route already does this correctly.
Where: https://pisa.maahir.io/login
Submitting demo@pisa.org.pk / Demo-Pisa-2026! or gcsap-demo@pisa.org.pk / Gcsap-Demo-2026! returns "Incorrect email or password" — wait, no. Demo creds ARE accepted. They redirect to /dashboard. But no cookie is set, no localStorage, no HttpOnly auth. Reload = logged out.
document.cookie = ""admin@maahir.io / Maahir@1234 (the play.maahir.io admin) → "Incorrect email or password" — wrong systemInvestigate login API. Either Set-Cookie header is missing, cookie is HttpOnly (good) but not being sent back, or login bypasses real auth and routes to a mock page. The /admin route correctly redirects — same pattern needed for /login response.
Where: https://pisa.maahir.io/pledge
Latest signatures section shows: 6× "E2 E2E T. · Karachi" + 1× "AM Ammar J. · Islamabad". Tested the POST API — single submission increments count from 7→8 (so the form is NOT auto-duplicating). Either someone tested 6 times, or there's no de-duplication by name+IP.
curl -X POST https://pisa.maahir.io/api/pledge \
-H "Content-Type: application/json" \
-d '{"name":"X2-Test","city":"Karachi"}'
→ {"ok":true,"count":8} ✓ single submission works
Add rate limit + dedup (same name + same IP within X minutes → reject or replace). Or convert wall to a tally with recent names only.
Where: https://pisa.maahir.io/status — "💬 Cybercrime? Call 1799" button
Button links to https://wa.me/?text=Hello%20PISA%20support — a generic WhatsApp prefill with NO phone number. On a status page that advertises cybercrime reporting, this is misleading.
Change to tel:1799, complaint.nccia.gov.pk, or the actual PISA WhatsApp number. NOT a generic WA placeholder.
Where: All pages — top navigation bar
After login, the header still shows "Login / Register Free" buttons. The "Logout" button only appears inside /dashboard. Across every other page (/events, /courses, /scam-gallery, etc.), authenticated users see the same nav as anonymous.
Header should conditionally render based on auth state — show user avatar/menu + Dashboard + Logout when authed; show Login + Register Free when not. Will require actual auth working first (B6).
Where: https://pisa.maahir.io/register — "🎭 Try demo" footer link
Links to /gcsap (GCSAP volunteer signup) instead of /login (which has the demo creds right there). Confusing for new users.
Change href to /login, OR create a dedicated /demo route.
Where: Top of every page
Yellow marketing banner "📢 Welcome to Cyber Secure Pakistan Month!details →" appears on every page including dashboard. After Oct 1 the campaign is live — banner should auto-hide, or hide for logged-in users.
Add date check (hide after Oct 1) OR session check (hide for authed users) OR cookie-based dismiss.
Where: /tracks/kids vs /tracks/women
/tracks/kids shows "👧 Kids 8-12 · All ages welcome" (2 tags). /tracks/women shows "📱 Teens 13-19 · 🎓 Youth · 👤 Adults · All ages welcome" (4 tags). Every track should use the same audience-tag format.
Standardize the audience-tag UI across all 11 tracks: each track should declare which segments it targets using the same chip system.
Where: https://pisa.maahir.io/forms
Page renders "No forms are currently open — check back soon". Footer links to /forms from every page, leading to dead end.
Either hide /forms from footer until ready, or deploy at least one active form (post-event survey, partner signup, instructor signup, etc.).
Where: https://pisa.maahir.io/dashboard/security
Page renders fully without auth — "Delete my account" button + "Download my data" link both shown. API endpoint (/api/me/export) IS auth-protected (returns 401), so data is safe, but the button presence is misleading.
Add auth middleware to /dashboard/*. Same fix as B5.
| Page / Feature | Status | Notes |
|---|---|---|
| Homepage / | ✅ Working | Hero, countdown, stats, 11 tracks, certificate preview, all sections render |
| /events | ✅ Working | 11 events, ICS export, calendar links, Reserve seat buttons |
| /scam-gallery | ✅ Working | 9 documented scams, submit form, auto-defang protection |
| /first-aid | ✅ Working | 7 emergency scenarios |
| /partner | ✅ Working | 6 partner types, EOI form |
| /institutes/register | ✅ Working | Institute application form |
| /activity | ✅ Working | National Activity Wall (live counters) |
| /roleplay | ✅ Working (UI) | 30+ AI scam drill scenarios listed (B4: drills themselves fail) |
| /gcsap | ✅ Working | 30-day calendar, signup form |
| /media-kit | ✅ Working | Posters, brand assets, sample social posts |
| /report | ✅ Working | Problem report form with severity buttons |
| /courses/women-cyber-safety | ✅ Working | 3 lessons, exam, 0/3 attempts |
| /courses/be-a-cyber-hero | ✅ Working | 4 lessons, exam |
| /learn/<course>/<id> | ✅ HTTP 200 | All lesson pages load |
| /exam/<id> | ✅ HTTP 200 | All exam pages load |
| /leaderboard | ✅ Working | Tabs work, Karachi=50 XP, no defenders yet |
| /pledge | ✅ Working | Sign + count (B7: dedup issue) |
| /admin | ✅ Redirects to /login | Auth-protected correctly |
| /api/me/export | ✅ HTTP 401 unauth | API is auth-protected (data safe) |
| /api/status | ✅ HTTP 200 | Returns db:true, ai:false |
| /api/pledge (POST) | ✅ Working | count went 7→8 on test submit |
| cti.pisa.maahir.io | ✅ HTTP 200 | OpenCTI external lab alive |
| /tools/scam-checker | ✅ Working | 7-question verdict flow |
| /tools/phish-game | ✅ Working | 15s timer, 10 rounds |
| /tools/soc-lab | ✅ Working | 5 alerts, scored triage |
| Role | Password | Notes | |
|---|---|---|---|
| Trainee demo | demo@pisa.org.pk | Demo-Pisa-2026! | ✅ Accepts, redirects to /dashboard, ❌ NO session set (B6) |
| GCSAP Incharge demo | gcsap-demo@pisa.org.pk | Gcsap-Demo-2026! | ✅ Accepts, redirects to /dashboard, ❌ NO session set (B6) |
| Admin (play.maahir.io) | admin@maahir.io | Maahir@1234 | ❌ "Incorrect email or password" — stale from play.maahir.io |
# Verify critical bugs are fixed:
echo "=== B1: /verify ==="
curl -sk -o /dev/null -w "%{http_code}\n" https://pisa.maahir.io/verify
# expect: 200
echo "=== B2: /ctf ==="
curl -sk -o /dev/null -w "%{http_code}\n" https://pisa.maahir.io/ctf
# expect: 200 (or 301 to ctfd.pisa.maahir.io)
echo "=== B3 + B4: /api/status ==="
curl -s https://pisa.maahir.io/api/status | python3 -c "import json,sys; d=json.load(sys.stdin); print('db:',d['services']['db'],' ai:',d['services']['ai'])"
# expect: db: True ai: True
echo "=== B5/B6: dashboard auth ==="
curl -sk -o /dev/null -w "%{http_code} %{redirect_url}\n" https://pisa.maahir.io/dashboard
# expect: 307 → https://pisa.maahir.io/login (when no cookie)
echo "=== B7: pledge API dedup ==="
for i in 1 2 3 4 5; do
curl -s -X POST https://pisa.maahir.io/api/pledge -H 'Content-Type: application/json' -d '{"name":"Dedup-Test","city":"Karachi"}'
done
# expect: only first succeeds, rest 429 or 4xx
echo "=== B8: status WA link ==="
curl -s https://pisa.maahir.io/status | grep -oE 'wa.me[^"]*'
# expect: real wa.me/ or tel:1799, NOT wa.me/?text=Hello