Is AI-Generated Code Safe? What Actually Runs Before You See Your App
A look inside the checks GenMB runs on every generation and every refinement: static validation, behavioural and design checks, a bounded heal loop, an OWASP security scan, and an SEO scan. Written by the people who built it.
Ambuj Agrawal
Founder
"Is AI-generated code safe" is really two questions. Does the code do what the app claims it does? And does it hand an attacker something? Neither is answered by a model saying "done".
So we stopped asking the model. Since release 1.7.1 every app generated on GenMB goes through a quality pipeline before it reaches you, and every refinement goes through the parts of it that can run on a change. This is what that pipeline does, what it fixes on its own, and what it deliberately refuses to touch.
What runs on a generation
The stage is called validate-and-heal, and it runs in one place for both the REST and the WebSocket generation paths so the two cannot drift apart. In order:
Deterministic fixers first. Before anything is judged, a set of rewrites runs that are always correct: attribute braces in htm templates, a Fragment-aware html binding (the shorthand <>...</> used to render as createElement('') and blank the page), an error boundary around the app root, and off-scale spacing values pulled back onto the design scale. These cost nothing and are idempotent, so a clean app pays nothing, and the heal loop never burns an LLM round on a bug class a rewrite resolves for free.
Static checks. A tree-sitter parse of every file, import and export resolution across the file tree, package.json dependency versions (hallucinated versions get caught here rather than at install time on deploy), and for TypeScript apps a real tsc pass whose diagnostics become issues.
Contract checks. Every fetch in the frontend has to hit something real: a GenMB proxy route, a handler you have, or a database table that is actually provisioned. A call to a route nobody serves is a feature that silently never works, which is worse than a crash because nothing tells you.
Behavioural checks. These are the ones we care most about, because each came from a real broken app. A few, by their names in the code: check_billing_amounts (a wrong amount on a checkout call is a 100x overcharge), check_phantom_api_calls, check_dead_handlers (a row of buttons whose entire body is a toast renders enabled and does nothing), check_missing_functions, and a group that catches backend handlers written against APIs the function sandbox does not have. Function findings are sorted to the front of the heal context, because a handler that fails on every call is the whole feature.
Design and UX checks. Undefined CSS variables, a theme toggle wired to nothing, a splash screen that never clears, a login wall in front of an app that has no accounts, an unguarded JSON.parse of stored state that blanks the app on reload (check_unguarded_state_parse), the generation date written in as "today" (check_frozen_dates), and a literal rendered as if it were a computed score.
A security scan and an SEO scan. Both static, both roughly 50 milliseconds, neither uses a model. The security scan maps findings to OWASP categories: cross-site scripting, exposed secrets, weak auth, injection, privilege escalation, misconfigured permissions, insecure transport, risky dependencies. Critical and high findings are handed to the heal loop as issues. Both scans are saved on the app so the editor panels can render them immediately.
What auto-heal changes, and what it refuses to
If the checks find nothing, no model runs. That is the point of ordering the cheap deterministic work first.
If they do find something, a bounded loop asks the model to fix the collected issues in one pass with full context, rather than chasing symptoms one at a time. The cap on attempts scales with the size of the app. Three rules keep it honest:
- A heal that introduces a new problem is reverted. After each attempt the issue set is recomputed and compared to the pre-heal set by fingerprint. If any new fingerprint appears, the attempt is thrown away even when the raw count went down. Swapping one bug for another does not count as progress.
- A heal that changes nothing stops the loop. Re-running the same prompt against the same code cannot produce a different result, so the loop exits instead of spending another minute per attempt.
- Design findings are capped, and never take the budget from a real defect. At most six are passed to the healer. When an app's only findings are cosmetic, it gets exactly one attempt and whatever is left is logged as advisory. When a behavioural finding opened the loop but the app has no crash-class problem of its own, design findings stay out of the context entirely, so the model is not handed one billing bug plus six styling notes.
When the generation path is on a time budget (the WebSocket path still owes time to the build), the expensive model rounds are skipped once the remaining budget drops below what an attempt needs. The static scans always run. Losing a heal attempt is a much smaller problem than a generation that times out.
What runs on a refinement
Every chat refinement, visual edit and Figma import saves through one function, and that function runs a syntax hard-gate before anything is persisted. A module-level parse error crashes the page on every render, so it is the one class that never ships silently: the gate detects it with the same tree-sitter checks, runs a single targeted syntax-only repair, and only then saves.
Two deliberate asymmetries:
- If a you-initiated save still does not parse after that repair, it is saved anyway, and the chat reply tells you which file does not parse instead of leaving you with a dead preview and a cheerful "Changes applied". Your code is yours. We will not refuse your save.
- If an unattended auto-heal produces code that still does not parse, it is aborted. Nobody asked for that change, and writing a broken version over a working one is strictly worse than doing nothing.
The wider static checks also run after a refinement, but as logging only. They are not allowed to open a second heal loop there: it would fight the pre-save gate over the same files and spend model rounds on findings the deterministic fixers are about to resolve anyway.
Why the security scan never blocks a publish
This is a product decision, and it is worth stating plainly. When you publish, the code being published is scanned fresh (not read from a stored verdict, which can be stale), and any critical findings come back on the success response. The publish goes through. The dialog shows you what was found and offers a fix path.
A static scanner is a pattern matcher. It is useful and it is sometimes wrong, and being wrong is much cheaper when the consequence is a warning than when the consequence is "you cannot ship". Blocking would mean a false positive stands between you and your users at the worst possible moment. So the scan informs, and you decide.
Where you see all this
In the editor, under the "Run it" group of the app panel, there are two sections: Security and Discoverability. Security shows a score out of 100, findings grouped by severity, each with its file, line number and OWASP id, and a suggested fix. Discoverability does the same for the SEO scan: title length, meta description, heading structure, image alt text, the lang attribute, robots directives and structured data.
Both have one button. Pressing it does not call a hidden repair endpoint, because there is not one: it builds a prompt from the critical and high findings and sends it to your chat panel. The fix streams like any other refinement, saves as a version you can restore, and appears in your chat history. Medium and low findings are left alone and the panel says so, because they usually need a judgement call rather than a pattern replacement.
Both panels re-scan themselves whenever the app version changes, so after any refinement you are looking at the current code, not the state at generation time.
What this costs
Nothing extra. The scans, the checks, the heal loop and both panels run on every plan, including the free one. There is no "security add-on". Pro buys you more credits, no daily cap, and the automation layer on top: it does not buy you a safer generation, because that would be a strange thing to sell.
Want the detail? See Security and code quality, SEO and discoverability, and App health.
Frequently Asked Questions
Is AI-generated code safe to put in front of real users?▼
Does the security scan block my app from being published?▼
What does auto-heal refuse to change?▼
Do these checks run on refinements too, or only on the first generation?▼
Do I need a paid plan for the security and SEO scans?▼
Ambuj Agrawal
Founder
Award-winning AI author and speaker. Building the future of app development at GenMB.
Follow on LinkedIn