SchoolPulse — QA Report

Target: school.34.jugaar.ai · Date: 2026-08-27 · Tester: X2 automated + manual flow

Ships With Issues

Core complaint flow + AI work end-to-end. The entire SPA-fallback routing is broken, the marquee "decoy password" feature is marketing-only, and a critical PHP error page crashes with a server-path leak.

14Verified Working
4Critical Bugs
6High Severity
5Medium
23Total Findings

1. What Works — Verified Implementation

PASS
Registration → onboarding → login — full flow works

Created qa_test_1787797203 / TestPass123!, walked through 7-step onboarding, logged out, logged back in with username. Field name is login on the form, username on register — minor inconsistency.

PASS
Anonymous handle assigned — random animal+number pattern

Got handle SharpFalcon302 on my filed complaint. Marketing claims "BraveFalcon342" — the pattern matches.

PASS
Complaint filing + auto AI verification

POST /complaints/new → 302 to /complaints/view?token=035670eab5575f47f2457b2cb677d4d1. Status auto-set to verified, auto-categorized to Sanitation / Cleanliness within ~30 seconds. Public view shows only anonymous handle — no user PII leaked.

PASS
Vouching works with proper anti-self-vouch

POST /api/vouch with another user's cid → {"vouched":true,"vouch_count":1,"status":"verified"}. Trying to vouch my own complaint (cid=23) → {"error":"Cannot vouch your own complaint"}. CSRF enforced (mismatched token → 403).

PASS
AI chat endpoint (auth-gated)

POST /api/ai/chat with prompt about school toilets → returns structured Pakistani-school context advice. Returns 401 unauth — proper gating.

PASS
AI draft endpoint (auth-gated) — strongest feature

POST /api/ai/draft with "GHS Kotla toilet is dirty" → returns full structured object: title, description, category="sanitation", category_id=6, severity=3, sentiment="negative", confidence=0.7, school{school,branch,city,province,district,geo,confidence}, missing[], council. Excellent schema.

PASS
Schools API + Geo API return real JSON

GET /api/schools → 2 schools (PECHS Govt Girls College + Programmer Girls School). GET /api/geo.php?type=schools_map → 2 markers with lat/lng. Both Content-Type: application/json.

PASS
Dashboard live counters update in real time

After filing + vouches: 3 → 4 Complaints, 5 → 6 Voices. Trending chart picked up new "Sanitation/Cleanliness" category automatically. School rankings, district clustering (Karachi East: 4 complaints), category funnel, status funnel all render.

PASS
Schools/finder.php, /schools/compare.php, /schools/view?slug=… all return real distinct pages

Finder has typo-tolerant search; compare.php has a real comparison view; per-school view shows 30-facility tracker, complaints, location.

PASS
Auth gating on admin routes

