FixVibe

// docs / security guides / pre-ship checklist

바이브 코딩 보안 체크리스트: 출시 전 44가지 항목

A practical, phase-organised checklist for apps built with Cursor, Claude Code, Lovable, Bolt, v0, Replit, and Windsurf. Each item is actionable in under five minutes. Run through it before you push to production, then again before each major release. Items are grouped into seven categories — secrets, database, auth, headers, third-party, deployment, monitoring — and tagged with the deploy phase they apply to.

PRE = pre-deploy (audit your source). DEPLOY = at deploy time. POST = post-deploy verification. Items reference FixVibe check IDs in category.check-id form where relevant.

Secrets and API keys (8 items)

Hardcoded keys are the single most common finding in vibe-coded apps. Eight items to keep them out:

  1. PRE — Audit NEXT_PUBLIC_ env vars. Anything prefixed NEXT_PUBLIC_ ships in client bundles. If one is a Supabase service_role key (decodes to JWT with "role":"service_role"), delete it and route through a server-only client (src/lib/supabase/service.ts with import 'server-only').
  2. PRE — Grep for hardcoded provider keys. Search source for sk_live_, pk_live_, STRIPE_SECRET, sk-ant-, sk-, AIza, AKIA, and JWT-looking strings (eyJ). Move every hit into .env.local and reference via process.env.* server-side only.
  3. PRE — Verify .gitignore. Confirm .env*.local, .npmrc, .yarnrc, and any provider-specific credentials files are ignored. Run git ls-files piped through your provider patterns to find anything already committed.
  4. PRE — Scan the built bundle. Run npm run build, then grep .next/static and any dist/ output for the same patterns. If a key reaches the bundle, the dev never had clean env separation.
  5. DEPLOY — Set secrets per environment. Vercel: Settings → Environment Variables, scope each to Production / Preview / Development. Never share sk_live_* with the Preview env. Use Vercel's encrypted env-var storage, not inline workflow secrets.
  6. DEPLOY — Disable build-log secret echo. Some CI configs echo env vars during build. Audit your vercel.json, GitHub Actions workflows, or Cloudflare Pages settings for any echo $SECRET that would push the value into public build logs.
  7. POST — Run a passive scan. FixVibe's Free tier covers this: paste the deployed URL, wait ~20s, look for secrets.* findings. The secrets.browser-storage check catches keys that landed in localStorage or sessionStorage via a misuse of the SDK.
  8. POST — Rotate any key that ever shipped. If a key was in a public bundle for even minutes, treat it as compromised. Rotate Supabase service-role keys via the dashboard, regenerate Stripe restricted keys, revoke Anthropic / OpenAI / Google keys via their consoles.

Database access control: RLS and Firestore rules (6 items)

BaaS defaults are permissive on purpose so the first tutorial works. Production needs explicit policies.

  1. PRE — Force RLS on every public.* table. In Supabase: each table must have ALTER TABLE ... ENABLE ROW LEVEL SECURITY and FORCE ROW LEVEL SECURITY. Force matters: without it, Postgres bypasses RLS for table owners.
  2. PRE — Write a policy per (table, role, action). Minimum: a SELECT policy that joins on auth.uid(). Better: separate INSERT / UPDATE / DELETE policies so an UPDATE can't smuggle in user_id changes that reroute ownership.
  3. PRE — Replace default Firebase rules. Default test-mode rules read allow read, write: if true;. Replace with auth-bound rules per collection: match /users/{userId} with allow read, write: if request.auth.uid == userId;
  4. PRE — Lint migrations in CI. Run supabase db lint or an equivalent before merging. CI should fail the build if any CREATE TABLE public.* lacks a matching RLS policy.
  5. DEPLOY — Confirm RLS survived deploy. Re-check in Supabase Studio after deploy: Tables → each row → RLS toggle is ON. Production database migrations occasionally race ahead of policy files; verify the policy is live.
  6. POST — Run an active scan against a verified domain. The baas.supabase-rls active check writes to a tiny seed row using the anon key and reports back if the write succeeded — i.e. RLS isn't actually enforcing.

Authentication and sessions (7 items)

Auth bugs in AI-coded apps tend to be subtle: an off-by-one in token verification, a missed HttpOnly flag, a getSession() where there should be a getUser().

  1. PRE — Replace getSession() with getUser(). getSession() reads the cookie and trusts it; getUser() verifies with the Supabase backend. On server routes always use getUser().
  2. PRE — Confirm token expiry. Magic-link, password-reset, and email-verification tokens need server-enforced expiry. Default Supabase magic-links expire after 1 hour — don't override that to a higher number without a real reason.
  3. PRE — Verify JWT aud and exp. If you decode tokens manually anywhere, check both claims. Better: use the SDK's getUser() which does it for you.
  4. PRE — Audit cookie flags. Custom session cookies should be Secure; HttpOnly; SameSite=Lax (or Strict for non-OAuth flows). No session material in localStorage.
  5. PRE — Validate the next redirect param. The next query param after sign-in must start with / and not // (open-redirect to attacker.example). Reject anything else server-side.
  6. POST — Test logout. Sign in, sign out, inspect cookies (DevTools → Application → Cookies). The session cookie must be cleared on the same response. If it persists, the logout handler isn't actually destroying server-side state.
  7. POST — Active probe. The active.auth-flow and active.account-enumeration checks surface broken auth boundaries — different responses on "user exists" vs "wrong password", missing rate-limit on login, unsigned reset tokens.

HTTP security headers and Content Security Policy (6 items)

