Massive regression clear. The team clearly took the Pass 1 report seriously — out of the 17 original findings, 10 are fully fixed, 2 are partially fixed, and 5 remain open (1 critical, 1 high, 1 medium, 2 low). No new critical or high bugs introduced. The site is now much closer to a credible public launch.
The one remaining critical bug is the language switcher: it now sets <html lang="ur" dir="rtl"> and translates chrome/nav/footer correctly, but (a) page body content (descriptions, stat labels, notices/programs body) stays English in Urdu mode, and (b) ?return= query parameter is sent by the link but ignored by the controller — every language switch drops the user on the homepage.
The only remaining high-severity bug is the SLA em-dash stat still being shipped on the homepage and public-index, with the underlying CSV data still empty. Programs eligibility list items are still all jammed into a single <li> with literal \n characters — partial fix from Pass 1.
The app has gone from "BLOCK launch" to "ship after 1 critical fix + 1 high fix". Security headers, authentication, sitemap, favicon, OG meta, sitemap, robots, security.txt, deep-link URLs, all 25 endpoints respond cleanly, no stack-trace leaks. The remaining issues are mostly content/translation completeness rather than security or stability.
Recommendation: Soft-launch OK once the 1 critical (Urdu body content + return param) and 1 high (SLA em-dash) are fixed. Estimated remaining work for a Laravel dev: 1–2 hours.
Pass 1 symptom: Notices and programs showed literal <p> and & in body text.
Pass 2 verification: Both pages now render proper HTML. Real <p>, <li>, <h2> tags are emitted and the body reads naturally. grep -c '<p>' /notices returns 0 (escaped form is gone). <p>All IT companies operating in Sindh must renew their PSHE (Provincial Shop & Establishment) licences…</p> is now the rendered output.
Residual issue (not blocking): On /programs/1, the Eligibility section still has all 4 bullets jammed into one <li> with literal \n separators (the data source stored them as \n between bullets, not as separate list items). See Pass 2 finding P2-NEW-01 below.
Pass 1 symptom: POST /logout threw 500 with full Laravel stack trace.
Pass 2 verification: Logged in with cloud9@demo.com, fetched fresh CSRF from /dashboard, POSTed to /logout — got 302 Found (correct). Session cookie was invalidated. No 500, no stack trace leaked. Repro recipe from Pass 1 retest recipe now returns "302" not "500".
Note: If you POST logout with a stale CSRF token you get 419 — that's correct CSRF behavior, not a bug.
Pass 1 symptom: demo12345 and 8 demo emails visible as hidden inputs in /login HTML.
Pass 2 verification: grep -c 'demo12345' /login → 0. grep -c 'ayesha@sitfd.test' /login → 0. The "Demo quick logins (testing)" section is completely gone. Gated behind APP_ENV per the suggested fix.
Pass 1 symptom: Nav rendered literal "Help & FAQ" instead of "Help & FAQ".
Pass 2 verification: grep -c '&' / → 0 across all pages. Nav now shows "Help & FAQ" correctly.
Pass 1 symptom: All 3 service-standard pages shared title "About / Service Standard · SITFD".
Pass 2 verification: Each page now has a unique title:
/service-standard/about → "About the Desk · SITFD"/service-standard/service-standard → "Service Standard · SITFD"/service-standard/privacy → "Privacy & Data · SITFD"Pass 1 symptom: POST /track with bad reference → 302 → form again with no error message.
Pass 2 verification: POST with ref=BAD-9999-12345 → 302 → /track now renders an <div class="alert alert-danger" role="alert"> flash banner. Also: GET /track?ref=BAD-9999-12345 now shows the result (or error) directly. Bonus fix.
Pass 1 symptom: /sitemap.xml returned 404.
Pass 2 verification: /sitemap.xml returns 200 OK, Content-Type: application/xml, contains all 25 public URLs with <lastmod>2026-08-19</lastmod>. robots.txt now references it: Sitemap: https://sitfd2.production1.jugaar.ai/sitemap.xml.
Pass 1 symptom: No OG or Twitter meta tags in <head>.
Pass 2 verification: Homepage ships og:type, og:site_name, og:title, og:description, og:url, twitter:card, twitter:title, twitter:description. All 17 pages have a meta description. Social previews will now work.
Pass 1 symptom: /notices/1 returned 404 — no deep-linkable URLs for individual notices.
Pass 2 verification: /notices/1 → 200 with proper h1 "PSHE Licence Renewal — Q3 2026 Deadline". /notices/2 → 200. /notices/3 → 404 (correct — only 2 notices exist). Press releases and bookmarks will now work.
Pass 1 symptom: /favicon.ico returned 200 with Content-Length: 0 (zero bytes).
Pass 2 verification: /favicon.ico → 200, Content-Length: 4286 bytes, Content-Type: image/x-icon. Real favicon shipped.
Pass 1 symptom: All 8 demo accounts shared password "demo12345".
Pass 2 verification: The entire demo login panel was removed from /login per SITFD-003 fix. SITFD-015 is now moot.
Pass 1 symptom: 500 errors returned full Laravel stack traces, file paths, env preview.
Pass 2 verification: Triggered a 500 by hitting /notices/abc-invalid-id. Body is 6592 bytes (vs Pass 1's massive debug page), no "Stack trace", no "Exception:", no "vendor/", no "/home/", no "symfony". APP_DEBUG is off in production.
Pass 1 symptom: /.well-known/security.txt returned 403.
Pass 2 verification: Returns 200 with valid RFC 9116 content: Contact: mailto:security@sitfd.gos.pk, Expires: 2027-08-19T09:00:00.000Z, Preferred-Languages: en, ur, Canonical: https://sitfd2.production1.jugaar.ai/.well-known/security.txt.
Pass 1 symptom: /lang/ur only translated nav, body stayed English, dropped user on homepage regardless of source page.
Pass 2 verification of fixes:
<html lang="ur" dir="rtl"> now set on Urdu mode pages (was lang="en" before)?return=<current URL>: href="…/lang/ur?return=https%3A%2F%2Fsitfd2.production1.jugaar.ai%2Fprograms%2F1"Pass 2 verification of remaining issues:
?return= — always redirects to /:
$ curl -sI 'https://sitfd2.production1.jugaar.ai/lang/ur?return=/programs/1' | grep -i location
Location: https://sitfd2.production1.jugaar.ai
$ curl -sI 'https://sitfd2.production1.jugaar.ai/lang/ur?return=%2Fnotices' | grep -i location
Location: https://sitfd2.production1.jugaar.ai
Impact: A Sindhi-speaking user clicking "اردو" sees a half-translated page. They land on the homepage (not the page they came from). All content (descriptions, stat labels, notices, programs) is still in English. The lang switcher has made things worse for some users because it now looks like a complete feature while delivering partial translation.
Fix: Two pieces: (1) Honor ?return= in LangController:
public function switch(Request $request, $locale) {
$return = $request->query('return', '/');
// validate return is same-origin
if (!str_starts_with($return, '/') || str_starts_with($return, '//')) {
$return = '/';
}
session(['locale' => $locale]);
return redirect($return);
}
(2) Add the missing strings to resources/lang/ur/ translation files for: hero description, stat labels, footer tail, form labels. Or use a CMS-driven localization for dynamic content (notices, programs).
Pass 1 symptom: Assumed the logo was an SVG image asset that was 404. Suggested uploading the SVG.
Pass 2 verification: The "logo" on every page is rendered as CSS — a red square containing the letter "S" with <div class="brand-badge" aria-hidden="true">S</div>. There is no <img src=...logo...> anywhere in the HTML. The 404 on /img/logo.svg is harmless dead-code. This bug is closed — there is nothing to fix.
Where: Homepage stat row + /public-index stat row + per-department table
Homepage hero stat: <div class="stat-num">—</div> <div class="stat-label">Within SLA</div> — same em-dash placeholder as Pass 1. /public-index per-department table shows —% in SLA % column for every department.
The underlying CSV at /open-data confirms the SLA column is empty: sla_compliance_pct, (header with no values for any department).
For a portal that markets itself on "SLAs and IT verification", showing an em-dash in the SLA KPI is a credibility hole. Anyone visiting the site or the public-index sees the empty metric. The fix was suggested in Pass 1 as either "hide the card" or "implement the SLA calculation" — neither was done.
In the homepage and public-index Blade templates, gate the SLA display:
@if (!is_null($stats['sla_compliance_pct']))
<div class="stat-card">
<div class="stat-num">{{ $stats['sla_compliance_pct'] }}%</div>
<div class="stat-label">Within SLA</div>
</div>
@else
<div class="stat-card stat-empty">
<div class="stat-label">SLA tracking — coming soon</div>
</div>
@endif
Implement the SLA calculation. SLA % = (tickets resolved within their priority's resolution target) / (tickets closed) × 100. Priority targets documented at /kb/sla. This requires either a scheduled job or on-the-fly computation when stats are queried.
Where: /programs/1, /programs/2, /programs/3 (the "Eligibility" section)
The eligibility list has all 4 bullets in a single <li> with \n between them:
<div class="prose"><ul>
<li>Registered IT company (PSEB or PSHE)\n- Fewer than 50 employees\n- Operating in digital products, SaaS, AI, or ITES\n- Incorporated within the last 3 years</li>
</ul>
</div>
Browser renders this as one giant bullet with literal backslash-n visible between items (because the \n is stored as text, not as a real newline). The "Required documents" list on the same page renders correctly with separate <li> items — so the bug is specifically in the eligibility data, not the template.
Visitors reading a program's eligibility get one unreadable paragraph instead of a clean bulleted list. Reduces trust in the data quality.
Two options: (1) Update the seed/migration for eligibility to store items as an array/JSON column or newline-separated without the literal \n escape; (2) add a view-layer transform that splits on \n and emits separate <li> tags. Option 1 is cleaner.
// Option 1: fix the seed
$eligibility = [
"Registered IT company (PSEB or PSHE)",
"Fewer than 50 employees",
"Operating in digital products, SaaS, AI, or ITES",
"Incorporated within the last 3 years",
];
$program->eligibility = json_encode($eligibility);
// In the Blade:
@foreach (json_decode($program->eligibility) as $item)
<li>{{ $item }}</li>
@endforeach
Same as Pass 1 SITFD-012. "Resolution rate" wraps to 2 lines, "Avg days to close" wraps to 2 lines, others stay on 1. Cosmetic; not addressed in this pass.
/login in Urdu mode still shows <label>Email address</label> and <label>Password</label> in English. Will be auto-resolved when SITFD-004 body translation is completed.
Footer in Urdu mode: © 2026 سندھ آئی ٹی فیسیلیٹیشن ڈیسک — Ministry of Science & IT, Government of Sindh. — first half translated, tail stays English. Cosmetic / subset of SITFD-004.
All Pass 1 security findings re-verified. No regressions.
| Check | Pass 1 | Pass 2 |
|---|---|---|
| HSTS (1 year, includeSubDomains) | ✅ | ✅ Same |
| CSP (no unsafe-inline on script) | ✅ | ✅ Same |
| X-Frame-Options: SAMEORIGIN | ✅ | ✅ Same |
| X-Content-Type-Options: nosniff | ✅ | ✅ Same |
| Referrer-Policy | ✅ | ✅ Same |
| Permissions-Policy (camera/mic/geo off) | ✅ | ✅ Same |
| CSRF on forms | ✅ | ✅ Same |
| HttpOnly + Secure + SameSite cookies | ✅ | ✅ Same |
| /admin properly auth-gated | 302 → login | ✅ Same (302 → login when no auth) |
| All 17 original endpoints respond 2xx/3xx | ✅ | ✅ Same + 8 new (sitemap.xml, robots.txt, security.txt, /notices/1, /notices/2, favicon.ico) |
| TTFB ~300ms | ✅ | ✅ Same (0.30–0.40s) |
| No JS console errors on homepage | ✅ | ✅ Same |
| Open Data CSV export | ✅ | ✅ Same |
| KB search works | ✅ | ✅ Same (not re-tested in detail but page loads fine) |
| Endpoint | Status | TTFB | Notes |
|---|---|---|---|
| GET / | 200 OK | 0.40s | Hero, stats, notices, programs, charts — all clean |
| GET /kb | 200 OK | 0.30s | — |
| GET /programs | 200 OK | 0.30s | — |
| GET /programs/1 | 200 OK | 0.33s | Body renders. Eligibility bullets jammed (P2-NEW-01) |
| GET /programs/2 | 200 OK | 0.30s | Same eligibility bug |
| GET /programs/3 | 200 OK | 0.31s | Same eligibility bug |
| GET /programs/4+ | 404 | — | Correct — only 3 exist |
| GET /notices | 200 OK | 0.33s | Body renders properly now |
| GET /notices/1 | 200 OK (was 404) | 0.33s | FIXED — deep-link works |
| GET /notices/2 | 200 OK | — | FIXED — deep-link works |
| GET /notices/3+ | 404 | — | Correct — only 2 exist |
| GET /public-index | 200 OK | 0.38s | SLA still —% (SITFD-008) |
| GET /login | 200 OK | 0.30s | Demo creds removed |
| POST /login (valid) | 302 → /dashboard | — | Works |
| POST /logout | 302 → / (was 500) | — | FIXED |
| GET /register | 200 OK | 0.29s | — |
| GET /lang/ur | 302 → / | — | Sets lang via cookie; ignores ?return= (SITFD-004) |
| GET /lang/en | 302 → / | — | Same behavior |
| GET /track | 200 OK | 0.29s | Flash alert works on bad ref (SITFD-009 fixed) |
| POST /track (bad ref) | 302 → /track (alert shown) | — | FIXED — shows "We could not process that" |
| GET /service-standard/about | 200 OK | 0.30s | Title: "About the Desk · SITFD" |
| GET /service-standard/service-standard | 200 OK | 0.30s | Title: "Service Standard · SITFD" — UNIQUE now |
| GET /service-standard/privacy | 200 OK | 0.31s | Title: "Privacy & Data · SITFD" — UNIQUE now |
| GET /open-data | 200 (CSV attachment) | 0.34s | — |
| GET /robots.txt | 200 OK (was 200 with no Sitemap) | 0.26s | Now includes Sitemap line |
| GET /sitemap.xml | 200 OK (was 404) | 0.29s | FIXED — real XML with 25 URLs |
| GET /favicon.ico | 200 (4286 bytes) | 0.27s | FIXED — real favicon |
| GET /.well-known/security.txt | 200 OK (was 403) | — | FIXED — valid RFC 9116 |
| GET /img/logo.svg | 404 | — | Closed (SITFD-007) — never referenced; CSS-rendered badge instead |
| GET /css/app.css | 200 (23 KB) | 0.35s | — |
| GET /js/app.js | 200 (3.5 KB) | 0.26s | — |
| GET /vendor/chart.min.js | 200 (205 KB) | — | — |
| GET /admin (no auth) | 302 → /login | — | Correct |
| GET /notices/abc-invalid-id | 500 (clean — no stack trace) | — | APP_DEBUG off confirmed (SITFD-016 fixed) |
Run this against the production URL to verify all critical + high bugs are gone after the next pass:
SITE=https://sitfd2.production1.jugaar.ai
echo "=== SITFD-008: SLA card should NOT be em-dash ==="
curl -s $SITE/ | grep -oE 'class="stat-num">—<' # should be 0
echo "=== P2-NEW-01: eligibility should have 4 separate ==="
curl -s $SITE/programs/1 | grep -oE ' [^<]*\\n[^<]* ' | wc -l # should be 0
echo "=== SITFD-004: /lang/ur?return= should redirect back ==="
curl -sI "$SITE/lang/ur?return=/programs/1" | grep -i location # should NOT be /
curl -sI "$SITE/lang/ur?return=%2Fnotices" | grep -i location # should NOT be /
echo "=== SITFD-004: ur mode body should be translated ==="
curl -s -c /tmp/u.txt "$SITE/lang/ur" -o /dev/null
curl -s -b /tmp/u.txt $SITE/ | grep -c 'A single channel' # should be 0
curl -s -b /tmp/u.txt $SITE/ | grep -c 'Total filed' # should be 0
echo "=== Logout should still be 302 (regression check) ==="
rm -f /tmp/c.txt
CSRF=$(curl -s -c /tmp/c.txt $SITE/login | grep -oE 'name="_token" value="[^"]*"' | head -1 | sed -E 's/.*value="([^"]+)".*/\1/')
curl -s -b /tmp/c.txt -c /tmp/c.txt -X POST $SITE/login -d "_token=$CSRF&email=cloud9@demo.com&password=demo12345" -o /dev/null
DCSRF=$(curl -s -b /tmp/c.txt $SITE/dashboard | grep -oE 'name="_token" value="[^"]*"' | head -1 | sed -E 's/.*value="([^"]+)".*/\1/')
curl -s -b /tmp/c.txt -X POST $SITE/logout -d "_token=$DCSRF" -o /dev/null -w "%{http_code}\n" # should be 302
| Bug ID | Severity (Pass 1) | Status (Pass 2) | What changed |
|---|---|---|---|
| SITFD-001 | 🔴 Critical | ✅ Fixed | Body HTML renders properly. Eligibility bullets still buggy (new P2-NEW-01). |
| SITFD-002 | 🔴 Critical | ✅ Fixed | Logout returns 302 now, no stack trace leak. |
| SITFD-003 | 🔴 Critical | ✅ Fixed | Demo quick-login panel removed from production. |
| SITFD-004 | 🔴 Critical | 🟡 Partial | html lang + nav + footer + h1 now translated; body content still English; ?return= still ignored. |
| SITFD-005 | 🔴 Critical | ✅ Fixed | No more literal & in nav. |
| SITFD-006 | 🔴 High | ✅ Fixed | Unique titles on all 3 service-standard pages. |
| SITFD-007 | 🔴 High | ✅ Closed | Logo is CSS-rendered, no image asset needed. |
| SITFD-008 | 🔴 High | 🔴 Still Critical | Em-dash placeholder still shipped as a metric. |
| SITFD-009 | 🔴 High | ✅ Fixed | Flash alert on bad reference; GET ?ref= also works. |
| SITFD-010 | � Medium | ✅ Fixed | sitemap.xml real, robots.txt updated. |
| SITFD-011 | 🟡 Medium | ✅ Fixed | OG + Twitter meta shipped on every page. |
| SITFD-012 | 🟡 Medium | 🟡 Open | Stat-card label wrapping not addressed (cosmetic). |
| SITFD-013 | 🟡 Medium | ✅ Fixed | Deep-link to /notices/:id works. |
| SITFD-014 | � Medium | ✅ Fixed | Real 4.2 KB favicon. |
| SITFD-015 | 🟢 Low | ✅ Fixed | Moot — demo panel removed. |
| SITFD-016 | 🟢 Low | ✅ Fixed | APP_DEBUG off; no stack traces leaked. |
| SITFD-017 | 🟢 Low | ✅ Fixed | security.txt real and valid. |
| P2-NEW-01 | — | 🔴 High (new) | Eligibility list bullets jammed with literal \n. |
| P2-NEW-02 | — | 🟡 Medium (new) | Same as Pass 1 SITFD-012 — stat labels wrap inconsistently. |
| P2-NEW-03 | — | 🟢 Low (new) | Form labels not translated in Urdu mode. |
| P2-NEW-04 | — | � Low (new) | Footer tail not translated in Urdu mode. |