vediqx

A real check of Harbor, our own demo app with problems left in on purpose. Run a3b27609-c6e0-47d6-b71f-21ea63c0457c, made on 5 November 2026. This is a saved copy, not a live check.

https://demo-app.example/
2 critical2 high

We found 4 findings

Your app hands every visitor a key with Stripe's live prefix. And 3 more things to look at.

  • 3 checks
  • 37 requests
  • 0 writes kept
  • 2 seconds
  • 5 November 2026 at 3:27 am UTC
  • 2 not checked

We couldn’t run 2 tests from outside. Add this app and connect your database to run them.

4 findings, worst first

What a stranger or your users can reach.

Add this app to get a fix for each finding and to track which ones you’ve fixed.

  1. CriticalYour app hands every visitor a key with Stripe's live prefix.It starts with the prefix Stripe gives a live secret key. We did not try it. It is in page-042961aeec436551.js, which a browser fetches in the open.page-042961aeec436551.jsWho: a stranger with no account

    Proof

    We asked for the JavaScript your app serves to anyone who opens it.

    The same download

    also handed us your app's own public key

    Your app's JavaScript, as a visitor downloads it

    carries a key 42 characters long

    Your app's JavaScript carries a key 42 characters long; the same download also handed us your app's own public key. Same minute.

    where we found it, and what was seen of it
    Where
    https://demo-app.example/_next/static/chunks/app/orders/page-042961aeec436551.js?dpl=dpl_DYtH2DLxLSQwMsRCSkSizy1NJF2P
    What was seen of it
    sk_liv…0000

    How to fix it

    • A fix drafted for this finding, ready to paste into Supabase
    • The same fix as a prompt for your AI builder
    • Verify fix, which tests the finding again and marks it verified when the fix holds

    Add this app to get a fix for each finding and to track which ones you’ve fixed.

  2. CriticalYour app hands every visitor a key that bypasses every rule you have set.Anyone who opens your site can read, change or delete any of your data with it. It is in page-042961aeec436551.js, which a browser fetches in the open.page-042961aeec436551.jsWho: a stranger with no account

    Proof

    We asked for the JavaScript your app serves to anyone who opens it.

    The same download

    also handed us your app's own public key

    Your app's JavaScript, as a visitor downloads it

    carries a key 228 characters long

    Your app's JavaScript carries a key 228 characters long; the same download also handed us your app's own public key. Same minute.

    where we found it, and what was seen of it
    Where
    https://demo-app.example/_next/static/chunks/app/orders/page-042961aeec436551.js?dpl=dpl_DYtH2DLxLSQwMsRCSkSizy1NJF2P
    What was seen of it
    eyJhbG…TElE
    What it claims to be
    role service_role, project demo-project

    How to fix it

    • A fix drafted for this finding, ready to paste into Supabase
    • The same fix as a prompt for your AI builder
    • Verify fix, which tests the finding again and marks it verified when the fix holds

    Add this app to get a fix for each finding and to track which ones you’ve fixed.

  3. HighAnyone can list the files in avatars.1 file was named to a request anyone can make, and a file's name usually says what is in it.avatarsWho: a stranger with no account

    Proof

    We asked for a listing of the files in avatars, over your app's public web address.

    The same listing, for a file store with nothing behind it

    answered, and we read what came back

    A stranger, with the key your app ships to every visitor

    listed 1 file

    A stranger listed 1 file; the same listing for a file store with nothing behind it answered, and we read what came back. Same minute, same key.

    the request and both answers
    What we asked
    POST https://demo-project.example/storage/v1/object/list/avatars
    Headers we sent
    apikey, authorization
    What came back
    200
    What we asked for the control
    POST https://demo-project.example/storage/v1/object/list/witness_control_52aebd28
    What the control got back
    200
    Fields a stranger could see
    name (string), version (null), id (null), updated_at (null), created_at (null), last_accessed_at (null), metadata (null), archived_at (null), is_delete_marker (null), is_versioned (null)

    Run both, and compare

    View · 11 lines
    # What a stranger asked, and got:
    curl -s -i -X POST \
      -H 'apikey: <your app's public key>' \
      -H 'authorization: Bearer <your app's public key>' \
      'https://demo-project.example/storage/v1/object/list/avatars'
    
    # And the same listing for a file store with nothing behind it, for comparison:
    curl -s -i -X POST \
      -H 'apikey: <your app's public key>' \
      -H 'authorization: Bearer <your app's public key>' \
      'https://demo-project.example/storage/v1/object/list/witness_control_52aebd28'

    How to fix it

    • A fix drafted for this finding, ready to paste into Supabase
    • The same fix as a prompt for your AI builder
    • Verify fix, which tests the finding again and marks it verified when the fix holds

    Add this app to get a fix for each finding and to track which ones you’ve fixed.

  4. HighAnyone can sign up as anyone.An email address is never proven, so someone can register with a customer's address and be treated as that customer.email sign-upWho: a stranger with no account

    Proof

    We asked for your app's own sign-up settings, which anyone may read.

    The same read of the project's own sign-in settings

    answered, and we read it

    Your app's own sign-up settings

    say mailer_autoconfirm is true

    Your sign-up settings say mailer_autoconfirm is true; the same read of your own sign-in settings answered, and we read it. Same minute, same key.

    the setting we read, and where
    Where
    https://demo-project.example/auth/v1/settings
    Setting
    mailer_autoconfirm = true

    How to fix it

    • A fix drafted for this finding, ready to paste into Supabase
    • The same fix as a prompt for your AI builder
    • Verify fix, which tests the finding again and marks it verified when the fix holds

    Add this app to get a fix for each finding and to track which ones you’ve fixed.

How the check went

The four steps of every check, and what each one found.

  1. Reading your app’s code, done

    The code your app ships: Found something.

  2. Testing as a stranger, done

    Your data API, from outside: Found something. Signing up and signing in: Found something.

  3. Testing as your users, not checked

    Not checked. Add this app and connect your database to run it.

  4. Writing your report, done

    Done in 2 seconds. Nothing in your app changed.

Show everything we checked, line by line
AreaStatusResult
The code your app shipsFound something16 of 16 script files read, 1 check found something to look at, below.
Your data API, from outsideFound something1 check found something to look at, below.
Signing up and signing inFound something1 check found something to look at, below.
Your app's pages
8 pages loaded: /, /orders, /signin, /why, /profile, /settings, /checkout, /signup
The code it ships
16 of 16 script files the pages named
Tables
4 tables: orders, invoices, profiles, notes
File stores
1 file store: avatars
Sign-up settings
read, as anyone may
What we sent
37 requests, at most 4 requests at a time, and nothing that changes anything
Your app's code, and the files it ships
something to look at, below
What a stranger could reach
something to look at, below
Your sign-up and email settings
something to look at, below

Not checked

What this check couldn’t test, and what would let it.

  • Needs a deeper check

    This needs a deeper check than this run had.

    Connect the level it names; the card on the Connect page says exactly what that takes.

    Show why

    Whether the 1 table that answered a stranger with no rows is empty or hidden by a rule needs the Inside check. Two statements on screen unlock it, each adding one limited database login.

Check your own app