KeyDrift
Free scan
criticalLovable·AWS access key ID

Stop Lovable shipping your aws access key id in the Vite bundle

Identifies an IAM principal. Paired with its secret, it grants that principal’s permissions.

Why Lovable does this

Lovable builds a Vite single-page app with no server of its own. When you ask it to call an API that needs a key, the only place it can put that key is the browser — so it adds a `VITE_`-prefixed variable, and Vite substitutes the literal value into the bundle at build time.

Confirm it first

Before rotating anything, check whether the key is actually being served. Paste your deployed URL — KeyDrift downloads the same JavaScript a visitor gets and tells you what is in it.

No account. Read-only — the scanner only ever issues GET requests, and never stores a key: findings carry a masked prefix and a fingerprint.

Rotate the key

Do this before changing any code. The key has been served to browsers, cached by CDNs and very likely scraped already — removing it from the source does not un-publish it.

  1. 1Deactivate the access key in IAM, then delete it once nothing is broken.
  2. 2Read CloudTrail for the period the key was public. This is the one provider where you can find out exactly what was done.
  3. 3Replace long-lived keys with a role, and issue presigned URLs from a server route instead of shipping credentials.
Open the revocation page

Move the call to a server

The replacement key must not follow the old one into the bundle, which means the code that uses it cannot live in the browser. Any variable named VITE_ is inlined at build time by design — the prefix is the mechanism, not a mistake.

Before — shipped to the browser

// src/components/Chat.tsx
// Vite substitutes the literal value here at build time.
const key = import.meta.env.VITE_API_KEY;
const result = await callTheApi(key, body);

After — stays on a Supabase Edge Function

// supabase/functions/proxy/index.ts  — runs on a Supabase Edge Function
Deno.serve(async (request) => {
  const key = Deno.env.get('API_KEY')!; // never sent to the browser
  const result = await callTheApi(key, body);
  return Response.json(result);
});

// src/components/Chat.tsx
const result = await fetch('/functions/v1/proxy', { method: 'POST' }).then((r) => r.json());

How KeyDrift detects it

Matches the `AKIA` and `ASIA` prefixes. `ASIA` is a temporary STS credential and expires on its own, so it is reported one level lower. The documented example key from the AWS guides is excluded by name.

It will happen again

Lovable builds a Vite single-page app with no server of its own. That has not changed because you fixed this one file — the next feature request produces the same shape of code. Continuous monitoring re-scans every deploy and tells you the moment a key comes back.