An interactive museum of vibe-coded software. Every app here works perfectly, right up until someone uses it. Poke them, break them, then go check your own code.
No signup · Runs entirely in your browser · Nothing real gets hurt
~/startup — vibe-sessionshipping
0live exhibits
0categories of regret
0real servers harmed
∞"LGTM" comments
git commit -m "LGTM 👍"works on my machine// TODO: securityship it friday 5pmremove debug logs (later)temp fix (permanent)who needs testsit's just an MVPthe AI said it was finechmod 777 everythinggit commit -m "LGTM 👍"works on my machine// TODO: securityship it friday 5pmremove debug logs (later)temp fix (permanent)who needs testsit's just an MVPthe AI said it was finechmod 777 everything
The collection
Nineteen apps. Each one shipped to production with one small, catastrophic flaw. Find them all.
0/0 found
🏆
Museum fully exploited.You found every dumb way. Please don't ship any of them.
#01AuthExploited
The Self-Delivering OTP
Two-factor authentication, except both factors are on your screen.
What's wrong here?
The OTP is generated in the browser and printed on the page (and in the console). The whole point of a one-time code is that it arrives through a different channel you control. Generate and verify it on the server; never send it back in the response.
#02AuthExploited
Password Already Taken
Usernames must be unique. So, apparently, must passwords.
What's wrong here?
To know a password is "taken", the app must compare it against everyone else's, which means passwords are stored in plain text (or unsalted). And it then tells you whose account it opens. Hash passwords with a slow, salted algorithm (bcrypt, scrypt, argon2) and never compare across users.
#03AuthExploited
Login, Checked by the Browser
The backend was "out of scope for the MVP".
What's wrong here?
Anything that runs in the browser can be read by the person using it. Credentials, checks and "secret" logic in frontend code are public. Authentication has to happen on a server that the user can't see or modify.
#04SecretsExploited
BizBot™ AI Assistant
Calls the AI provider straight from your browser. What could go wrong?
What's wrong here?
The request to the AI provider carries the company's secret API key, so anyone can open DevTools, copy it, and run up the bill. Keep provider keys on a server and have the browser talk to your own endpoint, with auth and rate limits.
#05PaymentsExploited
Name Your Own Price
The server trusts whatever price the checkout page sends it.
What's wrong here?
Prices, discounts and totals sent from the client are suggestions from a stranger. The server should look up the price itself from the item ID and calculate the total.
#06AccessExploited
Everyone's Invoices
You're logged in as customer #1041. Here's your invoice.
What's wrong here?
This is an IDOR (insecure direct object reference). The server returns any invoice whose ID you ask for without checking it belongs to you. Every record lookup needs an ownership check, and sequential IDs make the problem easy to exploit.
#07PaymentsExploited
The Infinite Coupon
One coupon per customer. Per click.
What's wrong here?
The coupon is never marked as used, and nothing stops the total going below zero. Redeem coupons on the server, record them per account and order, and clamp totals.
#08AccessExploited
Admin by Query String
Role-based access control, where the role is in the URL.
What's wrong here?
Permissions must come from the server-side session, not from anything the user can type: URLs, cookies they can edit, localStorage, or hidden form fields.
#09InjectionExploited
User Search, Lovingly Concatenated
The query is built with string templates. It's so readable!
What's wrong here?
Pasting user input into SQL lets the input become SQL. Try ' OR '1'='1. Always use parameterized queries or an ORM's bound parameters.
#10InjectionExploited
The Trusting Guestbook
Comments support "rich formatting" via innerHTML.
What's wrong here?
Rendering user content as HTML lets anyone run scripts in every visitor's browser, stealing sessions or acting as them. (This demo only simulates it.) Render text with textContent, or sanitize with a library like DOMPurify.
#11SecretsExploited
Very Helpful Error Messages
Debug mode was on in prod "just for a day" (eight months ago).
What's wrong here?
Stack traces reveal file paths, library versions and, all too often, environment variables with credentials. Show users a generic error, log the details on the server, and keep secrets out of anything that could be printed.
#12Business logicExploited
The Eternal Free Trial
Your trial has ended. Please upgrade to Pro for $49/mo.
What's wrong here?
The trial's expiry date lives in the user's own browser, so they can just change it. Subscription and trial state belong in your database, checked on every request that uses a paid feature.
#13AuthExploited
The Unlimited PIN Pad
A 4-digit PIN protects your vault. Attempts: unlimited.
What's wrong here?
There are only 10,000 four-digit PINs, so without rate limiting a script tries them all in seconds. Limit attempts per account and IP, add delays or lockouts, and use longer secrets.
#14AuthExploited
Password Reset, Hand-Delivered
The reset link is emailed to the owner. And returned to whoever asked.
What's wrong here?
The API response includes the reset token, so anyone can reset anyone's password without access to their inbox. Only deliver tokens through the email channel, make them single-use and short-lived, and return the same response whether or not the account exists.
#15AccessExploited
The Honour-System Session
The session cookie is just base64. No signature, because "it's encoded".
What's wrong here?
Encoding is not encryption, and neither one stops tampering. Sessions should be an opaque random ID looked up on the server, or a token signed with a secret key (and the signature actually verified).
#16PaymentsExploited
Floating-Point Finance
Money is stored as a JavaScript number. Accountants, look away.
What's wrong here?
Binary floating point can't represent 0.1 exactly, so 0.1 + 0.2 !== 0.3. Store money as integer cents (or use a decimal type) and only format it for display.
#17InjectionExploited
Upload Anything™
Profile pictures are saved with their original name, in a public folder.
What's wrong here?
Accepting any file type and serving it from your domain lets attackers upload scripts, HTML pages or executables. Check the real file contents (not only the extension), rename files, and serve uploads from a separate domain with safe headers.
#18SecretsExploited
Security Through robots.txt
The admin panel is hidden. Search engines were asked very politely not to look.
What's wrong here?
robots.txt is public, and listing "secret" paths there is a map for attackers. Hidden URLs aren't access control; protect admin pages with real authentication.
#19Business logicExploited
Check, Then (Eventually) Spend
Withdrawals check your balance first. Then wait a moment. Then withdraw.
What's wrong here?
This is a race condition: several requests all pass the balance check before any of them deducts. Do the check and the update atomically, for example in one database transaction with a row lock, or a conditional update like UPDATE … WHERE balance >= 100.
How it works
Three steps. Roughly the same three steps an attacker takes.
01
Poke
Every card is a tiny working app. Use it like a normal person would. Sign up, check out, leave a comment.
02
Break
Now use it like a curious one. Edit the request, change the URL, click faster. When it breaks, the card gets stamped.
03
Learn
Open "What's wrong here?" for the real fix. Then search your own codebase for the same pattern. We'll wait.