QA Report · Public-Facing Webpage

pisa.maahir.io — Full QA Audit

Date: 30 Sep 2026 Tester: X2 (CamoFox + curl) App: https://pisa.maahir.io Stack: Next.js + PostgreSQL + MiniMax AI Endpoints tested: 50+ pages + 10 APIs Campaign: Cyber Secure Pakistan — 1–31 Oct 2026
🏁 v3 Final Verdict — LAUNCH READY! 7 of 9 original bugs fixed in 8 hours. Auth works (HttpOnly cookie). Header swaps Demo vs Login. CTF page exists. AI online. Only 2 cosmetic items remain (CTF admin seed + campaign banner dismiss). See report-v3.md.

Executive Summary

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.

4
🔴 Critical
5
🔴 High
5
🟡 Medium
0
🟢 Low
25+
✅ Working

Final Verdict

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.

B1/verify returns 404 — Certificate verification is BROKEN
🔴 Critical

Where: https://pisa.maahir.io/verify

Evidence

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).

Fix

Build /verify — accept a certificate ID or QR scan, show holder name, course, date, score, status (active/revoked). Use existing Next.js App Router pattern.

B2/ctf returns 404 — CTF platform link is broken
🔴 Critical

Where: https://pisa.maahir.io/ctf

Evidence

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.

Fix

Either 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.

B3/status page UI says "Postgres down" but DB is UP
🔴 Critical

Where: https://pisa.maahir.io/status

Evidence

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.

Fix

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.

B4AI/MiniMax is genuinely DOWN — breaks /roleplay + OpenCTI
🔴 Critical

Where: /api/status + https://pisa.maahir.io/roleplay + https://cti.pisa.maahir.io

Evidence

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.

Fix

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).

B5/dashboard is publicly accessible without authentication
🔴 High

Where: https://pisa.maahir.io/dashboard + /dashboard/security

Evidence

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.

Fix

