Last updated · May 18, 2026
Security
How undercue protects your data, your credentials, and your customers' content. No marketing fluff — only the things we actually do.
Authentication
- Passwords are hashed with bcrypt (cost factor 10). We never store plaintext passwords. We can't recover them — only reset them.
- Session cookies are HttpOnly, Secure, SameSite=Lax, and signed.
- Sessions expire after 30 days of inactivity. Password change invalidates all existing sessions.
- Rate-limiting on login (5 failed attempts → 15-min cool-down per IP).
Encryption
- In transit: TLS 1.2+ on every connection. HSTS enabled.
- At rest (filesystem): EBS volumes are AWS-encrypted by default.
- At rest (sensitive fields): Social-platform access tokens are encrypted with AES-256-GCM before being written to the database. The encryption key (
BACKUP_ENCRYPTION_KEY, 256-bit) lives outside the database — typically in a `.env` file with chmod 0600. - Backups: hourly database snapshots, also AES-256-GCM encrypted, with optional offsite S3 replication.
Data isolation
- Every multi-tenant table has a
company_idcolumn and every query is scoped through middleware that asserts the requested tenant matches the authenticated session's tenant. - The API handler wrapper (
apiHandler) enforces tenant context at a single point — there is no way to write a route that accidentally leaks cross-tenant data. - Workspaces add a second layer of isolation: social accounts, posts, and team permissions are scoped to the workspace AND the company.
Request protection
- CSRF: mutating API requests require an
X-Requested-Withheader that browsers add automatically on same-origin fetches but cross-origin attackers can't forge. - Rate limiting: per-route tiers (STRICT / MODERATE / RELAXED) prevent enumeration and brute force.
- Input validation: every endpoint that accepts JSON validates the body against a Zod schema. Unknown fields are stripped; type mismatches return 400 before touching the database.
- SQL injection: all database access uses parameterised statements via
better-sqlite3. No string concatenation, ever. - XSS: post content is treated as plain text everywhere it's displayed — never
dangerouslySetInnerHTML'd. The Facebook composer escapes user input before previewing.
Observability
- Every API request is metered (path, status, duration, IP) into a
request_metricstable. - Security-sensitive actions (login, signup, password change, billing change, token disconnect) are logged to an
audit_eventstable with IP and timestamp. - A health endpoint at
/api/healthreports database, cron-scheduler, and backup status.
Vulnerability disclosure
If you find a security vulnerability, please email hello@undercue.io with subject [security]. We'll acknowledge within 48 hours and aim to patch within 14 days for high-severity issues.
We don't have a paid bug-bounty program yet, but we're grateful and will credit you publicly (with your permission) if your report leads to a fix.
What we don't do
- We don't run third-party analytics that fingerprint users.
- We don't sell, share, or trade your data.
- We don't store Stripe card numbers — Stripe does.
- We don't log Facebook tokens in plaintext, ever — not even briefly, not even in dev.
- We don't have an offline backup process that's only "encrypted in transit". Backups are encrypted at rest, period.
Compliance
We aim for compliance with GDPR (EU), CCPA (California), and Australian Privacy Principles. We don't currently hold SOC 2 — if your procurement team needs it, email us for a current security questionnaire.