Headers are the cheapest hardening in the entire pipeline and the most consistently skipped by codegen.

  1. PRE — Ship a real CSP. Minimum: script-src 'nonce-{NONCE}' 'strict-dynamic'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'. No 'unsafe-inline' in script-src. Next.js auto-applies the nonce when middleware sets the x-nonce request header.
  2. PRE — Add the legacy three. X-Content-Type-Options: nosniff, X-Frame-Options: DENY (or rely on CSP frame-ancestors alone), Strict-Transport-Security: max-age=31536000; includeSubDomains.
  3. PRE — Tighten Referrer-Policy. Default strict-origin-when-cross-origin is fine for most apps. Don't ship unsafe-url or no header at all.
  4. PRE — Replace Access-Control-Allow-Origin: *. Grep for it. Replace with an explicit origin allowlist. Anywhere it's * alongside credentials: include, the browser will refuse the request — but that's no defence against a misconfigured backend.
  5. DEPLOY — Verify headers post-deploy. Open DevTools → Network → click your root document → Headers tab. CSP, HSTS, X-Frame-Options, X-Content-Type-Options should be present. CSP must not have 'unsafe-inline' in script-src.
  6. POST — Run headers.security-headers. The passive header check reports each missing header with deploy-platform fix guidance (Vercel vercel.json, Cloudflare Pages _headers, Netlify _headers, Next.js middleware).

Third-party integrations and APIs (5 items)

Every script you include is a CSP exemption and a potential supply-chain surface. Treat third parties as part of your trust boundary.

  1. PRE — Reverse-proxy analytics where possible. PostHog, Plausible, Umami all support proxying through your own domain (e.g. /api/posthog). This keeps connect-src on the same origin and survives ad-blockers.
  2. PRE — CSP-allowlist the rest. For Google Analytics, Stripe.js, Sentry, Intercom, GTM, etc., add each vendor's origins to the matching CSP directive (script-src for loaders, connect-src for telemetry, frame-src for iframes, img-src for pixels).
  3. PRE — Use Stripe Checkout, not raw card forms. Stripe Checkout is a top-level redirect; no CSP entry needed for the script. Hosted PCI surface lives entirely on Stripe's domain. Roll your own only if you have a strong reason.
  4. PRE — Lock package-lock.json in CI. Run npm ci (not npm install) in production builds. Audit dependencies with npm audit or Snyk before each release.
  5. POST — Watch discovery.tech-fingerprint. The passive tech-stack discovery surfaces library versions visible to a crawler. If you ship an EOL React, jQuery, or Bootstrap, FixVibe flags it and links to known CVEs.

Deployment hygiene and infrastructure (8 items)

How you deploy matters as much as what you deploy. AI-coded apps especially benefit from explicit deploy hardening.

  1. PRE — Disable x-powered-by. In next.config.js: poweredByHeader: false. Removes a free version-disclosure signal.
  2. PRE — Confirm middleware lives at src/middleware.ts. With the src/ directory layout, Next.js ignores a root-level middleware.ts. Misplaced middleware silently fails to set CSP / auth headers / rate limits.
  3. PRE — Sanity-check Vercel deployment protection. Production should be public; Preview should be password-protected or limited to org members. discovery.platform-vercel reports the surface.
  4. PRE — Block dotfile and config probes at the edge. Add a rewrite or a deny rule for /.env, /.git/*, /.aws/*, /.next/trace patterns. Vercel returns 403 for many of these by default; cross-check.
  5. DEPLOY — Separate environments. Production, Preview, Development. Each gets its own set of secrets. Live keys never reach Preview, Stripe test mode never reaches Production.
  6. DEPLOY — Enable Vercel Web Application Firewall. Pro and Enterprise plans include WAF with managed rules. Cloudflare Pages has Bot Fight Mode. Both reduce automated-scanner abuse and password-spray load.
  7. POST — Verify TLS configuration. SSL Labs or testssl.sh against your production domain. TLS 1.2 minimum, prefer TLS 1.3, no weak ciphers, HSTS preload eligible.
  8. POST — Confirm health-check endpoints are minimal. A /api/health should return 200 OK with no body. Don't echo environment, build hash, or deploy timestamp without auth.

Ongoing monitoring and re-scanning (4 items)

Security is not a one-shot pre-ship audit. Drift happens on every deploy.

  1. Verify your production domain in FixVibe. Dashboard → Domains → DNS TXT or HTTP file verification. This unlocks scheduled re-scans, active probing, and live threat monitoring.
  2. Schedule passive re-scans. Pro plans support 3-hour cadence; Unlimited supports 6-hour cadence. Every scheduled scan that surfaces a new finding triggers an email (and a webhook if you've wired one).
  3. Wire outbound webhooks. Account → Webhooks → add an HTTPS endpoint, subscribe to scan.completed + finding.created + scan.active_api.first_used. Route into Slack / Discord / PagerDuty.
  4. Enable live threat monitoring on Unlimited. Certificate-transparency log diffs, DNS changes, JS bundle secret leaks, threat-intel listings — fired the moment they're detected, not on the next scheduled scan.

Next steps

Want the educational backdrop on why these items matter? Read AI-generated code security scanning. Want concrete code snippets for each hardening step? See How to secure an app built with AI coding tools.

// scan your app

그만 읽고, 당신 앱의 취약점을 직접 찾아보세요.

Drop in a URL — FixVibe runs every passive check from this guide plus 200+ others in under a minute. Free, no install, no card.

  • Free tier — 3 scans / month, no card.
  • Passive scans against any URL — no domain verification needed.
  • Tuned for Cursor, Claude Code, Lovable, Bolt, v0, Replit.
  • AI fix prompts on every finding — paste back into your IDE.
무료 스캔 실행

가입 불필요

바이브 코딩 보안 체크리스트: 출시 전 44가지 항목 — Docs · FixVibe