Vibe Coding — your first app without knowing how to code — 9. Debugging like a pro & securing your app

19 min read min de lecture
Chapter 09

Debugging like a pro & securing your app

Chapter 9 of 10 · 90%

Chapter objectives

  • Protect your secrets and react correctly to a leak
  • Validate user input and set limits everywhere
  • Methodically diagnose a bug in production

The evening everything almost went sideways

One Tuesday evening, two messages land back to back. The first comes from Karim, a student with questionable humor: "Sir, I put something weird in a habit name and now the page displays nonsense 😂". The second comes from the French-teacher colleague: "The app was very slow yesterday around 7 pm, is that normal?". Tom realizes something every app creator discovers one day: with real users come real bugs… and real mischief. His app is no longer a personal project, it's a miniature public service — and a service gets secured.

Reassure yourself: "pro-beginner level" security stands on three fronts, and you already have foundations on each. Secrets (your infrastructure keys and passwords), inputs (everything users type), and limits (what one is allowed to do, and how many times). This chapter covers all three, then arms you for debugging in real conditions — the kind where the bug is at a user's place, on a device you don't have, at a time you weren't there.

Front 1 — Secrets

You already know the keys live in a .env file server-side. Two things remain to verify. First, that this file is properly ignored by Git: if it's committed, it goes to GitHub with everything else — and a repository, even private today, can become public tomorrow. Second, that you know how to react to a leak: you never "hide" a compromised key, you revoke it (you disable it in the service's dashboard) and generate a new one. Always, without exception, even when in doubt.

bash
# .gitignore — at the project root
.env
.env.local

# Verify that .env is NOT tracked by Git:
git status
# .env must not appear anywhere in the output.

# If a key leaked: you don't hide it, you REVOKE it
# in the service's dashboard, and you generate a new one.
A key pushed to GitHub is compromised permanently, even if you delete it in the next commit: Git history keeps everything, and bots scan public repositories non-stop — an exposed key is typically tested within minutes. The only remedy is immediate revocation. Deleting the file fixes nothing.

Front 2 — Never trust inputs

Back to Karim's joke. What did he do? He typed HTML code into the "habit name" field — something like <h1>HELLO</h1> — and the app displayed it as is, interpreting it as layout. Funny here; serious in general: if an app displays what users type without precaution, a malicious user can slip in code that will run on other people's machines. It's the most common family of attacks on the web (insiders say "XSS"), and the defense is called escaping: typed text is always displayed as text, never interpreted as code.

The principle to engrave: all user input is suspect by default. A habit name must be treated as plain text, checked for length, and validated server-side — not just in the browser, because a well-equipped user can bypass everything that happens client-side. You don't have to implement this yourself: you have to demand it and test it. The test is simple and delicious: do as Karim did, try to break your own app.

The security audit: your AI ally

Good news: the AI is remarkably good at auditing the code it wrote itself, provided you give it a precise mandate. Here is Tom's audit prompt — keep it preciously, it will serve again at every important stage of all your projects:

PROMPT
Run a complete security audit of my habit-tracking app, adapted to my beginner level.
Context: HTML/JS on Vercel, Supabase with magic link authentication and RLS rules, server functions for emails and AI, payment via Stripe Payment Links.
Check as a priority:
1. keys or secrets present in browser-side code, in the Git repository or in its history
2. user inputs displayed without escaping: habit names, form contents
3. the RLS rules: can a user read or modify someone else's data?
4. the server functions: can they be called without being logged in? without a rate limit?
5. dependencies or libraries with known vulnerabilities
For each problem found: explain the risk in one simple sentence, show the minimal fix, and rank everything by severity from critical to minor.
Fix NOTHING without my explicit approval, point by point.

Two requirements give this prompt its value. The ranking by severity: you handle the critical first, and decide knowingly for the rest — not all flaws are equal, and a beginner who wants to fix everything at once fixes nothing well. And the "fix nothing without my approval": an audit that modifies the code while analyzing it is uncontrollable. First understand, then fix, one fix at a time — the chapter 4 method applies to security too.

Front 3 — Set limits everywhere

Third front, the simplest to understand: anything without an explicit limit will eventually be pushed to the absurd — through malice, accident, or a bot. In chapter 8, you limited emails and AI calls; now generalize the reflex to the whole app. Every field has a maximum length, every list has a maximum size, every action has a maximum frequency. And every refusal comes with a polite, clear message — the limit protects, it doesn't punish.

  • Habit name: 50 characters maximum, plain text only.
  • Number of habits per account: 30 maximum — nobody tracks 200, but a script can create 100,000.
  • Email recap: 1 send per user per day.
  • AI message: 1 call per user per day, monthly spending cap at the provider.
  • Account creation: protected by the magic link's email verification — already in place, thank you chapter 6.
flowchart TD
  E["User input or action"] --> V{"Valid content ?"}
  V -->|"No"| R["Clear and polite error message"]
  V -->|"Yes"| L{"Within limits ?"}
  L -->|"No"| R2["Explained refusal : limit reached"]
  L -->|"Yes"| S["Safe processing and storage"]
The double filter: validate the content, then check the limits — before any processing.

Debugging in production: the method