Unauth /admin/users → 302 /auth/login. Logged-in regular user /admin → 403 "Super Admin only". Logged-in user /admin/users → 302 to /pages/error.php?c=403 (which itself crashes — see bug #1).

PASS
Security headers present

HSTS (max-age=31536000; includeSubDomains), CSP (default-src 'self' with cdn exceptions), X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin.

PASS
PWA infrastructure complete

manifest.json valid (with icon-192/512), sw.js caches dashboard/roadmap/CSS and serves offline.html fallback, IndexedDB draft store declared.

PASS
CSRF tokens enforced on state-changing endpoints

Mismatched or missing _csrf on POST → 403 Invalid CSRF token. Per-session token rotation on login.

PASS
Public complaint view is genuinely anonymous

Token-based share URL shows: title, description, category, school, anonymous handle, share count, view count. No user_id joined, no email/phone/IP surfaced.

2. Critical Bugs — Fix Before Public Launch

CRIT #1
/pages/error.php crashes with PHP fatal + server path leak

GET /pages/error.php?c=403

Fatal error: Uncaught Error: Undefined constant "SESSION_NAME" in /home/school/public/pages/error.php:3
Stack trace:
#0 {main}
thrown in /home/school/public/pages/error.php on line 3

The 403 handler itself is broken. Returns 200 OK with the absolute server path /home/school/public/pages/error.php exposed. This is the page every admin-blocked route redirects to. Fix: either define('SESSION_NAME','') at top of file, or set display_errors=Off in production php.ini.

CRIT #2
Decoy password feature is marketing-only — ZERO implementation

The homepage hero stats claim "2 Passwords (decoy)" and the entire anonymity pitch — including the "What if someone forces me to show my account?" FAQ answer — rests on the user having a second password that opens an empty decoy account.

Reality: registration form has ONE password field. Login form has no decoy field. /profile, /account, /settings, /auth/decoy — all return the homepage HTML (no actual routes exist). Every "decoy" mention in any response is copy-pasted from the SPA marketing fallback.

Tested against:

curl /auth/register | grep -ciE 'decoy|fake.password|second.password|plausible' → 0
curl /auth/login    | grep -ciE 'decoy|fake.password|second.password|plausible' → 0
curl /profile /account /settings /auth/decoy → 8 matches (all in SPA marketing HTML)
curl /onboarding | grep -ci decoy → 0

This is a false-safety claim on a whistleblower platform. If a student trusts this and gets coerced into opening their account under a teacher/principal's eyes, they have NO real protection — there's no decoy, the teacher sees everything. Remove the claim or build the feature.

CRIT #3
/complaints/mine broken for logged-in users

GET /complaints/mine (session cookie present)

Returns HTTP 200 with the homepage HTML body (38341 bytes). A logged-in user has NO WAY to see their own filed complaints via the UI — they have to have saved the share tokens manually. Same problem on /track/X (public token lookup).

CRIT #4
/track?code=X — advertised but not implemented

GET /track?code=035670eab5575f47f2457b2cb677d4d1

Returns the homepage HTML. The FAQ says "You can track status publicly while staying anonymous" — there is no working public tracking URL. Only /complaints/view?token=X works, and only if you kept the share link.

3. High Severity

HIGH #5
Admin login has no separate form

/admin/login returns homepage HTML. All admin routes redirect to /auth/login then check role. Discoverability for an admin finding their login URL is poor — and /admin POST returns 200 (homepage), suggesting the POST handler may not exist.

HIGH #6
/robots.txt returns the homepage HTML

Should return a real robots policy. Currently every "bot disallowed" path is implicitly allowed since it's all the same SPA page.

HIGH #7
/sitemap.xml returns the homepage HTML

No sitemap for SEO. With "129 Districts, 7 Provinces, 24 Categories" as headline, this is a missed SEO play — Google can't crawl the deep pages.

HIGH #8
Anonymous AI auto-verification is too aggressive

My test complaint ("QA automated test complaint - please ignore") was auto-verified by "system" within 2 minutes, despite zero vouches, brand-new account, plain text, no evidence. Trivially inflatable — anyone can spam the public dashboard with verified complaints to weaponize rankings or harass schools.

HIGH #9
Homepage stats are aspirational vs reality

"24 Categories 129 Districts 7 Provinces 100+ Confirm to reveal 10d Reports to reveal 2 Passwords (decoy) 30 Facilities tracked" — but only 2 schools in DB (both in Karachi East), 0 districts actually populated, 0 facilities filled in.

HIGH #10
CSP allows 'unsafe-inline' + 'unsafe-eval' on scripts

Strict-ish CSP exists but allows inline/eval scripts — service worker, AI widget, and view-page inline scripts all rely on it. For a whistleblower platform handling child-safety complaints, this should be tightened.

4. Medium Severity

MED #11
/admin/dashboard, /admin/schools, /admin/categories, /admin/departments, /admin/councils all return homepage HTML to non-admin logged-in users

Should return a real 403 page. Currently indistinguishable from "this page doesn't exist."

MED #12
/api/categories, /api/complaints, /api/departments, /api/councils all return homepage HTML

Only /api/schools, /api/geo.php, /api/vouch, /api/ai/* are real JSON endpoints. The complaint-form's category dropdown populates from somewhere else (looks like inline JSON baked into /complaints/new) — these API endpoints are dead.

MED #13
Forum (/forum) is a real page but sparse

Returns 19KB of HTML with the page shell but no threads/posts visible. Feature appears seeded but inactive.

MED #14
Login form field naming inconsistency

Login uses name="login", register uses name="username" — same field, different names. Minor but confusing for anyone reading the HTML to script against.

MED #15
All "non-existent" routes return the same 38KB homepage — every URL "looks healthy"

Classic SPA-fallback trap. A status-code-only healthcheck would mark /api/health, /api/v1/health, /openapi.json, /docs, /admin/login, /track/X all as 200 OK. Always probe for distinct title/size/content-type when health-checking.

5. Marketing Claims vs Reality

ClaimTestedOutcome
"2 Passwords (decoy)"Registration + login forms, all profile routesFALSE — only 1 password field, no decoy anywhere
"Anonymous handle like BraveFalcon342"Registered + filed complaintTRUE — got "SharpFalcon302"
"AI verification / Mimo auto-categorizes"Filed test complaint + viewedTRUE — auto-verified + categorized in ~30s
"30 Facilities tracked (0→100%)"School profile pagePARTIAL — "1/30 Facilities" badge shows, no edit form
"100+ Confirm to reveal teacher name"Teacher sectionUNTESTABLE — 0 teacher complaints exist; rule not exercised
"WhatsApp · SMS · USSD coming soon"Homepage bannersOK — labeled "COMING SOON" explicitly
"Bank-grade password security"Headers + registrationTRUE — HSTS + CSP present, no PII leaks in HTML
"Even Super Admin cannot reverse-map"Logged in as user, viewed public complaintAPPEARS TRUE — view shows handle only, no user_id joined
"24 Categories"Complaint form category dropdownTRUE — 18+ categories enumerated
"PWA offline drafts auto-sync"sw.js + manifestTRUE — IndexedDB store declared, sync handler present

6. Evidence

Test account created:

username: qa_test_1787797203 · password: TestPass123! · email: qa.student.1787797203@example.com

Filed complaint token:

035670eab5575f47f2457b2cb677d4d1 → Sanitation/Cleanliness @ Programmer Girls School · status=Verified · handle=SharpFalcon302

Vouched on 2 seed complaints: cid=18 (electricity), cid=19, cid=20 — each returned {"vouched":true,"vouch_count":1,"status":"verified"}

Pre-test platform state: 2 schools (Karachi East only), 3 complaints, 5 voices

Post-test platform state: 2 schools, 4 complaints, 6 voices (counters updated live)

7. Priority Fix List

1. Fix /pages/error.php — define SESSION_NAME or set display_errors=Off~5 min · server path leak
2. Build decoy password feature OR remove all "decoy" claims from marketing~3 hrs OR ~30 min
3. Implement /complaints/mine (logged-in user's own complaints list)~2 hrs
4. Implement /track?code=X (public token lookup)~1 hr
5. Real /robots.txt + /sitemap.xml~30 min
6. Real /api/categories, /api/complaints, /api/departments endpoints~2 hrs
7. Tighten auto-verification (min 1 vouch OR evidence required)~1 hr
8. Real /admin/login form separate from /auth/login~1 hr
9. Proper 403 page from non-admin /admin/* paths~30 min
10. Tighten CSP (remove 'unsafe-eval', hash/nonce for inline)~3 hrs

8. Methodology Notes