OWASP Top 10:2025 — A02 Security Misconfiguration for AI-Generated Code

Written by the Rafter Team

An LLM is asked to "fix the CORS error." Two seconds later your Express app has app.use(cors({ origin: '*', credentials: true })), your /api/admin route is reachable from any browser tab on the internet, and the diff was small enough that you accepted it without scrolling. The feature works. The misconfiguration ships. This is A02, and AI codegen is a near-perfect engine for producing it — because the model is optimizing for "the error goes away," not "the production posture is sane."
What A02 actually is
Security Misconfiguration is the bug class where the code is fine but the settings around the code aren't. Defaults left on, debug mode enabled in prod, CORS wide open, S3 buckets readable to the world, framework error pages dumping stack traces and DB connection strings, admin endpoints mounted without auth because "we'll add auth later." Concrete example: a Next.js app where NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY ends up in the client bundle because someone prefixed an env var to "make it accessible." That single character — the NEXT_PUBLIC_ — is the entire vulnerability. Nothing in the application code is wrong. The configuration is.
Why AI-generated code trips on it
LLMs are trained on tutorials, and tutorials optimize for "this works on localhost." That distribution shows up in the diffs:
- Permissive defaults to make the demo work.
cors({ origin: '*' }),app.use(express.static('uploads'))with no auth,DEBUG = True,ALLOWED_HOSTS = ['*'], Helmet/CSP stripped because a script tag failed to load. - Errors thrown back to the client verbatim.
res.status(500).json({ error: err })is the default LLM pattern when wiring up an endpoint. In Express that ships the stack trace, file paths, and sometimes the raw SQL. - Cloud resources created world-open. Generated Terraform and Supabase setup scripts skip RLS, skip bucket policies, skip private VPCs. The infra "works" because everything can talk to everything.
- Headers and middleware stripped to silence warnings. When CSP or CORS or COOP blocks something during local dev, the LLM's most common reflex is to delete the policy rather than scope it.
- Frameworks that punish defaults. Supabase ships tables RLS-off by default — every Lovable/base44 app that creates a table is one prompt away from a public database. Firebase's open-rule default has the same shape. FastAPI's
docs_url=/docsis harmless in dev and a free reconnaissance endpoint in prod.
The pattern: the model is rewarded for code that compiles and renders. The configuration that would make it safe is invisible to that reward signal.
The fix on agentic CLIs (Claude Code, Codex)
You have file edit access. Use it. Don't ask the agent "is this secure" — it will say yes. Ask it to enumerate every place a default is in play, then fix each one explicitly.
A prompt that works:
Audit this repo for A02 Security Misconfiguration. For each finding, output:
1. The file and line.
2. What the current setting is.
3. What an attacker can do with it.
4. The exact diff to fix it.
Look specifically for: CORS origin '*', credentials:true with wildcard origin,
DEBUG/development flags in production code paths, error handlers that return
err.stack or err.message to the client, missing security headers (CSP, HSTS,
X-Frame-Options), Supabase/Firebase tables or buckets without RLS or rules,
admin/internal routes without auth middleware, env vars prefixed with
NEXT_PUBLIC_ / VITE_ / REACT_APP_ that contain anything resembling a secret,
and any Terraform/IaC that creates a publicly readable storage bucket.
Do not say "looks good." If you find nothing in a category, say so explicitly.
Then run rafter run on the diff before you merge. Rafter's findings have a Copy-for-AI button — it produces a prompt with the file, the bug, and the fix shape, ready to paste back into Claude Code or Codex. The loop is: scanner finds the misconfig, the prompt patches it, you re-scan. That loop is the actual control. A single audit isn't.
Two non-negotiables for CLI users: read the diff line-by-line before accepting, and keep a .env.example that lists only variables that are safe to expose. Anything not in that file gets server-side only.
The fix on opinionated platforms (base44, Greta, OpenClaw)
You usually can't open the file. You can tell the platform's agent what to do, and you can verify the result from outside. Both matter.
What to tell the platform's agent, in plain English:
- "Enable Row-Level Security on every table in my database and add policies that restrict every row to its owner. Tables that should be world-readable need an explicit
SELECTpolicy — assume nothing is public by default." - "Make sure no API key, service-role key, or webhook secret is shipped to the browser. Anything sensitive must be called from a server function."
- "Set CORS to allow only my production domain. No wildcards. No
credentials: truewith a wildcard origin." - "Turn off debug/development mode in the production build. Replace any error response that returns a stack trace or raw exception with a generic message and a server-side log."
- "Remove or auth-gate any auto-generated API docs, admin panels, or
/healthendpoints that leak version or environment info."
Then verify from the outside, because the platform's agent will confidently tell you it did the work whether or not it did:
- Hit your Supabase/Firebase REST endpoint with
curland the anon key. If a table comes back full of data you didn't authorize, RLS is still off. - View source on your deployed app and search for
sk_,service_role,SUPABASE_SERVICE, AWS access key prefixes, and any env var name withSECRETin it. None of those should appear. - Trigger a deliberate error (post garbage to a form) and inspect the response. If you see a stack trace or a file path, the error handler is leaking.
- Run
rafter runagainst the GitHub repo the platform exports to. Most opinionated platforms have a "connect to GitHub" or "export to repo" path — that repo is your only direct lever on the underlying code, and Rafter will scan it the same way it scans hand-written projects.
The constraint on opinionated platforms is real: you don't fully own the surface area. The corollary is that your verification has to come from outside the platform, because anything the platform tells you about its own security is marketing.
See also
- OWASP A05: Injection — the other half of "the LLM wrote a one-liner that ships a CVE."
- OWASP A07: Authentication Failures — most A02 findings on AI apps eventually cash out as "the admin route had no auth." This is the deeper diagnosis.
- OWASP A06: Insecure Design — when the misconfiguration isn't a setting but the architecture itself.