11月3日、Nirmataが「Cursor rules vs enforced policy: what stops an agent」と題した記事を公開した。CursorのAIエージェントを本当に止められるコントロールと、止められないルールの違いについて詳しく解説している。
AIエージェントの自律化が急速に進み、CursorのようなAI駆動の開発環境を企業チームで導入するケースが増えている。コードの生成だけでなく、シェルコマンドの実行・ファイル操作・外部API呼び出しまでエージェントが自律的にこなす時代になったことで、「エージェントが意図しない操作を行うリスク」をどう制御するかが開発組織の実務的な課題になってきた。NirmataはKubernetesのポリシーエンジン「Kyverno」の開発元として知られるセキュリティポリシー分野の企業であり、そのNirmataがCursorのエージェントセキュリティについて論じていることは、この問題が単なる開発者の関心事にとどまらない重要性を示している。
「ルール」はエージェントを止められない
Cursorを使っているチームで、.cursor/rulesにルールを書いて「これで安全対策は完了」と考えているなら、それは誤解だ。
Cursorのルールファイル(.cursor/rules/*.mdcやAGENTS.md)は、あくまでモデルへのガイダンス(助言)にすぎない。モデルがそのガイドラインを読んだうえで別の行動を選ぶことは、技術的に可能だ。たとえば「rm -rfを実行するな」とルールに書いても、そのシェルコマンドを失敗させる力はルールにない。Cursor公式ドキュメントも「AIガイダンスを唯一のセキュリティコントロールにするな」と明記している。
なお、.cursor/rulesに置いた純粋な.mdファイルはフロントマターがないためそもそも読み込まれない。ルールシステムが読むのは.mdcファイルのみだ。
アドバイザリーか、強制か——コントロール一覧
記事では各コントロールを「アドバイザリー(助言)」と「強制(enforced)」に分類した表を掲載している。要点を抜粋する。
| コントロール | 設定者 | ツール呼び出しを止められるか |
|---|---|---|
| プロジェクトルール(.mdc, AGENTS.md) | 開発者 | No(助言のみ) |
| Team Rules(非強制) | 管理者 | No(メンバーがオフにできる) |
| Team Rules("Enforce this rule"オン) | 管理者 | No(必須化されるが、あくまで助言) |
| permissions.json アローリスト | 開発者 | No(承認プロンプトを省略するだけ) |
| チームダッシュボードのRunモード | 管理者 | 承認なしで実行できる呼び出しを制限 |
| サンドボックスのnetworkPolicy | 開発者+チーム管理者 | サンドボックス内のコマンドが到達できるネットワークを制限 |
| hooks.json のフック | 開発者またはIT部門(MDM経由) | Yes(exit code 2またはdenyで完全にブロック) |
| MCPアローリスト | 管理者(Enterpriseのみ) | メンバーが使用できるMCPサーバーを制限 |
エージェントの動作を確実に止められるのはフック(hooks)とサンドボックスのnetworkPolicy、そして管理者が設定したRunモードだ。
エージェントを本当に止める方法:フックの書き方
フックはCursorが各アクションの前後に実行するスクリプトで、exit code 2またはJSONでdenyを返すと、そのツール呼び出しをブロックする。
フックの設定例
{
"version": 1,
"hooks": {
"beforeShellExecution": [
{ "command": "./hooks/block-git.sh", "failClosed": true }
]
}
}
スクリプトが返すJSONの例:
{
"permission": "deny",
"user_message": "Git command blocked. Use the GitHub CLI (gh) instead.",
"agent_message": "Git is blocked by policy. Use gh for this task."
}
重要なのは**failClosed: trueの設定だ。これを指定しておかないと、スクリプトがクラッシュしたりタイムアウトした場合にアクションが通過してしまう**(フェイルオープン)。セキュリティ上重要なフックには必ずfailClosed: trueをつけること。
また、フックが不正なJSONや予期しない形式を返した場合は、failClosedの設定に関わらずブロックされる。スクリプトの出力とexit codeの両方をテストすること。
フックの配置場所と優先順位
Cursorは複数の場所からフックを読み込み、該当するすべてのフックを実行する。優先度順:
- エンタープライズファイル(MDMが配置):macOS
/Library/Application Support/Cursor/hooks.json、Linux/etc/cursor/hooks.json、WindowsC:\ProgramData\Cursor\hooks.json - チームフック(Enterpriseのダッシュボードで設定、メンバーに同期)
- プロジェクトフック:
<project-root>/.cursor/hooks.json - ユーザーフック:
~/.cursor/hooks.json - プラグインフック
複数のフックが競合した場合、いずれか1つでもdenyを返せばブロックされる。
permissions.jsonはセキュリティコントロールではない
permissions.jsonでMCPツールやターミナルコマンドをアローリストに登録できるが、これは承認プロンプトをスキップするための利便性ツールだ。Cursor公式ドキュメントも「セキュリティの保証ではない」と明記している。
{
"mcpAllowlist": ["github:*", "linear:*", "notion:search"],
"terminalAllowlist": ["git", "npm", "cargo build", "cargo test"]
}
ターミナルエントリはプレフィックスの文字列マッチで、大文字小文字を区別する。コロンでベースコマンドと引数グロブを区切る(例:npm:install*)。管理者がチームダッシュボードでRunモードを設定している場合、そちらが優先され、permissions.jsonやIDEのアローリストは上書きできない。
よくある落とし穴
記事が挙げる「エージェントが抜け道を通ってしまう」パターンをまとめる。
- 非強制のTeam Ruleは提案にすぎない。メンバーがCustomizeでオフにできる
- Auto-reviewはセキュリティ境界ではない。分類器が誤りを犯す可能性があると公式が認めている
- Run Everythingモードはサンドボックスも分類器もない。読み取りアクセス制限も適用されない
- Cloud AgentsはRunモードを使わない。独自のマシンで動作し、承認を求めない。設定は別途確認が必要
- フックはデフォルトでフェイルオープン。
failClosed: trueがないと、クラッシュ時にアクションが通過する askはpreToolUseで強制力を持たない。確実に止めたい場合はdenyを使う- アローリストは文字列マッチ。引数が変わるとエントリが一致しなくなる
ポリシーを機能させるチェックリスト
記事は最後に以下のチェックリストを提示している。
- 「絶対に起きてはいけない」操作は、
failClosedを設定したフックに書き、拒否されるべきコマンドで実際にテストする - チームのRunモードとサンドボックスのnetworkPolicyはダッシュボードと
sandbox.jsonで設定し、開発者のpermissions.jsonで拡張できないようにする - Team Rulesの"Enforce this rule"は、全員が読むべきガイダンスに使う。コントロールとしてカウントしない
- EnterpriseならMCPアローリストを設定し、エントリを1つのリストに記録する
- 各コントロールを、実際にデプロイするCursorのビルドで開発者マシンにてテストする
詳細はCursor rules vs enforced policy: what stops an agentを参照していただきたい。




