Django ships with DEBUG = True. Next.js will happily bundle a secret into the browser if you prefix it wrong. Supabase creates tables that anyone can read until you turn on row-level security. None of this is a bug in the framework. It's the default behavior doing exactly what it says, in a place you didn't mean for it to run. IOnclad knows the footgun for each stack and checks whether you pulled the trigger.
A footgun is a security feature that exists, is documented, and is simply not turned on (or is turned off in the wrong environment). The framework gives you the safe path. The default, or a fast copy-paste, takes the unsafe one. IOnclad detects the stack from your files, then runs the checks specific to it:
Each of these is a single line or a single missing line, and each one quietly hands an attacker something they shouldn't have.
DEBUG = True in Django (or debug=True in Flask, or APP_DEBUG=true in a Laravel .env) turns every error page into a full disclosure: stack traces, SQL queries, settings, sometimes environment variables. Flask's debugger goes further and lets a visitor run arbitrary Python from the browser. It's the fastest way to hand a stranger a map of your whole app.
Any env var named NEXT_PUBLIC_, VITE_, REACT_APP_, or EXPO_PUBLIC_ gets inlined into the client bundle. That's by design for things like a Supabase anon key or a Stripe publishable key. But name it NEXT_PUBLIC_STRIPE_SECRET_KEY and your live secret is now in JavaScript that every visitor downloads. View source, find key, done.
On Supabase, a Postgres table without RLS is readable and writable by anyone holding the anon key. The anon key is public by design and ships in your client bundle. So "create table, build the UI, launch" with no policy in between is a table the whole internet can dump and edit. This is the canonical day-one breach for vibe-coded apps.
React's dangerouslySetInnerHTML (and Vue's v-html) render raw HTML. Feed them user input without sanitizing and you have stored XSS. On the server, an Express app with no rate limiting lets attackers brute-force logins; a Rails app with protect_from_forgery except: leaves some state-changing actions open to CSRF. These are the defaults the framework gives you a way out of, if you take it.
A Supabase table with no RLS is not a "later" problem. The moment your anon key is in a shipped bundle (which is its whole job), every row in that table is public read and public write. There is no auth in front of it. Turn on RLS and write a policy before the table holds anything real.
The Next.js version is almost a joke because it's so easy to do:
# .env.local, "I'll just expose it to the client real quick" NEXT_PUBLIC_STRIPE_SECRET_KEY=sk_live_51H...redacted...4242 # works in dev, ships to prod, lives in the JS bundle forever # the fix is one prefix: STRIPE_SECRET_KEY=sk_live_51H... # server-only, read via process.env in an API route # and the Supabase version, same energy: create table profiles (id uuid, email text, ssn text); -- no RLS, anon key can read every row alter table profiles enable row level security; -- + a policy that scopes rows to auth.uid()
IOnclad has a dedicated framework scanner with 60+ per-stack checks. It first detects which frameworks are in your project, then runs only the checks that apply, all 100% offline:
DEBUG = True (Critical), wildcard ALLOWED_HOSTS = ['*'], default or placeholder SECRET_KEY, and HTTPS not enforced. It requires a real Django signal in the file first, so a random settings.py doesn't trip it..env and source for NEXT_PUBLIC_ / VITE_ / REACT_APP_ / EXPO_PUBLIC_ vars. It allowlists public-by-design names (anon, publishable, site keys, analytics) and only fires Critical on a secret-shaped name like SECRET, SERVICE_ROLE, or PASSWORD, so it won't cry wolf on a correctly built Supabase + Next.js app.CREATE TABLE and every ENABLE ROW LEVEL SECURITY across all migration files, then flags only tables that are never protected anywhere. RLS enabled in a separate or later migration clears the finding, which is the recommended Supabase pattern.dangerouslySetInnerHTML and v-html bound to request, props, state, or user data.helmet(), missing rate limiting (including auth routes and Next.js route handlers), no request size limit, insecure session cookies, plus webhook signature checks for Stripe, GitHub, Clerk, Slack, Shopify, and more.IOnclad finds known patterns and common misconfigurations. It's not a pentest and not a guarantee, but these are exactly the defaults that ship broken.
DEBUG = os.environ.get('DEBUG','False') == 'True'. Never hardcode True.process.env in an API route or server component. Only anon and publishable keys belong in the bundle.ALTER TABLE ... ENABLE ROW LEVEL SECURITY plus at least one policy scoped to auth.uid(). Verify policies exist for every table.dangerouslySetInnerHTML or v-html, or render it as text.app.use(helmet()), a rate limiter on auth and write routes, secure session cookies, and verify every webhook signature with timingSafeEqual.Scan your code and full git history in under a minute. Free verdict, no signup, nothing leaves your machine.