EMERGENT SECURITY CHECKLIST
Emergent is an agentic vibe-coding platform: an autonomous agent builds and deploys a full-stack app from a prompt — typically React on the frontend, FastAPI (Python) on the backend, MongoDB for data — and hands you a live public URL in one session. That speed is the risk profile. The two patterns behind most findings: MongoDB queries that fetch by ID without scoping to the requesting user, so any authenticated user can read other users’ documents; and generated JWT auth with a weak or hardcoded signing secret and long-lived tokens. The checklist below is what we look for first when we audit an Emergent-built app.
Treat Critical as launch-blocking. High is week-one. Medium is the cleanup once you have users.
How to use this checklist
Walk it once against the code and a second time against the deployed URL with two logged-in accounts. After each fix, sign in as user B and try to read user A’s data. Do all of this before sharing the public URL — the window between “deployed” and “hardened” is where incidents happen.
Critical (fix before launch)
1. Scope every MongoDB query to the current user
Why it matters. Agent-generated CRUD reads documents by ID: find_one({"_id": id}). There is no per-user scoping, so any authenticated user who obtains another user’s document ID can read it. This is BOLA, the most common finding in AI-generated CRUD.
How to check. Grep the backend for find_one, find, update_one, delete_one and confirm every query on a user-scoped collection includes the owner in the filter. Then test it live: user A creates a record, user B requests it by ID — must be 403 or 404.
How to fix. Compound filters everywhere: find_one({"_id": id, "user_id": current_user.id}). Same for updates and deletes.
2. Replace the JWT signing secret and shorten token lifetime
Why it matters. Generated auth code ships with weak or hardcoded secrets (SECRET_KEY = "secret", or an agent-invented value committed to the repo) and tokens that live for weeks. A guessable secret lets anyone forge tokens for any user.
How to check. Find where the JWT is signed and decoded. Confirm the secret comes from environment config, is 32+ random bytes, and jwt.decode pins the algorithm. Check the expiry claim.
How to fix. Random secret in environment config, algorithms=["HS256"] pinned on decode, access-token lifetime in hours. See JWT alg:none and kid traversal.
3. Put an auth dependency on every FastAPI route
Why it matters. FastAPI routes are public unless a dependency gates them. The agent applies Depends(get_current_user) inconsistently — especially on routers generated later in the session — so some endpoints that return user data are open to anyone.
How to check. List every route (app.routes or grep for @router. / @app.) and confirm each data-returning or state-changing route has the auth dependency, directly or via its router.
How to fix. Attach the dependency at the router level (APIRouter(dependencies=[Depends(get_current_user)])) so missing it on one route fails closed.
4. Move hardcoded secrets to environment config
Why it matters. Keys pasted into the prompt or added by the agent end up as string literals — OpenAI keys, Stripe keys, the MongoDB connection string. Anything in the React bundle is public; anything in the repo leaks with the repo.
How to check. Grep the source and the built frontend bundle for sk-, sk_live_, mongodb+srv://, AIza, and other provider prefixes. The Token Leak Checker automates the deployed-bundle pass.
How to fix. Server-side secrets go to environment configuration in the platform’s settings. Rotate every key that ever appeared in source.
5. Block self-service privilege escalation
Why it matters. If the users collection has a role or is_admin field and the generated update endpoint applies the request body with {"$set": body}, any user can promote themselves to admin.
How to check. Read the profile-update endpoint. If the request body is written to the document without an explicit field allowlist, it’s vulnerable. Test by PATCHing your own profile with {"is_admin": true}.
How to fix. Build the update dict from an allowlisted set of fields, and set extra="forbid" on the Pydantic model.
6. Stop verbose error responses
Why it matters. Generated handlers return str(e) or full tracebacks. Clients see file paths, Mongo error strings, and package versions — a map of your stack for whoever probes next.
How to check. Hit an endpoint with malformed input and a nonexistent ID. If the response contains a traceback, a path, or a raw database error, it leaks.
How to fix. Global exception handler: log server-side, return {"detail": "Internal server error"} to clients. No debug/reload mode in production.
High (fix in the first week)
7. Rate-limit auth endpoints
No per-IP throttle exists unless you add one. Login, signup, and password reset need limits (e.g. via slowapi) or they’re open to credential stuffing.
8. Rate-limit and meter LLM-backed endpoints
Any endpoint that calls an LLM on your API key needs a per-user quota and a per-IP limit. Without one, a single scripted user runs up your provider bill in minutes.
9. Lock down CORS
Generated apps ship allow_origins=["*"] so the preview works. With credentials enabled, that’s an account-takeover surface. Restrict to the production domain. See CORS credentials misconfig.
10. Harden file uploads
Size cap, MIME allowlist by content sniff, UUID filenames regenerated server-side. See file upload zip-slip / XXE.
11. Verify webhook handlers check signatures
If the agent wired Stripe or similar, the generated handler often trusts the request body without verifying the signature. Fix before payments go live.
12. Disable email enumeration in the auth flow
Signup, password reset, and login must return identical responses for “user exists” and “user does not exist.”
Medium (fix when you can)
13. Tighten Pydantic models
Replace dict and unbounded str fields with constrained types and length caps; extra="forbid" everywhere user input enters.
14. Sanitize user content before rendering
Anything rendered via dangerouslySetInnerHTML or a Markdown renderer must be sanitized first. See LLM-rendered HTML/Markdown.
15. Check the MongoDB network surface
The database must only be reachable from the backend, with a scoped user — not an admin account on a public port. Verify especially after exporting the project to your own infra.
16. Remove seed data and test accounts
Agent sessions leave demo users and sample documents behind. Delete them before launch.
17. Add security headers
CSP, HSTS, X-Frame-Options, X-Content-Type-Options via middleware — one middleware, five headers.
18. Set up basic access logging
Log auth events and per-user request counts somewhere queryable. Enumeration looks like one user reading thousands of documents in seconds.
After every agent session
Emergent’s agent regenerates and extends code across sessions. After each one:
- Re-grep for unscoped Mongo queries in new or modified endpoints.
- Confirm new routers still carry the auth dependency.
- Re-check for hardcoded keys the agent may have inlined.
- Re-run the two-account BOLA test.
- Confirm error handling and CORS settings survived the regeneration.
Common attack patterns we see in Emergent apps
The unscoped collection. orders fetched by _id only; a signed-in user iterates IDs from network traffic and dumps every order in the database.
The forged token. JWT secret is a short hardcoded string; attacker signs a token with user_id set to the admin’s and walks in.
The open router. The second router the agent generated never got the auth dependency; /api/analytics returns aggregate user data to anonymous callers.
The leaked key. OpenAI key pasted into a frontend helper during the session; it ships in the React bundle and gets scraped within days.
Related Resources
How to Secure Emergent
Step-by-step guide for hardening an Emergent app — query scoping, JWT fixes, auth dependencies, and rate limiting in long form.
Is Emergent Safe?
In-depth analysis of Emergent’s defaults — what the platform handles, what the generated app leaves open.
Automate Your Checklist
A checklist tells you what to look for. A scanner tells you what’s actually broken in the deployed app right now. VibeEval drives a real browser through your Emergent deployment, attempts the cross-user BOLA and token-forgery attacks above, and reports what got through — with the endpoint and query to fix.
SCAN YOUR EMERGENT APP
14-day trial. No card. Results in under 60 seconds.