Sindh IT Facilitation Desk · Ministry of Science & IT, Government of Sindh
https://sitfd2.production1.jugaar.ai/
Major win. The dev team fixed 5 of 7 critical endpoints between the two audits (~2 hours apart). The full lifecycle now flows:
file complaint → ack → note → escalate → resolve → "Fix ready". Ticket detail pages now render action forms (/note, /escalate, /acknowledge, /assign) for the relevant roles.
Remaining issues: POST /tickets/{id}/reply still returns 403 (now superseded by /note for staff, but the endpoint still exists and fails). POST /tickets/{id}/reopen still 403. POST /tickets/{id}/feedback still 409. CSAT state-machine probably needs validation per ticket state.
| Endpoint | Before | Δ | Now | Note |
|---|---|---|---|---|
| POST /tickets (file complaint) | 500 | → | 302 ✓ | FIXED. Filed KDA-2026-000001 (id=16) successfully. Next page has full detail with action forms. |
| POST /tickets/{id}/acknowledge | 500 | → | 302 ✓ | FIXED. Helpdesk confirmed Ack on DEMO-LAB-00001. |
| POST /tickets/{id}/escalate | 500 | → | 302 ✓ | FIXED. Fields: level (int), reason (≥10 chars). |
| POST /tickets/{id}/resolve | 500 | → | 302 ✓ | FIXED. Status transitioned to "Fix ready — awaiting your confirmation". |
| POST /track (public ticket lookup) | 500 | → | 200 ✓ | FIXED. Now returns the looked-up ticket page (200 OK for both real and fake refs — see note). |
| POST /tickets/{id}/reply (deprecated by /note) | 403 | → | 403 | STILL BROKEN (or intentionally replaced). The UI now uses a /note endpoint with visibility (public/private). The legacy /reply endpoint is still 403 — should be removed or repaired. |
| POST /tickets/{id}/reopen | 403 | → | 403 | STILL BROKEN. As escalation officer (should be authorized) returns 403. As company, returns 500. Likely either a missing policy rule or field-name mismatch (UI does not expose a Reopen button yet). |
| POST /tickets/{id}/feedback (CSAT) | 409 | → | 409 | STILL 409 even on freshly-filed ticket (state mismatch). Likely a state-machine check — only valid in certain states (Fix ready → customer acknowledges). Either the check is wrong, or there's no documented way to enter that state from a customer role. |
/reply. Fields: body, visibility (client | staff). Works from helpdesk / dept_admin / escalation officer. ✓assigned_to (user id). Visible only to escalation officer. ✓ endpoint exists; behavior showed 500 on the demo option value, may need real user id.The following chain completes without any 500/403 errors:
POST /tickets with category + subject + description → 302 → ticket created with reference KDA-2026-000001 (DEMO-tested)
POST /tickets/{id}/acknowledge → 302 (DEMO-LAB-00001 and KDA-2026-000001)
POST /tickets/{id}/note with body + visibility → 302, reply added to conversation
POST /tickets/{id}/escalate with level + reason ≥10 chars → 302
POST /tickets/{id}/resolve with note (or resolution_note) → 302, ticket state → "Fix ready — awaiting your confirmation"
Remaining gaps in the chain:
POST /tickets/{id}/reopen — if customer is unsatisfied and wants to reopen, still cannot.POST /tickets/{id}/feedback — CSAT cannot be collected end-to-end.Previously the page rendered ZERO action forms for any role. Now it exposes role-appropriate ones:
| Role | Form actions visible |
|---|---|
| Master Admin | /note /escalate |
| Helpdesk Staff | /acknowledge /note /escalate |
| Department Admin | /note /escalate |
| Escalation Officer | /acknowledge /assign /note /escalate |
| Supervisor | /note /escalate |
| Company (owner) | /upload evidence (Upload) + the company-side Reply form via /note with visibility=client |
| Other companies (e.g. Cloud9) | (403 Forbidden — correctly blocked from viewing ticket that isn't theirs) |
Still missing from UI: Reopen and CSAT/feedback forms. The endpoints exist server-side but there's no button on the page yet.
| Route | Status | Verdict |
|---|---|---|
GET / · /kb · /programs · /notices · /public-index · /register | 200 | OK ✓ |
POST /register (unique NTN) | 302 → /tickets/create | OK ✓ |
POST /register (dup NTN) | 302 → /register | OK ✓ (validation) |
POST /track with KDA-2026-000001 | 200 | FIXED ✓ |
POST /track with DEMO-LAB-00001 | 200 | FIXED ✓ |
POST /track with NONEXISTENT-999 | 200 | OK but suspicious — returns 200 even for fake ref; should probably 404 or show explicit "not found" |
GET /open-data (auth CSV) | 200 | OK ✓ |
GET /public-data | 404 | Harmless — nothing links to it |
GET /tickets/16 anon | 302 → /login | OK ✓ |
All 12 admin sub-modules still functional. Verified working across both audit passes:
| Module | Verdict |
|---|---|
| /admin (overview) | OK ✓ |
| /admin/departments (create + toggle) | OK ✓ |
| /admin/categories (create) | OK ✓ |
| /admin/users (create + deactivate) | OK ✓ |
| /admin/sla (settings + holidays) | OK ✓ |
| /admin/canned (responses) | OK ✓ |
| /admin/companies (suspend) | OK ✓ |
| /admin/applications | EMPTY (working) |
| /admin/ai (9 toggles + add engine) | OK ✓ |
| /admin/sub-dept-requests | EMPTY (working) |
| /admin/settings (save + demo-purge) | OK ✓ |
| Role | Admin link | Intake queue | Escalations | Ticketing access |
|---|---|---|---|---|
| Master Admin | yes | yes | yes | all tickets |
| Supervisor | no | yes | yes | all tickets |
| Department Admin | yes | yes | yes | scoped to dept |
| Helpdesk Staff | yes | yes | yes | intake → ticket |
| Escalation Officer | yes (via Admin) | depends | yes | all (assigned only) |
| Company | n/a | no | no | own tickets only |
| Other company viewing ticket not theirs | — | — | — | 403 correctly |
| Test | Status |
|---|---|
| HSTS (31536000 + includeSubDomains) | PRESENT ✓ |
| CSP (default-src 'self', frame-ancestors, form-action) | PRESENT ✓ |
| X-Frame-Options | PRESENT ✓ |
| X-Content-Type-Options | PRESENT ✓ |
| Referrer-Policy | PRESENT ✓ |
| CSRF enforced (419 on missing token) | YES ✓ |
| SQL injection in track ref | CLEAN ✓ |
| XSS in track ref <script> | CLEAN ✓ (escaped) |
| XSS in /register representative | CLEAN ✓ |
| Anonymous /tickets/{id} | BLOCKED ✓ |
| Anonymous /admin | BLOCKED ✓ |
| Other company viewing ticket not theirs | 403 ✓ |
| Session HttpOnly | YES ✓ |
| Session Secure + SameSite=Lax | not flagged |
| OpenAPI / Swagger | missing |
| /api/* routes | none |
| Demo creds in /login HTML | yes (dev/demo concern) |
| # | Bug | Endpoint | Code | Severity | Repro |
|---|---|---|---|---|---|
| 1 | Reopen a resolved ticket returns 403 | POST /tickets/{id}/reopen | 403 (also 500 as customer) | CRITICAL | 100% — escalation officer & company |
| 2 | Legacy /reply endpoint still 403 (now superseded by /note) | POST /tickets/{id}/reply | 403 | MEDIUM | 100% — should be removed or repaired |
| 3 | CSAT endpoint returns 409 even on fresh ticket | POST /tickets/{id}/feedback | 409 | MEDIUM | 100% (every payload variant) |
| 4 | /track returns 200 for fake ticket refs (should 404) | POST /track FAKE-999 | 200 | LOW | 100% — UX issue, not security |
| 5 | Demo credentials in plain text on /login | GET /login | 200 | LOW | Dev/demo only |
| 6 | No /api/* routes expose programmatic surface | n/a | n/a | LOW | By design? |
POST /tickets with fields type, category_id, priority, subject, description, city, notice_deadline (multipart allowed). Result: 302 → /tickets/{newId}. Tested ticket = KDA-2026-000001 (id=16).
POST /tickets/{id}/note with body + visibility ∈ {client, staff}. Result: 302 + message in conversation. Replaces legacy /reply.
POST /tickets/{id}/escalate with level (integer, 1=department, 2=council) + reason (≥10 chars). Result: 302.
POST /tickets/{id}/assign with assigned_to (user id). Only escalation officer UI exposes this. (Probe returned 500 with demo option "2" — may need a real user id; UI form will only let you pick real users.)
POST /tickets/{id}/resolve with note or resolution_note. Ticket state → "Fix ready — awaiting your confirmation". Result: 302.
POST /track with reference. Result: 200 with ticket details (or 200 with empty result for fake refs — should probably 404).
POST /tickets/{id}/reopen with reason. As escalation: 403. As customer (company_nexbyte): 500. UI does not expose Reopen button yet. The endpoint exists but authorization policy/middleware is rejecting it.
POST /tickets/{id}/feedback. Returns 409 even on a brand-new ticket in "Received" status. Tested with {satisfaction,rating,comment}, {score,comment}, {rating}, {satisfied}. Likely needs a specific ticket state (post-resolve "Fix ready") but state check is rejecting too aggressively.
POST /tickets/{id}/reply. Returns 403 to staff. Endpoint is superseded by /note but still in the codebase — should be removed or its policy updated for backward compatibility (if any client integration depends on it).
state = "fix_ready"; the customer needs a UI step "Acknowledge fix" that transitions state, then feedback submits. Or relax the check./reply endpoint — superseded by /note. Returns 403 because no policy gates it. Either delete the route or document that it's deprecated.Secure; SameSite=Lax to session cookie, remove demo passwords from /login HTML for any non-dev environment./public-data → 404 — harmless, nothing links to it./api/* JSON API endpoints — everything is server-rendered HTML.demo12345 password visible in plain text on /login HTML page. OK for dev/staging only.Full role-based + browser-verified. 8 demo accounts exercised across the entire ticket lifecycle. Each endpoint probed with fresh CSRF tokens and verified via curl + browser screenshots.
| Artifact | Location |
|---|---|
| Full report (markdown) | /root/qa-sitfd/REPORT.md |
| Login + 1st-pass logs | /tmp/qa/login_all.log, /tmp/qa/qa_full.log, /tmp/qa/lifecycle.log, /tmp/qa/final.log |
| Re-audit delta logs | /tmp/qa/delta_qa.log, /tmp/qa/delta_full.log, /tmp/qa/delta_clean.log, /tmp/qa/role_views.log, /tmp/qa/probe_resolve2.log |
| Per-role cookie jars | /tmp/qa/jars/*.jar |
| New filed ticket | KDA-2026-000001 (id=16) — filed by Nexbyte during QA |
| Browser screenshot, Ack 500 (OLD) | /root/.hermes/browser_screenshots/browser_screenshot_cb9d02e9.png |
| Browser screenshot, /track 500 (OLD) | /root/.hermes/browser_screenshots/browser_screenshot_a085cb61.png |