FREE1 free audit every month. No card, every finding included.

SAMPLE REPORT

See a real report
before you sign up.

This small shop app has the mistakes AI tools often make. Below is the full report Aster gives for it, word for word, so you know exactly what you’d get.

  • Real output from Aster’s checks
  • Fictional app, fake keys
  • Nothing left out
aster*example/little-shop · mainFICTIONAL APP · REAL OUTPUT

AUDIT RESULT

13 findings to review.

4 are critical severity. Start there.

  • 18files read
  • 40checks run
  • 0lines of code run
  • CRITICAL4
  • HIGH6
  • MEDIUM2
  • LOW1

CRITICAL4 findings

CRITICALSupabase service_role key committed.env · line 2 · Pattern match
.envline 2
1NEXT_PUBLIC_SUPABASE_URL=https://demo-project.supabase.co2SUPABASE_SERVICE_ROLE_KEY=eyJhbG••••••••3STRIPE_SECRET_KEY=sk_liv••••••••4 

What Aster noticed

A value in the format of a supabase service_role key is committed here. The value is redacted; Aster does not test keys against the provider.

Your next step

If this is a real credential, rotate it in the provider dashboard first, then move the new value to a server-only environment variable and remove the old one from the code and the repository history. Turn on GitHub secret scanning with push protection so the next one is blocked.

CRITICALSecret read from a browser-public environment variablelib/ai.ts · line 4 · Pattern match
lib/ai.tsline 4
2 3export const openai = new OpenAI({4  apiKey: process.env.NEXT_PUBLIC_OPENAI_API_KEY,5  dangerouslyAllowBrowser: true6});

What Aster noticed

NEXT_PUBLIC_OPENAI_API_KEY is read here. Variables with this prefix are copied into the browser bundle at build time, so its value is visible to every visitor even though it is set in your hosting dashboard.

Your next step

Move the call that needs this key to server code, rename the variable without the public prefix, and rotate the key because past builds already exposed it.

CRITICALTable public.profiles has Row Level Security turned offsupabase/migrations/0001_init.sql · line 1 · Pattern match
supabase/migrations/0001_init.sqlline 1
1create table public.profiles (2  id uuid primary key references auth.users,3  email text,

What Aster noticed

public.profiles is created here and no migration turns on Row Level Security for it. Tables in public are reachable through Supabase's public API, so anyone with your project URL and public key could read or change its rows.

Your next step

Add a new migration that runs "alter table … enable row level security;" and one policy per action limited to the row owner, for example using ((select auth.uid()) = user_id), with "with check" on inserts and updates. Then test with only the public key: you should get [] back.

CRITICALPolicy on orders lets everyone read every rowsupabase/migrations/0001_init.sql · line 13 · Pattern match
supabase/migrations/0001_init.sqlline 13
11);12alter table public.orders enable row level security;13create policy "orders are readable" on public.orders for select using (true);14 

What Aster noticed

The SELECT policy "orders are readable" on public.orders uses true as its condition for public. Row Level Security is on, but this policy lets anyone with your public key read all rows.

Your next step

Replace true with an ownership condition such as ((select auth.uid()) = user_id), and add "with check" with the same condition for inserts and updates. Keep a true SELECT policy only for tables whose every row is meant to be public.

HIGH6 findings

HIGHPotential secret committed in an environment file.env · line 2 · Needs review
.envline 2
1NEXT_PUBLIC_SUPABASE_URL=https://demo-project.supabase.co2SUPABASE_SERVICE_ROLE_KEY=eyJhbG••••••••3STRIPE_SECRET_KEY=sk_liv••••••••4 

What Aster noticed

A sensitive-looking environment assignment has a non-placeholder value. The value is redacted; whether it is active is unknown.

Your next step

Verify whether this value is confidential. Rotate real credentials and store them outside the repository. Keep only empty or clearly fake values in example files.

HIGHPotential secret committed in an environment file.env · line 3 · Needs review
.envline 3
1NEXT_PUBLIC_SUPABASE_URL=https://demo-project.supabase.co2SUPABASE_SERVICE_ROLE_KEY=eyJhbG••••••••3STRIPE_SECRET_KEY=sk_liv••••••••4 

What Aster noticed

A sensitive-looking environment assignment has a non-placeholder value. The value is redacted; whether it is active is unknown.

Your next step

Verify whether this value is confidential. Rotate real credentials and store them outside the repository. Keep only empty or clearly fake values in example files.

HIGHDynamic text passed to an unsafe query methodapp/api/search/route.ts · line 5 · Needs review
app/api/search/route.tsline 5
3export async function GET(req: Request) {4  const q = new URL(req.url).searchParams.get("q") ?? "";5  const rows = await db.$queryRawUnsafe(`SELECT * FROM products WHERE name LIKE '%${q}%'`);6  return Response.json(rows);7}

What Aster noticed

An unsafe raw-query method receives a dynamic first argument. This is a review candidate, not a reproduced SQL injection.

Your next step

Use the database client’s parameterized query API. Keep query structure fixed and pass user values as bound parameters.

