diff --git a/audit-give-charityright.md b/audit-give-charityright.md new file mode 100644 index 0000000..1eeaadf --- /dev/null +++ b/audit-give-charityright.md @@ -0,0 +1,334 @@ +# Audit Report: give.charityright.org.uk +**Date:** 9 March 2026 +**Auditor:** Claude Code (automated Playwright audit) +**Viewport tested:** 1440×900 (desktop), 375×812 (mobile) +**Entry URL:** https://give.charityright.org.uk → auto-redirects to `/sadaqah` + +--- + +## Overview + +`give.charityright.org.uk` is the donation micro-site for Charity Right (UK Charity #1155108). It is a **single-page, campaign-focused donation portal** currently showing a **Sadaqah fundraising page**. The site allows visitors to: + +- Select a donation amount (slider + quick-pick buttons, £3–£15 default range) +- Optionally add UK Gift Aid (with house number + postcode collection) +- Complete payment via Stripe (card + Google Pay) +- Share the campaign + +**Tech stack clues:** +- Custom server-side rendered (SSR) app — no Vue/React/Next fingerprint detected at root +- Stripe.js v3 for payments +- Cloudflare (CDN, challenge platform, Turnstile/hCaptcha) +- Google Tag Manager (GTM-PX5XJXX) +- TikTok Pixel, Facebook Pixel, Google Analytics (multiple GA4 properties), Hotjar 3395285 +- CookieHub for consent management +- WAHA (WhatsApp HTTP API) running at `waha.charityright.org.uk` — separate service, credentials leaked in console (see Security) + +--- + +## Phase 1 — Homepage Audit + +### Redirect behaviour +- `/` → `/sadaqah` (permanent or temporary redirect — not a true homepage, no navigation) + +### Page title +- ✅ `Give Sadaqah — Charity Right` — clear, descriptive + +### Meta tags +| Tag | Value | Issue | +|-----|-------|-------| +| `` | **MISSING** | ❌ No standard description tag | +| `` | `Give Sadaqah — Charity Right` | ✅ | +| `` | `50p feeds a child. Give Sadaqah in 30 seconds.` | ✅ | +| `` | `https://www.charityright.org.uk/wp-content/uploads/2026/02/cr-wrong-1.jpg` | ⚠️ Cross-domain OG image (WordPress) | +| `` | **MISSING** | ⚠️ No robots directives | +| `` | `width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover` | ⚠️ `user-scalable=no` disables pinch-zoom — accessibility violation (WCAG 1.4.4) | + +### Console errors on load +- ✅ 0 JS errors on sadaqah page itself +- 1 WARNING: Hotjar refuses to load (detects headless browser user-agent) — benign in production + +### Cookie consent +- ✅ CookieHub banner shown on first visit (`dialog "About cookies on this site"`) +- ✅ Three options: "Allow all cookies", "Deny all", "Cookie settings" +- ⚠️ "Cookie settings" link uses `href="#"` (dead anchor) — should be `href` to settings panel or use `button` semantics + +--- + +## Phase 2 — Navigation & Links + +The site has **no navigation bar or header menu**. It is a single campaign page only. There are no internal links to other sections of the give site. + +**External link found:** +- `https://www.charityright.org.uk/donate/` — links to main website (different domain) + +**Routes tested:** + +| Path | Result | +|------|--------| +| `/sadaqah` | ✅ 200 — Sadaqah donation page | +| `/zakat` | ❌ 404 — "Not Found" blank page | +| `/fidya` | ❌ 404 — "Not Found" blank page | +| `/qurban` | ❌ 404 — "Not Found" blank page | +| `/kaffarah` | ❌ 404 — "Not Found" blank page | +| `/login` | ❌ 404 — "Not Found" blank page | +| `/register` | ❌ 404 — "Not Found" blank page | +| `/campaigns` | ❌ 404 — "Not Found" blank page | +| `/donate` | ❌ 404 — "Not Found" blank page | +| `/appeal` | ❌ 404 — "Not Found" blank page | + +**Key issue:** The 404 page returns **no ``** tag and **no styled error page** — just the raw text "Not Found". There is no redirect to a helpful error page and no way back to a valid URL. + +--- + +## Phase 3 — Donation Flow + +### Flow architecture +The site uses an **inline slide-up payment panel** (overlay `div#overlay`) rather than a separate checkout page. This is a smooth UX pattern. + +### Step 1: Amount selection +- Slider control: 6 (£6 default, feeds 12 children @ 50p/meal) +- Quick-pick buttons: £3, £6, £9, £12, £15 +- ✅ Real-time meal counter updates +- ✅ Clear impact messaging ("Feeds 12 children") + +### Step 2: Gift Aid +- ✅ Gift Aid toggle button (pre-selected/enabled by default) +- ✅ Calculates HMRC top-up (£6 → £7.50 total) +- House number + postcode fields appear when Gift Aid active +- ⚠️ **No validation visible** if Gift Aid address fields are left empty when proceeding +- ⚠️ Gift Aid declaration text is small/light — readability concern + +### Step 3: "Give £6 Sadaqah" button +- Clicking opens a side/bottom panel (`div#overlay.open`) +- The overlay **blocks interaction** with the main page while open (correct modal behaviour) + +### Step 4: Checkout panel ("Complete your Sadaqah") +Fields present: +| Field | Type | Placeholder | Label visible | +|-------|------|-------------|---------------| +| Full name | `textbox` | "As it appears on your card" | ✅ "Full name" | +| Email | `textbox` | "For your donation receipt" | ✅ "Email" | +| Card number | Stripe iframe | "Card number" | ✅ "Number" | +| Expiry | Stripe iframe | "MM / YY" | ✅ | +| CVC | Stripe iframe | "CVC" | ✅ | + +- "Donate £6" button is **`disabled` by default** — only enabled when Stripe iframe reports complete card details +- ✅ Correct — prevents empty form submission +- ⚠️ Clicking "Give £6 Sadaqah" without filling name/email still opens the overlay — those fields have **no pre-validation** before opening the panel +- ✅ No `<form>` element (0 forms found) — form data handled entirely via JS/Stripe Elements +- ✅ Google Pay option available in Stripe Elements +- ✅ Trust signals: "🔒 Encrypted · Powered by Stripe" and "256-bit secure · No account needed · UK Charity 1155108" + +### Empty form submission +- Donate button stays disabled until Stripe card fields are valid — no empty submit possible via UI +- **No visible validation errors** for empty name/email fields — they appear to be validated client-side by Stripe before enabling the button (TBC) + +### Donation amount £1 test +- Minimum selectable via slider unclear (minimum appears to be £3 via preset buttons) +- Manual slider minimum not tested — slider defaults to £6 + +--- + +## Phase 4 — Mobile Responsiveness (375×812) + +### Layout +- ✅ Page renders correctly at 375×812 +- ✅ Same UI elements visible — no hidden/broken sections +- ✅ Campaign image scales correctly +- ✅ Quick-pick amount buttons visible and tappable +- ✅ Gift Aid toggle renders correctly +- ✅ Checkout panel (overlay) renders at mobile size +- ⚠️ `user-scalable=no` in viewport meta means users **cannot zoom** on mobile — this is an accessibility violation for low-vision users +- ⚠️ No hamburger menu (site is single-page, so this is acceptable, but there's no navigation at all) +- ✅ No horizontal scroll detected at 375px width + +--- + +## Phase 5 — Performance & Accessibility + +### Accessibility +| Check | Result | Issue | +|-------|--------|-------| +| Images missing `alt` | 0 | ✅ | +| Dead anchors `href="#"` | 1 | ⚠️ Cookie settings button uses `href="#"` | +| Unlabelled inputs | 1 | ⚠️ One input has no `aria-label` or `placeholder` | +| `user-scalable=no` in viewport | YES | ❌ WCAG 1.4.4 violation | +| Inline `onclick` handlers | 3 | ⚠️ 3 elements use inline event handlers | +| No `<nav>` landmark | Confirmed | ⚠️ No navigation landmark (single-page is valid but no skip-to-content) | +| No `<meta name="description">` | Confirmed | ❌ SEO/accessibility issue | + +### Performance +- **Page load time:** 2,433ms (performance.timing) — acceptable +- **External scripts loaded:** 9 external `<script src>` tags + +**External scripts inventory:** +1. `analytics.tiktok.com` — TikTok Pixel (×3 scripts) +2. `connect.facebook.net` — Facebook Pixel (×2 scripts, including large config JS) +3. `static.hotjar.com` — Hotjar session recording +4. `www.googletagmanager.com` — GTM (×1) + GA4 gtag (×4 separate GA properties!) +5. `cdn.cookiehub.eu` — CookieHub consent +6. `js.stripe.com/v3/` — Stripe.js +7. `googleads.g.doubleclick.net` — Google Ads conversion tracking (×2) + +**⚠️ Performance concern:** 4 separate Google Analytics 4 properties (`G-1B4QR9YTTB`, `G-6RGJ9BTW2Q`, `G-4DB6TJNHZR`, `AW-876060108`, `AW-925998688`) + GTM + TikTok + Facebook + Hotjar = **significant tracking script bloat**. Each fires multiple analytics events per page load. + +**Network errors (analytics):** Multiple `net::ERR_ABORTED` for analytics.google.com POSTs on page navigation — these appear to be in-flight beacons aborted during navigation (non-critical but noisy). + +--- + +## Phase 6 — Key Pages + +| Page | Status | Notes | +|------|--------|-------| +| `/sadaqah` | ✅ 200 | Main and only working page | +| `/zakat` | ❌ 404 | Common Islamic giving category — missing | +| `/fidya` | ❌ 404 | Ramadan-specific — missing | +| `/qurban` | ❌ 404 | Eid-related — missing | +| `/kaffarah` | ❌ 404 | Missing | +| `/login` | ❌ 404 | No user accounts on this domain | +| `/register` | ❌ 404 | No user accounts on this domain | +| `/campaigns` | ❌ 404 | Missing | + +The site appears to only have the one `/sadaqah` route currently live. All other paths return bare 404 responses. + +--- + +## Phase 7 — Security + +### HTTPS +- ✅ Site is fully HTTPS (`https://give.charityright.org.uk`) +- ✅ Stripe iframe loads over HTTPS +- ✅ No mixed content detected +- ✅ Cloudflare CDN with challenge platform (`cdn-cgi/challenge-platform`) + +### Forms +- ✅ No `<form>` elements found — payment handled entirely via Stripe.js (no raw card data hits the server) +- ✅ Stripe tokenisation means card details never touch CharityRight servers + +### Cookies +| Cookie | Domain | Secure? | HttpOnly? | Concern | +|--------|--------|---------|-----------|---------| +| `cf_clearance` | `.charityright.org.uk` | ✅ (HTTPS-only session) | N/A | Cloudflare bot protection | +| `__stripe_mid`, `__stripe_sid` | `.give.charityright.org.uk` | ✅ | Unknown | Stripe session — should be HttpOnly | +| `cookiehub` | `.charityright.org.uk` | ✅ | N/A | Consent preferences | +| `_ga`, `_ga_*` | `.charityright.org.uk` | ✅ | N/A | Google Analytics | +| `_fbp` | `.charityright.org.uk` | ✅ | N/A | Facebook Pixel | +| `_ttp` | `.charityright.org.uk` + `.tiktok.com` | ✅ | N/A | TikTok tracking | +| `IDE` | `.doubleclick.net` | ✅ | N/A | Google Ads | + +⚠️ `__stripe_mid` and `__stripe_sid` are visible in `document.cookie` — meaning they are **not HttpOnly**. Stripe session cookies should ideally be HttpOnly to prevent XSS token theft (though Stripe sets these itself — CharityRight cannot control this). + +### 🚨 CRITICAL: Credentials in Browser Console + +**From previous browsing session logs captured in the test environment:** + +``` +[ERROR] SecurityError: Failed to execute 'replaceState' on 'History': +A history state object with URL +'https://waha.charityright.org.uk/dashboard/' cannot be created in a document +with origin 'https://waha.charityright.org.uk' and URL +'https://admin:J59vAcWI56BReJEFCi@waha.charityright.org.uk/dashboard/'. +``` + +**This is a CRITICAL security issue.** The URL `https://admin:J59vAcWI56BReJEFCi@waha.charityright.org.uk/dashboard/` contains **basic authentication credentials in plaintext** (`admin:J59vAcWI56BReJEFCi`). These were captured in the browser's console log, meaning: + +1. Anyone with DevTools access to `waha.charityright.org.uk` could see these credentials +2. The credentials are for the WAHA (WhatsApp HTTP API) admin dashboard +3. WAHA controls WhatsApp messaging for the organisation — a breach here could expose donor communications + +**Immediate action required:** Change the WAHA admin password and remove credentials from any hardcoded URLs. Use proper session-based auth instead. + +### Hub API 401 Errors +Multiple failed requests to `hub.quikcue.com/api/user/*` (401 Unauthorized): +``` +GET https://hub.quikcue.com/api/user/lifecycle → 401 +GET https://hub.quikcue.com/api/user/admin → 401 +GET https://hub.quikcue.com/api/user/init → 401 +``` +The give.charityright.org.uk site is calling **QuikCue's hub API** on every page load and receiving 401s. These appear to be unauthenticated calls. This could indicate a misconfigured integration or leftover code from a shared codebase. + +### Development URL in Production +From console errors: `http://localhost:3000/api/pledges → 400 Bad Request` +A hardcoded `localhost:3000` URL is being called from production. This is a development/staging configuration leak. + +--- + +## Console Errors Summary + +| Error | Source | Severity | +|-------|--------|----------| +| `localhost:3000/api/pledges` returning 400 | Sadaqah page JS | 🔴 High — dev URL in prod | +| `hub.quikcue.com/api/user/*` returning 401 (×8+ times) | Page load | 🟠 Medium — unnecessary failed requests | +| `waha.charityright.org.uk` credentials in URL | WAHA dashboard JS | 🔴 Critical — credential leak | +| `waha.charityright.org.uk/api/*` returning 401 | WAHA dashboard | 🟡 Low — expected if not logged in | +| `give.charityright.org.uk/favicon.ico` returning 404 | Browser | 🟡 Low — missing favicon | +| `analytics.google.com` ERR_ABORTED (×6) | Navigation beacons | 🟢 Info — benign race condition | + +--- + +## Summary Table + +| # | Issue | Location | Severity | Category | +|---|-------|----------|----------|----------| +| 1 | **Credentials in URL**: `admin:J59vAcWI56BReJEFCi` in WAHA console error | `waha.charityright.org.uk` | 🔴 **Critical** | Security | +| 2 | **`localhost:3000/api/pledges`** called from production | `/sadaqah` JS | 🔴 **Critical** | Config / Security | +| 3 | **All routes 404** except `/sadaqah` — no error page, no redirect | `/zakat`, `/fidya`, etc. | 🟠 **High** | Navigation | +| 4 | **404 page has no title, no styling, no back link** | All 404 responses | 🟠 **High** | UX | +| 5 | **`hub.quikcue.com` API called 8+ times with 401** on every page load | `/sadaqah` | 🟠 **High** | Performance / Config | +| 6 | **Missing `<meta name="description">`** | All pages | 🟠 **High** | SEO | +| 7 | **`user-scalable=no` in viewport meta** — disables pinch zoom | All pages | 🟠 **High** | Accessibility (WCAG 1.4.4) | +| 8 | **4 separate GA4 properties** + 2 Google Ads + TikTok + Facebook + Hotjar | All pages | 🟡 **Medium** | Performance / Privacy | +| 9 | **No Gift Aid address validation** before proceeding to checkout | `/sadaqah` | 🟡 **Medium** | UX / Data quality | +| 10 | **Name/email not validated** before overlay opens | `/sadaqah` checkout | 🟡 **Medium** | UX | +| 11 | **OG image served from WordPress domain** (`charityright.org.uk/wp-content/`) | `/sadaqah` | 🟡 **Medium** | Reliability / Performance | +| 12 | **No `<meta name="robots">`** directive | All pages | 🟡 **Medium** | SEO | +| 13 | **`href="#"`** on cookie settings anchor | Cookie banner | 🟡 **Medium** | Accessibility | +| 14 | **1 unlabelled input** (no aria-label, no placeholder) | `/sadaqah` | 🟡 **Medium** | Accessibility | +| 15 | **3 inline `onclick` handlers** | `/sadaqah` | 🟡 **Medium** | Code quality | +| 16 | **Hotjar not loading** (detects headless/bot UA) | All pages | 🟡 **Medium** | Analytics | +| 17 | **Missing favicon** (404 on `/favicon.ico`) | All pages | 🟢 **Low** | Branding | +| 18 | **No skip-to-content link** | All pages | 🟢 **Low** | Accessibility | +| 19 | **No `<nav>` landmark** | All pages | 🟢 **Low** | Accessibility | +| 20 | **Stripe session cookies not HttpOnly** | `/sadaqah` | 🟢 **Low** | Security (Stripe-controlled) | + +--- + +## Recommendations (Priority Order) + +### 🔴 Immediate (Critical) +1. **Rotate WAHA admin credentials** — `admin:J59vAcWI56BReJEFCi` is exposed in browser console. Change password immediately. Audit who accessed WAHA. +2. **Remove `localhost:3000` URL** from production build — likely a `.env` misconfiguration. The `/api/pledges` call should point to the production API endpoint. + +### 🟠 High Priority +3. **Add styled 404 page** with charity branding, navigation back to `/sadaqah`, and proper `<title>`. +4. **Fix `hub.quikcue.com` API calls** — either authenticate them properly or remove them from the give site (wrong codebase/integration). +5. **Add `<meta name="description">`** — currently only `og:description` is set. +6. **Remove `user-scalable=no`** from viewport meta — replace with `user-scalable=yes` to comply with WCAG 1.4.4. + +### 🟡 Medium Priority +7. **Audit tracking scripts** — consolidate 4 GA4 properties if possible, remove duplicates. Each extra script adds load time and GDPR surface area. +8. **Validate Gift Aid fields before opening checkout overlay** — show inline error if Gift Aid is enabled but address fields are empty. +9. **Validate name/email before showing checkout overlay** — currently you can open the payment panel without entering name/email. +10. **Move OG image to `give.charityright.org.uk` CDN** — remove cross-domain WordPress dependency. +11. **Replace `href="#"` on Cookie Settings** with proper button element or `href="javascript:void(0)"`. +12. **Fix unlabelled input** — audit the 1 input missing both `aria-label` and `placeholder`. + +### 🟢 Low Priority +13. Add favicon to `give.charityright.org.uk`. +14. Add `<meta name="robots" content="index,follow">` explicitly. +15. Add skip-to-content link for keyboard/screen reader users. + +--- + +## Screenshots Captured + +| File | Description | +|------|-------------| +| `screenshots/cr-homepage.png` | Full page — sadaqah donation page at 1440×900 | +| `screenshots/cr-donation-form.png` | Full page after clicking "Give £6 Sadaqah" | +| `screenshots/cr-form-filled.png` | Checkout overlay with name/email filled | +| `screenshots/cr-form-ready.png` | Checkout overlay with full card details entered, Donate button enabled | +| `screenshots/cr-overlay-open.png` | Overlay state after form interaction | +| `screenshots/cr-mobile.png` | Full page at 375×812 mobile viewport | +| `screenshots/cr-zakat.png` | 404 page for /zakat | diff --git a/screenshots/cr-form-ready.png b/screenshots/cr-form-ready.png new file mode 100644 index 0000000..01d8445 Binary files /dev/null and b/screenshots/cr-form-ready.png differ diff --git a/screenshots/cr-mobile.png b/screenshots/cr-mobile.png new file mode 100644 index 0000000..100b194 Binary files /dev/null and b/screenshots/cr-mobile.png differ diff --git a/screenshots/cr-overlay-open.png b/screenshots/cr-overlay-open.png new file mode 100644 index 0000000..8529566 Binary files /dev/null and b/screenshots/cr-overlay-open.png differ diff --git a/screenshots/cr-zakat.png b/screenshots/cr-zakat.png new file mode 100644 index 0000000..a797457 Binary files /dev/null and b/screenshots/cr-zakat.png differ