There remains the colleague's message: "it was slow yesterday around 7 pm". A typical production bug: you didn't see it, you can't reproduce it at will, and the user gives almost no detail. The pro method comes in three moves: collect (ask the user the right questions: which device, which browser, what time, what exact action, a screenshot), consult (the logs of Vercel and Supabase, which record what really happened on the server at that time), and conjecture (formulate ranked hypotheses, then test them one by one, from the simplest to the most complex).

PROMPT
Production bug to diagnose methodically.
Context: a student tells me the app "erases his checks" on his Android phone with Chrome, yesterday around 7 pm. On my computer, impossible to reproduce.
What I have: the Vercel logs from that evening, access to the Supabase dashboard, and the ability to write back to the student.
Guide me in three stages:
1. the exact list of questions to ask the student to pin down the context
2. what to look for precisely in the Vercel logs and in the Supabase data around 7 pm
3. how to simulate an Android mobile and a slow network from my browser to try to reproduce
Then propose your 3 most likely hypotheses, ranked from the easiest to verify to the most complex, with the test to run for each.

Notice the structure: this prompt doesn't ask for a solution, it asks for an investigation. That's the right reflex when facing an unreproduced bug — looking for the fix before pinning down the cause is fixing at random. The "3 ranked hypotheses" save you from the other trap: exploring the exotic lead before the mundane one. In real life, the mundane hypothesis wins nine times out of ten: a network dropping at the wrong moment, an old cached version, an expired session.

Keep an incident log: a text file with, for each bug, five lines — date, symptom, cause found, fix, lesson. By the tenth incident, you'll see patterns repeat, and you'll know where to start each new diagnosis. It's the most profitable tool of this chapter, and it costs only two minutes per bug.

Backups, your life insurance

Last piece of the setup: data backups. Your code is safe in Git, but your users' habits and checks live only in the database. One wrong move in the dashboard — a table emptied by one too-quick click — and everything disappears. Check what your Supabase plan backs up automatically, and complement it with a regular export (the AI can create a small export script for you, or use the dashboard's export feature). And above all, do the restoration exercise once: a backup never tested is a promise, not a protection.

With his three fronts covered, his incident log started and his first backup tested, Tom sleeps better. Karim received a polite message ("invalid habit name 😉"), the 7 pm slowness turned out to be a saturated boarding-school network — the mundane hypothesis, as predicted. The app is solid. It's time to look at it differently: no longer as a project, but as a product. That's the final chapter.

🛠️ Your turn

Context

Tom dedicates a full evening to the security review: AI audit, fixes by severity, limits everywhere, and a first backup restoration test. As a bonus, he treats himself to playing Karim: trying to break his own app before someone else does. Run the same program on your app — it's the most profitable evening of your journey.

Instructions

  1. Check your secrets: .env in the .gitignore, clean git status, and search for "key" in the browser-side sources (F12).
  2. Play the attacker: type HTML into your fields, 5,000-character texts, click frantically — note everything that breaks.
  3. Run the full security audit prompt and read the entire report before fixing anything.
  4. Fix the problems in decreasing order of severity, ONE fix at a time, with a test and a commit after each.
  5. Set your limits (lengths, quantities, frequencies) with polite refusal messages, and test each limit.
  6. Export a backup of your database, then do the restoration exercise on a test project — and note the procedure in your log.
Hint — If the audit surfaces a lot of problems, don't panic: it's normal for a first time, and it's the point. Handle the "critical" ones tonight, schedule the "medium" ones over the week, and note the "minor" ones in your "later" list. Security is a process, not an exam.

In summary

  • Three fronts: secrets, user inputs, limits — cover all three and you eliminate most of the risk.
  • A key that touched Git is compromised forever: you revoke and regenerate, you never "hide".
  • All user input is suspect: escaped on display, validated server-side, limited in length.
  • The AI audit works with a precise mandate: scope, ranking by severity, and no fix without your approval.
  • Every field, list and action gets an explicit limit, with a polite refusal message.
  • Production bug: collect the context, consult the logs, conjecture ranked hypotheses from mundane to exotic.
  • A backup never restored is a promise, not a protection: test the restoration once.

Quiz — check your understanding

1. You discover an API key was pushed to GitHub a week ago. What do you do?

Git history keeps everything and repositories are scanned by bots non-stop: the key is permanently compromised. Only revocation fixes it — deleting the file just hides the trace.

2. Karim types HTML into a field and the page interprets it. What is the defense called?

Displaying inputs without escaping opens the door to XSS-type attacks, where injected code runs on other users' machines. Typed text = displayed text, always.

3. Why validate inputs server-side and not just in the browser?

Browser code is in the user's hands: they can modify or ignore it. Server validation is the only authoritative one — the browser's is just interface comfort.

4. What are the two key requirements of the security audit prompt?

The ranking makes you handle the critical first; the explicit approval keeps you in control. An audit that modifies code while analyzing it is uncontrollable.

5. A user reports a bug you can’t reproduce. Where do you start?

Collect, consult, conjecture: fixing before pinning down the cause is fixing at random. And the mundane hypothesis (network, cache, session) wins nine times out of ten.

Auteur(s)

R

REHOUMA Haythem

Haythem Rehouma est un ingénieur et architecte IA et cloud, formateur et enseignant technique, avec un profil orienté IA médicale, AWS, MLOps, LLM/RAG et vision par ordinateur.