Kleared
Security that opens the PR.

Other scanners report it.We fix it.

Kleared scans your AI-built app, opens fix-PRs for the issues, and gives you a badge that proves you shipped clean. Self-serve. No calls. No demos.

Trusted by builders shipping on

The problem

Three things we find in nearly every audit.

AI-built apps ship fast. Security review usually doesn't ship at all. These are the patterns we catch on day one.

How it works

Connect, scan, merge. That's it.

1

Connect your repo

One-click GitHub App install. We never ask for a personal access token, and we never get write access to anything outside the repos you pick.
2

We scan with seven tools

Gitleaks, Semgrep, Trivy, Nuclei, our Supabase RLS analyzer, our Next.js auth analyzer, and dependency CVE scoring. Results land in your dashboard in minutes.
3

Claude opens fix-PRs

Each finding gets its own branch, its own PR, and its own Claude-generated diff. You review. You merge. Or you reply on the PR and we regenerate.

Want the full pipeline? Read the deep-dive →

What we actually ship

Ten fix-PRs we open, top of mind.

Anonymised from real audits. RLS holes, secrets in the client bundle, missing security headers, weak CSPs, missing SRI on CDN scripts, permissive CORS, open redirects, unrate-limited auth, dependency CVEs, unauthenticated admin routes. Each one ships in its own PR with a passing diff.

  • Openkleared main
    #42

    Add RLS policy to posts table (was open to anonymous reads)

    RLS was enabled on posts but no policies existed, so anyone with your project URL could `select *` on it.

    supabase/migrations/20251104_posts_rls.sql@@ -1,3 +1,8 @@ alter table public.posts enable row level security;+create policy "posts: read own or public" on public.posts+  for select to authenticated using (author_id = auth.uid() or is_public);+create policy "posts: insert own" on public.posts+  for insert to authenticated with check (author_id = auth.uid());
    scanner: supabase_rls · severity critical
  • Openkleared main
    #43

    Move Stripe secret out of NEXT_PUBLIC_ env var

    `NEXT_PUBLIC_STRIPE_SECRET_KEY` was inlined in the client bundle. The secret is rotated and moved server-side.

    .env.example + lib/stripe.ts@@ env.example @@-NEXT_PUBLIC_STRIPE_SECRET_KEY=sk_live_xxxxxxxx+STRIPE_SECRET_KEY=sk_live_xxxxxxxx@@ lib/stripe.ts @@-const stripe = new Stripe(process.env.NEXT_PUBLIC_STRIPE_SECRET_KEY!);+const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
    scanner: gitleaks · severity critical
  • Openkleared main
    #44

    Add HSTS, X-Content-Type-Options, Referrer-Policy

    Three baseline security headers were missing in production responses. Adding them via Next's headers() config.

    next.config.ts@@ next.config.ts @@ async headers() {   return [{     source: "/:path*",     headers: [+      { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" },+      { key: "X-Content-Type-Options", value: "nosniff" },+      { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },     ],   }]; }
    scanner: nuclei · severity high
  • Openkleared main
    #45

    Tighten CSP — drop unsafe-inline, allow nonced scripts only

    CSP allowed `'unsafe-inline'` for scripts, defeating the point. Switched to nonced scripts via Next middleware.

    middleware.ts@@ middleware.ts @@-const csp = "script-src 'self' 'unsafe-inline' 'unsafe-eval'";+const nonce = crypto.randomUUID();+const csp = `script-src 'self' 'nonce-${nonce}' 'strict-dynamic'`;+response.headers.set("x-nonce", nonce);
    scanner: nuclei · severity high
  • Openkleared main
    #46

    Add SRI integrity attributes to CDN <script> tags

    Three CDN-hosted scripts loaded without integrity hashes. A CDN compromise would have run arbitrary code in your users' browsers.

    app/layout.tsx@@ app/layout.tsx @@-<script src="https://cdn.example.com/widget.js" />+<script src="https://cdn.example.com/widget.js"+  integrity="sha384-Zk+Pf..."  crossOrigin="anonymous" />
    scanner: semgrep · severity medium
  • Openkleared main
    #47

    Replace Access-Control-Allow-Origin: * with allowlist

    API responded with `Access-Control-Allow-Origin: *` and `Allow-Credentials: true`, exposing authenticated endpoints cross-origin.

    app/api/public/route.ts@@ app/api/public/route.ts @@-headers.set("Access-Control-Allow-Origin", "*");+const allow = new Set(["https://kleared.app", "https://app.kleared.app"]);+const origin = req.headers.get("origin");+if (origin && allow.has(origin)) {+  headers.set("Access-Control-Allow-Origin", origin);+}
    scanner: semgrep · severity high
  • Openkleared main
    #48

    Validate redirect target against allowlist

    `?next=` query param was redirected to without validation. A phishing-friendly open-redirect; bounded to same-origin paths now.

    app/auth/callback/route.ts@@ app/auth/callback/route.ts @@-return NextResponse.redirect(searchParams.get("next") ?? "/");+const next = searchParams.get("next") ?? "/";+const safe = next.startsWith("/") && !next.startsWith("//");+return NextResponse.redirect(new URL(safe ? next : "/", request.url));
    scanner: semgrep · severity medium
  • Openkleared main
    #49

    Rate-limit /api/auth/* with Upstash sliding window

    Auth endpoints had no rate limit. Added a sliding-window limit (10 req/min/IP) so credential stuffing isn't free.

    lib/rate-limit.ts + middleware.ts@@ middleware.ts @@+if (pathname.startsWith("/api/auth/")) {+  const { success } = await ratelimit.limit(`auth:${ip}`);+  if (!success) return new Response("too many", { status: 429 });+}
    scanner: nextjs_auth · severity medium
  • Openkleared main
    #50

    Bump axios past CVE-2024-39338 (SSRF in 1.7.2)

    `axios@1.7.2` had a known SSRF when paths starting with `//` were resolved against an absolute base URL. Bumped to 1.7.4.

    package.json + package-lock.json@@ package.json @@-    "axios": "1.7.2",+    "axios": "^1.7.4",
    scanner: trivy · severity high
  • Openkleared main
    #51

    Check auth.uid() in /api/admin route handler

    Route handler called supabase.auth.getUser() but never branched on the null case. Anyone unauthenticated could hit /api/admin.

    app/api/admin/route.ts@@ app/api/admin/route.ts @@ const { data } = await supabase.auth.getUser();+if (!data.user) return new Response("unauthorized", { status: 401 });+const isAdmin = await checkAdmin(data.user.id);+if (!isAdmin) return new Response("forbidden", { status: 403 });
    scanner: nextjs_auth · severity critical

← scroll to see 10 fixes →

The badge

A trust signal you can put on your Stripe page.

Every customer gets a public, signed verification page at verified.kleared.app/<slug>. Anyone can verify the Ed25519 signature themselves — the public key is committed to this repo.

Klearedacme/launchpad·94/100
Klearedship-it/app·88/100
Klearedindie/dash·76/100

See how verification works → About the badge

Pricing

One scan, or always-on.

Continuous Protection

Recurring scans on every push so your badge never goes stale.

$99/ month
  • Auto-rescan on push
  • Up to 4 scans per month
  • Live badge that revokes on regressions

Launch Audit

A deep scan, fix-PRs, and a verification badge — with room to re-run.

$299one-time
  • Full scanner suite (gitleaks, semgrep, trivy, RLS analyzer)
  • Up to 5 scans so you can fix and re-verify
  • Claude-generated fix-PRs
  • Public Kleared badge with signed verification

Founder Compliance

Continuous + lightweight SOC2 + cyber insurance affiliate access.

$499/ month
  • Everything in Continuous
  • Up to 30 scans per month
  • SOC2-lite documentation pack
  • Cyber insurance affiliate access

Need a custom plan? Email us.

FAQ

Questions we get from cold-email replies.

  • How is this different from Snyk, Aikido, or a Vibe App Scanner?+
    They produce reports. We open the PR. Every finding ships with a Claude-generated diff that's been validated locally before it hits your repo. You spend your time reviewing fixes, not triaging a 40-page report.
  • Will this break my code?+
    Each fix lands in its own branch and its own PR. CI runs on the branch the same way it would for any human change. If a fix breaks tests, the PR sits there until you decide what to do — nothing gets force-merged.
  • What languages and stacks do you support?+
    JavaScript, TypeScript, Python, Go, Rust, Ruby — anything Semgrep + Trivy can read. Our Supabase RLS analyzer is the deepest signal we have, so the strongest fit is Next.js / SvelteKit / Remix on Supabase. Other stacks still get the full SAST + secrets + IaC scanner suite.
  • Do you store my source code?+
    We clone your repo into ephemeral Railway storage for the duration of the scan, then delete it. We never write source code to a database. The only thing that persists is the findings + the diffs we generated, all scoped to your org.
  • What if I'm not on Supabase?+
    You still get six other scanners, plus the Next.js auth analyzer that catches unauthenticated routes. The Supabase RLS analyzer just sits idle. Most of our highest-severity findings come from secret leaks and dependency CVEs anyway, neither of which need Supabase.
  • How fast are scans?+
    Most repos finish in 3–7 minutes. Fix-PRs land 1–2 minutes after that, in parallel. You'll have a badge URL within 10 minutes of clicking Run scan.

Something we missed? Email hello@kleared.app.