FixVibe

// docs / security guides / ai tooling analysis

為何 AI 撰寫工具會留下安全漏洞

Cursor, Claude Code, Lovable, Bolt, v0, and similar AI coding tools ship fast and release developers from boilerplate busywork. They also have structural blindspots around security. This isn't a failure of any single tool — it's a byproduct of how LLMs are trained and how they are optimized. Understanding the root causes of these gaps is the first step to closing them.

Why AI-generated code differs in security

AI coding tools have structural reasons for the security gaps they produce. They're not random oversights; they're predictable artifacts of training and optimization.

  • Training data skews toward "make it work." Open-source repositories and tutorials dominate LLM training. The median OSS repo was written to solve a problem, not to be an exemplar of security hardening. An LLM learns the distribution of code in the wild, not the distribution of secure code.
  • Autocomplete is sticky. When you paste a code snippet that uses a service_role key, it becomes the template for the next file. Cursor's autocomplete will suggest it in a client component where it never belonged. The tool is doing what it's optimized for (speed) — but it's not aware of the security boundary.
  • No long-term context or incident memory. A human developer who burned down a production database with a missing WHERE clause carries that lesson forward for years. An LLM has no episodic memory. Every file is a fresh start. The tool doesn't learn from your last RLS bypass or your team's incident postmortem.
  • Speed is the rewarded metric. Developers choose these tools because they ship fast. Latency feedback is immediate and direct. Security feedback is absent or delayed — a vulnerability found in a FixVibe scan three weeks after ship. The LLM was optimized for the metric humans rewarded in real time.
  • Implicit trust in platform defaults. When Cursor generates a Vercel app, it assumes Vercel's defaults are hardened. Some are: auto-HTTPS, signed cookies, DDoS protection. Others aren't: no CSP by default, no HSTS, permissive CORS. The generated code inherits platform assumptions that aren't always justified.

Gap 1: Secrets in client bundles

Service-role API keys, OAuth tokens, and private keys end up in JavaScript bundles shipped to the browser. FixVibe flags these as secrets.browser-storage and secrets.bundle-leak findings. The keys are discoverable in source maps, minified code, or plaintext JS.

Why it happens: Cursor pasting a Supabase snippet that initializes a client with the service role means that code is now in the autocomplete. A generated React component asks for "get all items from the database" and Cursor suggests the service-client import. The boundary between server-only and client-side code is abstract to the LLM.

Fix: Store secrets in environment variables flagged NEXT_PUBLIC_ only for public keys. Service keys, private API keys, and signing secrets must live in src/lib/secrets.ts with import 'server-only'. Use server actions or API routes to call sensitive services, never client components.

Gap 2: Missing or incomplete Row-Level Security

Supabase tables are created without RLS enabled. Firebase Firestore rules are never written or are left in permissive test mode. Anon users can read and write every row. FixVibe flags this as baas.supabase-rls and baas.firebase-rules.

Why it happens: RLS is a Postgres-specific feature. LLMs trained on Rails, Django, Laravel, and Express see application-layer auth checks as the norm. Enabling RLS on Supabase requires explicit ALTER TABLE statements and policy definitions — patterns less common in training data.

Fix: For Supabase, enforce RLS with ALTER TABLE public.table_name ENABLE ROW LEVEL SECURITY; ALTER TABLE public.table_name FORCE ROW LEVEL SECURITY; on every table. Author policies that scope rows to the authenticated user or organization. For Firebase, never leave rules as default test mode; write explicit rules scoped to the authenticated user.

Gap 3: Auth boundary confusion

Sessions are validated client-side using getSession() (which reads an unverified cookie). Magic links have no expiration. JWTs skip aud and exp checks. Password resets are reversible. FixVibe flags these as active.auth-flow findings.

Why it happens: Supabase Auth, Clerk, and similar services handle session state, but their APIs have safe and unsafe modes. getSession() is convenient but unverified. The LLM sees the convenience API more often than the safe one in training data. Server-side token validation is abstract and requires explicit HTTP headers or middleware invocation.

Fix: Always use supabase.auth.getUser() server-side. Never trust getSession() on protected routes. Validate JWTs on every request, checking exp, aud, and signature. Set token expiry to 1 hour for access tokens and use refresh tokens for longer sessions.

Gap 4: Missing HTTP security headers

No Content-Security-Policy, no X-Frame-Options, no Strict-Transport-Security, no X-Content-Type-Options. FixVibe flags this as headers.security-headers findings.

Why it happens: Security headers are deployment-platform-specific. Cursor generates code for Next.js; setting CSP requires a next.config.js tweak, a middleware, or a vercel.json override. These aren't in default project scaffolds. The mental model of "headers are for DevOps" is still common.

Fix: Set CSP in next.config.js or middleware with nonce support: Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-...'; .... Add HSTS: Strict-Transport-Security: max-age=31536000; includeSubDomains. Use Vercel's vercel.json headers or middleware for static hosts.

Gap 5: Third-party integration misconfigurations

Stripe keys, Sentry tokens, Anthropic API keys are hardcoded or committed to repos. Analytics origins are over-permissive. npm dependencies are outdated. FixVibe scans flag these as findings under discovery.tech-fingerprint and our secrets checkers.

Why it happens: Integrations are pasted from documentation and tutorials. The LLM copies the pattern, including any hardcoded values. Env-var discipline requires explicit discipline in CI/CD — which the LLM can't enforce.

Fix: Use environment variables for every third-party credential. Store secrets in your deployment platform (Vercel, Netlify, Heroku, or a vault). Audit npm dependencies with npm audit monthly. Use Dependabot or Renovate for automated PRs when security updates are available.

The remediation pattern

Closing these gaps doesn't require rebuilding from scratch. The pattern is consistent:

  1. Audit: Run FixVibe against your live app. For repo scans, enable the FixVibe GitHub App. Collect the findings — secrets, RLS, auth, headers, third-party.
  2. Harden: Fix the high-confidence findings. Enable RLS + FORCE. Move secrets to env vars. Set CSP and HSTS in middleware. Use server-side auth validation. Use the AI-fix prompt on each finding if you're using Cursor or Claude Code.
  3. Monitor: Schedule daily passive scans or weekly active scans on a verified domain. Set up webhooks to Slack. Every critical finding should trigger an alert within minutes of ship.
  4. Respond: When a finding surfaces, copy the FixVibe AI-fix prompt, paste it into Cursor or Claude Code, and let the agent draft a fix. Re-scan to confirm.

Where the field is headed

Fixing these gaps is work for teams today. Over the next 2-3 years, the frontier is moving: better defaults in frameworks and tools (Next.js middleware auto-CSP, Supabase RLS as the default), IDE-time security feedback (Cursor suggestions that warn when you're about to paste a service key into a client component), and MCP-driven autofix (your coding agent has access to FixVibe findings and can fix them autonomously). FixVibe's public changelog tracks which gaps are closing first.

Next steps

For the go/no-go checklist before launch, see Pre-launch SaaS security checklist. For a step-by-step hardening walkthrough with code snippets and real failure patterns, read 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.
為何 AI 撰寫工具會留下安全漏洞 — Docs · FixVibe