Vibe Coding — Vulnerability as a Service

Vibe coding feels great until the bill comes due in production. I wrote down where that speed actually costs you, with real examples from 25 years of watching "fast" and "done right" collide. My honest take, not a hype post.

I generated a working file upload endpoint in about four minutes last spring, using an agent inside Cursor, for the resume parsing flow on ITJobOpportunities. It compiled. It passed the happy-path test I wrote five minutes later. It also let anyone upload a 400MB file with no content-type check, straight into a directory built from the original filename. I caught it during review, not because I'm some security expert, but because I'd been burned by path traversal once before, on a document ingestion pipeline years ago at a different shop. If I hadn't caught it, that code would have shipped. That's basically my whole opinion on "vibe coding" in one story. The term describes coding by feel: you prompt, you accept, you ship, and you trust the vibes. And honestly, I get the appeal. When an agent writes 80% of your boilerplate correctly, it's tempting to stop reading the other 20% carefully. I do it too, some days. But calling it "vulnerability as a service" isn't clickbait. It's just accurate more often than any of us want to admit. Why "Vulnerability as a Service" is the right label, not just a good line People treat that phrase as a joke, but the mechanics actually line up. A SaaS product takes something that used to require setup, expertise, and time, and turns it into something you get on demand, cheap, with almost no friction. Vibe Coding does the exact same thing to insecure code. Once I started looking for it, I found the pattern showing up in three or four repeatable scenarios, not just one abstract idea. Scenario one: the on-demand delivery model. Before agents, a broken upload handler still cost you something to write. You wired the file I/O yourself, decided on a path yourself, and somewhere in that slower process you usually had a moment, even a rushed one, to think about edge cases. Vibecoding deletes that moment entirely. You type "add an endpoint that accepts a resume upload," and four minutes later you have a working feature with a path traversal hole riding along, no extra request required. Nobody asked for the vulnerability. It came bundled with the feature, the same way a free-tier signup comes bundled with no SLA and no support line. That's the service model exactly: you ordered convenience, and the thing you didn't specify is the thing you didn't get. Scenario two: zero expertise required to trigger the failure. With a real Vulnerability-as-a-Service kit, the buyer doesn't need to understand how the exploit works, they just need to know it exists and point it at a target. Vibecoding does this to the builder, not the attacker. I didn't need to understand path traversal to introduce it. The agent didn't need to intend it. Neither of us had to be an expert in the failure mode for the failure mode to ship. That's a strange inversion. Usually you need skill to create a security hole on purpose. Vibecoding lets you create one entirely by accident, at full competence, with zero specialized knowledge on either side of the keyboard. Scenario three: the subscription model for repeat exposure. This is the one that worries me most. A vulnerability-as-a-service kit isn't a one-time exploit, it's a recurring capability you can hit again and again. Vibecoding creates the same recurrence inside a single codebase. If the pattern that generated my unsafe upload handler is still sitting in an agent's context, or in a skill file, or in muscle memory from a prompt that worked "well enough" last time, it will produce the same category of gap on the next feature, and the one after that, unless something actively interrupts it. I've watched an agent propose the same unbounded file read twice in one sprint, on two unrelated endpoints, because the underlying pattern it leaned on was identical both times. That's not a one-off mistake. That's a subscription. Scenario four: low cost makes it invisible. SaaS products succeed partly because the per-unit cost is so low that nobody scrutinizes it the way they'd scrutinize a big capital purchase. A four-minute endpoint feels too cheap to deserve a security review. That feeling is the whole problem. The cost of the vulnerability doesn't show up at generation time, it shows up later, in an incident report, and by then the four minutes it took to create is irrelevant next to the hours it takes to clean up. I don't think the phrase is unfair to the tools. It's fair to the practice of skipping review. The model isn't selling vulnerabilities on purpose. But the workflow, prompt, accept, ship, is structured in a way that delivers them reliably unless someone interrupts the pipeline. Once I saw it that way, I stopped thinking of security review as an extra step and started thinking of it as the only thing standing between "vibecoding" and "vulnerability as a service" being literally the same product. Where I actually stand I'm not anti-AI-coding. That would be a strange position for someone who runs production LLM pipelines for résumé scoring and Job Fit checks. I use Claude Code and Cursor daily. I maintain an agent-skills catalog with close to a hundred skills that I install into repos specifically so agents write code closer to how I'd write it myself. So no, I'm not the "real engineers type every character" guy. What I am against is treating generated code the way you'd treat a junior dev's first pull request but skipping the part where you actually review it like one. If a junior engineer on my team submitted that upload endpoint, I'd have flagged the missing size limit, the missing MIME check, and the filename sanitization in about ninety seconds during a normal code review. The problem isn't that AI writes insecure code sometimes. Humans do too. The problem is that vibecoding, as a practice, strips out the review step that used to be non-negotiable. A concrete example, cleaned up for the write-up Here's roughly what the agent handed me for the resume upload (simplified; I'm not pasting our actual controller): @PostMapping("/upload") public ResponseEntity<String> uploadResume(@RequestParam("file") MultipartFile file) { String path = "uploads/" + file.getOriginalFilename(); file.transferTo(new File(path)); return ResponseEntity.ok("uploaded"); } Looks fine at a glance. It even "works" if you test it with a normal PDF. But file.getOriginalFilename() comes straight from the client, unsanitized, and there's no size cap, no content-type allowlist, and no check that uploads/ even resolves where you think it does. Someone sends a filename like ../../config/application.yml and now you've got a much worse day ahead of you. What I actually shipped, after rewriting it myself: @PostMapping("/upload") public ResponseEntity<String> uploadResume(@RequestParam("file") MultipartFile file) { if (file.isEmpty() || file.getSize() > MAX_RESUME_SIZE_BYTES) { throw new InvalidResumeException("File is empty or exceeds 5MB limit"); } String contentType = file.getContentType(); if (!ALLOWED_TYPES.contains(contentType)) { throw new InvalidResumeException("Unsupported file type: " + contentType); } String safeName = UUID.randomUUID() + getExtension(contentType); Path target = uploadRoot.resolve(safeName).normalize(); if (!target.startsWith(uploadRoot)) { throw new SecurityException("Path traversal attempt blocked"); } file.transferTo(target); return ResponseEntity.ok(safeName); } Not glamorous. Nobody's writing a post about how clever this is but it's the difference between a resume upload feature and an open door. Why this happens more than people admit I don't think LLMs are worse programmers than the average junior. I think the failure mode is structural, not a model problem: Models optimize for "code that plausibly does what you asked," not "code that survives a hostile input." Those are different goals, and nobody tells the model which one you actually want. Context windows lie to you. The agent that wrote my upload endpoint had zero idea what our stor...