Browse the wiki
Security basics

Common vulnerabilities, explained

The holes that show up most in AI-built apps are leaked or hardcoded secrets, including ones still buried in git history, secrets shipped to the browser, missing or client-side-only authentication, exposed admin pages, open CORS, Supabase row-level security left off, injection, cross-site scripting, personal and card data sitting in code or logs, and vulnerable dependencies.

Updated 2026-08-31 For everyone

This is a reference. Each entry covers one vulnerability in three parts: what it is, how it shows up in AI-built apps, and what the finding looks like in your report. Skim to the one your report flagged.

For why AI builders introduce these, see security for AI-built apps. For how to read severity, see reading your report.

Secrets in the wrong place

Leaked or hardcoded secrets

A secret is a key, password, or token that proves your app is allowed to do something: reach a database, charge a card, send email. Written into the code, anyone who sees the code has it. AI builders paste keys into files to get things working, and those files get committed to your repo, sometimes including the whole .env file meant to stay private. The finding points at the file and line where the key lives and tells you to remove and rotate it, without ever printing the key in full.

Secrets exposed to the browser

Some secrets end up shipped to the browser, where every visitor can open developer tools and read them. Builders use naming like NEXT_PUBLIC_ or VITE_ for values the browser needs, and a real secret gets that prefix by mistake, so it goes out to everyone. Auth tokens saved in the browser's local storage are a related habit, readable by any script on the page. The finding flags the browser-exposed variable or the token; a secret here is a public secret. The fix moves it to the server.

Secrets still sitting in your git history

Deleting a key from a file does not delete it from the repository. Every earlier commit still holds it, and anyone who can clone the repo can read it. This is the one people are most often wrong about, because the file looks clean today. The scan walks up to three years of your commit history looking for keys that were committed and later removed. The finding names the file and the commit, and the fix is always the same: rotate the key, because the old one is public whether or not you tidy the history.

Secrets that are still live

A key found in code is a problem. A key found in code that still works right now is an emergency, and the two deserve different urgency. Where the provider supports it, the scan confirms a candidate against that provider before reporting it, so a confirmed finding means the credential was live at the moment of the scan and not merely that it looked like one. Placeholders, expired keys and revoked keys do not survive that check.

To check a single file yourself, paste it into the free secret scanner. It runs entirely in your browser, nothing is uploaded, and it looks for the same credential shapes described above. A scan reads the whole repository, including the git history a pasted file cannot show you.

Missing or fake access control

Missing authentication and client-side-only auth

Authentication decides who is allowed to do something. Missing auth means there is no check. Client-side-only auth means the check runs in the browser, where the user controls it, so it is not a real check. The AI builder hides a page unless you are logged in, but the underlying address still answers anyone who calls it directly. The finding flags an endpoint or page that changes or shows private data with no server-side check. The fix moves the guard to the server.

Exposed admin pages

An admin page, dashboard, or internal tool sitting at a reachable address with no login in front of it. The AI builder scaffolds an admin area and never wires up access control, or leaves the page at a guessable path. The finding flags the admin route as reachable without authentication. Anything that can change data or list users needs a login.

Row-level security disabled

Row-level security (RLS) is the database setting that limits each user to their own rows, so one customer cannot read another's. With it off, one account can often read everyone's. Hosted databases used by AI builders frequently start with RLS off, and since the app works either way, nobody turns it on. The finding flags tables reachable without row-level protection.

Open Firebase rules

Firebase security rules decide who can read and write your data. Rules left open mean anyone can read or change anything. The default test rules are wide open so you can build fast, and never get tightened before launch. The finding flags rules that allow public read or write and points you to lock them down.

Open CORS

CORS (cross-origin resource sharing) is the setting that decides which other websites are allowed to call your app's backend from a visitor's browser. Set to a wildcard, or made to reflect whatever origin asks, any site on the internet can make requests to your API riding on your users' logged-in sessions. Builders reach for the widest setting to stop a cross-origin request from failing while they build, and the wildcard ships to production. The finding flags a backend that allows any origin, and is more serious when it does so with credentials enabled; it points to restricting the allowed origins to the domains you actually serve.

Injection and input handling

SQL injection

User input is stitched straight into a database query, so a crafted input can rewrite the query and read or destroy data. The AI builder glues strings together with user input in the middle, instead of using safe parameters. The finding flags the query where untrusted input reaches the database directly and points to a parameterized version.

Command injection

The same idea aimed at the operating system: user input reaches a system command, so an attacker can run their own commands on your server. The app shells out to run a tool and drops user input into the command string. The finding flags where input flows into a system call and points to keeping raw input out of the shell.

Path traversal

User input decides which file to open, and an attacker uses it to climb out of the intended folder and read files they should not, like config or secrets. The app builds a file path from user input, such as a filename in a download link, without checking it stays inside the allowed directory. The finding flags where user input builds a path and points to confining it.

