Field note

The leaks an external scanner cannot see

A repo checkup found a live token in git history, two secrets in AI agent transcripts, an unverified webhook and nine functions open to anon. What changed.

SK Semih Kavaklıoğlu·7 Sept 2026·5 min read Share

An external scanner sees your site the way a stranger does: the HTML, the JavaScript bundle, the headers, the exposed .env if you shipped one. It cannot see your git history, your local machine, or the transcripts of the AI assistant you build with. On 7 September 2026 we had a coding agent run the checkup that covers those places on one of our own repositories. This is what it found, masked, and what changed because of it.

What did the checkup find?

Answer. Eight issues, four of them high, in a repository whose public surface had passed every external check.

Eight issues by severity (bar = severity, 4 = high)
Live Notion token in three tracked scripts, in git history since Feb 2026
high
Shopify app secret and a Google OAuth client secret in local agent transcripts
high
Telegram webhook with no secret-token verification
high
Nine SECURITY DEFINER functions executable by the anonymous role
high
Public storage bucket with two anonymous SELECT policies (listable)
medium
npm audit: 1 critical, 11 high
medium
Leaked-password protection off; three functions without a fixed search_path
low
Experimental API routes reachable in production
low
Source: repo security checkup on one of our own repositories, 7 September 2026. Values masked, never stored.

The last four are the kind of thing a database advisor or a dependency bot also reports. The first four are not visible to anything that looks at the repository from the outside.

How does a secret end up in an agent transcript?

Answer. By being typed, pasted or echoed in a session with a coding assistant. The transcript is a file on disk, and it syncs to the assistant's vendor.

The Google OAuth client secret in the table above was ours, from a different project. On 31 August one of our own sessions passed the value inline to a shell command instead of reading it from a file. The command ran fine. The value also landed in the transcript, in the command and in the tool output, six times. A week later the checkup on an unrelated repository found it, because on this machine every project's sessions start from the home directory and share one transcript folder.

That is two lessons in one line. A secret inside a command you run is as exposed as a secret you print. And "my project's transcripts" may be everyone's transcripts.

We rotated the client secret the same evening: new secret in the console, in the host's environment and in the auth provider that shares the client, then the old one disabled, then a login and a token refresh to prove the new one is in use.

Why is the git history the part people skip?

Answer. Because deleting the line feels like fixing it, and the diff says the secret is gone.

The Notion token was hard-coded in three scripts in February 2026. At some point the scripts were changed to read from the environment, and the current files are clean. Every clone of the repository still contains the February commits, and so does every fork and every CI cache. A token that has been in history for seven months should be treated as public.

The order of operations matters:

  1. Rotate the token with its provider. Until you do, the old value works for anyone who has it.
  2. Check the provider's usage log for the exposure window. Leaked keys are often already in use.
  3. Only then decide whether to rewrite history with git filter-repo. In a private repository with three collaborators we chose not to: a history rewrite breaks every other clone, and the rotated token is worthless anyway.

What does a checkup like this actually run?

Answer. A list of commands the agent executes locally and reports back with counts, never with values.

  • git log --all --full-history -- .env .env.* to confirm the env files were never committed.
  • gitleaks detect over the full history, or git log -p --all | rg with the same secret patterns when gitleaks is not installed.
  • A grep of the agent transcript folders for the same patterns and for the exact values of the current .env (compared locally, never printed).
  • A grep of tracked source for hard-coded keys, skipping SQL, examples, docs and lockfiles.
  • A check that no server-only variable is referenced in client code without a public prefix.
  • select tablename, rowsecurity from pg_tables and the list of SECURITY DEFINER functions with their grants, run against the project's own database.
  • select id, public from storage.buckets and whether public buckets are listable.
  • npm audit --omit=dev --audit-level=high.

Every item the agent reports becomes an open finding in Moonleap with the masked location and a fix recipe, so it is closed the same way as everything else: the agent fixes it with the owner's approval, reports what changed, and the next checkup confirms it is gone.

What changed in how we work?

Answer. Three rules, all of them about the terminal.

  1. Secret values move by pipe, never by hand. grep KEY .env | cut -d= -f2- | vercel env add KEY production puts a value in place without it appearing anywhere a person or a transcript can read it.
  2. Anything a session prints, pastes or passes inline is treated as exposed and rotated. Not "probably fine". Rotated.
  3. A scan that looks in shared places attributes what it finds. The checkup instructions now say that transcript folders may hold other projects' secrets, and that those are reported to the owner but kept out of the scanned project's record.

FAQ

Is a private repository enough to keep a hard-coded key safe?

No. Private means access-controlled, not secret. Collaborators, CI systems, backups and every laptop with a clone hold the full history.

Do AI assistant transcripts really leave the machine?

For hosted assistants, yes: the conversation is sent to the vendor to produce each answer, and most tools keep a local copy as well. Treat both copies as outside your control.

How often should the checkup run?

After any change to authentication, webhooks or database policies, and at least monthly. Moonleap's project settings let you pick the cadence and remind you when a deploy has happened since the last run.

Sources

  1. Repo security checkup on one of our own repositories, 7 September 2026, run by the owner's coding agent with the Moonleap checkup instructions (results masked, values never stored)
  2. gitleaks https://github.com/gitleaks/gitleaks
  3. git filter-repo https://github.com/newren/git-filter-repo
  4. Supabase, "SECURITY DEFINER functions" https://supabase.com/docs/guides/database/database-advisors

Share the number

8

issues in one repository that had passed every external check, 4 of them high

moonleap.io · 7 Sept 2026Post

Related