# It's Never Too Late (Or Too Early) to Harden Your Vibecoded App
*Your vibecoded app has security holes. You probably know this. What you might not know is exactly where they are, why your AI agent keeps creating them, and — crucially — that fixing them doesn't require a rewrite.*
---
> **Vibecoded app security** refers to the systematic process of identifying, remediating, and preventing the security vulnerabilities that LLMs introduce when generating application code without explicit security constraints. These aren't random bugs — they're predictable, structural omissions that follow from how AI models are trained, and they can be addressed at any point in the software engineering lifecycle through constraint-driven auditing, phased remediation, and proactive guardrails.
Here's the lifecycle of a vibecoded app's security posture:
**Day 1**: You prompt an AI agent to build a login page. It generates clean, working code. No input validation. No rate limiting. Auth middleware is present but ordered wrong.
**Week 2**: You've shipped 15 features. Each one introduced its own approach to error handling, its own assumptions about what's authenticated, and zero security tests. You're in **goblin mode vibecoding** — maximum velocity, no guardrails, shipping features like they're free. (They're not.)
**Month 3**: A user reports they can access another user's data by changing an ID in the URL. You realize you've been shipping broken access control since day one. Goblin mode was fun until the goblins found the production database.
This isn't a failure of your AI agent. It's a failure of the inputs. LLMs generate what you ask for, not what you need. And security is almost never what anyone asks for.
The good news: **it doesn't matter where you are in this timeline.** Whether you're still prompting your first feature or running a production app with real users, the fix follows the same structural pattern.
## Why Every Vibecoded App Has the Same Security Gaps
LLMs are trained on the internet. The internet is full of tutorials that skip validation, examples that hardcode API keys, and Stack Overflow answers that use string concatenation for SQL queries. When your AI agent generates code, it's drawing from this statistical distribution of "what code usually looks like." And what code usually looks like is insecure.
Three forces make this worse:
**Security requirements are implicit.** Your prompt says "build a user profile endpoint." The unspoken requirements — validate input types and lengths, check authorization, sanitize output, rate-limit the route, log access for audit — aren't in the prompt. The LLM can't enforce what it doesn't know about.
**Security is cross-cutting.** A single requirement like "all PII must be encrypted at rest" touches every file that handles user data. LLMs generate code file-by-file, prompt-by-prompt. They can't maintain cross-cutting invariants across a codebase because they have no persistent memory of what they decided in the last session.
**Insecure code is the statistical default.** The training data contains orders of magnitude more insecure code than secure code. The model's prior is "code that works," not "code that's safe." You have to actively override that prior — every time, for every feature. When you don't — when you're in goblin mode vibecoding, prompting features as fast as you can think of them — you're compounding insecure defaults at the speed of autocomplete.
## How to Fix It: The Three-Phase Approach
Regardless of where you are in your app's lifecycle, the remediation pattern is the same: **Vibe Check. Constrain. Verify.**
### Phase 1: Vibe Check — Find What's Actually Wrong
You can't fix what you can't see. The first step is a structured security vibe check that goes beyond "does the code look right?" and systematically checks OWASP-mapped categories against your actual codebase.
A useful vibe check isn't "read through the code and see if anything looks wrong." It's a checklist-driven scan that forces coverage of every security domain:
**Authentication & Authorization (OWASP A01/A07)**
- Are all API routes protected by auth middleware?
- Is middleware ordered correctly (auth before business logic)?
- Do endpoints verify the requesting user owns the resource?
- Are admin routes separated from user routes?
**Injection (OWASP A03)**
- Is all user input validated before use?
- Are database queries parameterized?
- Are file paths constructed safely?
**Data Protection (OWASP A02)**
- Are secrets in environment variables, not code?
- Is PII encrypted at rest and in transit?
- Are API responses stripped of internal data?
**API Security (OWASP A04/A08)**
- Do public endpoints have rate limiting?
- Is CORS restricted to known origins?
- Are error responses free of stack traces?
**Infrastructure (OWASP A05)**
- Are database/Firestore rules restrictive?
- Are CSP headers configured?
- Is HTTPS enforced?
**Dependencies (OWASP A06)**
- Are there packages with known CVEs?
- Are lockfiles committed?
- Are unused dependencies removed?
If you're running [Cutline](https://thecutline.ai/start), the security vibe check tool does this automatically. Point it at your workspace and it runs a 28-item, OWASP-mapped security checklist against your codebase, extracts security-relevant files, and returns specific pass/fail/warn verdicts for each category. It also detects your tech stack and seeds framework-specific security constraints — for example, Next.js API route protection patterns or Firebase security rule checks.
The key output isn't a list of problems. It's a **structured map** of which security domains are covered and which aren't. That map becomes the input for Phase 2.
### Phase 2: Constrain — Make Security Persistent
Finding security gaps is the easy part. The hard part is making sure they don't come back.
The fundamental problem with vibecoded security isn't that the gaps exist — it's that nothing prevents the AI from recreating them in the next feature. You fix the auth check in the user endpoint, and the next prompt generates a new endpoint without auth. You add input validation to one form handler, and the LLM generates three more without it.
The fix is structural: **encode security requirements as typed constraints in a constraint graph**, not as documentation or code comments.
A constraint graph is a persistent layer that sits alongside your codebase. Each constraint is a typed node with a category, severity, and scope. Constraints are bound to file patterns — so the constraint "all `/api/**` routes must have auth middleware" is automatically surfaced whenever your AI agent opens or generates a file in that directory.
With Cutline, this happens in two ways:
- **Proactive blueprint seeding**: When you declare your tech stack (Next.js, Firebase, Express, etc.), Cutline seeds universal and framework-specific security constraints into your constraint graph automatically. These constraints fire via `constraints_auto` whenever you edit matched files — before you've even run an audit.
- **Retroactive vibe check ingestion**: When the security vibe check finds FAIL or WARN items, it converts them into constraint nodes, creates security domain entities (e.g., `component:security_authentication`), and binds them to the relevant file paths. Your vibe check findings become persistent guardrails.
Either way, the result is the same: your AI coding agent can't generate a new API route without seeing the auth requirement. Security becomes the default context, not an afterthought.
### Phase 3: Verify — Harden Incrementally with Red-Green-Refactor
You have an audit. You have constraints. Now you need to actually fix the code — without breaking everything.
The worst thing you can do is a "big bang" security fix — a single pass where you try to address every vulnerability at once. LLMs handle focused instructions well and broad instructions poorly. Asking an AI to "fix all security issues" destabilizes the codebase because security was never part of the architecture.
Instead, use **phased Red-Green-Refactor (RGR)**:
1. **Security phase**: Auth, authorization, access control. The constraint graph generates test specs for every auth-related constraint. The AI agent must pass them.
2. **Functional phase**: Input validation, error handling, data sanitization. New test specs, new assertions.
3. **Performance phase**: Rate limiting, caching, resource management.
4. **Economics phase**: Cost controls, usage metering, LLM token limits.
Each phase adds tests that persist into subsequent phases. By the time you reach economics, the security tests are still running. Nothing regresses.
In Cutline, the `rgr_plan` tool assesses constraint complexity for any file or feature and returns a phased plan. After you complete a phase, `rgr_complete_phase` marks it done — your security readiness score and engineering readiness score both increase, giving you a quantified view of progress.
## Where You Are Determines Where You Start
The three-phase pattern is universal, but your entry point depends on your app's lifecycle stage.
### Just Starting? Start with Constraints.
If you haven't written code yet, you have an enormous advantage: you can make security the default from day one.
Declare your tech stack in Cutline's deep dive form. The constraint graph seeds framework-specific security blueprints immediately — Next.js API route patterns, Firebase security rule requirements, Express middleware ordering, whatever your stack demands. Every feature your AI agent generates will see these constraints as context.
You'll never have a "find all the security bugs" phase because the bugs won't be introduced in the first place.
### Built an MVP? Start with a Vibe Check.
If you've already shipped features, start with Phase 1. Run the vibe check. Get the structured map of what's covered and what isn't. You'll probably find:
- 60-80% of your endpoints lack proper authorization checks
- Input validation is present on some routes and absent on others
- Secrets are in environment variables (good) but also in one or two config files (bad)
- Error responses leak stack traces in production
This is normal. This is what goblin mode vibecoding produces — a codebase that works, ships, and has the security posture of a screen door on a submarine. The vibe check turns a vague sense of "we probably have security issues" into a specific, prioritized remediation plan.
### In Production with Users? Start with the Highest-Severity Gaps.
If real users are hitting your app, triage by severity. The vibe check's FAIL items with `critical` severity — broken access control, SQL injection, exposed secrets — get fixed first. WARN items get scheduled. PASS items confirm what's already working.
The phased RGR approach is designed for this: you can harden one security domain at a time without destabilizing the features your users depend on.
### Planning a Rewrite? Don't. Constrain Instead.
The temptation when you discover widespread security gaps is to rewrite from scratch. Resist it. A rewrite with the same AI tools and no constraints will reproduce the same gaps. You'll spend months and end up in the same place.
Instead: vibe check the existing codebase, seed the constraint graph, and refactor incrementally. The constraint graph ensures that any code the AI generates — new or rewritten — meets your security requirements. You get the benefits of a rewrite without the risk.
## What "Fixed" Looks Like
Security isn't binary. You don't go from "insecure" to "secure" in one step. But you can measure progress.
Cutline tracks two scores that quantify your security posture:
**Security Readiness (0-100%)** — Computed from your security NFR coverage, constraint density, blueprint/vibe check coverage, binding health, and PII exposure density. A freshly vibecoded app with no constraints might start at 15%. After a vibe check and blueprint seeding, it jumps to 35-45%. After completing the security RGR phase, it reaches 60-75%. The more PII-handling code you have without explicit guardrails, the harder the score is penalized.
**Engineering Readiness (0-100%)** — A composite score that blends security readiness (15% weight) with NFR coverage, speedup factor, constraint depth, graph structure, conflict density, and dependency chain risk. High conflict counts between constraints drag the score down, as do deep, unguarded dependency chains. Completing RGR phases boosts the score — each phase represents verified progress.
Both scores increase after each RGR round. The `rgr_complete_phase` tool marks a phase done, recomputes the scores with phase bonuses applied, and persists the result. The **Score History** timeline in the Deep Dive UI tracks every change — each snapshot is paired with an event label (deep dive, code vibe check, RGR phase, constraint learned, etc.) so you can see exactly when and why a score moved.
## The Security Checklist You Can Run Right Now
If you want to start before setting up any tooling, here's the minimum checklist. Go through your codebase and verify each item:
- [ ] Every API route has authentication middleware
- [ ] Middleware is ordered correctly (auth before handler)
- [ ] Endpoints verify the requesting user owns the requested resource
- [ ] All user input is validated (type, length, format)
- [ ] Database queries use parameterized statements
- [ ] No secrets are hardcoded in source files
- [ ] Environment variables are documented and `.env` is gitignored
- [ ] Error responses don't include stack traces or internal details
- [ ] CORS is restricted to known origins (no wildcards in production)
- [ ] Rate limiting exists on auth endpoints and public APIs
- [ ] Dependencies are audited for known CVEs (`npm audit` / `pip audit`)
- [ ] Database/Firestore security rules deny by default
- [ ] HTTPS is enforced
- [ ] Logging captures auth events without logging sensitive data
Every item you check is one fewer vulnerability in production. Every item you can't check is a candidate for your first RGR round.
## Start Now
The best time to harden your vibecoded app was before you wrote the first line of code. The second best time is now.
[Run a deep dive](https://thecutline.ai/start) to get your engineering and security readiness scores. Declare your tech stack to seed security constraints. Run the security vibe check from the MCP server to get your OWASP-mapped checklist. Then start the first RGR round — and watch your Score History timeline track every improvement.
Your AI agent built the app. Now give it the constraints to secure it.