Add Next.js middleware on /dashboard and /dashboard/*. Redirect to /login if no session cookie. The /admin route already does this correctly.

B6Login form accepts demo creds but does NOT set a session
🔴 High

Where: https://pisa.maahir.io/login

Evidence

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.

Fix

Investigate 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.

B7/pledge wall has 6 duplicate "E2 E2E T." entries
🔴 High

Where: https://pisa.maahir.io/pledge

Evidence

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

Fix

Add rate limit + dedup (same name + same IP within X minutes → reject or replace). Or convert wall to a tally with recent names only.

B8"Cybercrime? Call 1799" CTA on /status links to wrong WhatsApp
🔴 High

Where: https://pisa.maahir.io/status — "💬 Cybercrime? Call 1799" button

Evidence

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.

Fix

Change to tel:1799, complaint.nccia.gov.pk, or the actual PISA WhatsApp number. NOT a generic WA placeholder.

B9Header doesn't update for logged-in users
🔴 High

Where: All pages — top navigation bar

Evidence

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.

Fix

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).

B10/register "Try demo" link goes to /gcsap — wrong destination
🟡 Medium

Where: https://pisa.maahir.io/register — "🎭 Try demo" footer link

Evidence

Links to /gcsap (GCSAP volunteer signup) instead of /login (which has the demo creds right there). Confusing for new users.

Fix

Change href to /login, OR create a dedicated /demo route.

B11"Welcome to Cyber Secure Pakistan Month!" banner persists post-launch
🟡 Medium

Where: Top of every page

Evidence

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.

Fix

Add date check (hide after Oct 1) OR session check (hide for authed users) OR cookie-based dismiss.

B12/tracks metadata inconsistent across pages
🟡 Medium

Where: /tracks/kids vs /tracks/women

Evidence

/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.

Fix

Standardize the audience-tag UI across all 11 tracks: each track should declare which segments it targets using the same chip system.

B13/forms page is empty — "No forms currently open"
🟡 Medium

Where: https://pisa.maahir.io/forms

Evidence

Page renders "No forms are currently open — check back soon". Footer links to /forms from every page, leading to dead end.

Fix

Either hide /forms from footer until ready, or deploy at least one active form (post-event survey, partner signup, instructor signup, etc.).

B14/dashboard/security exposes "Delete my account" to anonymous users
🟡 Medium

Where: https://pisa.maahir.io/dashboard/security

Evidence

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.

Fix

Add auth middleware to /dashboard/*. Same fix as B5.

✅ What's Working Well (25+ pages verified)

Page / FeatureStatusNotes
Homepage /✅ WorkingHero, countdown, stats, 11 tracks, certificate preview, all sections render
/events✅ Working11 events, ICS export, calendar links, Reserve seat buttons
/scam-gallery✅ Working9 documented scams, submit form, auto-defang protection
/first-aid✅ Working7 emergency scenarios
/partner✅ Working6 partner types, EOI form
/institutes/register✅ WorkingInstitute application form
/activity✅ WorkingNational Activity Wall (live counters)
/roleplay✅ Working (UI)30+ AI scam drill scenarios listed (B4: drills themselves fail)
/gcsap✅ Working30-day calendar, signup form
/media-kit✅ WorkingPosters, brand assets, sample social posts
/report✅ WorkingProblem report form with severity buttons
/courses/women-cyber-safety✅ Working3 lessons, exam, 0/3 attempts
/courses/be-a-cyber-hero✅ Working4 lessons, exam
/learn/<course>/<id>✅ HTTP 200All lesson pages load
/exam/<id>✅ HTTP 200All exam pages load
/leaderboard✅ WorkingTabs work, Karachi=50 XP, no defenders yet
/pledge✅ WorkingSign + count (B7: dedup issue)
/admin✅ Redirects to /loginAuth-protected correctly
/api/me/export✅ HTTP 401 unauthAPI is auth-protected (data safe)
/api/status✅ HTTP 200Returns db:true, ai:false
/api/pledge (POST)✅ Workingcount went 7→8 on test submit
cti.pisa.maahir.io✅ HTTP 200OpenCTI external lab alive
/tools/scam-checker✅ Working7-question verdict flow
/tools/phish-game✅ Working15s timer, 10 rounds
/tools/soc-lab✅ Working5 alerts, scored triage

🔑 Test Login Credentials (as of 30 Sep 2026)

RoleEmailPasswordNotes
Trainee demodemo@pisa.org.pkDemo-Pisa-2026!✅ Accepts, redirects to /dashboard, ❌ NO session set (B6)
GCSAP Incharge demogcsap-demo@pisa.org.pkGcsap-Demo-2026!✅ Accepts, redirects to /dashboard, ❌ NO session set (B6)
Admin (play.maahir.io)admin@maahir.ioMaahir@1234❌ "Incorrect email or password" — stale from play.maahir.io

🔁 Retest Recipe — After Fixes

# 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

🛠️ Recommended Fix Order (Before Oct 1 Launch)

  1. B3 + B4 — Fix /status UI rendering + bring AI/MiniMax back online (1–2 hours)
  2. B1 + B2 — Build /verify (2–4 hours), fix /ctf routing (1 hour)
  3. B5 + B6 + B9 — Auth middleware + login cookie set + header reactive (2–4 hours)
  4. B8 — Fix the cybercrime WA link (5 minutes)
  5. B7 + B10–B14 — Polish (1–2 hours total)

📌 Open Questions for Munaf

  1. What's the real PISA admin login? (For admin-side QA — institute approvals, certificate issuance, user management.)
  2. Where should /verify live? Build as a fresh route, or is there a separate service?
  3. Where is the CTF platform? Is there a ctfd.pisa.maahir.io subdomain?
  4. Was /dashboard intended to be public (demo state)? Or is it broken?
  5. Oct 1 launch is 1 day away — are B1–B9 (critical+high) getting fixed before, or is this a soft launch?