Lightning Lesson
Run the live demo yourself
This is the exact live demo from Casky's free Lightning Lesson, "Your Vibe-Coded App Is Already Compromised. Did You Find It?": a synthetic vibe-coded SaaS running in a container, two scripted checks that find its RLS gap and its leaked secret, then a real Casky investigation of the same evidence, evidence through to a CISO-ready report. Nothing here touches a real company; everything runs on your own machine.
The Loopline homepage
Loopline is a fictional customer-support SaaS, "vibe-coded" in a weekend and shipped straight to its first 40 customers, no security review, per the FastLane / SpeedBump story. It ships two real gotchas from the sourced Top 10: Row-Level Security left off, and a Supabase service-role key sitting in the client bundle.
Prerequisites
- Docker Desktop (or any Docker runtime), running.
- This repo, checked out; every command below runs from
fastlane/.
Pull and run it
cd casky-workshops/fastlane
# Pull the pre-built image from GHCR (rebuilt nightly with the latest patches) instead
# of building locally — much faster at a live session.
docker pull ghcr.io/casky-ai/casky-loopline:latest
docker tag ghcr.io/casky-ai/casky-loopline:latest casky-loopline
docker run -d --name casky-loopline -p 8787:8787 casky-loopline
# Fallback, kept for reference — rebuild locally if you're iterating on Dockerfile.demo
# itself, or if GHCR is unreachable (offline/air-gapped venue):
# docker build -f Dockerfile.demo -t casky-loopline .
Open http://localhost:8787, Loopline's own landing page: dark CyberForge theme, checkered-flag stripe, and a plainly-labeled synthetic-demo notice at the bottom.
docker pull ghcr.io/casky-ai/casky-loopline:latest confirmed working (no
docker login needed, publicly pullable). Homepage returns 200, both scripts
below return the expected finding, no manual fixups needed.
How Loopline broke it
Lovable scaffolded Loopline's database. It never turned Row-Level Security on for the
customers table, not disabled by a developer, never enabled at all. Weeks
later, a security blog post scared the founder into fixing one table: tickets
got a policy, but it was USING (true), which matches every row for every
requester. Looks fixed in a schema diff. Isn't. customers was never revisited.
Separately, an env var named NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY shipped
Supabase's admin key straight into the browser bundle, because the coding assistant didn't
flag what NEXT_PUBLIC_ does in Next.js. Nobody caught it before it shipped.
Full story: NARRATIVE.md.
The five-minute check
Two scripts, no special access, same two checks from
the Top 10 blog post,
already in fastlane/, pointed at the local container instead of a real app.
Check 1: does the anon key read every row?
cd casky-workshops/fastlane
./check_1_rls.sh
Runs curl -s "http://localhost:8787/rest/v1/customers?select=*" -H "apikey: <anon key>"
and explains the result. Returns all five Loopline customer rows, no login required. The
apikey is the public "anon" key, meant to ship to every browser.
rowsecurity: false is Postgres's own flag: RLS was never turned on for this
table, so the public key alone is enough to read everything.
Check 2: is a secret sitting in the shipped bundle?
./check_2_secrets.sh
Runs curl -s http://localhost:8787/_next/static/chunks/app-a91f3c2e.js | grep -oiE '(service_role|sk-live-|AKIA)[A-Za-z0-9_.":-]{5,}'
and explains the result. Returns the Supabase service-role key: Supabase's
admin key, meant only for trusted server-side code. The anon key (check 1) respects RLS
when RLS is configured; the service-role key bypasses RLS entirely, by design, correctly
configured or not.
The customer rows, the domain, and the keys above are all fabricated for this workshop (see NARRATIVE.md). No real company, customer, or credential is involved.
Now let Claude find it
The two checks above are deterministic — curl and grep, no AI involved, so the result doesn't depend on trusting an agent. Here's the other half: point Claude Code at the same two pieces of evidence with a plain-English ask, no hints beyond the filenames, and watch it reach the identical findings on its own.
Setup
cd casky-workshops/fastlane
# Your own key, unquoted — this path doesn't need casky-box/Section 2 set up first.
echo 'ANTHROPIC_API_KEY=sk-ant-...' > .env
docker pull ghcr.io/casky-ai/casky-fastlane:latest
docker tag ghcr.io/casky-ai/casky-fastlane:latest casky-fastlane
docker run -d --name casky-fastlane --env-file .env -v "$(pwd)":/root/fastlane casky-fastlane
docker exec -it -w /root/fastlane casky-fastlane bash
# ...now inside the container:
export PATH="$HOME/.local/bin:$PATH"
claude
The prompt
You are auditing Loopline, a Supabase-backed customer-support SaaS. I have two pieces of
evidence in this folder: app/rls-policies.json (a Postgres RLS policy export) and
app/client-bundle-excerpt.txt (strings pulled from the production client-side JS bundle).
Investigate both files, tell me what security issues you find, which skill(s) you used, why
each issue matters, and how to fix it. Be concise.
Claude Code, given only that prompt (10 skills mounted, no other hints), named both
gotchas correctly and cited the skills it used —
testing-api-for-broken-object-level-authorization for the RLS gap,
testing-for-sensitive-data-exposure for the bundle secret — with the same fix
each check's own explanation gives: rotate the service-role key and move it server-side;
ALTER TABLE customers ENABLE ROW LEVEL SECURITY with an org-scoped policy.
Unprompted, it also cross-referenced app/access-log.txt to confirm the
service-role key wasn't just present but actually used (1,842 rows pulled in one request),
and caught a third, related bug sitting in the same RLS export — tickets' policy
is enabled but reads USING (true), which matches every row for every requester —
while explicitly noting it only reviewed the two files named and offering to go further.
exit the claude session and the container shell (two
exits) when you're done, then docker rm -f casky-fastlane — or see
Cleanup on the Troubleshooting page.
Evidence to CISO report
That was one container, one prompt, two gotchas. Here's the same evidence run through Casky's full pipeline instead — the flow every other Casky workshop uses:
| # | Stage | What happens |
|---|---|---|
| 1 | Evidence | The check output above, saved as-is. No scoping, no cleanup, just what a real attacker would have. |
| 2 | Skill matching | Casky's classifier reads the evidence and selects the right skills from the 818-skill library, no manual picking. |
| 3 | Actions | Each selected skill actually runs. Every tool call is logged and independently verified ([VERIFIED] Skill script executed: YES/NO). |
| 4 | Remediation | Specific, prioritized fixes: enable RLS on customers, rotate the service-role key, move it server-side. |
| 5 | CISO report | An executive summary, mapped to MITRE ATT&CK and NIST CSF, the format a board asks for after every incident. |
Two independent paths run this pipeline against this exact evidence, both already verified. Pick whichever matches your setup; full steps live on the Setup page:
| Path | What it needs | What you get |
|---|---|---|
| Section 1: Kali + Claude Code | Docker, fastlane/.env |
Interactive Claude Code session, 10 hand-picked skills, ./verify.sh at 12/12 PASS |
| Section 2: Casky Box | Casky Box running (casky-box/, pulled from GHCR) |
casky harness --auto over all 818 skills, auto-classified, MITRE-mapped findings, a CISO-ready executive summary |
Section 2, run against this exact evidence, independently found 7 MITRE
techniques at 84.5% confidence and, unprompted, asked whether Loopline's RLS
default "matches the known CVE-2025-48757 pattern": stage 2 (skill matching) converging on
the real incident this scenario is modeled on, from the evidence alone. Full transcript on
the Setup page. Open casky-ui
(http://localhost:8766) afterward for the Plan / Execution / Findings /
Remediation tabs, stages 3 and 4 above, and the CISO-ready executive summary, stage 5.
Cleanup
docker stop casky-loopline && docker rm casky-loopline
Non-destructive, nothing else on your machine is touched. For the Casky investigation containers, see Cleanup on the Troubleshooting page.