What we found
CriticalJavaScript file /assets/app.js
A database password is in the public page
The page includes a database address with a username and password.
A database link with a password was visible. The password was not saved.
That password can open the database directly, past any app-level rules.
A database connection string with a password is in public client code. Remove it. The browser must never talk to the database with a password. Rotate the database password. Use a server-only environment variable for the connection string.
CriticalJavaScript file /assets/app.js
A GitHub token is in the public page
This token can read or change repositories, depending on what it was allowed to do.
The token starts like ghp_…Ab9Q. The full token was not saved.
A visitor can copy it and use your GitHub access.
A GitHub personal access token is in public client code. Remove it. Revoke the token on GitHub now. If the app needs GitHub, call the GitHub API from a server using a token stored as an environment variable.
CriticalJavaScript file /assets/app.js
A private key is in the public page
A private key in the browser is like publishing the password to a server or certificate.
A private key header was visible. The key body was not saved.
Anyone can download it and use it.
A PEM private key is present in a public file. Remove the key from the repository and from client code. Treat it as stolen: generate a new key pair and revoke the old one. Private keys belong only on the server or in a secrets manager.
CriticalJavaScript file /assets/app.js
A SendGrid key is in the public page
This key can send email as your domain.
The key starts like SG.A…m2Np. The full key was not saved.
Spammers look for these keys and burn the domain's reputation.
A SendGrid API key is in public client code. Remove it, delete the key in SendGrid, and send mail only from the server.
CriticalJavaScript file /assets/app.js
A Slack token is in the public page
This token can post messages or read workspace data, depending on its type.
The token starts like xoxb…2Np4. The full token was not saved.
Anyone who views the page source can take it.
A Slack token (it starts with xox) is in public client code. Remove it and revoke it in the Slack app settings. Call Slack only from a server that reads the token from an environment variable.
CriticalJavaScript file /assets/app.js
A Stripe secret key is in the public page
Stripe secret keys and restricted keys can move money and read customer data. The publishable key (pk_) is the only Stripe key that belongs in the browser.
The key starts like sk_l…w8Zk. The full key was not saved.
Someone who copies this key can act on your Stripe account.
A public JavaScript file contains a Stripe secret or restricted key (sk_ or rk_). Remove it from the client. The browser may only use a publishable key (pk_). Move charges, refunds, and customer lookups to a server route that reads the secret from an environment variable. Roll the exposed key in the Stripe dashboard immediately.
CriticalJavaScript file /assets/app.js
A Supabase service role key is in the public page
The service role key skips every Supabase lock, including Row Level Security. It must never be in the browser. We did not use this key.
The token's role is service_role. The token itself was not saved.
Anyone who loads the page can copy this key and read or change every row in the database.
The frontend bundle contains a Supabase service_role key. This key bypasses Row Level Security. Remove it from every client file, including env variables that start with NEXT_PUBLIC_, VITE_, or PUBLIC_. Keep the service role key only in a server environment variable. In the Supabase dashboard, rotate the service role key now, because this copy is public. The anon key may stay in the browser. Add Row Level Security policies so the anon key cannot read other people's rows.
CriticalJavaScript file /assets/app.js
An Amazon Web Services access key is in the public page
This key ID can be paired with a secret to control cloud resources. It should never ship in JavaScript.
The key id starts like AKIA…EF01. The full id was not saved.
Attackers hunt for these IDs and try the nearby secret. Cloud bills can grow fast.
A public file contains an AWS access key ID (it starts with AKIA or ASIA). Remove it and any nearby secret access key from client code. Use a server role or a short-lived credential instead. Deactivate the exposed key in the AWS IAM console and check CloudTrail for unexpected use.
CriticalJavaScript file /assets/app.js
An Anthropic secret key is in the public page
This key can use your Claude account. It does not belong in the browser.
The key starts like sk-a…0Hd3. The full key was not saved.
A visitor can copy it and make requests billed to you.
A public JavaScript file contains an Anthropic secret key (it starts with sk-ant-). Remove it from all client code. Call Claude only from a server route that reads the key from an environment variable. Rotate the key in the Anthropic console now, because this copy is public.
CriticalJavaScript file /assets/app.js
An OpenAI secret key is in the public page
This key can spend money on your OpenAI account and read data the key is allowed to see. It should live only on a server.
The key starts like sk-p…Jc5V. The full key was not saved.
Anyone who loads your site can copy this key from the browser and use it as you.
A public JavaScript file contains an OpenAI secret key (it starts with sk-). Remove every secret OpenAI key from client-side code. Move the call to a server-only route that reads OPENAI_API_KEY from an environment variable. Then rotate the key in the OpenAI dashboard, because the old one is public. Do not print the key in logs or error messages.
Your .env file is public
The site serves /.env to anyone. That file usually holds secret keys and database passwords.
Variable names visible: OPENAI_API_KEY, DATABASE_URL. Values were not saved.
A visitor does not need to hack anything. They can open the file.
The web server is serving the /.env file to the public. Stop publishing dotfiles and secret files. On Vercel and similar hosts, environment variables belong in the project settings, not in a file that is deployed as a public asset. Remove .env from the published folder, rotate every secret that was in it, and confirm that opening https://YOURDOMAIN/.env returns a not-found page.
Your Git folder is public
The site serves /.git/config. That means the source history may be downloadable.
The Git config file is readable. Its contents were not saved.
Git history often still contains old passwords even after they were deleted from the latest code.
The site is serving /.git/config. Stop the host from publishing the .git directory. If this is a static export, do not copy the .git folder into the public directory. Assume the repository history is public and rotate any secret that was ever committed.
Your Git HEAD file is public
The site serves /.git/HEAD, which is a sign the whole .git folder may be public.
The Git HEAD file is readable. Its contents were not saved.
From that folder, someone can often rebuild your source code.
https://YOURDOMAIN/.git/HEAD is publicly readable. Block the entire /.git path on the host and rotate secrets that lived in the repository.
HighJavaScript file /assets/app.js
A Resend key is in the public page
This key can send email through your Resend account.
The key starts like re_A…0Hd3. The full key was not saved.
A public email key gets abused quickly.
A Resend API key (it starts with re_) is in public client code. Remove it, rotate it in Resend, and send email only from server code.
Highvulnerableprojectref1.supabase.co / profiles
The “profiles” table answers visitors who are not signed in
Supabase returned a row from “profiles” when we used only the public anon key. We asked for one row and did not save it. Row Level Security is missing, or a policy lets everyone read this table.
The table name is profiles. No row contents were saved.
If a stranger can read one row, they can usually read the rest: emails, notes, files, or whatever the table holds.
In Supabase, the public table "profiles" returns rows to the anon key with no signed-in user. Enable Row Level Security on "profiles" and on every other table in the public schema. Add policies that only allow a user to read and write their own rows (auth.uid() = user_id), or a tighter rule that fits this table. Do not use the service_role key in the browser to “make it work.” After the change, confirm that opening the REST URL with the anon key returns an empty list or an error, not real rows. Existing copied data should be treated as public.
Highvulnerable-demo.firebaseio.com
The Firebase database answers visitors
We requested one shallow look at the Realtime Database and it returned data without a login. We did not save the data. The security rules are probably open.
The database responded to a signed-out request. No records were saved.
Open Firebase rules are a common way a whole customer list ends up public.
The Firebase Realtime Database at this project returns data to signed-out visitors. In the Firebase console, set the Realtime Database rules so reads and writes require auth, for example: { "rules": { ".read": "auth != null", ".write": "auth != null" } }. Tighten that further so users can only touch their own path. Do not leave ".read": true. Deploy the rules and confirm that opening the database URL with .json is denied.
Highvulnerable-demo / customers
The Firestore collection “customers” is readable without a login
Firestore listed a document to a request that only used the public web API key. We did not save the document. The security rules are probably open.
The collection name is customers. Document contents were not saved.
If one collection is open, others often are too.
Firestore in project "vulnerable-demo" returned a document from "customers" without a signed-in user. Update firestore.rules so that collection is not world-readable. A safe starting point is to require request.auth != null and to match the user id on the document. Deploy the rules from the Firebase console. Do not work around this by hiding the API key; that key is supposed to be public, and the rules are the lock.
Your .npmrc file is public
The site serves /.npmrc. That file sometimes contains a package-registry token.
An npm auth token setting is visible. The token value was not saved.
A registry token can publish packages or download private ones.
https://YOURDOMAIN/.npmrc is publicly readable. Remove it from the deploy. If it contains an _authToken, revoke that token in npm (or the registry you use).
Your original source code is public
A source map is available next to the JavaScript. It can include the original file names and, often, the original code.
Original file names include: src/lib/supabase.ts, src/pages/Admin.tsx. The source was not saved.
Source maps make it much easier to find leftover keys, hidden routes, and comments you did not mean to publish.
The production JavaScript references a public source map, and that map includes original source. Turn off production source maps in the build (for Vite, build.sourcemap false; for Next.js, productionBrowserSourceMaps false). Delete the existing .map files from the host. Do not rely on source maps to hide secrets; remove the secrets too.
Lowthe main page response
A few browser safety headers are missing
The public page does not send: Content-Security-Policy, X-Content-Type-Options, X-Frame-Options or frame-ancestors, Referrer-Policy. These headers tell browsers how to treat your site. They are not the same as a leaked key, but they are easy to add.
Missing: Content-Security-Policy, X-Content-Type-Options, X-Frame-Options or frame-ancestors, Referrer-Policy.
Without them, browsers are more willing to load your site inside a trick frame or to sniff a file as a script.
Add these HTTP response headers on the public site: Content-Security-Policy, Strict-Transport-Security, X-Content-Type-Options: nosniff, X-Frame-Options: DENY (or Content-Security-Policy frame-ancestors 'none'), and Referrer-Policy: strict-origin-when-cross-origin. On Vercel this can go in vercel.json headers or in next.config. Do not weaken the app to make the headers pass; start with a simple policy and tighten it.
LowJavaScript file /assets/app.js
A Google API key is in the page
Firebase and Maps apps often include a Google key in the browser. Lock it to your domain in Google Cloud so other sites cannot spend it.
The key starts like AIza…gAb9.
An unlocked key can be copied and used from somewhere else, which can create a surprise bill.
A Google API key is in the public page. In Google Cloud, restrict that key to your website's domain and to only the APIs you use. If this key is a server key, move it out of client code and rotate it.
NoteJavaScript file /assets/app.js
Your Supabase public key is in the page. That part is normal.
Supabase apps include a public key, called the anon key, in the browser. By itself that is not a leak. The danger is a service role key, or tables that return rows to people who are not signed in.
The token's role is anon. That is the public Supabase key.
You do not need to hide the anon key. You do need locks on the tables it can reach.
The Supabase anon key in the browser is expected. Do not replace it with the service_role key. Turn on Row Level Security for every public table and add policies so visitors can only read the rows they should see.
What this check included
- Public page
- Browser safety headers
- 1 JavaScript file
- Common private files (.env, .git, .npmrc)
- Supabase tables using the public anon key
- Firebase open-rule signals
- This check looks only at what a visitor’s browser can already see. It does not try passwords, guess hidden pages, or use any secret it finds.