Security Scanning: Catch what the framework hides
Reflex Cloud scan reviews your app for Reflex-specific security mistakes and fails the build on high severity findings, so they never reach production.
PAPalash Awasthi
A growing share of application code is AI-generated rather than written by hand. On the surface, this generated code may look like it follows security conventions; reading secrets from the environment, keeping handlers small, and typing the fields. However, since LLMs struggle to accurately determine the rules of a framework, a lot of this code raises dangerous security vulnerabilities.
A framework's rules determine what a client can read, and existing tooling does not encode them; a linter checks style and a general security scanner checks the general web vulnerability classes, but neither has rules for how a specific framework handles state.
Here's an example of one such rule within Reflex. api_key: str = os.environ["OPENAI_API_KEY"] loads a secret from the environment. On a Reflex state class, that var has no leading underscore. Non-underscore vars are serialized to the browser on every state sync. This means anyone who loads the app can read the key and use it.
The security scanner looks for this pattern and others, which seem to be common failure modes across Reflex apps made by LLMs.
Run a scan from your project directory:
What the scanner checks
A setter on a var that gates authorization. Event handlers are callable from the frontend. A setter event handler assigns whatever argument it receives. This means a client can set the var to any value it wants.
Change the var inside a handler that validates the value on the server, or make it a backend var with no setter.
Server-only state in a frontend var. Every non-underscore var is serialized to the browser on each state sync, so anyone who loads the app can read its value. Secrets, database handles, and rows the UI never renders belong in backend vars.
Add an underscore to each one.
A synchronous call inside an async handler. A synchronous call blocks the event loop until it returns, so every session connected to the app waits for it.
How the scan works
reflex cloud scan zips your project and uploads it to a private staging bucket through a presigned URL. Only the object key is passed to the review queue, and the upload is deleted once the review finishes.
The review is one pass over the source with the rules in the prompt. Each rule carries a description, an incorrect example, and a correct example. The scanner reports which rule each violation breaks, with the file, line, and offending snippet. Each rule carries its own severity.
The scanner is prompted for precision over recall, so backend vars, setters with server-side validation, and awaited I/O are skipped. A violation is reported only where it can be located in a specific file. The review runs on a model, so the same source can produce different findings across runs.
The scanner reads up to 5,000 files, skips files over 1 MB, and caps total text at 25 MB. It also skips .git, .venv, .web, node_modules, __pycache__, build, dist, and tool caches.
Running it in CI
--fail-on returns a non-zero exit code at or above the severity you pass.
A pull request that introduces one of these patterns fails the check. --json returns the findings as structured output.
What's in scope today
The scanner checks the three patterns above. SQL injection, XSS, dependency vulnerabilities, and secrets committed to source are out of scope today. General-purpose scanners cover those, and they have no rules for how Reflex handles state.
Every rule carries high or medium severity today, so --fail-on critical matches nothing.
We are working on:
- More rules. Any pattern we see go wrong repeatedly in Reflex apps is a candidate.
- CVE scanning for the Python packages an app depends on.
Run reflex login, then reflex cloud scan in any project. Docs at reflex.dev/docs.
More Posts

If you need to own, deploy, and audit your app, Replit's model has limits. Here are 7 alternatives matched to your needs.

xy is the fastest Python charting library, written in Rust. Plot up to 100 million points with responsive hover and zoom, and export interactive charts as 258 KiB files.

Will your no-code app survive at scale? A no-code app builder launches fast, but comes with limitations. See where it fits and when it's best to own your code.