← Back to home

Security & Responsible Disclosure

Last updated: April 19, 2026

ChurnShield handles Stripe payment events, customer PII, and sends dunning and win-back emails on behalf of SaaS operators. The integrity of that data is load-bearing for our customers’ businesses. This page explains how to report security issues, what’s in scope, and the defenses we already maintain.

Report a vulnerability: security@getchurnshield.com
We acknowledge every report within 48 hours on weekdays.
Machine-readable policy: /.well-known/security.txt

Reporting a vulnerability

Please do not open a public GitHub issue for security problems. Instead, email us directly. If you are demonstrating an issue against live customer data, please stop the demonstration as soon as you’ve confirmed the vulnerability is real and document what you observed without exfiltrating further data. Report by email and we’ll coordinate retesting on an isolated account.

What we ask you to avoid

  • Don’t run scanners against getchurnshield.com that would consume our Stripe, Resend, Anthropic, or Supabase quotas.
  • Don’t attempt to access other customers’ data. A single record of PII is enough to demonstrate a finding — please don’t pull more.
  • Don’t social-engineer our team.
  • Don’t publicly disclose a vulnerability before we’ve confirmed a fix is live.

What we commit to

  • Acknowledging your report within 48 hours.
  • Keeping you informed of remediation progress.
  • Crediting you by name once a fix is deployed (with your consent).
  • Not pursuing legal action against good-faith security research that follows this policy.

Scope

In scope

getchurnshield.com and *.getchurnshield.com:

  • Every page, every API endpoint (/.netlify/functions/*)
  • The embeddable widget (/widget.js) and the Stripe audit flow (/audit.html)
  • Any authentication, session, webhook, or MFA mechanism

Out of scope

  • Third-party services we use (Stripe, Supabase, Netlify, Resend, Anthropic, Plausible, Twilio). Report those directly to the respective vendors.
  • Social engineering of our team or customers.
  • Physical attacks against our infrastructure providers.
  • DoS / volumetric attacks (our CDN and rate limits handle those; no bounty for these).
  • Findings that require a compromised customer device (keyloggers, browser extensions).

Our current security posture

ChurnShield takes a defense-in-depth approach. The list below is intentionally detailed so researchers know what to test without duplicating defenses that already exist.

Authentication

  • Email magic-link login; no passwords to phish or leak.
  • Session tokens and magic-link codes stored as SHA-256 hashes in Postgres.
  • Magic-link codes are single-use with a 15-minute TTL; sessions expire after 7 days.
  • Step-up MFA via 6-digit email code required for sensitive actions (API key generation, email change, webhook URL change, webhook secret rotation, opening the Stripe billing portal).

Authorization

  • Every API handler derives account_id from the session, never from the request body or query string.
  • All database tables have Row Level Security enabled with service-role-only policies.
  • A BEFORE-UPDATE trigger blocks non-service-role changes to plan, is_active, trial_end, trial_plan.

Input validation

  • Allowlist validation on every settings payload.
  • JSON body size capped at 64 KB on every POST handler.
  • HTML output sanitized via sanitize-html on all AI-generated email and dashboard content.
  • Prompt-injection mitigation: user-controlled fields are wrapped in structured <customer_data> fences before being passed to the model.
  • CSV-injection prevention on exports (cells starting with =, +, -, @, tab, or CR are prefixed with a quote).

SSRF and outbound requests

  • All outbound webhook / Slack / OAuth fetches go through src/lib/safe-fetch.js, which DNS-resolves the hostname and rejects any response in RFC1918, loopback, link-local, CGNAT, multicast, IPv6 ULA, or the AWS IMDS (169.254.169.254) range.
  • Redirects are surfaced, never followed.

Webhooks and integrations

  • Stripe webhook signature verified via stripe.webhooks.constructEvent.
  • Webhook event idempotency enforced via public.stripe_webhook_events.
  • Scheduled processors require either Netlify’s scheduler signature or a PROCESSOR_SECRET bearer header. Public HTTP invocations return 403.
  • Stripe Connect onboarding callback uses an HMAC-signed state parameter with a 30-minute TTL.

Transport and headers

  • HSTS with preload, 1-year max-age, includeSubDomains.
  • Strict Content Security Policy with frame-ancestors 'none', object-src 'none', base-uri 'self', and upgrade-insecure-requests.
  • X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin.
  • Cross-Origin Opener / Resource policies set to same-origin.

Monitoring

  • Every sensitive action is recorded in a security_events audit log: magic-link send, login, logout, session-expire, MFA challenge issue / verify / fail, API key generate / revoke, Stripe Connect authorize / callback.
  • Novel-device login alerts email the account holder whenever a session is minted from an IP and user-agent family the account hasn’t used recently.
  • Distributed rate limiting on every public endpoint (per-IP and per-account where applicable).

Coordinated disclosure timeline

We aim to fix confirmed vulnerabilities within 30 days of the initial report. Critical issues are remediated immediately and disclosed publicly after the fix is deployed. We may request a longer embargo on complex fixes; if we do, we’ll explain why and propose a date.

PGP

We don’t currently publish a PGP key. Email us if you’d like us to generate one and we’ll publish it under /.well-known/.

Not about a specific vulnerability? Email hello@getchurnshield.com for anything else.