How to scan your website for security holes with an AI agent
One line in the chat, an ownership check, a graded report with the worst finding first, and a weekly monitor that only speaks up when something new appears. One scan, start to finish.
Fasrad · September 17, 2026 · 6 min read
A Fasrad agent scans a website you own by reading the same public JavaScript your visitors download, recovering the backend keys and table names the app ships, and then, once you have proved you control the domain, testing whether that backend hands data to anyone holding the public key. The whole thing starts from one line in the chat and ends in a graded report with the fix for the worst finding first. Theo, a founder in Tampa who built a customer portal over a weekend with AI coding tools, ran his on a Tuesday night.
Minute one: the public bundle
Theo's portal runs on Supabase. The AI tools that wrote it were fast, and he shipped, but he kept picturing a customer opening the browser's network tab and reading the whole database with the key sitting in the page. He turned on the Security skill and typed "Security-scan https://portal.example.com".
The activity line under his message reads "Scanning https://portal.example.com for security issues" next to a small shield, and it stays there for about half a minute. In that time the agent fetches the page, follows the script tags to the bundles, and reads them the way a browser would. From that text it recovers the Supabase project reference, the key the app ships and which role that key carries (anon, or the far worse service_role), and every table name the code calls. Theo's bundle referenced three: customers, invoices and portal_settings.
It also checks the things a weekend build forgets. Is /.env served? Is /.git/config reachable? Do the bundles publish their source maps? Which security headers are missing, and is the TLS certificate valid and not about to expire? All of that is passive reading. None of it touches his database.
The passive pass: keys and table names from the bundle, a provisional grade, and the live check held back until ownership is proven.
The first reply is short. A provisional grade, the counts, the backend the app ships, and one line saying the live backend check was skipped because ownership is not verified. The agent will not test a database for someone who cannot show they own the site, so the next step is his.
Proving you own the domain
Theo asked "How do I verify ownership?" and got two options and two buttons. Option one is a DNS TXT record: host _fasrad-verify.portal.example.com, type TXT, value a token that starts with fasrad-verify-. Option two is a plain file at https://portal.example.com/.well-known/fasrad-verify.txt whose entire body is that same token. He picked the file, since his host makes static files easy, and pressed File is in place.
Two ways to prove control of the domain. Either button sends the agent to check and, if it passes, run the deeper scan.
The agent fetched the file, found the token, and ran the deeper scan on the spot. When a check fails instead, the reply shows the TXT values it can actually see at that name, the HTTP status of the file, and the usual fixes: a DNS provider that appends the domain twice, a single-page app whose catch-all route serves index.html instead of the file, a CDN cache that needs purging. Theo's passed on the first try. Ownership goes on record for that domain, so the weekly monitor later in this post gets the full check without asking again.
The report
The live check is one read request per table the bundle referenced, made with the key the app ships and nothing else. A table with row-level security in place returns an empty list. A table without it returns a row, and the agent records the table name, the row count and the column names, never the contents. The reply is two sentences and a chip labeled Full security report. Opening it shows the graded page.
Theo's grade was F. customers was readable with 1,284 rows and columns including email, phone and billing address, and invoices was readable too. Both count as critical because the table names signal personal data. portal_settings was readable as well but rated medium, since nothing in its name suggests people. Under those came the medium and low items: no HSTS header, no content security policy, no clickjacking protection, an exposed source map, and a server header that advertises its version. The grade follows a fixed rule. Any critical finding is an F, any high is a D, more than two mediums is a C, and a site with only a few mediums or lows is a B.
The report chip opened: grade, counts, and every finding numbered in severity order.
Every finding in the report has a location, an explanation of why it matters, a fix, and a link to the vendor's own documentation. Build-time dependency noise is excluded from the grade, so the letter reflects what is actually deployed. An AI security agent that reads your site the way a stranger would finds what a stranger would find, and nothing it does requires access you did not grant.
Walk me through the fixes
Theo typed "Walk me through the fixes" and got one finding, not nine. The agent takes the most severe item from the index and answers only that, in about a hundred words: the table, the location in his project, the exposed columns, and the fix. For customers that means enabling row-level security on the table and adding a policy so a signed-in user reads their own rows and nobody else's, then re-running the scan to confirm. It does not pile the header fixes on top until he asks for the next one.
He worked down the list in an evening. "Next one" moved to invoices, then to portal_settings, then to the headers, where the agent gives one implementation path for the stack he named instead of five. "Re-scan it" after the changes produced a B, with the two remaining lows. A developer agent in the same chat drafted the policies for the third table from the column names, which saved him the documentation trip.
Every week from now on
"Check it every week" creates a recurring monitor. The agent confirms it in one line and says it will message him if new issues appear. Because the domain is verified, the weekly run includes the live database check, not only the passive read. Each run is compared with the last: a finding that was open and is now absent is marked fixed, a finding that comes back is marked reopened, and a finding never seen before is new.
The alert goes out only when something is new or reopened. A quiet week produces nothing. Three weeks later, Theo shipped a support_tickets table on a Friday afternoon and forgot the policy. The Monday scan, timed to land before his morning briefing, sent one Telegram message with the grade, a line reading one new and six fixed since the last scan, and the single new finding with its location. For a founder who is the whole engineering department, a virtual assistant for entrepreneurs that also watches the database is one fewer thing to remember, and an incident responder built on the same agent picks up from that message.
The weekly monitor speaks up once, about the one thing that changed.
The live check only ever runs against domains their owner has verified, and the passive read is the same public bundle any visitor downloads. What Theo got was an answer to the feeling that started this: yes, the database was readable, here is the table, here is the fix, and here is the message you get if it ever happens again.
Frequently asked questions
Does the scan touch my database before I verify ownership?
No. The first pass reads the public page and its JavaScript bundles, the same files any visitor downloads, plus a few well-known paths and the response headers. The live backend check runs only after the DNS TXT record or the verification file is confirmed, and the reply says explicitly when it was skipped.
What does the live check actually do?
One read request per table the bundle references, using the key your app already ships. An empty list means row-level security is enforced. A row coming back means the table is readable, and the agent stores the table name, the row count and the column names, never the row contents. Firebase Realtime Database and Firestore get the equivalent open-rules check.
Which exposures does it look for in the bundle?
The Supabase key and its role (the anon key is public by design; a service_role key in the browser is critical), the Supabase URL and project reference, and Firebase configuration. On the server it checks for a served .env file, .git metadata, an AWS credentials file, config.json and JavaScript source maps, along with missing security headers and TLS certificate problems.
How is the grade decided?
By a fixed rule. Any critical finding is an F, any high is a D, more than two mediums is a C, a site with a few mediums or lows is a B, and a site with none is an A. Build-time dependency vulnerabilities that do not ship in the deployed bundle are left out of the grade.
Do I have to own the site?
Yes, that is what the scanner is for. The live backend check is refused outright without ownership verification, and the report, the fix walkthrough and the monitor are built for the person who can act on them.
Can it scan my source code as well?
Yes. Give it a public GitHub, GitLab or Bitbucket URL and it runs a static scan for vulnerable dependencies, leaked secrets and risky patterns. For a private repository the agent opens a secure field for a read-only token, which is encrypted on submit and never passes through the chat.
When does the weekly monitor send an alert?
Only when a finding is new or has reopened since the previous run. The message names the grade, the count of new, reopened and fixed items, and each new finding with its location. Monitors can run daily or weekly, and a verified domain gets the live backend check on every run.