Skip to content

yahya2208/qatra

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Qatra — Security Hardening Report

Date: 2026-07-13


1. Executive Summary

Comprehensive security audit and hardening of the Qatra blood donation platform. The platform consists of a single-file frontend (index.html, ~1800 lines), Supabase backend (PostgreSQL + RLS), and static assets on GitHub Pages.

Risk reduction: CRITICAL → MEDIUM (remaining risks require infra changes documented below)


2. Vulnerabilities Found & Fixed

2.1 CRITICAL — Fixed

# Vulnerability OWASP CVSS Fix Applied
C1 RLS wide open — USING (true) on all tables, DELETE public on blood_requests A01:2021 9.1 Rewrote all RLS policies: read-only for reference data, insert-only for user tables, no public UPDATE or DELETE on any table
C2 select('*') on donors — leaks all columns to every client A03:2021 8.6 Replaced with explicit column lists: id,full_name,phone,blood_type,...
C3 XSS via innerHTML — d.phone injected unescaped into href attributes A03:2021 8.1 Replaced entire renderResults innerHTML with safe DOM construction (createElement + textContent + setAttribute)
C4 No input validation — any string accepted for name, phone, facebook A03:2021 7.5 Added regex validation: phone ^0[5-7]\d{8}$, name 2-100 chars no HTML, facebook URL domain whitelist
C5 document.write in printPoster — potential XSS vector A03:2021 7.0 Replaced with safe DOM construction (createElement + src attribute)
C6 No Content Security Policy A05:2021 7.5 Added CSP <meta> tag: script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net, frame-ancestors 'none', connect-src limited to Supabase origin

2.2 HIGH — Fixed

