GUIDE · INCIDENT 9 min ·

Cursor uploaded my .env to the cloudWhat was synced. What to do now.

Cursor's cloud sync feature indexed .env files sitting inside open workspaces in January 2026. Privacy mode did not prevent it. Here is what got uploaded, how to estimate your exposure, and what to do today.

TL;DR· the answer, in twenty seconds

What happened: Cursor's cloud sync feature, enabled by default, indexed workspace files including .env files containing plaintext API keys, database credentials, and OAuth secrets. Knostic confirmed in late January 2026 that the indexing occurred even when users had enabled privacy-related settings. The data landed in vendor-side infrastructure.

The minimum fix: disable Cursor cloud sync now (Cursor Settings > General > Privacy > disable "Enable Codebase Indexing"), then rotate every credential in any .env file that lived inside a Cursor workspace during January 2026.

The lesson: IDE cloud sync defaults assume code is safe to ship offsite. .env files in workspaces are not code. The two assumptions live in the same directory tree and conflict.

In late January 2026, developers on the Cursor Discord and several engineering blogs reported something specific: their .env files had been picked up by Cursor's cloud indexing pipeline. Not some fringe configuration. The default one.

Knostic, the same firm that disclosed the Claude Code settings.local.json leak three weeks later, confirmed the Cursor behavior in its January 2026 research. The indexing pulled workspace files for codebase-context features. .env files sit in project roots. Cursor's indexer did not distinguish them from .ts or .go files. Privacy mode in the settings UI toggled a narrower set of behaviors than users assumed.

The gap between what "privacy mode" implied and what it actually stopped is the incident. Nobody broke into Cursor. The product worked as designed. The design did not account for .env files as a special category.

If you ran Cursor during January 2026 with cloud sync on and a .env file in your workspace, treat your credentials as potentially exposed.

The 60-second version

  • Cursor's codebase indexing feature is on by default. It uploads file contents to Cursor's servers to power AI context features.
  • "Privacy mode" in January 2026 did not disable indexing of all local files. Knostic found .env files in the indexed corpus.
  • The credentials at risk: any key, token, or password in .env, .env.local, .env.development, .env.production, or any dotenv variant inside your workspace.
  • Scope of impact: anyone using Cursor with cloud indexing active during January 2026 who stored .env files in their workspace root or any subdirectory Cursor scanned.
  • Rotation is the only safe response. Disabling sync and deleting the file does not remove data already indexed on Cursor's servers.

What the file looked like

A typical .env file that got indexed:

DATABASE_URL=postgresql://admin:s3cr3tpassword@prod-db.internal:5432/app
STRIPE_SECRET_KEY=sk_live_...
OPENAI_API_KEY=sk-proj-...
SENDGRID_API_KEY=SG.xxxxx
JWT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Every value is plaintext. No encryption at the file layer. The format exists for convenience during development, not for safety in any environment where the file leaves the machine.

Cursor's indexer read these the same way it reads any source file. From the indexer's perspective, the content was workspace context, useful for answering questions like "what database does this project use?" From a secrets perspective, those values were credentials for production systems.

The .env spec does not distinguish between development and production values. Developers often promote the same file across environments or copy production values locally for debugging. Which specific credential categories were present depends on the project, but the Knostic disclosure described finding database URLs with embedded passwords, third-party API keys, and internal service tokens.

Estimate your exposure

Before rotating anything, figure out what you're rotating for.

Step 1: Find which workspaces were at risk.

Open Cursor's workspace history or check recently opened projects. Any project you opened in Cursor between roughly November 2025 (when the affected indexing behavior was active) and late January 2026 is in scope.

Step 2: Find .env files in those workspaces.

find /path/to/your/workspace -name ".env*" -not -path "*/node_modules/*" -not -path "*/.git/*"

Common variants Cursor would have indexed:

.env
.env.local
.env.development
.env.production
.env.test
.env.staging
.env.*.local

Step 3: Read each file and catalog the credential types.

Do not paste the values into any chat interface, including Cursor itself. Write them down offline or into a local scratch file you will delete.

For each credential you find, record: the provider (Stripe, OpenAI, GitHub, your database), the key type (secret key, access token, password), and whether it is a development or production value.

Step 4: Check git history.

Some .env files were committed at some point and then ignored. If the file is in git history, your exposure surface includes anyone who ever cloned the repo. The Cursor indexing is one vector; git history is a separate one.

git log --all --oneline -- '.env' '.env.*'

If that returns output, you have a separate git-history leak to address alongside the Cursor issue.

Revoke and rotate, in order

Rotation without revocation leaves the old key active. Revocation without rotation locks you out of your own system. Do both, in the right order.

Work through your credential catalog from Step 3 above.

API keys and service tokens (Stripe, OpenAI, Anthropic, GitHub, Vercel, SendGrid, Twilio):

Revoke first, then issue a new key, then update every consumer before the revocation takes effect. Most providers let you create the replacement key before revoking the old one. Use that window.

1. Create new key in provider dashboard
2. Update consumers (app config, CI env vars, hosting platform secrets)
3. Verify consumers work with the new key
4. Revoke the old key

Verifying first prevents a gap where your production service drops because the old key was revoked before the new one propagated.

Database credentials:

This one requires a maintenance window on most databases. You cannot rotate a database password the same way you rotate a stateless API key. Steps:

# PostgreSQL example
ALTER USER app_user WITH PASSWORD 'new_strong_password';

Update your connection strings in every environment before executing the password change on the database server. If you have read replicas or connection pools (PgBouncer, RDS Proxy), each needs the new credential.

OAuth client secrets:

Regenerate in the OAuth provider settings panel. Active user sessions typically survive a client secret rotation because the secret authenticates your server, not the user's session. Verify this with your specific provider before rotating in production hours.

