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_id column 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-With header 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_metrics table.
  • Security-sensitive actions (login, signup, password change, billing change, token disconnect) are logged to an audit_events table with IP and timestamp.
  • A health endpoint at /api/health reports 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.