Vibe Coding Security: Three Updates to My April Prevention Playbook, Two Months Later
Two months after Lovable's 170-app vulnerability incident (April 2026), the industry debate has shifted from 'detecting holes' to 'updating implementation standards.' Here are the 3 updates I'd make to my own April prescriptions—plus minimum implementations runnable in 30/15/60 minutes.
What you'll learn in this article
- The key point to grasp before reading the full article
- How the issue changes the way developers should work next
- Which follow-up article is worth opening next
What this article covers
- Two months after the Lovable incident (April: 170/1,645 apps, 10.3%), the industry debate has shifted from “awareness that holes exist” to “updating implementation standards”
- How I would rewrite the 3-part prevention series I wrote in April (170 back doors / 45% vulnerabilities / Tenzai 15 apps 69 holes) for June
- Three minimum implementations you can run starting this week: 30 min / 15 min / 60 min
Assumed reader: Developer or PM who has been using vibe coding (Cursor, Claude Code, Lovable, etc.) for 1–3 months. You’ve been operating under “works = good enough” but realize you don’t have a security standard of your own.
In April I wrote a three-part series on vibe coding security. The Lovable incident: critical vulnerabilities found in 170 of 1,645 apps. Veracode’s report that 45% of AI-generated code has holes. Tenzai’s comparative report finding 69 holes across 15 apps. My prescriptions at the time were: “For now, add SAST, separate your environment variables, and review generated output.”
Two months later. If I were writing on the same topic now, I would revise the prescription in three ways. One reason: A category of holes has emerged that “stopping and reviewing at the moment something works” can’t catch.
This article is an attempt by my June self to rewrite my April self’s prescriptions. I’m watching Infosecurity-related industry discussions while working from the facts I can confirm: the April numbers I wrote, and my own implementation logs from my work tools. From there I’ll compress everything down to “3 minimum implementations changeable this week.”
Two Months After Lovable: What Changed
First, lock in one number. In April, 170 of 1,645 Lovable-built apps (10.3%) were found to have security defects. I covered this in my April 1st article, framing it as “the hidden cost of vibe coding’s spread.”
Two months later—how has the industry’s discussion shifted? Three phases, as I see it.
Phase 1 (April): Awareness phase. The period when “AI-generated code has holes” reached audiences outside security specialists. My articles were part of this wave. The most common reader response: “I knew it,” “I’m going to review our internal tools too.” Voices of recognition.
Phase 2 (May): Detection phase. “So how do you detect them?” became the central question. Articles and study groups widely shared prescriptions: “Add SAST,” “run Snyk,” “write custom Semgrep rules.” I covered the static analysis pipeline setup in the final installment of my April series (April 16th) as “the design of prevention.”
Phase 3 (June): Tracking phase. This is the update territory. People who added detection tools hit the next wall: “Code that tools called OK still has holes.” “Places the tool flagged were actually fine.” Both false negatives and false positives are accumulating.

