Skip to content
Plauditlyplauditly
LegalSecurity & vulnerability disclosure

Security & vulnerability disclosure

Last updated: May 24, 2026

What we do to protect your data, what to do if something goes wrong, and how to report a vulnerability without ending up in a lawyer's inbox.

Encryption

  • TLS 1.2+ enforced on every connection. HSTS preload (2-year max-age, includeSubDomains).
  • AES-256 at rest in Supabase (Postgres) and Stripe.
  • Secrets (Stripe webhook secrets, Supabase service-role key, Anthropic gateway tokens) are stored as encrypted Vercel environment variables and never reach the browser.

Access controls

  • Row-Level Security (RLS) enabled on every table in the public schema. Owner-scoped policies enforce per-account isolation at the database layer — verified by a cross-user isolation test in the repo.
  • Authentication via Supabase Auth (magic link + Google OAuth). PKCE flow for OAuth. Sessions are HTTP-only Secure cookies.
  • Webhook signature verification on all third-party callbacks (Stripe).
  • Admin operations use a separate service-role client that bypasses RLS only on intentional code paths (e.g. the Stripe webhook); never reachable from end-user input.

Network and application security

  • Content-Security-Policy on every response — script/style/connect/frame sources explicitly allowlisted; object-src 'none'; frame-ancestors 'none' except on /embed/iframe.
  • X-Frame-Options DENY everywhere except the iframe-embed page (intentionally embeddable).
  • X-Content-Type-Options: nosniff site-wide.
  • Permissions-Policy disabling 16 unused browser features (camera, microphone, geolocation, USB, magnetometer, etc.).
  • Cross-Origin-Opener-Policy: same-origin-allow-popups for Spectre isolation while preserving OAuth.
  • Cross-Origin-Resource-Policy: cross-origin on embed assets; same-site implicit elsewhere.
  • CORS limited to the embed-flow endpoints; auth, chat, billing, and newsletter endpoints reject cross-origin POSTs with 403 (CSRF defense).
  • Open-redirect guard on every user-supplied `next` parameter — rejects protocol-relative URLs, backslash tricks, CRLF/control-character injection, and non-path schemes.
  • Distributed rate limiting on every public POST endpoint, backed by Postgres (Supabase) so caps are global across all serverless instances, with a safe in-process fallback. Current caps: chat 15/min/IP, newsletter 5/5min/IP, testimonial submission 5/hour/project/IP + 30/hour/IP global.
  • Honeypot fields on every public form (newsletter, testimonial submission).
  • Privacy-by-design: testimonial author emails are stored in a column the public API physically cannot read; the row is exposed only via a `testimonials_public` view that omits the column and is now `security_invoker = true` (honors caller RLS).
  • Stripe webhook idempotency: every event ID recorded in `processed_stripe_events`; duplicate deliveries short-circuit with 200 without re-processing.

Defenses against AI-bill exhaustion

The chatbot calls Anthropic Claude via Vercel AI Gateway, which would be a tempting target for cost-exhaustion abuse without guardrails. Five overlapping defenses:

  • Origin check — the endpoint rejects POSTs whose Origin header doesn't match our domain. Stops curl loops from third-party sites instantly.
  • Per-IP rate limit — 15 messages per minute, distributed via the shared Postgres backend (consistent across Vercel Function instances).
  • Message-count cap — only the last 20 messages of conversation history are forwarded; an attacker can't grow context by appending unbounded history.
  • Output-token cap — every response is hard-limited to 500 tokens via `maxOutputTokens`.
  • Model choice — Claude Haiku, the cheapest tier (~$1/M input, ~$5/M output).

Audit log

An append-only audit_log table records mutations on testimonials, widgets, projects, and subscriptions plus security-relevant events (sign-in, rate-limit triggers, suspicious activity). Owners can read their own events via the dashboard; writes go through a SECURITY DEFINER helper that only the service-role client can invoke. Schema and policy live in migration 0005_security_hardening.sql.

Subprocessors and data location

The full list of every third party we share data with, what data they touch, and where they're located is at /legal/subprocessors. Cross-border transfers are documented with the relevant mechanism (EU-US DPF where the recipient is certified; Standard Contractual Clauses otherwise).

Incident response

If we detect or are notified of a security incident affecting customer data:

  • T+0 — Confirm scope; rotate any compromised credentials; revoke affected sessions.
  • T+1h target — Internal incident channel opened; on-call engineer triaging.
  • T+24h target — Containment + initial root cause identified.
  • T+72h required (GDPR Art. 33) — Notify supervisory authority if the breach is likely to result in a risk to data subjects' rights and freedoms.
  • Without undue delay (GDPR Art. 34) — Notify affected users directly when the risk is high.
  • Post-incident — Public post-mortem published at /changelog. Remediation tracked through completion.

We publish post-mortems for any incident that affected customer data, regardless of whether legal notification was required.

Backups and recovery

  • Supabase performs automated daily backups with 7-day retention on free / 14-day on paid Supabase plans.
  • Stripe is the source of truth for subscription state; the local mirror is rebuildable from webhooks.
  • Recovery point objective (RPO): 24 hours. Recovery time objective (RTO): 4 hours.

Compliance posture

  • GDPR — controller/processor obligations documented; SCCs + DPF as transfer mechanisms; DPA available on request (see /legal/dpa).
  • CCPA / CPRA — California rights honored; GPC signal respected.
  • CAN-SPAM — postal address and one-click unsubscribe on all marketing email; no marketing content in transactional email.
  • PCI DSS — out of scope: card data is handled entirely by Stripe (PCI Service Provider Level 1). We never see or store PANs.
  • SOC 2 — not currently audited. On the roadmap once revenue justifies. Underlying infrastructure (Supabase, Stripe, Vercel) is SOC 2 Type II.

Dependency security

  • pnpm audit on every install; transitive vulnerabilities tracked and pinned via overrides in pnpm-workspace.yaml when upstream hasn't shipped a patched parent.
  • GitHub Dependabot enabled for security + version updates (weekly).
  • Production deploys block when a known high or critical advisory affects a runtime dependency.

Machine-readable security contact

A /.well-known/security.txt file (RFC 9116) tells automated scanners and researchers exactly where to report. Re-generated every deploy so the expiration date stays under one year.

Vulnerability disclosure

We welcome reports from security researchers. Please email security@plauditly.app with:

  • A clear description of the vulnerability and where it lives (URL, parameter, payload).
  • Steps to reproduce, including any account credentials you used. Do not test against accounts that are not yours.
  • Your assessment of impact.
  • Whether you'd like to be credited publicly (and the name to use).

We aim to acknowledge reports within 3 business days and provide a remediation timeline within 10 business days. We will not pursue legal action against researchers who:

  • Make a good-faith effort to avoid privacy violations, data destruction, and service degradation.
  • Don't access more data than necessary to demonstrate the vulnerability.
  • Don't publicly disclose before we've had a reasonable opportunity to fix.
  • Don't extort, demand payment for a fix beyond a published bounty (we currently have none), or breach applicable law.

Out of scope for disclosure: DoS / volumetric attacks, social engineering of staff, physical attacks, and findings that require full admin access to a user account you don't own.

Security contact

Security disclosures: security@plauditly.app. PGP key available on request. For non-security questions, use hello@plauditly.app.