Security at ProposalForge
The controls we run on top of solid managed infrastructure (Supabase, Vercel, Stripe) — and what you can do to keep your account safe.
ProposalForge runs on managed infrastructure built by teams with deeper security budgets than ours: Supabase (database, auth, file storage, RLS), Vercel (application hosting, edge middleware), and Stripe (payments). Our job is to use those primitives correctly and to add the controls that depend on knowing the product.
Encryption everywhere
TLS 1.2+ in transit. AES-256 at rest in our managed Postgres and object storage. Backups are encrypted with provider-managed keys.
Auth that fights brute force
Supabase Auth with HaveIBeenPwned breach screening at signup, hashed and salted credentials, mandatory current-password verification on change, and Same-Site=Lax cookies.
Row-level security on every table
RLS policies enforce per-user isolation in Postgres. The application tier scopes every query by user_id even though RLS would catch it, so the failure mode is fail-closed.
Hardened secrets
Server-only env imports prevent secret leakage to the browser. Stripe webhook signatures verified on every event. Service-role keys are scoped and rotated.
Monitoring & rate limits
Per-user rate limits on AI generation, section regeneration, and PDF rendering. Structured JSON logs. Stripe events deduplicated via atomic claim.
Incident response
We notify affected users without undue delay (and within 72 hours where required by GDPR) of any confirmed personal-data breach, and disclose the mitigation steps taken.
Application controls in detail
- Origin enforcement. State-changing requests (POST/PUT/PATCH/DELETE) to dashboard or API routes must come from our origin. Cross-site requests are rejected with HTTP 403.
- Strict CSP. Content-Security-Policy restricts scripts to self, Stripe, and inline (Next.js requirement). No unsafe-eval. Frame ancestors denied.
- HSTS preload. Strict-Transport-Security with max-age 2 years and includeSubDomains.
- Quota reservation is atomic. AI generation slots are reserved via a Postgres SQL function (
UPDATE ... WHERE used < limit RETURNING) so concurrent requests cannot bypass plan limits. - Public proposal access is by high-entropy slug. Slugs are 96 bits of cryptographic randomness; brute-forcing is not feasible.
- Acceptances bind to slug, not id. Acceptance submissions are scoped by the slug in the URL the visitor is looking at, preventing cross-proposal forgery.
- IP addresses are hashed. View events store SHA-256 hashes; we never display raw IPs.
- Functions hardened. All SECURITY DEFINER functions have a pinned
search_pathto prevent schema-hijacking.
Infrastructure
- Hosting: Vercel (US edge for static, Singapore region for serverless functions where applicable).
- Database: Supabase managed Postgres (Singapore). Daily encrypted backups; 30-day point-in-time restore.
- File storage: Supabase Storage with signed-URL access for non-public assets.
- Edge protections: Cloudflare DNS and DDoS protection on the marketing surface.
Continuous security
- GitHub branch protection on
main, signed releases. - Automated CI: lint, type-check, unit tests, audit on every PR.
- Dependency scanning (gitleaks, npm audit, semgrep, bandit, bearer, trivy).
- Weekly Supabase security advisor sweeps; remediation tracked in public migrations.
What you can do
- Use a strong, unique password (HIBP screening blocks known breaches at signup).
- Enable a password manager and 2FA on your email account — your email is the recovery vector.
- Don't share your account login. Agency teams should use the team plan, not shared credentials.
- Review the Privacy Policy for retention and rights.
Reporting
We accept vulnerability reports at [email protected]. Please give us reasonable time to fix issues before public disclosure. We do not currently run a paid bug bounty but recognize researchers in our security acknowledgments.