Why did we enter Phase 3? My hypothesis: vibe-coding-generated code has a different “hole pattern” from human-written code. Human code’s typical holes are “forgetting to add an authentication check.” AI-generated code tends to produce “plausible-looking authentication logic with a permission leak via side effect elsewhere.” Holes hide not in visible places but in connections between components. SAST is primarily syntax analysis, so it’s poorly suited for detecting connection-based holes.
My April prescriptions were optimized for Phase 2 debate. Add SAST, separate env vars, review generated output. That’s not wrong. But two months later, it’s clear that it alone isn’t enough to stop everything.
Here are the three updates I’d add as of June.
Update 1: Track the “Why” That SAST Can’t See—Save Prompt Logs as Evidence
The first update: save prompt logs.
My April prescription was “review generated code.” That’s correct, but when doing the review, there’s no information at hand about why this code was generated. The generation prompt (what I asked the AI to do) and its context (which files were open, what instructions were given) are gone by the time I’m doing the review.
This is what’s creating Phase 3’s wall. When you see code with “plausible-looking authentication logic but a side-effect permission leak elsewhere,” a reviewer asks “why wasn’t an authorization check added here?” The person who wrote it says “I’m not sure, I asked AI and this came out.” If the prompt log was saved, the root cause can be identified: “The context I gave AI didn’t include the other file’s permission check, so AI wrote assuming ‘existing permission checks are already in place.’” The issue wasn’t whether human or AI is responsible—it was how context was handed over.
What to do specifically: In Cursor, enabling cursor.chat.exportHistory in settings allows local saving of chat history (feature name as of June 2026—may change by version). With Claude Code, conversation logs are auto-saved under ~/.claude/projects/.
# Check Claude Code conversation logs
ls -la ~/.claude/projects/
# Each project directory contains
# conversation history JSONLs (implementation depends on version)
That alone is “just having logs,” so let’s go one step further: add a rule to include a summary of the generation prompt in commit messages.
feat: add authentication middleware
AI-prompt-summary:
- Asked for "JWT verification middleware in the same style as existing logger.ts"
- Existing permission table structure was NOT given to AI
- Return type adjusted manually
The AI-prompt-summary section becomes a handle for reviewers later. Same principle as “3 hours of debugging can be avoided by 3 minutes of context”—30 seconds added per commit prevents 3 hours of debugging later.
One pitfall to share upfront: I initially tried to “copy-paste full prompts.” That didn’t last. Quit after 3 days (I’ve done this). Summary only, max 3 lines — that’s what sticks naturally. I’ve been running this rule for one month.
Update 2: Stop “Works = Done”—Add a 2-Stage Review Gate
Second update: split the review into 2 stages.
My April prescription said “always review generated output.” Correct—but a single-stage review tends to collapse into “does it work or not?” Works = merge, doesn’t work = fix. Code that “works but has holes” slips through.
The 2-stage gate I run now:
Gate 1: Functional review. Does it work? Does it pass tests? Does it meet specs? This is fast—around 15 minutes.
Gate 2: Contextual review. What assumptions does this code make? How does it break when assumptions fail? This takes time—30 min to an hour.
Gate 2 checklist:
| Check item | What to verify | Example of NG |
|---|---|---|
| Input trust boundary | Where is external input validated? | Request body passed directly to DB.find |
| Authentication boundary | Which layer has the authentication check? | Auth in middleware once, internal calls unchecked |
| Environment variable handling | How is the secret injected? | Hardcoded in code, or .env included in git |
| External API calls | Is behavior on failure defined? | No try-catch, or catch that swallows the error |
| Permission leakage | Does permission state change before/after an operation? | Role can be changed after admin check |
I didn’t have this checklist in April. I compiled it from 2 incidents in May and 1 in June where I let “works but has holes” code through my own work tools.

Implementation: embed Gate 2’s checklist into the PR template. For GitHub, write it into .github/PULL_REQUEST_TEMPLATE.md. For GitLab, put it in the Merge Request Template. When a checklist is there, reviewers can ask “did you go through these checks?” Without it, things end with “it’s working so merge is fine, right?”
One pitfall: having more than 10 checklist items causes them to become ceremonial. Keep it to 5. I run 5.
Update 3: Track the “Origin” of Generated Output
Third update: build a mechanism to track the origin of generated code.
This is the area I couldn’t write about in April at all. At the time I thought: “Human-written and AI-generated code both end up in production, so if you review it, it’s the same thing.”
Two months of use taught me: without origin information, you can’t reproduce the “why” when something breaks. When a bug surfaces, if you can’t distinguish where the code came from, you can’t build a recurrence prevention plan. You need to record whether it was “from Cursor Chat,” “from Claude Code Edit,” or “from Lovable” separately.
What I do now: record origin at the commit level, not the file level.
# Auto-attach via commit hook example
# Add the following to .git/hooks/prepare-commit-msg
#!/bin/bash
# Assumes the tool name is written to environment variable AI_TOOL
if [ -n "$AI_TOOL" ]; then
echo "" >> $1
echo "Generated-by: $AI_TOOL" >> $1
fi
Launch with AI_TOOL=cursor to record Cursor origin; AI_TOOL=claude-code for Claude Code origin. Not perfect, but much better than nothing.
What does this mechanism do? When 3 bugs surface, you can see whether the origins are skewed. “2 of 3 from Lovable,” “all 3 from Cursor’s Composer feature”—the distribution becomes visible. From that you can make local rules like “don’t delegate permission check areas to Cursor Composer.” The patterns of holes and tool compatibility get preserved as data.
This isn’t about large enterprises. Even running solo on internal tools like me—30 commits in 3 months, 60 commits in 6 months of origin data—the biases in your own choices become visible. For company-wide Claude Code deployment patterns, Claude Code company-wide rollout, KAG’s design has the details. Even for personal tools, tracking tool-specific compatibility is the same habit.
One pitfall: if you don’t automate origin information into commit messages, you’ll forget to write it. I tried manually for two weeks and gave up. Automating via hook is the correct answer.
3 Minimum Implementations You Can Run Starting This Week
Compressing the three updates above into minimum implementations runnable starting this week.