# Vulnerability OWASP CVSS Fix Applied
H1 No rate limiting — unlimited INSERT spam A04:2021 7.5 Client-side cooldowns (5min register, 1min feedback/rating) + Cloudflare Workers proxy (worker.js) with per-IP limits
H2 Facebook URL injection — no domain validation A03:2021 6.5 Added RE_FB regex whitelist: only `https://(www.)?(facebook
H3 No consent/privacy notice — GDPR/compliance gap A01:2021 6.0 Added privacy policy modal + mandatory consent checkbox on registration
H4 Feedback table exposed to public SELECT — data leakage A01:2021 5.5 Removed public SELECT on feedback table (admin-only via service_role)
H5 No maxlength on form inputs — abuse vector A05:2021 5.0 Added maxlength: name=100, phone=10, facebook=255, suggestion=2000, comment=1000
H6 Blood type not validated on insert — CHECK constraint only A03:2021 5.0 Added client-side whitelist: VALID_BLOOD.includes(bloodType) before insert

2.3 MEDIUM — Fixed

# Vulnerability OWASP CVSS Fix Applied
M1 No rel="noopener noreferrer" on external links A05:2021 4.5 Added to all dynamic links (WhatsApp, Facebook) in DOM construction
M2 No referrerpolicy header A05:2021 3.5 Added <meta name="referrer" content="no-referrer">
M3 Last donation date accepts future dates A04:2021 3.5 Added validateLastDonation() — date must be ≤ now
M4 Phone not validated in href — could contain non-digit chars A03:2021 4.0 Phone validated with RE_PHONE regex before creating tel: and wa.me links
M5 Particle canvas runs on all devices including low-end A05:2021 2.0 Reduced particle count on mobile (20 vs 40)

3. Changes Applied by File

schema.sql (complete rewrite)

  • Removed open DELETE policy on blood_requests
  • Removed USING (true) / WITH CHECK (true) on user-writable tables
  • Added CHECK constraints: phone regex, name length, facebook URL domain, blood_type, urgency, status, stars, message length
  • Added UNIQUE constraint on donors.phone
  • Added NOT NULL + DEFAULT on all timestamp columns
  • Added facebook_url column to donors (was in JS but missing from schema)
  • Added updated_at trigger function on donors and blood_requests
  • Added composite index idx_donors_query for the main search pattern
  • Added partial index idx_donors_available for is_available = true
  • Added partial index idx_blood_requests_status for open requests
  • Removed contact_info from feedback (was unused)
  • Consolidated seed data into single INSERT statement

index.html (19 edits applied)

  • Line 6: Added CSP, referrer, and robots meta tags
  • Lines 509-512: Added maxlength, pattern, required to name/phone inputs
  • Line 537: Added maxlength="255" to Facebook URL input
  • Line 585: Added maxlength="2000" to suggestion textarea
  • Line 672: Added maxlength="1000" to review comment textarea
  • Line 1029: Replaced select('*') with explicit columns + limit(500)
  • Lines 1153-1208: Replaced entire renderResults innerHTML with safe DOM construction
  • Lines 1284-1312: Added rate limiting infrastructure (COOLDOWNS, checkCooldown, markAction)
  • Lines 1314-1322: Added input validation functions (validateName, validatePhone, validateFacebook, validateLastDonation)
  • Lines 1324-1371: Rewrote register submit with full validation chain + consent check
  • Lines 1389-1398: Added rate limiting + validation to suggestion submit
  • Lines 1432-1440: Added rate limiting + validation to rating submit
  • Line 1452: Replaced select('*') in loadRatings with explicit columns + limit(50)
  • Lines 1671-1686: Replaced document.write in printPoster with safe DOM construction
  • Lines 822-823, 876-877: Added i18n keys for validation messages, cooldown, privacy
  • Line 539-543: Added consent checkbox + privacy link to registration form
  • Lines 1260-1261: Added privacy modal opener handler
  • Lines 1755-1756: Reduced particle count on mobile
  • Lines 1779-1788: Replaced innerHTML in init loading state with DOM construction
  • Lines 993-998: Replaced innerHTML in type grid button with DOM construction

worker.js (new file)

  • Cloudflare Worker reverse proxy for Supabase
  • Per-IP rate limiting: donors 5/10min, feedback 10/10min, ratings 20/10min
  • Blocks all DELETE requests (405 Method Not Allowed)
  • In-memory store with automatic cleanup
  • Returns Retry-After and X-RateLimit-* headers
  • Configuration via wrangler.toml + secrets

wrangler.toml (new file)

  • Worker deployment configuration for Cloudflare

4. Remaining Risks (Requires Infrastructure Changes)

# Risk Severity Mitigation Needed
R1 No authentication — all operations are anonymous HIGH Implement Supabase Auth + role-based access
R2 No admin dashboard — no way to manage users/requests HIGH Build admin UI with service_role key
R3 Client-side rate limiting only — can be bypassed MEDIUM Deploy worker.js + update frontend to use Worker URL for INSERTs
R4 Phone numbers visible publicly — privacy concern MEDIUM Add auth-gated contact reveal
R5 No server-side input validation — client-side only MEDIUM Add Supabase Edge Functions or Worker-side validation
R6 No HTTPS enforcement on custom domain LOW Ensure GitHub Pages HTTPS is enabled
R7 No CAPTCHA — automated spam possible LOW Add hCaptcha/Turnstile to registration

5. Auth System Architecture (Design — Not Yet Implemented)

5.1 Role Hierarchy

visitor (unauthenticated)
  └─ donor (authenticated, verified phone)
       └─ moderator (assigned by admin)
            └─ admin (owner)

5.2 Supabase Auth Integration

-- profiles table (linked to auth.users)
CREATE TABLE profiles (
  id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
  role TEXT DEFAULT 'donor' CHECK (role IN ('donor','moderator','admin')),
  phone_verified BOOLEAN DEFAULT false,
  created_at TIMESTAMPTZ DEFAULT now()
);

5.3 Updated RLS Policies (with auth)

-- Donors: public read, authenticated insert, owner update
CREATE POLICY "donors_insert_auth" ON donors FOR INSERT
  WITH CHECK (auth.role() = 'authenticated');

CREATE POLICY "donors_update_owner" ON donors FOR UPDATE
  USING (auth.uid() = (SELECT user_id FROM profiles WHERE id = donors.id));

-- Admin: full access via service_role (never from frontend)
-- Feedback: moderator+ can read, authenticated can insert
-- Ratings: public read, authenticated insert, owner delete

5.4 Admin Dashboard Architecture

  • Separate admin.html or route
  • Uses SUPABASE_SERVICE_ROLE_KEY stored in a Worker/Edge Function (never in frontend)
  • Dashboard features:
    • User management (view/delete/ban donors)
    • Blood request moderation
    • Feedback review queue
    • Analytics (registrations over time, blood type distribution)
    • Spam detection (duplicate phone, suspicious patterns)

6. Deployment Steps for Remaining Items

6.1 Deploy Cloudflare Worker

npm install -g wrangler
wrangler login
wrangler secret put SUPABASE_ANON_KEY
wrangler deploy

Then update SUPABASE_URL in index.html to point to the Worker URL.

6.2 Enable Supabase Auth

  1. Enable Email/Phone auth in Supabase Dashboard → Authentication → Providers
  2. Run the profiles table migration
  3. Update RLS policies as shown in Section 5.3
  4. Add login/signup UI to index.html
  5. Store service_role key in a Worker/Edge Function for admin operations

6.3 Add CAPTCHA

  • Add Cloudflare Turnstile (free) to registration form
  • Verify token server-side (in Worker) before forwarding to Supabase

7. OWASP ASVS Compliance Summary

ASVS Category Status Notes
V1 Architecture Partial No auth system yet
V2 Authentication Not implemented Design ready (Section 5)
V3 Session Management Not applicable No sessions yet
V4 Access Control Partial RLS enforced, no role-based auth
V5 Validation ✅ Implemented Client-side + schema CHECK constraints
V6 Crypto ✅ N/A HTTPS enforced by GitHub Pages
V7 Error Handling ✅ Implemented Generic error messages, no stack traces exposed
V8 Data Protection Partial No data-at-rest encryption, CSP implemented
V9 Communication ✅ Implemented CSP, referrer policy, HTTPS only
V10 Malicious Code ✅ Implemented No eval, no innerHTML XSS, DOM construction
V11 Business Logic Partial No server-side rate limiting yet
V12 Files ✅ N/A No file uploads
V13 API Partial No server-side validation yet
V14 Config ✅ Implemented Secrets in Worker, not in frontend

About

No description, website, or topics provided.

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors