Pass review
The categories safety review rejects, and what to fix before submitting
This is what review is looking for. If you're publishing, read it before you submit — most findings are unintentional.
The scan below doesn't run when you submit. It runs when a reviewer opens your app and starts it — see How we review apps before they list.
What the scan checks
Nine categories are scanned and published on your listing, each marked passed or flagged:
| Category | What it covers |
|---|---|
| Cryptomining | Mining code, in any disguise |
| Dynamic code execution | eval(), the Function constructor, loading WebAssembly |
| Network & data exfiltration | navigator.sendBeacon, WebSocket connections, and forms that submit to another domain |
| External code loading | Scripts loaded from another origin, importScripts, service worker registration |
| DOM injection / XSS | document.write, raw innerHTML assignment |
| Redirects & navigation | Code that sends the user to another page |
| Sensitive data access | Cookies, reading the environment, and saving values with sensitive-looking names (tokens, keys, passwords) to local storage |
| Powerful browser APIs | Web Crypto, SharedArrayBuffer |
| Encoding & obfuscation signals | Deliberately obfuscated code |
A tenth category, leftover debug output, is checked but kept internal — it's a code-quality note, not something a buyer needs.
Some things are recorded for the reviewer but never raise a category on their own: plain fetch() and XMLHttpRequest calls, ordinary local storage use, postMessage, iframes, geolocation, notification requests and plain Base64. They are common in legitimate apps, so the reviewer sees them in context rather than as a flag.
A flag is not a rejection. Most of these categories cover things a legitimate app can do on purpose: a live multiplayer game opens a WebSocket, an app that keeps you signed in stores a session token. The scan finds them; the review decides what they mean in context. A flagged category on a live listing means a person looked and cleared it.
What turns a finding into a problem is a mismatch with what your listing says. Sending user data to a third party is fine if that's the app's stated purpose and it's disclosed; it's a rejection if the listing doesn't mention it.
What the human review adds
The scan is mechanical. Two things it can't decide are decided by a person:
Content policy. Whether the app breaks the platform's rules. The Charter's bright lines are the outer boundary — no weaponization, no exploitation, no deception at scale, no surveillance. See the Charter.
Whether the app does what it claims. A working app that isn't the app the listing describes is a problem no pattern scan will catch.
What buyers see
The report on your listing shows which categories were checked and whether each passed or was flagged, the reviewer's note, and the review date. It never shows the detection rules, the patterns, or a count of matches — a published match count is a resubmit-and-watch-the-number oracle for anyone trying to evade the scan. See How we review apps before they list.
Practical advice
Most rejections come from things nobody meant to do:
- Analytics you forgot about. A third-party tracker bundled into your build reads as exfiltration.
- A stray API call. Debug code pointing at your dev server, still in the shipped bundle.
- Credentials in the source. Never ship a key in client-side code — it's visible to anyone who buys the app. If your app needs an AI key, use the buyer's, held in the Key Vault: Connect your own AI keys.
- Dependencies you didn't audit. You're responsible for what your dependencies do. Check what you're pulling in.
Test the built output before submitting. What review sees is what builds, not what's in your editor.