Server-side request forgery (SSRF)

An attacker makes your server fetch a URL they choose, reaching internal systems it should not, including cloud metadata that can hand over credentials. The app fetches a user-supplied URL, such as an image address or webhook target, without restricting where it can point. The finding flags the request and points to allow-listing the destinations.

Cross-site scripting (XSS)

Attacker-controlled text gets rendered as live code in someone else's browser, letting it steal sessions or act as that user. User input, a comment or a name, is put onto the page without being neutralized first, so a script in the input runs. The finding flags where untrusted input reaches the page unescaped and points to rendering it as text, not code.

Mass assignment and missing input validation

Mass assignment is when a request can set fields it should not, like flipping your own account to admin. Missing input validation is the broader problem of trusting whatever the client sends. The AI builder binds an incoming request straight onto a database record, so any field in the request gets written, including ones the user was never meant to touch. The finding flags an endpoint that writes fields without limiting them, and points to an allow-list of what a request may set.

Insecure deserialization

Deserialization turns stored or received data back into live objects. If the data is untrusted and the method is unsafe, crafted data can run code. The app unpacks user-supplied data with a method that can build arbitrary objects. The finding flags the unsafe unpacking and points to a safe data-only format.

Weak crypto and trust checks

Weak password hashing

Passwords should be stored as slow, salted hashes so a stolen database is hard to crack. A weak or fast hash means stolen passwords fall quickly. The app uses a fast general-purpose hash instead of a purpose-built password hash. The finding flags it and points to a strong password-hashing approach.

Weak randomness

Tokens, session ids, and reset codes need to be unguessable. A weak random source makes them predictable, so an attacker can guess a valid one. The app uses a general-purpose random function, meant for things like shuffling, to generate security values. The finding flags it and points to a secure generator.

Unverified JWT and webhook signatures

A JWT is a signed token that proves who a user is. A webhook signature proves an incoming event really came from the service that claims to have sent it. If the signature is not verified, anyone can forge either one. The app decodes the token or accepts the webhook without checking the signature. The finding flags where a token or webhook is trusted without verifying its signature.

Personal data and payment data

These rules care about whose data it is, not only whether a hole exists. They are the ones that turn a code problem into a privacy or compliance problem.

Indian personal identifiers, under the DPDP Act

An Aadhaar number or a PAN card number written into source code is a direct personal identifier sitting in a file that gets cloned, forked and shared. Test fixtures are the usual route in: a real number is used once to make something work and never gets swapped for a fake one. An IFSC code, which identifies a specific bank branch, is flagged at a lower severity for the same reason.

Personal data written to your logs

Logging is where personal data quietly accumulates. An email address or a phone number passed into a log call ends up in log storage, in your log provider's systems, and in front of everyone with access to either, usually with no retention policy behind it. The finding points at the log call. The fix is to log an id instead of a person.

Card data, under PCI-DSS

A card number, a CVV, or a card value passed into a log call are all things PCI-DSS forbids you from storing, and a database column named after raw card data with no encryption is the version that survives longest. These are flagged as Critical because the standard is not advisory: holding them is a compliance failure whether or not anyone attacks you.

International identifiers

The same checks run for identifiers outside India: a US Social Security Number, a UK National Insurance number, a Brazilian CPF, an IBAN, and a SWIFT or BIC code hardcoded into a variable named after bank identifiers. If you have users outside India, this is the family that finds their data sitting in your repository.

Configuration and dependencies

Soft-delete data exposure

Soft delete marks a record as deleted without removing it. If queries do not exclude those records, deleted data keeps showing up. The app hides records with a deleted flag, but some queries forget to filter on it, so removed data leaks back into responses. The finding flags queries that do not exclude soft-deleted rows.

Debug mode on in production

Debug mode shows detailed error pages, stack traces, and internal settings. Helpful while building, dangerous in production, where it hands attackers a map of your app. The debug flag is left on when the app ships because it was on during development and nobody flipped it. The finding flags debug mode enabled in a production config and points to turning it off.

Over-broad cloud IAM

Cloud permissions (IAM) decide what each part of your system can do. Over-broad permissions grant far more than needed, so a single compromise reaches everything. Infrastructure config grants wildcard or admin-level permissions to get past a blocked request, and the broad grant stays. The finding flags overly permissive permissions and points to narrowing them to what is actually used.

Vulnerable dependencies

Your app is built on open-source packages, and some have known security holes with public fixes. Running an old, vulnerable version means shipping a hole someone already documented how to exploit. Builders pin whatever version was current, and versions age; a known-CVE flaw gets patched upstream, but your app still runs the old one. The finding flags the vulnerable package and the version with the known issue, and points to the fixed version.

Scan your app

Seeing which of these are in your app beats reading about them. Sign in with GitHub and scan a repo free, one a month, no card: start a scan.