AI6 min read

Your AI Coding Tool Left Its Notes on Your Server

A .cursorrules file, a CLAUDE.md, an MCP config with an API key in it — none of these are meant to be public, and all of them ship when you deploy a repository instead of a build. Here is how to check yours, in one command.

By ShipReady

AI coding tools write instruction and config files at your repository root — .cursorrules, CLAUDE.md, .windsurfrules, .cursor/mcp.json. None are meant to be served to visitors. If your deployment serves repository files rather than build output, they are public right now. Check with curl -I https://yourdomain.com/.cursorrules: a 200 means anyone can read your architecture notes, and it means other repository files are probably reachable too.

This one is specific to how AI-built apps get deployed, which is why it barely appears in general security advice. The files did not exist five years ago, and the deployment pattern that exposes them — push the whole repo, let the platform figure it out — is exactly what makes shipping fast.

Check it now

shell
for f in .cursorrules CLAUDE.md .windsurfrules .aider.conf.yml .cursor/mcp.json .mcp.json .env; do
  printf "%-24s %s\n" "$f" "$(curl -s -o /dev/null -w '%{http_code}' "https://yourdomain.com/$f")"
done

Anything answering 200 is readable by anyone. A 404 is what you want. Watch for a 200 that returns your app’s HTML — that is a soft 404, and it means the check was inconclusive rather than clean; compare the body against a path you know does not exist.

What is in these files, and why it matters

Instruction files

.cursorrules, CLAUDE.md, .windsurfrules and their equivalents exist to give a model context about your project. That is precisely what makes them useful to someone else: they typically describe your architecture, your database schema conventions, which services you use, where the auth boundary is, and which parts of the codebase are fragile. It is the document you would write for a new engineer, published at a predictable URL.

None of that is a vulnerability by itself. It is reconnaissance — it tells someone where to look and what to try, which turns a scattershot probe into a targeted one. ShipReady reports an exposed AI tooling config file at medium severity on that basis.

Credential-bearing configs

MCP server configurations are a different matter. .cursor/mcp.json and .mcp.json describe which servers your tooling connects to, and the standard way to configure many of them is to put the token straight in the file. A public mcp.json can therefore be a public API key — for a database, a ticketing system, a cloud account. ShipReady rates an exposed AI credential config high for that reason, and it is worth checking before the instruction files.

Aider’s chat history (.aider.chat.history.md) deserves a mention too: it is a transcript of your development conversation, which tends to contain more of your reasoning — and occasionally more of your secrets — than any config file.

The real finding is not the file

This is the part worth internalising. A readable .cursorrules is a symptom. The condition it indicates is that your deployment is serving repository files rather than build output — and the repository contains more interesting things than a rules file.

  • .env and .env.local, which is the whole point of .env files existing.
  • .git/, from which the entire source history can be reconstructed. ShipReady checks an exposed .git directory and git credentials separately.
  • docker-compose.yml, routinely carrying database passwords (reported separately).
  • Source maps, which reconstruct your original source from the bundle (reported separately).
  • Database dumps someone left in the project root while debugging.

So when the check comes back 200, do not add one file to an ignore list. Fix the deployment.

Fixing it properly

  1. Serve only build output. For a framework app that means the platform serves .next, dist or build — not the working tree. If your host is serving the repository root, that is the setting to change.
  2. Add the files to .gitignore anyway. CLAUDE.md is often deliberately committed, which is fine, but MCP configs and chat histories should not be in the repository at all.
  3. Move MCP tokens out of the config file into environment variables, and rotate any token that was in a file you just found publicly readable. Treat it as disclosed, because it was.
  4. If you must serve a static directory, deny dotfiles at the server or CDN level rather than listing paths — an allowlist of what is served beats a denylist of what is not.
  5. Re-run the curl loop against production after deploying, not against a preview URL.
A denylist of files not to serve is a list you will forget to update. An allowlist of build output is a decision you make once.

The other half: what your bundle says

Files on disk are one of two AI-specific exposure patterns. The other is what ends up compiled into the JavaScript you ship.

  • A provider key inlined through a browser-exposed environment variable. The NEXT_PUBLIC_ and VITE_ prefixes instruct the build tool to publish the value — they are for things that are safe to be public, and an assistant asked to "make the key available in the component" will reach for one. ShipReady rates a secret published through a browser-exposed variable critical.
  • An AI SDK configured to run in the browser. dangerouslyAllowBrowser is named the way it is for a reason: enabling it means the API key travels to the browser, where any visitor can read it. Reported as an AI provider client configured to run in the browser.
  • Your system prompt shipped to the client, where it can be read and worked around. Reported as an exposed system prompt.
  • An MCP endpoint reachable without authentication. Reported as an unauthenticated MCP endpoint.

The bundle checks and the file checks fail for the same underlying reason: a boundary between "my machine" and "the public internet" that was never explicitly drawn, because the tooling made it feel like there was only one place.

The low-severity one: builder fingerprints

Separately, most AI builders leave identifiable traces — a meta tag, a comment, a characteristic asset path. ShipReady reports an exposed AI builder fingerprint at low severity, and it genuinely is low: knowing a site was built with a particular tool tells an attacker which default misconfigurations to try first, which is a nudge rather than a hole. Worth knowing about, not worth an evening.

If you build with a specific tool, the stack security guides cover what each one tends to get wrong by default, and the security risks of AI-generated code covers the wider pattern.

How ShipReady detects this

ShipReady requests each known AI tooling path on your origin and reports the ones that return real content — instruction files and credential-bearing configs separately, because they carry different consequences. It reads your published JavaScript for the bundle-side patterns above. Every finding names the URL or the variable it saw, so you can verify it yourself with the same curl command.

The AI Exposure pillar is the one ShipReady exists for — it covers failures that only occur because of how AI-built applications get made and deployed. Scan your site free to see it against your own origin, or read every AI exposure check first.

Frequently asked questions

Should I stop committing CLAUDE.md?
No — it is genuinely useful in the repository, and plenty of open-source projects publish theirs deliberately. The problem is not that it is committed; it is that your deployment serves repository files. Fix the deployment and keep the file.
Is an exposed .cursorrules file actually dangerous?
On its own, mildly. It is reconnaissance rather than a hole. Its real value is as a signal: if that file is reachable, check `.env`, `.git/` and everything else in the repository root, because those are the ones that matter.
I found an MCP config with a token in it. What now?
Rotate the token first, then fix the deployment, then remove the file from the repository. The order matters — the token should be assumed compromised from the moment it was reachable, and you cannot tell how long that was.
Does a .gitignore entry protect me?
It stops the file entering the repository, which helps for files you never committed. It does nothing for a file already committed, and nothing for a deployment that serves the working tree from a machine where the file exists.

Sources

Related reading

Is your site ready to ship?

ShipReady scans up to ten pages for security, AI exposure, email deliverability, SEO and launch readiness — with a paste-ready fix for each finding. Free, no signup.