A hobby Pokémon GO collection app let any website use its visitors' logins. The fix is one Agent prompt.
The app
A free fan site where Pokémon GO players sign in and track which Pokémon they own (shiny, shadow, lucky and so on). Built by a hobbyist with Replit Agent, no web background, no budget. Exactly the kind of app Replit Agent makes possible, and exactly the kind nobody security-reviews.
The problem
The API answered every website the same way: whatever site asked, the server echoed that site back in Access-Control-Allow-Origin and added Access-Control-Allow-Credentials: true. In plain words: "any website is welcome, and it may bring the visitor's login cookies."
Here is what a request from an unrelated site got back, including on the endpoint that holds each user's collection:
$ curl -I -H "Origin: https://attacker.example" https://[app]/api/collection
access-control-allow-origin: https://attacker.example
access-control-allow-credentials: true
Why it matters
If a signed-in user visited any other page on the web, that page could quietly call the app's API as them: read their collection, and change it. No password needed, nothing visible to the user. On a hobby site the stakes are a collection; on a business app built the same way they are customer records.
This is not the builder's fault. "Allow every origin, with credentials" is a common default when an AI agent wires CORS to make the front end work quickly. It works in testing, so nobody notices.
The fix
Replace the echo with an allowlist: only the app's own domains get CORS headers, and credentials are never sent to anyone else. The owner is not a web builder, so the fix had to be something he could paste, not code he had to write. This is the prompt we sent, ready for Replit Agent:
In the Express server, replace the CORS setup for all /api routes with a
strict allowlist. Allowed origins: https://[your-domain] and
https://www.[your-domain] (plus localhost in development only). Never echo
back the request's Origin header, and only send
Access-Control-Allow-Credentials: true to the allowed origins. Keep any
existing origin checks. Don't change anything else.
Then: paste it into the Agent chat, let it finish, and redeploy. To confirm, the same request from an outside origin should come back with no CORS headers (or a 403):
$ curl -I -H "Origin: https://attacker.example" https://[app]/api/collection
HTTP/2 403
{"error":"Collection origin not allowed"}
Result so far
- Collection endpoint: fixed. When we re-checked on 16 October 2026, the endpoint holding user collections rejected outside origins and only answered the app's own domains (the output above is from that check).
- Rest of the API: fix sent, pending redeploy. The remaining endpoints only serve public data (the Pokémon list, stats), so the risk is low, but they still echo any origin with credentials. The prompt above makes the allowlist global so future features don't inherit the open door. We'll update this page once it's live.
- Cost to the owner: $0. One outside check, one email, one prompt.
Check your own app in 30 seconds
Run this against one of your API routes, with a made-up origin:
curl -I -H "Origin: https://example.org" https://your-app.com/api/anything
If the reply contains access-control-allow-origin: https://example.org together with access-control-allow-credentials: true, you have the same issue. More checks like this one are in the Replit Agent security checklist.