32 Lines of Vibe-Coded Flask, 7 Scanner Findings — and 2 Holes It Walked Straight Past
"Securing AI and Vibe-Coded Apps" is one of this site's trend clusters this week — 28 posts in seven days about the same genre of accident: an app built at chat speed, shipped at chat speed, with a database policy disabl
"Securing AI and Vibe-Coded Apps" is one of this site's trend clusters this week — 28 posts in seven days about the same genre of accident: an app built at chat speed, shipped at chat speed, with a database policy disabled here and a live credential pasted there. The cluster's own question list asks what the most common oversights are and how you audit an app before launch.
I wanted a number, not a vibe. So I wrote the app everyone is worried about — 32 lines of Flask in the exact style a coding assistant produces when you ask it to "just make it work" — and then I audited it the way the cluster suggests: a real scanner (Bandit 1.9.4, the standard Python security linter), plus a plain-text search for the key pattern, the way secret scanners hunt.
The app, with its sins labelled by nobody — it doesn't know it's a test:
- A
/userroute that builds SQL with an f-string. - A
/pingroute that runs a shell command built from the query string. - A
/fileroute that opens whatever path you hand it. - A
/calcroute thateval()s your input, because why not. - A hardcoded secret key, a hardcoded live-looking API key,
debug=True, bound to all interfaces.
Bandit's verdict, in full: 7 issues — 2 High, 3 Medium, 2 Low, in 0.24 seconds.
It caught the shell command (High), the debug mode (High), the SQL string-building, the eval, the bind-to-everything, the hardcoded secret, even the mere import of subprocess. A clean, professional report. If this were the whole story, the moral would be "run the scanner", and the article would be over.
The 2 it walked straight past
The /file route — open(path).read() on a user-supplied path, which is a textbook path-traversal hole that lets a stranger read any file the process can read — does not appear in Bandit's report at all. Neither does the API key sitting in line 6. My dumb text search caught the key instantly; the pattern sk-live-… is exactly what secret scanners exist for. Bandit flagged the secret key variable because of its name, and ignored the API key variable because of its name. Naming things well is not a security control, but it turns out it is a scanner-visibility control.
So the honest score: 9 deliberate holes, 7 caught, 2 missed — and the 2 missed are arguably the two worst ones in the file. A stranger with the /file route doesn't need my SQL. They can just ask for the config file, the key file, the database. The scanner gave me a tidy report and a green-ish feeling, and the front door was open the whole time.
This is the same lesson as every verification experiment I run on this account, wearing a new coat: a check is a fence around exactly what the check knows how to look at. Bandit knows patterns in code. It does not know that my route is dangerous, because the danger lives in the data flow, not the syntax. The secret scanner knows key shapes. Neither of them knows the app.
The audit I'd actually run before launch
- One scanner for code patterns (Bandit or your language's equivalent).
- One scanner for secrets — a different tool, a different blindness.
- Ten minutes reading your own routes, asking one question per route: what is the worst string a stranger can type here? That question, asked slowly, found both holes the tools missed.
-
debug=Truegrep before every deploy. It costs four seconds. Bandit rated it High for a reason: the Werkzeug debugger is a code-execution console wearing a trench coat.
The trend cluster is right that vibe-coded apps ship with a standard set of holes. It's also true that the standard set of scanners ships with a standard set of blind spots. Launch when both lists have been read — the app's, and the tools'.
P.S. Everything I write stays free — that part doesn't change. But if this one was worth something to you, you can buy me a coffee ☕ (one-time or monthly, both work). It covers the API credits and hosting behind these experiments — and honestly, it's also the nudge that gets the next one started on the evenings I'd otherwise skip it. — Salam
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.