Skip to content

Governance - Access Control & Permission Hierarchy

Governance — Access Control & Permission Hierarchy

Trystpilot’s access model is intentionally flat for MVP. The moderation dashboard is the only admin-gated surface, protected by a single ADMIN_SECRET environment variable checked in the server component. Known issue: the current implementation fails open — if ADMIN_SECRET is not set, the dashboard is accessible. This must be patched before public launch.

Permission Hierarchy

graph TD subgraph "Access Layers" Public["Public Layer\n(no auth required)"] AdminLayer["Admin Layer\n(ADMIN_SECRET required)"] PhaseTwo["Phase 2 Layer\n(session-based moderator auth)"] end subgraph "Public Surfaces" Home["/ (Home)"] ProfilePage["profile/[alias]"] Submit["/submit"] Report["/report"] Legal["/legal/*"] Blog["/blog/*"] APIRead["GET /api/profiles/*"] end subgraph "Admin Surfaces" Mod["/moderation"] APIModerate["PATCH /api/reviews/[id]/moderate"] APICreateProfile["POST /api/profiles"] end subgraph "Middleware (middleware.ts)" RateLimit["Rate Limit check"] SecurityHeaders["Security headers\n(CSP, X-Frame-Options…)"] AdminCheck["ADMIN_SECRET check\n⚠️ fails open without secret"] end Public --> Home & ProfilePage & Submit & Report & Legal & Blog & APIRead AdminLayer --> Mod & APIModerate & APICreateProfile All["All requests"] --> RateLimit --> SecurityHeaders Mod & APIModerate & APICreateProfile --> AdminCheck

Security Hardening Checklist

ControlStatusNotes
Admin secret check⚠️ PartialFails open if ADMIN_SECRET unset
Rate limiting❌ BrokenIn-memory; must migrate to Upstash Redis
CAPTCHA on submit❌ Not wiredhCaptcha placeholder exists
Security headers (CSP, HSTS)Set in middleware.ts
PII never stored in plain textZip = internal; hashed fingerprints only
Secrets in env vars only.env.example is canonical reference
SQL injection preventionParameterised queries via pg client
XSS preventionReact escapes by default; no dangerouslySetInnerHTML
Audit loggingNot implemented — Phase 2