This document is the self-assessment of SADDAD against the UAE Federal Decree-Law No. 45 of 2021 on the Protection of Personal Data (PDPL) and standard government-system security requirements. It is not an audit certification — it is the engineering team's honest accounting of what's protected today and what must change for a federal pilot deployment.
| Table | Personal Data fields | Retention category |
|---|---|---|
applicants |
full_name, full_name_ar, emirates_id, phone, property_address, monthly_salary, monthly_expenses |
Operational — kept while case is open |
cases |
full_name, emirates_id, phone, monthly_salary, arrears_amount, decision_reason, rationale_ar |
Operational + audit — kept per MOEI records policy |
audit_logs |
rationale (free text — may contain PII), financial_snapshot (JSONB with salary etc.) |
Audit / compliance — 7-year retention recommended |
job_queue |
form_data JSONB (includes salary, phone, name) — short-lived |
Operational — purge on completed/failed after 30 days |
agent_steps |
result_summary (may include applicant name) |
Operational — purge with case |
| Control | Implementation |
|---|---|
| HTTPS-only in production | Vercel / Azure / AWS managed TLS termination |
| Session cookies HTTP-only + Secure + SameSite=Lax | lib/auth/session.ts sets all three flags; secure enabled when NODE_ENV=production |
| HMAC-signed session tokens | SHA-256 with secret pulled from SESSION_SECRET env var (never in code) |
| Time-bounded sessions | 8-hour TTL, validated on every request |
| No auth secrets in browser bundle | NEXT_PUBLIC_API_KEY_* env vars were removed; the browser only knows the session cookie |
| RLS enabled on every table | All PII tables (applicants, audit_logs, job_queue) deny all anon access |
| Backend uses service-role key only | SUPABASE_SERVICE_ROLE_KEY is server-side only, never bundled to the browser |
| Rate limiting on login + API | Per-IP rate limit on /api/auth/login (10 req/min); per-key on data routes |
| Input validation on API routes | Every route validates body shape before touching the DB |
| Immutable audit log | audit_logs row written on every final decision; never updated or deleted by application code |
| Column-level anon access | cases/agent_steps use column-level GRANTs so the public anon key reads only non-PII columns; Emirates ID, phone, salary, names and free-text rationale are revoked from anon (see supabase/schema.sql) |
| Null-byte sanitization | LLM-generated rationale is stripped of \x00 before audit-log insert (PostgreSQL TEXT rejects them) |
| Gap | Mitigation plan |
|---|---|
| Demo username/password auth | Replace with UAE PASS OIDC integration before any pilot — full spec in UAEPASS.md. UAE PASS is the federal identity provider and the only acceptable production auth for citizen-facing federal services. |
Plaintext rationale column |
Encrypt-at-rest at the column level (Postgres pgcrypto or application-level via libsodium). Rationale text can reference applicant name + financial context — column-level encryption ensures DB administrators with table-read access cannot see PII. |
Plaintext form_data JSONB |
Same — encrypt before insert. |
Secrets in .env.local |
Move all secrets to Azure Key Vault or AWS Secrets Manager with IAM-scoped access. Rotate SESSION_SECRET and API_KEY_* quarterly. |
| LLM provider (Groq, US-hosted) | Migrate to G42 Jais or Falcon-Arabic for production. The LLM sees applicant PII in prompts; processing must happen in UAE-hosted infrastructure. |
| In-memory rate limiter | Move to Redis-backed sliding window for distributed enforcement across pods. |
| No audit-log replication | Replicate audit_logs to write-once cold storage (Azure Immutable Blob / AWS S3 Object Lock) for 7-year retention per UAE federal records policy. |
| No PII data subject access request flow | Implement /api/dsar/<emirates_id> (admin-only) returning the citizen's full decision graph + the right to request deletion of operational data. |
| No formal pen-test | Engage a CERT-accredited UAE security firm for pre-pilot penetration testing. |
| Realtime WAL payloads not column-filtered | Postgres logical-replication payloads carry all row columns regardless of column GRANTs. The dashboard mitigates today by treating realtime events as refetch triggers only — it never renders the payload (components/Dashboard.tsx) and reloads PII via the service-role API. Production moves the live feed to authenticated Realtime (per-officer JWT) or a server-emitted PII-free broadcast channel. |
Status: ✅ Compliant by design. Processing is performed under the authority of MOEI for the purpose of administering housing arrears rescheduling — a documented government service. Applicants are notified of AI involvement at submission (the UI banner explicitly says "AI-powered").
Status: ✅ In place. The submission form requires explicit consent to the 20% salary deduction. The system does not process cases without checked consent.
Status:
Status: ✅ In place. Each agent only receives the fields it needs (e.g. the Communication agent only sees phone + decision, not the full financial snapshot). The Critic agent's tool calls return only summary data, not raw rows.
Status:
Status:
- Sentry / equivalent for error capture
- On-call rotation with documented breach response runbook
- 72-hour notification path to UAE Data Office
Status: ✅ Compliant by design. PDPL Article 22 requires citizens to have the right not to be subject to a decision based solely on automated processing. SADDAD satisfies this in three ways:
- Every escalated case reaches a human officer
- Every auto-decision can be overridden by an officer within the existing 5-day SLA
- The decision rationale is delivered to the citizen with their decision, so they can appeal
| Threat | Likelihood | Impact | Current mitigation | Residual risk |
|---|---|---|---|---|
| Stolen session cookie | Medium | High | HTTP-only + SameSite + 8h TTL + rotating secret | Acceptable; standard web auth posture |
| Stolen API key for server-to-server | Low | High | Rotate quarterly; per-key rate limit; revoke in env var | Acceptable post-Key Vault migration |
| Stolen Supabase service-role key | Low | Critical | Server-side only, never in repo; secret manager in production | Acceptable post-Key Vault migration |
| SQL injection | Very Low | Critical | Parameterized queries via Supabase client; no string interpolation | Low |
| LLM prompt injection (malicious PDF) | Medium | Medium | LLM output never directly executes; rationale is text-only; hard rules override LLM judgment | Acceptable |
| Insider PII access | Medium | High | RLS + service-role separation; pre-pilot: column-level encryption on rationale + form_data | Reduced post-encryption |
| Audit log tampering | Low | Critical | Application code never updates/deletes audit_logs; pre-pilot: write-once cold storage replication | Reduced post-replication |
| Decision bias from training data | Low | Medium | Fairness agent compares against precedent; weekly officer disagreement review during pilot | Monitored continuously |
- This is not an external compliance certification
- This is not legal advice — the MOEI Legal and IT Security teams must conduct their own review
- This is the engineering team's honest accounting so reviewers can see exactly what's protected today and what the production gaps are
The point of being explicit about gaps is that you can close them. A document that claimed "fully PDPL compliant" without evidence would be less trustworthy than this one.