• Agents
  • Docs
  • Pricing
  • Blog
Log in
Get started

Security for apps built with AI. Paste a URL, get a report, fix what matters.

Product

  • How it works
  • What we find
  • Pricing
  • Agents
  • MCP Server
  • CLI
  • GitHub Action

Resources

  • Blog
  • Docs
  • FAQ
  • Glossary

Security

  • Supabase Security
  • Next.js Security
  • Lovable Security
  • Cursor Security
  • Bolt Security

Legal

  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • Imprint
© 2026 Flowpatrol. All rights reserved.
Home/What We Find/Exposed Secrets
CWE-798CWE-200OWASP #2

Exposed Secrets Hard-Coded Credentials & API Keys

AI tools put API keys and credentials right in your frontend code. Anyone who opens DevTools can grab them. One key is all it takes to bypass every security rule you set up.

What are exposed secrets?

Exposed secrets are API keys, database credentials, service role tokens, and other sensitive strings that end up in places attackers can reach. In AI-built apps, the most common case is a service role key hardcoded in a client-side React component. That key bypasses all Row Level Security on your database. Anyone who views your page source or opens the Network tab has full access to every table.

It goes beyond just Supabase keys. .env files served as static assets, secrets baked into JavaScript bundles, credentials visible in source maps -- these are all patterns AI tools generate constantly. The app works perfectly in development. The secret just happens to be visible to the entire internet.

How it works

The most dangerous pattern is using a service role key in a client component. The key gets bundled into your JavaScript, downloaded by every visitor, and grants unrestricted access to your database. Here's what that looks like -- and how to fix it.

Vulnerable: service role key in client component
// app/components/DataFetcher.tsx
'use client';

import { createClient } from '@supabase/supabase-js';

// This ships to every browser that loads your app
const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY!
);

export function DataFetcher() {
  // Anyone can open DevTools, grab this key,
  // and bypass all your Row Level Security
  const { data } = await supabase.from('users').select('*');
  // ...
}
Fixed: server action with server-only env vars
// app/actions/fetch-data.ts
'use server';

import { createClient } from '@supabase/supabase-js';

// Server-only — never touches the browser bundle
const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!
);

export async function fetchData() {
  // Service role key stays on the server
  const { data } = await supabase.from('users').select('*');
  return data;
}

Why AI tools expose secrets

AI code generators optimize for one thing: making the app work. Security boundaries between client and server are an afterthought.

  • AI copies from training data verbatim. Models learn from millions of tutorials and Stack Overflow answers where hardcoded keys are used for simplicity. The AI reproduces the pattern without understanding the security implication.
  • No concept of build-time vs. runtime. AI tools treat all environment variables the same. They don't distinguish between a server-only secret and a NEXT_PUBLIC_ variable that ships to every browser.
  • Fastest path to "it works." When you ask an AI to connect to Supabase, it grabs the first key that makes the API call succeed. That's often the service role key -- the one that bypasses all Row Level Security.

Common patterns

Service role keys in frontend

The Supabase service role key bypasses all RLS. AI tools regularly put it in client-side createClient() calls because it "just works" without permission errors.

.env files accessible via URL

Misconfigured deployments serve .env files as static assets. One GET request to /.env and every secret is exposed -- database URLs, API keys, JWT secrets.

Secrets in source maps

Production builds with source maps enabled expose your entire source code, including any hardcoded keys, tokens, and internal URLs baked into the bundle.

API keys in public repos

AI-generated code often includes real credentials in example files, seed scripts, or config files that end up committed to public GitHub repositories.

How Flowpatrol detects exposed secrets

Flowpatrol scans your live app the same way an attacker would -- from the outside, with no access to your source code.

  1. 1Bundle analysis. Flowpatrol fetches your JavaScript bundles and scans for high-entropy strings, known key prefixes (sk_live_, supabase service_role), and credential patterns.
  2. 2Environment file probing. We check common paths like /.env, /.env.local, /.env.production, and /env.js for accidentally exposed configuration files.
  3. 3Source map inspection. If source maps are publicly accessible, we download and parse them to find secrets embedded in the original source code.
  4. 4Key validation. When we find something that looks like a key, we classify it by provider and risk level. A publishable Stripe key is different from a secret Stripe key -- we flag accordingly.

Check your app for exposed secrets.

Paste your URL. Flowpatrol scans your bundles, environment files, and source maps in minutes.

Try it free