bayan-api / archive /phase_reports /audits /security-audit.md
youssefreda9's picture
Cleanup pass 2: Fix .gitignore corruption, archive docs/audit + phase10 helpers, fix PROJECT_DESCRIPTION.md
6e716e3
|
Raw
History Blame Contribute Delete
4.06 kB

BAYAN — Security Audit Report

Date: 2026-06-18


Attack Surface

Component Exposure Risk Level
Flask API (7860) Public via HF Spaces Medium
Supabase PostgreSQL Behind RLS + Auth Low
Google OAuth Delegated to Google Low
Model Inference CPU-bound, no GPU Low
localStorage Client-only Low

Authentication & Authorization

Supabase RLS (Row-Level Security)

Test Result Evidence
Unauthenticated REST access ✅ BLOCKED HTTP 401
Invalid JWT token ✅ BLOCKED HTTP 401
Cross-user document access ✅ BLOCKED RLS user_id = auth.uid()
Guest session isolation ✅ PASS Guest users have unique UUIDs

Google OAuth

Test Result
OAuth flow uses Supabase SDK
Tokens stored in Supabase session
No custom token handling ✅ (delegated)

API Input Validation

Test Endpoint Result Details
Empty text All ✅ Returns 200/400 gracefully No crash
Missing text field All ✅ Returns 400 "Text is required"
Very long text (10K+) /api/analyze ⚠️ Timeout Pipeline processes all 3 models
Non-JSON payload All ✅ Returns 400 "Request must be JSON"
SQL injection attempt /api/analyze ✅ SAFE NLP model treats it as text
XSS <script> tag /api/analyze ✅ SAFE Tags preserved as text (editor uses textContent)

Max Text Length Protection

MAX_ANALYZE_LENGTH = 5000  # Frontend
MAX_TEXT_LENGTH = 5000     # Backend (app.py)

Both frontend and backend enforce a 5000-character limit.


CORS Configuration

CORS(app, resources={r"/api/*": {"origins": "*"}})
Aspect Status
API routes protected /api/* only
Static files unaffected
Wildcard origin ⚠️ Allows any origin

Wildcard CORS (*) is acceptable because the API is public and requires no authentication. All sensitive operations go through Supabase SDK directly (client → Supabase, not client → Flask → Supabase).


Data Protection

Data Storage Protection
User profile Supabase profiles RLS + JWT
Documents Supabase documents RLS + JWT
Summaries Supabase summaries RLS + JWT
Settings Supabase settings RLS + JWT
Editor drafts localStorage Client-only
Dismissed words localStorage Client-only

Secrets Management

Secret Location Status
HF_API_TOKEN HF Spaces Secrets
SUPABASE_URL HF Spaces Secrets
SUPABASE_ANON_KEY HF Spaces Secrets
Google OAuth Client ID Frontend JS (public) ✅ (public key by design)

No private keys are exposed in frontend code. The Supabase anon key is a public key designed for client-side use, protected by RLS policies.


Vulnerability Assessment

Category Risk Status
SQL Injection None Supabase ORM + parameterized
XSS Low Editor uses textContent not innerHTML for user text
CSRF N/A No session cookies (stateless API)
Path Traversal None No file uploads
DDoS Medium No rate limiting (relies on HF proxy)
Model Poisoning None Models are read-only

Recommendations

  1. Rate Limiting: Add Flask-Limiter for /api/analyze (e.g., 10 req/min per IP)
  2. Input Size: Current 5000-char limit is good; consider per-endpoint limits
  3. CORS: Could restrict to specific domains when going to production
  4. Monitoring: Add request logging with IP for abuse detection

Security Score: 85/100 🟡

The system is secure for its current deployment model (public NLP API + Supabase-protected data). The main gap is lack of rate limiting, which is mitigated by HF Spaces' own proxy layer.