Minimum implementation 1: Set up prompt log saving (30 min)
If you’re using Claude Code, the logs under ~/.claude/projects/ should already be saving. Just verify. If you’re using Cursor, enable persistent chat history in settings. Set it once and it saves automatically going forward.
# Check Claude Code logs
du -sh ~/.claude/projects/
# If size is non-zero, logs are being saved
# For Cursor:
# Settings > Features > Chat > Export History → enable
Minimum implementation 2: Add Gate 2 fields to PR template (15 min)
For GitHub repos, create .github/PULL_REQUEST_TEMPLATE.md. Paste the following:
## Functional review (Gate 1)
- [ ] Tests pass
- [ ] Meets specs
## Contextual review (Gate 2)
- [ ] Confirmed input trust boundary
- [ ] Authentication check exists at each layer
- [ ] Location of secrets (code / config / env vars) is explicit
- [ ] Behavior on external API failure is defined
- [ ] Confirmed no permission leakage (permissions may change before/after operation)
## AI generation info
- Generation tool:
- Prompt summary (max 3 lines):
This alone gives reviewers a state where they can ask “did you go through these checks?”
Minimum implementation 3: Add one SAST scan to CI (60 min)
If you’re using GitHub Actions, try adding Semgrep’s free plan as one new workflow.
# .github/workflows/sast.yml
name: SAST Scan
on:
pull_request:
branches: [main]
jobs:
semgrep:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: returntocorp/semgrep-action@v1
with:
# Use public Semgrep Registry rule sets
config: p/default
Semgrep’s free plan includes public rule sets (p/default, p/security-audit, etc.). Start with p/default only. False positives will appear—get comfortable with them appearing as PR comments first. Set to blocking (block merges) only after you’ve run it smoothly for 3 weeks. Blocking from day one causes false positives to halt development and makes everyone hate it.
One pitfall I hit: I added p/owasp-top-ten from Semgrep Registry from the start—100+ false positives flooded the PR and made it unreadable. Start with p/default is the right approach.
How to Combine This With the April 3-Part Series
Finally, how to stack this update and my April series.
What the April series covered (recap):
- Lovable’s 170 back doors (April 1): The fact that apps “that work but have holes” are publicly deployed in large numbers behind vibe coding’s spread
- 45% vulnerability prevention—final installment (April 16): Prevention design built on SAST / env var isolation / review
- Tenzai 15 apps 69 holes (April 23): Tool-specific hole patterns visible from the 5-agent comparison
Stack the June updates on top:
| April prescription | June update |
|---|---|
| Add SAST | SAST continues. But also add Gate 2 that doesn’t pass even if “tool says OK” |
| Separate env vars | Continues. Also explicitly state “where secrets are” in the PR |
| Review generated output | Make review 2-stage (functional/contextual). Save prompt logs as evidence |
| Know tool-specific hole patterns | Record “origin of generated output” and see your own selection biases in data |
None of the updates negate the April prescriptions. All 3 from April are still valid. The relationship is: add 3 mechanisms to stop “works but has holes” code on top of those foundations.
My mindset when I wrote the April series was still “let’s build something that works.” That mindset hasn’t changed. But now “something that works” includes “no holes” in its definition. For a developer who came in through failures, this is significant progress.
I can’t match professionals. But I can write code that makes my own work easier. I can learn to see holes in the code I write. Professionals can see holes I can’t see, but I can close every visible hole. Use tools to expand the range of what’s visible. That’s the way of operating as vibe coders entering our second year.
The Lovable incident wasn’t an accident—it was a question posed to the whole industry. The answer I had in April and the answer I have in June are allowed to be different. Prescriptions are written with the assumption they’ll be updated. In two more months, I’ll be rewriting June’s prescription.
When I read this article then and smile thinking “ah, this is where June me got stuck”—that’s something I look forward to.
Summary of this article
- 2 months after Lovable’s 170/1,645 (10.3%). The industry debate progressed through “awareness phase,” “detection phase,” “tracking phase”
- The April 3-part series (SAST/env vars/review) remains valid. Stack 3 updates on top
- Update 1: Save prompt logs as evidence; put 3-line summary in commits
- Update 2: Make review 2-stage (functional/contextual). Contextual review uses 5-item checklist
- Update 3: Auto-record “origin of generated output” in commit messages; use tool-specific hole distribution as data
- Starting this week: prompt log setup 30 min / PR template addition 15 min / add 1 SAST 60 min
Developers who came in through failures can learn how to close holes one at a time. See you for the update in two months.

正直、一度エンジニアは諦めました。新卒で入った開発会社でバケモノみたいに優秀な人たちに囲まれて、「あ、私はこっち側じゃないな」って悟ったんです。その後はカスタマーサクセスに転向して10年。でもCursorとClaude Codeに出会って、全部変わりました。完璧なコードじゃなくていい。自分の仕事を自分で楽にするコードが書ければ、それでいいんですよ。週末はサウナで整いながら次に作るツールのこと考えてます。