HIGHNext.js 15.1.0 is open to React2Shell remote code executionpackage.json · line 9 · Needs review
package.jsonline 9
7  },8  "dependencies": {9    "next": "15.1.0",10    "react": "19.0.0",11    "react-dom": "19.0.0",

What Aster noticed

This project uses the App Router with next 15.1.0, which is affected by CVE-2025-66478 / CVE-2025-55182 (GHSA-9qr9-h5gf-34mp). One crafted request can run code on your server; it has been exploited in the wild since December 2025.

Your next step

Upgrade next to the latest patched release in your major (at least 15.0.5 / 15.1.9 / 15.2.6 / 15.3.6 / 15.4.8 / 15.5.7 / 16.0.7, ideally the newest 16.x), redeploy, and rotate secrets the server could read if it was exposed while unpatched.

HIGHNext.js 15.1.0 lets requests skip the login check in middleware.tspackage.json · line 9 · Needs review
package.jsonline 9
7  },8  "dependencies": {9    "next": "15.1.0",10    "react": "19.0.0",11    "react-dom": "19.0.0",

What Aster noticed

middleware.ts makes an authentication decision, and next 15.1.0 is affected by CVE-2025-29927 (GHSA-f82v-jwr5-mffw): a request with the x-middleware-subrequest header skips middleware entirely. Aster could not tell where this app is hosted.

Your next step

Upgrade next to 12.3.5 / 13.5.9 / 14.2.25 / 15.2.3 or later (ideally the latest release), and also check the session inside each route handler and Server Action, not only in middleware.

HIGHStorage bucket "invoices" is publicsupabase/migrations/0002_storage.sql · line 1 · Needs review
supabase/migrations/0002_storage.sqlline 1
1insert into storage.buckets (id, name, public)2values ('invoices', 'invoices', true);3 

What Aster noticed

The bucket "invoices" is created with public = true. Files in a public bucket can be downloaded by anyone who has or guesses the URL, with no login and no policy check. The name suggests private files.

Your next step

Make the bucket private (public = false), add storage.objects policies that limit each user to their own folder, for example (storage.foldername(name))[1] = (select auth.uid())::text, and hand out short-lived signed URLs instead of public links.

MEDIUM2 findings

MEDIUM.gitignore does not exclude .env files.gitignore · line 1 · Pattern match
.gitignoreline 1
1node_modules2.next3 

What Aster noticed

This project reads environment variables, but no pattern in the root .gitignore matches ".env". Templates such as Vite's only ignore "*.local", so a plain .env is committed by the next "git add .".

Your next step

Add ".env" and ".env*.local" (or ".env*" followed by "!.env.example") to the root .gitignore. If a .env was ever committed, rotate its keys first.

MEDIUMDynamic HTML rendered by Reactapp/product/[id]/page.tsx · line 5 · Needs review
app/product/[id]/page.tsxline 5
3export default async function Product({ params }: { params: { id: string } }) {4  const { data } = await supabase.from("products").select("*").eq("id", params.id).single();5  return <article><h1>{data.name}</h1><div dangerouslySetInnerHTML={{ __html: data.description }} /></article>;6}7 

What Aster noticed

dangerouslySetInnerHTML receives a dynamic value. Sanitization and input provenance require review.

Your next step

Prefer normal React text rendering. If HTML is required, verify sanitization and the provenance of the content.

LOW1 finding

LOWType errors may be ignored during buildsnext.config.js · line 2 · Needs review
next.config.jsline 2
1module.exports = {2  typescript: { ignoreBuildErrors: true }3};4 

What Aster noticed

An ignoreBuildErrors option is set to true. Check whether the application build consumes this setting.

Your next step

Restore build-time type checking and resolve the errors instead of skipping them.

See all 18 files Aster read
  • package.json2 findings
  • .gitignore1 finding
  • .env3 findings
  • next.config.js1 finding
  • lib/supabase.tsnothing found
  • lib/ai.ts1 finding
  • supabase/migrations/0001_init.sql2 findings
  • supabase/migrations/0002_storage.sql1 finding
  • app/layout.tsxnothing found
  • app/page.tsxnothing found
  • app/product/[id]/page.tsx1 finding
  • app/api/search/route.ts1 finding
  • app/api/chat/route.tsnothing found
  • app/api/stripe/webhook/route.tsnothing found
  • app/admin/page.tsxnothing found
  • middleware.tsnothing found
  • components/Cart.tsxnothing found
  • lib/orders.tsnothing found

Engine source-checks-2.1 · 40 checks · no AI read this code.

Real output from Aster’s current checks, run on little-shop, a small app we built with mistakes on purpose. The keys are fake. Your results will differ.

WHAT THIS REPORT CAN’T TELL YOU

Two more mistakes.
Aster doesn’t check these yet.

Aster checks keys, database rules, packages and risky code. It doesn’t yet check webhooks, admin access or rate limits. Our free guides cover those.

No findings means none of Aster’s checks matched, not that your app is secure.

app/api/stripe/webhook/route.tsNot checked yet

Anyone can fake a “payment succeeded”

The webhook trusts any request and never checks Stripe’s signature. Someone could mark an order paid without paying.

Read the free guide

app/admin/page.tsxNot checked yet

The admin page is only hidden

It uses display: none for anyone who isn’t an admin. The page still loads, and the server never checks who is asking.

Read the free guide

AFTER THE REPORT

Find it. Understand it.
Fix it. Check it worked.

  1. 01

    Pick what to fix

    Choose findings and a model. Aster Basic is free; premium models show their credit cost first.

  2. 02

    Review a draft pull request

    Aster drafts the change. It opens as a draft on GitHub only after you confirm. You merge it.

  3. 03

    Re-audit

    A fresh audit compares before and after. If the finding is gone, it says so: “Your fix worked.”

See every step

Your repo next.
Same checks, your code.

Connect one repo and get this report for your own app. Read-only GitHub access, 1 free audit every month, no card needed.

Aster

CHECKING

YOUR GUIDE TO ASTER

Hi, I’m Aster.
Ask me what Aster checks.

I can’t see your code from here. Please don’t paste secrets or private code.

Prepared answers are always available.