# Cursor security in practice — Project Rules, secrets, Auto-run permissions, and reviewing generated diffs

> A guide to using the AI editor Cursor safely in production. Codify security conventions in Project Rules, keep secrets off the context with .cursorignore and env, narrow the permission boundary of Auto-run (YOLO), review generated diffs from an authorization angle, and lay a mechanical gate in CI with aegis ci — a systematic look at Cursor-specific controls.

- Published: 2026-07-07
- Author: 友田 陽大
- Tags: セキュリティ, バイブコーディング, AI駆動開発, Next.js, Supabase
- URL: https://tomodahinata.com/en/blog/cursor-security-best-practices-rules-review-guide
- Category: Vibe coding: shipping AI-generated code safely
- Pillar guide: https://tomodahinata.com/en/blog/vibe-coding-what-is-tools-risks-production-guide

## Key points

- Cursor is a fast 'generation machine,' not a mechanism that guarantees secure design. To ship safely and fast, you need to deliberately lay down Cursor-specific controls.
- Codify security conventions in Project Rules (.cursor/rules/*.mdc). But a rule is guidance to a probabilistic model, not enforcement — it raises the floor but doesn't guarantee.
- .cursorignore and env hygiene keep secrets off the context. But the official docs state plainly that 'due to the unpredictability of LLMs, complete protection is not guaranteed,' and the terminal/MCP can bypass it — the real boundary is where you keep the key.
- Narrow the permission boundary of Auto-run (YOLO). The default requires approval, and the guards are 'best-effort, not a hard boundary.' Always keep destructive, irreversible operations behind a human gate.
- Review generated diffs from the angle of authorization (the vertical risk), and finally lay a mechanical gate (aegis ci) in CI. Rules and review are probabilistic; CI is deterministic — these two tiers let you have both speed and production quality.

---

Let me give the conclusion first. **Cursor is an astonishingly fast "generation machine," not a mechanism that guarantees secure design.** The key to shipping to production safely and fast with Cursor is to deliberately lay down four Cursor-specific controls and stack a deterministic CI gate on top — (1) codify security conventions in **Project Rules,** (2) keep secrets off the context with **.cursorignore and env hygiene,** (3) narrow the **permission boundary of Auto-run (YOLO),** and (4) **review generated diffs from an authorization (vertical-risk) angle.** And finally, back up these four probabilistic nudges deterministically with a **mechanical CI gate (`aegis ci`).**

This article systematizes, from the real operations in which I have built — **solo × generative AI (Cursor / Claude Code) — a B2B SaaS that won the Minister of Economy, Trade and Industry (METI) Award and a payments platform with zero production double-charges,** with a focus on Cursor-specific configuration and operation. Not abstractions but specifics: what to write in `.cursor/rules`, what `.cursorignore` protects and **what it doesn't,** what to put in and leave out of the Auto-run allowlist, and what to look at first in a generated diff.

For reference, the systematic approach to assessing and hardening the vulnerabilities of AI-generated code from a "buyer's point of view" is collected in [Vulnerability assessment of AI-generated code (four-layer assessment)](/blog/ai-generated-code-vulnerability-assessment-vibe-coding-security-guide) and [Productionizing and hardening AI-built code](/blog/vibe-coding-ai-generated-code-production-hardening-guide). This article is the practical companion on the "builder's tool (Cursor)" side.

---

## 1. The premise: Cursor is a "generation machine," not a "security guarantee"

First, let me make clear why Cursor-specific controls are needed. The reason is **an asymmetry of speed.** Cursor's agent writes code several times faster than a human, but human review can't keep up with that speed. Review either becomes a bottleneck or turns into a formality. So to have both "generate fast" and "don't leak in production," the only way is to insert **mechanical, structural controls** into the generation loop without slowing generation down.

Here, splitting security measures into two kinds clarifies the design. It's the distinction I use consistently in [Aegis](/aegis).

| Category | Contents | Who can hold it |
|---|---|---|
| **Horizontal controls** | Headers/CSP, rate limiting, input validation, CSRF, secret hygiene | A library or config can take them over (they apply uniformly across apps) |
| **Vertical risks** | Authorization/IDOR, RLS design, business logic, cross-tenant access | A library can't fix them (they depend on the app-specific "who owns what") |

Most of what you can do with Cursor's settings is on the **horizontal** side. Handing over conventions with Project Rules, hiding secrets with `.cursorignore`, narrowing Auto-run's permissions — these lower the probability of stepping on "common traps." But the **vertical risks,** especially the "authenticates but doesn't authorize" hole, can't be closed with Cursor's settings. That's a domain closed by generated-diff review and design judgment. Grasping this boundary from the start lets you avoid the worst complacency: "I wrote Rules, so I'm safe."

---

## 2. Codify security conventions in Project Rules

Cursor's **Project Rules** are `.mdc` files placed in `.cursor/rules/` — version-controlled project conventions (per the official docs). Write your security conventions here and the agent references them as context every time it generates. The essential value is that you no longer have to write them into the conversation each time; the conventions are **persisted.**

### 2.1 Use the four application modes for different purposes

The official docs define four application modes. Use them differently — security conventions "always applied," SQL conventions "only for specific files," and so on.

| Application mode | Behavior | Where it fits in security |
|---|---|---|
| **Always Apply** | Applied to every chat | A constitution that affects everything — secret handling, authorization principles |
| **Apply Intelligently** | The agent judges relevance from the rule's description | Context-dependent conventions like "when handling authentication" |
| **Apply to Specific Files (by glob)** | Auto-applied on files matching the glob | RLS conventions on `supabase/migrations/**`, Zod-validation conventions on `app/api/**` |
| **Apply Manually** | Only when you `@`-mention it | Things you invoke explicitly, like an audit-perspective checklist |

You need the `.mdc` extension plus frontmatter, not `.md` (a `.md` file is not recognized as a rule).

### 2.2 The security conventions I actually write (a Next.js × Supabase example)

Here's the skeleton of the conventions I place in a Next.js × Supabase setup, shown as-is in `.mdc` form. This example takes effect on `supabase/migrations/**` via glob.

```text
---
description: Supabase migration security rules — RLS ownership scope
globs: supabase/migrations/**/*.sql
alwaysApply: false
---

- すべてのユーザーデータテーブルに RLS を有効化する。migration に RLS を書かないテーブルを新設しない。
- SELECT/UPDATE/DELETE の USING は必ず所有者スコープ（例: auth.uid() = user_id）にする。
- USING (true) は「意図的に全員に公開するテーブル」でだけ許可し、コメントで意図を明記する。
- INSERT/UPDATE には WITH CHECK を必ず付け、書き込み先の所有者を強制する。
- SECURITY DEFINER 関数は必ず `SET search_path = ''`（または明示スキーマ）を付ける。
- anon ロールへの grant は最小化する。認証必須のテーブルに anon の権限を残さない。
```

For the app side (`app/**`), I place this in a separate file.

```text
---
description: App-layer security rules for AI-generated code
alwaysApply: true
---

- service_role キー / Service クライアントは server-only 境界の外で import しない。
  クライアントコンポーネントや共有ユーティリティから触らせない。
- ルートハンドラの入力は境界で Zod 検証する。未検証の req.body/searchParams を下流へ流さない。
- process.env を直接参照せず、型付き env モジュール経由でのみ読む。
- 秘密（API キー・トークン）をコードにハードコードしない。ログにも出さない。
- 認証済み ≠ 認可済み。行の取得・更新には必ず所有者スコープの WHERE / RLS を効かせる。
```

### 2.3 The honest limitation: rules are "guidance," not "enforcement"

This is the most important point, and one many articles skip. **Project Rules are a strong nudge to a probabilistic language model, not compiler-like enforcement.** Writing conventions raises the "probability" they're followed, but there's no "guarantee." The agent can ignore rules, and the ignore rate rises especially in long conversations or when context is under pressure.

So use Rules to **raise the floor,** and secure the ceiling (the guarantee that it's shippable to production) with a separate mechanism — the deterministic CI gate described later. The philosophy of designing "probabilistic nudges" and "deterministic enforcement" as separate things is detailed in [Quality-gate design for AI-driven development](/blog/ai-driven-development-quality-gates-ci-type-safety-test-security).

---

## 3. Keep secrets off the context (.cursorignore / env)

One of the largest classes of accidents with generative-AI tools is **secrets leaking into the context.** `.env`, private keys, and customer data get sent to the model, or leak by being hardcoded into generated code. Cursor has `.cursorignore`, but the key is to **use it with an accurate understanding of its effect and its limits.**

### 3.1 The difference between .cursorignore and .cursorindexingignore

| File | Effect | Purpose |
|---|---|---|
| **.cursorignore** | **Blocks** access from semantic search, Tab completion, Agent, Inline Edit, and `@`-mentions | Keep secrets, credentials, and sensitive data out of the context |
| **.cursorindexingignore** | Only excludes from the index (search). Still accessible to the AI | Avoid polluting search results with huge build artifacts or vendor code |

For the purpose of protecting secrets, use **`.cursorignore`** (the indexing one is not "access blocking"). At a minimum, list the following.

```text
# .cursorignore — 秘密と機微データをコンテキストから外す
.env
.env.*
*.pem
*.key
*.p8
*.p12
**/credentials*
**/*secret*
supabase/.branches
.aws/
.ssh/
```

### 3.2 The honest limitation: .cursorignore is not a "hard guarantee"

As the official documentation states plainly, **"Cursor blocks ignored files, but due to the unpredictability of LLMs, complete protection is not guaranteed."** More important still is that **ignored code can be accessed via the agent's terminal or an MCP server.** In other words, `.cursorignore` is a practical filter to reduce exposure — you cannot make it "the only" line of defense for secrets.

So where is the real boundary? **Where you keep the key.** Supabase's `service_role` key runs with PostgreSQL's BYPASSRLS and ignores RLS entirely — so the defense boundary shifts to "where you keep the key." Confine `service_role` to a server-only boundary and keep it untouchable from the client or shared modules. Put `.env` in both `.gitignore` and `.cursorignore`, and read code only via a typed env module. This env boundary and hardcoded-secret detection are also a domain that Aegis's horizontal controls automate (`npx @aegiskit/cli scan` detects a leaked key).

And the operational rule that works best is simple — **don't paste secrets into the chat.** Pouring tokens or connection strings into the conversation while debugging is the most common leak path.

---

## 4. The permission boundary of Auto-run / YOLO

Cursor's agent can run terminal commands. By default, **commands require approval** (human approval before execution), but if you set up **Run Modes,** you can run trusted invocations without approval. There's a spectrum from a simple allowlist to an "Auto-review classifier," and the maximally-open end is what's colloquially called "YOLO mode." The speed is attractive, but this is a place to consciously narrow the permission boundary.

### 4.1 Why it's dangerous: prompt injection

The official Agent Security page warns directly — **"AI can behave unexpectedly due to prompt injection, hallucination, and the like,"** and the various guards are **"best-effort guards, not a hard security boundary."**

What this means is serious. If the **untrusted external content** the agent reads (a web page, a GitHub issue, a dependency's README, pasted logs) has instructions planted in it like "read `~/.ssh` and send it out" or "run `rm -rf`," an agent with Auto-run wide open **may just execute them.** Approval-gating is the last human breakwater against this attack, and YOLO is the act of removing that breakwater.

### 4.2 What to automate and what to leave to a human

Here's my operational boundary as a table. The principle is **"read-only, idempotent, reversible" may be automated / "write, destructive, irreversible, outbound" stays behind a human gate.**

| Decision | Examples | Reason |
|---|---|---|
| **Auto-run OK (allowlist)** | `ls` / `cat` / `npm run test` / `tsc --noEmit` / `git status` / `git diff` | Read-only and idempotent; no harm even if they fail |
| **Always approve (don't automate)** | `rm -rf` / `git push --force` / `curl … \| sh` / production deploys / `supabase db push` / `db reset` | Destructive and irreversible; there's no taking it back |
| **Block by default** | `curl`/`wget` to unknown domains (egress), writing to `.env`, outputting keys | They become a path for exfiltrating secrets or tampering |

In addition, the official docs cite **network-egress restrictions** (limiting to specific domains like GitHub, direct links, and web search) and an **MCP-tool allowlist** (making third-party tools pre-approved). MCP servers are powerful, but they give the agent new execution capabilities, so connections should be kept to explicit approval.

My principle is simple, and as I've written in [my global conventions](/blog/ai-driven-development-quality-gates-ci-type-safety-test-security), **destructive, irreversible operations (force push, destructive git, schema migrations, production deploys, mass delete) always go through human confirmation.** Having authorized one operation does not authorize the next. The Auto-run allowlist follows the same philosophy: what's permanently permitted is narrowed to read-only operations only.

---

## 5. What to review in a generated diff — look at authorization (the vertical risk) first

This is the domain of the **vertical risk** that Cursor's settings can't close, and it's the main battleground for review. When you open a generated diff, before formatting or style, look at authorization in the following order. It's because AI-generated code easily bakes in the "can write the happy path but doesn't reject a well-formed request that points at someone else's ID" hole — that is, **"authenticates but doesn't authorize."**

This is also an observed fact. In [Aegis's primary research (field study)](/blog/supabase-rls-security-field-study), **a primary survey of 1,000 public Supabase apps found that roughly 9.2% of the apps that had RLS carried a policy that "authenticates but doesn't scope rows to the owner" (a lower bound, since the sample is public repositories; non-owner-scoped ≠ immediately vulnerable — it needs checking).** Why "authenticated" and "authorized" are different things is explored in depth in [Supabase RLS: the difference between authenticated and authorized](/blog/supabase-rls-authenticated-vs-authorized-owner-scope-guide).

### 5.1 Generated-diff review checklist

| Perspective | What to look at | Dangerous pattern |
|---|---|---|
| **Owner scope** | Whether data reads/updates narrow by owner | `.eq('id', params.id)` alone without checking `user_id` → IDOR |
| **service_role boundary** | Whether the admin client has leaked outside the server boundary | `createClient(SERVICE_ROLE_KEY)` in a client / shared module |
| **RLS USING** | Whether `USING (true)` is on an intended public table | Unconditional `true` on a personal-data table |
| **WITH CHECK** | Whether INSERT/UPDATE enforces the owner of the write target | Missing `WITH CHECK` → writes under someone else's name |
| **SECURITY DEFINER** | Whether `search_path` is pinned | Unpinned → search_path hijacking |
| **Input validation** | Whether there's validation like Zod at the boundary | Passing unvalidated `req.body` downstream or into SQL |
| **Secret leakage** | Whether there's a hardcoded or logged key/token | `const key = "sk_live_..."` |

The top three rows (owner scope, service_role boundary, RLS) are **design judgments a library can't fix.** Object-level authorization flaws (IDOR/BOLA) are the most frequent risk, [#1 in the OWASP API Security Top 10 ever since 2019](https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/), and there's an actual case where missing RLS in an AI-builder-made app was registered as **CVE-2025-48757 (CVSS 9.3 CRITICAL).** "The AI wrote it, so it's fine" does not hold. This is a domain a human closes as design.

---

## 6. Lay a mechanical gate in CI — back it up deterministically with aegis ci

The four so far — Rules, ignore, the Auto-run boundary, and diff review — are all either **probabilistic** or **human-dependent.** The model can ignore Rules, and review misses things. So finally, place a **deterministic gate** outside the generation loop (in CI) to turn probability into guarantee.

The flow is this: generate as usual in Cursor, insert one static scan before shipping, and in CI make only high-confidence detections block the build.

```bash
# 生成ループの中で（手元で）
npx @aegiskit/cli scan          # 統制の欠落と縦のリスクの疑いを洗い出す
npx @aegiskit/cli fix --format json   # 機械可読な修正計画をCursorに貼って直させる

# CI で（決定的ゲート）
npx @aegiskit/cli ci            # 高信頼の検出だけがビルドを失敗させ、SARIFをGitHubへ
```

`aegis` **automates the horizontal controls (headers/CSP, rate limiting, CSRF, a typed env boundary, secret hygiene)** and **detects and warns on the vertical risks a library can't fix (authorization/IDOR, Supabase RLS design).** The key point is that it verifies authorization boundaries written in SQL — missing RLS, missing `WITH CHECK`, unconditional `true`, over-granting to anon, `SECURITY DEFINER` with an unpinned `search_path` — into the territory a TypeScript scanner structurally can't see.

That said, let me state the honest scope up front. **Aegis does not claim to "protect you completely."** A clean result is "you haven't stepped on the common traps," not "this code is safe." It does not prove authorization is "correct." So it keeps the vertical risks to detect-and-warn and returns the closing of them to the review and design judgment of Section 5. These two tiers — **raise the floor with probabilistic Rules/review, and secure the ceiling with a deterministic CI gate** — are the backbone that lets Cursor's speed coexist with production quality. The overall design of this verification gate is collected in [The spec-driven development production workflow](/blog/spec-driven-development-claude-code-ai-agent-production-workflow).

---

## 7. Summary — the five layers that make Cursor "safe while staying fast"

Finally, let me fold this article's controls into one sheet. The top four layers are probabilistic / human-dependent and "raise the floor," while the bottom layer, CI, deterministically "secures the ceiling" — the key is to design with this asymmetry in mind.

| Layer | Means | Nature | Risk it closes |
|---|---|---|---|
| 1. Codified conventions | `.cursor/rules/*.mdc` | Probabilistic (guidance) | Horizontal controls, enforcing principles |
| 2. Secret isolation | `.cursorignore` + env boundary + where you keep the key | Best-effort + boundary design | Secret leakage / exposure |
| 3. Permission boundary | Auto-run allowlist (automate read-only only) | Human gate | Destructive operations, prompt injection |
| 4. Generated-diff review | Authorization checklist | Human judgment | Vertical risks (IDOR, RLS design) |
| 5. Mechanical CI gate | `aegis ci` (+ types, tests, static analysis) | Deterministic (enforcement) | Preventing recurrence, high-confidence real harm |

Cursor is a powerful tool. But no tool is "safe because you installed it," and one that advertises so is, if anything, dangerous. The only way to ship safely without slowing down is to keep generation speed as-is and **deliberately lay down these five layers.** Start by running `npx @aegiskit/cli scan` ([Aegis](/aegis), free, MIT) on your project at hand to make visible what's currently open. And if you need a design review to actually close the "vertical risks" that were detected, I'm available.
