You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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)
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)CREATETABLEprofiles (
id UUID PRIMARY KEYREFERENCESauth.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)