Counted from our database · refreshed every 5 minutes
Audit AI in numbers
What the open-source scanner has actually seen on auditai.sh, counted by one SQL query and shown unedited. Our own test repositories are left out. Small numbers stay small here.
As of 27 Oct 2026, 17:00 UTC · counting since 11 Sep 2026
What the scanner has looked at
What the rules found
By rule
| Rule | Findings | Share | Real / false positive / unsure | Precision from votes |
|---|---|---|---|---|
supabase.storage-policy-without-owner-checkHas a storage policy that checks only the bucket, so every user it applies to reaches every file in it. | 3 | 30% | no votes | no decided votes yet |
supabase.table-without-rlsQueries a table that has row level security turned off from a user-facing client. | 3 | 30% | no votes | no decided votes yet |
supabase.rls-policy-without-caller-predicateHas an RLS policy that lets rows through without checking the caller. | 2 | 20% | no votes | no decided votes yet |
supabase.mass-assignment-from-request-bodyWrites the request body into a table without an allow-list of columns. | 1 | 10% | no votes | no decided votes yet |
supabase.security-definer-function-without-caller-checkHas a SECURITY DEFINER function, callable through the API, that never checks who is calling. | 1 | 10% | no votes | no decided votes yet |
Share is the rule's part of all open findings. Precision is real ÷ (real + false positive), from votes on report pages; unsure votes are listed but never counted. n is the number of real and false-positive votes behind a figure. Votes on earlier scans of a repository count too, so a rule can have votes and no open findings.
Sandbox proofs
A finding is proven only when we reproduce it. "Prove it" on a report runs the app in an isolated sandbox with two synthetic tenants and tries the attack before and after the fix.
Nobody has requested a proof yet. Until someone does, every finding above stays "likely".
Are the findings real?
Anyone reading a report can mark each finding as real, false positive or unsure. This is the public, self-reported view of precision; it is not checked by us, and people who vote are not a random sample.
No votes yet. The first ones will show up here, next to the rule they are about.
Precision on code we had never seen
On 23 Sep 2026 we scanned 30 public Next.js + Supabase repositories the engine had never run on, none of them in an earlier sample. They were picked mechanically from GitHub code search. The selection rules, how many findings would be labeled and what counts as a real finding were all written down before the scan, so the result could not be tuned after the fact. The scan produced 4,428 findings; the code behind a random 100 of them was then read and each one labeled real, a false positive or unsure, with a sentence of reason that cites the line. A second labeller, who did not see those labels, labeled 30 of them independently and gave the same label to 27 (90%); the figures below are the first labeller's.
Every blind sample so far
| Sample | Repositories | Findings read | Precision | Blocking tier |
|---|---|---|---|---|
#5 · 23 Sep 2026 · scanner 2b735be | 30 | 100 of 4,428 | 22% n = 100 | 18% n = 17 |
#4 · 19 Sep 2026 · scanner 093aafc | 30 | 100 of 2,386 | 30% n = 96 | 32% n = 85 |
#3 · 14 Sep 2026 · scanner 414ae5e | 20 | 100 of 635 | 31% n = 99 | 32% n = 92 |
#2 · 13 Sep 2026 · scanner d25b393 | 20 | 100 of 839 | 46% n = 95 | 49% n = 87 |
#1 · 13 Sep 2026 · scanner 2ef4af1 | 20 | 100 of 414 | 36% n = 100 | 35% n = 92 |
Samples 1 to 3 took at most 20 findings from one repository out of 20, and three repositories ended up supplying 60 of the 100 findings of the third. From sample 4 on it is at most 10 from one repository out of 30, so no single project moves the figure by more than a tenth. That change was written into the protocol before the fourth sample was picked. Sample 4 came out the same as sample 3 (30% against 31%, p = 0.87): the fixes made after the third sample did not raise precision on code the engine had never seen. Sample 5 came out lower, 22% against 30%, but within noise (p = 0.19): the engine changes made since then, including the split into headline findings and leads, did not raise precision on new code either. Because leads never block, only 17 of its 100 findings are in the blocking tier, so that column says little for sample 5.
By rule, sample #5
| Rule | Findings | Real / false positive | Precision |
|---|---|---|---|
supabase.service-role-object-access-without-tenant-scopeReads or writes a row by an id from the request without checking whose row it is (IDOR). | 46 | 11 / 35 | 24% n = 46 |
supabase.security-definer-function-without-caller-checkHas a SECURITY DEFINER function, callable through the API, that never checks who is calling. | 17 | 3 / 14 | 18% n = 17 |
supabase.service-role-query-without-authenticationQueries with the service role in a handler that never checks who is calling. | 16 | 5 / 11 | 31% n = 16 |
supabase.user-controlled-tenant-scopeTakes the tenant or team id from the request instead of the signed-in session. | 13 | 1 / 12 | 8% n = 13 |
supabase.storage-object-access-without-owner-scopeReads or writes a stored file on a path from the request with the service role, without checking whose file it is. | 3 | 0 / 3 | 0% n = 3 |
supabase.storage-policy-without-owner-checkHas a storage policy that checks only the bucket, so every user it applies to reaches every file in it. | 1 | 1 / 0 | 100% n = 1 |
supabase.server-trusts-unverified-session | 1 | 1 / 0 | 100% n = 1 |
supabase.role-check-from-user-metadataDecides roles from user_metadata, which users can edit themselves. | 1 | 0 / 1 | 0% n = 1 |
supabase.rls-policy-without-caller-predicateHas an RLS policy that lets rows through without checking the caller. | 1 | 0 / 1 | 0% n = 1 |
supabase.mass-assignment-from-request-bodyWrites the request body into a table without an allow-list of columns. | 1 | 0 / 1 | 0% n = 1 |
Between the samples: the 64 false positives of the first one named gaps in the engine (credential checks other than Supabase auth, guards in a layout, a schema parse as an allow-list, a row picked by the caller's own identity, a write that Row Level Security refuses anyway). Two rounds of fixes removed 28 of those findings and lost none of the 36 real ones, which put precision at 50% on the same labels (n = 72). That number was not blind, because the fixes were made with those labels in view, so the fixed engine was run on new repositories instead, and every later round of fixes was measured the same way: those are the later samples above.
What these figures are not: they do not say we find everything. Measuring that would take a full manual audit of each repository. They also say nothing about findings we prove: a sandbox reproduction is real by construction, which is the whole point of the proof step. The rules with the lowest precision are where the noise lives, and they are what we are fixing next.
The older sample the engine was tuned against
The blind measurements above are the honest ones. This older count is kept because the engine was tuned against it, which is exactly what makes it worth showing next to a blind figure: on 13 Sep 2026 we scanned 26 public Next.js + Supabase repositories, then the code behind every one of the 183 findings was read and labeled real, false positive or unsure. As in the blind samples above, the reading was done by a model working from the code, not by a person. The figures below cover the 131 of them that scanner build ebf88ea still reports; the rest were false positives it no longer raises, and no finding labeled real was lost. Those fixes were made while looking at these labels, so this is not a blind test.
By rule
| Rule | Findings | Real / false positive / unsure | Precision |
|---|---|---|---|
supabase.service-role-query-without-authenticationQueries with the service role in a handler that never checks who is calling. | 57 | 9 / 37 / 11 | 20% n = 46 |
supabase.service-role-object-access-without-tenant-scopeReads or writes a row by an id from the request without checking whose row it is (IDOR). | 38 | 12 / 25 / 1 | 32% n = 37 |
supabase.security-definer-function-without-caller-checkHas a SECURITY DEFINER function, callable through the API, that never checks who is calling. | 17 | 9 / 8 / 0 | 53% n = 17 |
supabase.storage-policy-without-owner-checkHas a storage policy that checks only the bucket, so every user it applies to reaches every file in it. | 7 | 2 / 4 / 1 | 33% n = 6 |
supabase.mass-assignment-from-request-bodyWrites the request body into a table without an allow-list of columns. | 4 | 2 / 1 / 1 | 67% n = 3 |
supabase.user-controlled-tenant-scopeTakes the tenant or team id from the request instead of the signed-in session. | 4 | 0 / 0 / 4 | nothing decided |
supabase.table-without-rlsQueries a table that has row level security turned off from a user-facing client. | 3 | 3 / 0 / 0 | 100% n = 3 |
supabase.rls-policy-without-caller-predicateHas an RLS policy that lets rows through without checking the caller. | 1 | 0 / 1 / 0 | 0% n = 1 |
Repositories were chosen by GitHub search for Next.js + Supabase, not at random, and labeled by reading code, not by running attacks; a label can be wrong. A false positive here means a control exists that the rule missed, or the data is public by design. Rules with no row matched nothing in this scan. Method and label definitions: real-world labeled sample.
Methodology
What is counted
- Scans stored by auditai.sh: public GitHub repositories pasted into the site, and GitHub App checks on pull requests of public repositories. Every figure comes from one read-only SQL query over scans, proof requests and votes.
- Entry points, queries and findings use the latest scan of each repository, so re-scanning does not inflate them. "Scans run" counts every scan.
- A finding is a match of one of the open-source rules. Nothing on this page comes from a language model.
- Proofs come from the "Prove it" queue; votes from the buttons on report pages.
What is left out
- Our own repositories: everything under github.com/audit0, including the intentionally vulnerable test apps we scan on every change.
- Findings a repository suppressed with an
auditai:ignorecomment. - Private repositories: never stored, never counted. Scans in the CLI or in Claude Code and Cursor through MCP run on your machine; we never see them.
- Anything that identifies a repository: no names, links or code on this page.
The rules, and a vulnerable and a secure test app for each, are open source: what each rule catches · rule source. Numbers are regenerated at most every five minutes.
Add your repository to these numbers
Public repositories, read-only, no sign-up.
Scan a public repository