JWT signing keys:

If the leaked value was a JWKS signing key, rotate it and plan for a short window where old tokens are rejected. If it was a symmetric HMAC secret, rotate the secret and invalidate all outstanding tokens. Both require a deployment.

Internal service tokens and webhook secrets:

These are easy to miss. Check for values that look like whsec_, sk_test_, or any long random string used for HMAC validation. Rotate them the same way as API keys.

Stop future indexing today

Disabling sync is fast. Go to Cursor Settings (not system settings), find Privacy or General, and disable "Enable Codebase Indexing." Cursor's UI changes between versions; look for anything referencing "codebase index," "context index," or "cloud sync."

After disabling, add exclusions so that even if indexing is re-enabled, .env files stay out:

Create or update .cursorignore in your project root:

.env
.env.*
*.env
**/.env
**/.env.*
secrets/
.secrets/

.cursorignore follows .gitignore glob syntax. The **/.env pattern matches dotenv files in subdirectories as well as the project root.

Add the same patterns to .gitignore if they are not already there:

echo ".env" >> .gitignore
echo ".env.*" >> .gitignore
echo "!.env.example" >> .gitignore

The !.env.example exception keeps the committed example file in place while excluding all real variants.

For teams: enforce this at the repo level. A .cursorignore committed to the repository applies to every developer who opens the project in Cursor. One PR, broad coverage.

What gets missed in the coverage of this incident

Most of the follow-up coverage focused on whether Cursor's privacy mode was "misleading." That framing centers the vendor.

A broker like hasp moves the values upstream of the workspace entirely. Cursor's indexer reads .env.example and variable references, never the live key. When the indexer ships its corpus, the secret stays in an encrypted vault that the indexer never reached.

The harder question is structural. IDE cloud sync features are designed for source code. Source code is meant to leave the developer's machine: it gets pushed to git, reviewed, deployed, archived. The mental model behind cloud sync is that code is safe to move. .env files do not share that property. They sit in the same directory tree, use the same file extensions as configuration, and look like project files. But they contain the keys that protect everything the code touches.

Every major AI coding tool now has some form of codebase indexing: Cursor's context index, GitHub Copilot's repo-level embeddings, Sourcegraph Cody's remote index. The specific path and the specific vendor differ. The structural tension is identical. Dotenv files in project roots will keep getting caught by tools designed to index project roots.

Knostic's January 2026 disclosure followed their earlier work on Claude Code. Check Point published CVE-2025-59536 covering a command-injection path in Claude Code project files the same month. These are not isolated vendor bugs. They are different expressions of the same gap: developer tooling that reads your environment does not separate "environment for context" from "environment for secrets."

Cursor issued a patch. The behavior changed. That is good. It does not change the exposure window from November 2025 through late January 2026, and it does not change the structural condition that made this possible.

A checklist you can paste into a PR

## Cursor cloud-sync incident response

- [ ] Cursor codebase indexing disabled in Settings
- [ ] .cursorignore created with .env and .env.* patterns
- [ ] .gitignore updated to exclude all real .env variants
- [ ] Workspace inventory complete (all Cursor-opened projects Nov 2025-Jan 2026)
- [ ] .env file inventory complete for each workspace
- [ ] Git log checked for any committed .env files
- [ ] Credential catalog complete (provider, type, prod vs dev)
- [ ] All production API keys revoked + reissued
- [ ] Database passwords rotated (with consumer update before rotation)
- [ ] OAuth client secrets regenerated
- [ ] JWT signing keys rotated if applicable
- [ ] Internal service tokens and webhook secrets rotated
- [ ] CI/CD platform secrets updated with new values
- [ ] Hosting platform env vars updated (Vercel, Railway, Fly, Heroku, etc.)
- [ ] Teammates notified if any shared credentials were affected

Paste this into your incident response doc or a tracking issue. Complete it in one session if possible. Partial rotation leaves gaps that attackers can use.

What this means for your stack

The Cursor incident is a specific example of a category failure. IDE cloud sync, MCP server context, coding-agent state files, and similar features all index or persist parts of your workspace to power AI features. That is the product. The problem is that .env files are in the workspace too, and most tooling has no concept of "this file contains secrets, exclude it from everything that leaves the machine."

The durable fix is not to trust each tool to make the right call about which files contain secrets. It is to keep secrets out of the directories those tools write to and index from. A credential broker holds secrets in an encrypted local vault, injects values into specific processes at exec time, and leaves nothing in the workspace tree for an indexer to find. The coding agent reads a reference or a temp file with a 24-hour ceiling. The vendor's index captures the reference, not the value.

hasp is one working implementation. curl -fsSL https://gethasp.com/install.sh | sh, hasp setup, connect a project, and the next Cursor session reads a reference instead of a live key. Source-available (FCL-1.0), local-first, macOS and Linux, no account.

Whether you use hasp or not, the structural fix is the same: move secrets upstream of the workspace. An indexer that cannot reach the value cannot leak it.

Sources· cited above, in one place

NEXT STEP~90 seconds

Stop handing the agent your real keys.

hasp keeps secrets in one local encrypted vault, brokers them into the child process at exec, and never lets the agent read the value.

  • Local, encrypted vault — no account, no cloud, no telemetry by default.
  • Brokered run — agent gets a reference, the child process gets the value.
  • Pre-commit + pre-push hooks catch managed values before they ship.
  • Append-only HMAC audit log answers "did the agent touch the prod token?" in seconds.
→ okvault unlocked · binding ./api
→ okgrant once · pid 88421
→ okagent never read

macOS & Linux. Source-available (FCL-1.0, converts to Apache 2.0). No account.

Browse all clusters· eight threads, one index