Cursor rules vs enforced policy: what stops an agent

Cursor rules are guidance, so an agent can ignore them. What stops an agent is a control the harness applies to each action: a hook that returns deny, a sandbox network policy, an admin-set run mode, or an Enterprise MCP allowlist. Team Rules become mandatory only with “Enforce this rule” turned on.
Why a rules file is not a control
Cursor rules shape what the agent tries to do. Project rules live in .cursor/rules as .mdc files, and an AGENTS.md in the project root works as an alternative. Team Rules are managed in the dashboard and have no file. A plain .md file in .cursor/rules is ignored, because the rules system reads only files with frontmatter.
Cursor’s rules documentation is direct about the limit: AI guidance should not be your only security control. A rule can make the agent less likely to run rm -rf, but a rule cannot make a shell command fail. Treat rules as the first layer and put a blocking control under them.
Advisory and enforced controls side by side
The table lists each Cursor control we found in the docs, who sets it, and whether it can stop a call. Rows that answer “Yes” or name a restriction are the ones that act on a call. The rest only advise or remove a prompt.
| Control | Who sets it | Advisory or enforced | Can it stop a tool call? |
|---|---|---|---|
Project rules (.cursor/rules/*.mdc, AGENTS.md) |
Developer, in the repo | Advisory | No |
| Team Rules, not enforced | Admin, in the dashboard | Advisory. A member can turn it off in Customize | No |
| Team Rules, “Enforce this rule” on | Admin, in the dashboard | Required for every member. Still guidance | No |
permissions.json allowlists |
Developer, or the repo | Not a security boundary. Skips approval prompts | No. It removes a prompt |
| Run mode set in the team dashboard | Admin | Enforced. Overrides permissions.json and the IDE allowlist |
Sets which calls run without approval |
Sandbox networkPolicy |
Developer files, plus team-admin policy | Team-admin policy cannot be weakened by either file | Limits what sandboxed commands can reach |
Hooks in hooks.json |
Developer, or IT through MDM | Blocking when the hook exits with code 2 or returns deny | Yes |
| MCP allowlist (MCP Configuration) | Admin, in the dashboard | Enterprise only | Restricts which servers members may use |
Sources checked on 2026-11-03: Cursor’s rules, permissions, sandbox, hooks, run modes, MCP configuration, and enterprise model and integration management documentation.
Enforce Team Rules for guidance everyone must follow
An admin can make a Team Rule mandatory. Turn on “Enforce this rule” in the dashboard, and the rule is required for every team member and cannot be disabled in Customize. Team Rules need a Team or Enterprise plan, and they take precedence over Project and User Rules when guidance conflicts.
Enforcement changes whether a member can switch the rule off. It does not change whether the model follows it, so pair a rule with a hook or sandbox policy for anything that must never run. For how Team Rules compare with the Claude Code and Copilot controls, see governing Claude Code, Cursor and Copilot with one policy.
Use permissions.json for convenience, not for blocking
permissions.json takes three top-level keys: mcpAllowlist, terminalAllowlist and autoRun. The first two list calls that run without an approval prompt. The third holds instructions that steer the Auto-review classifier. Terminal entries are prefix strings matched case-sensitively, and a colon separates the base command from an argument glob, as in npm:install*.
The file can be per-user or per-repo, and both are optional. Admin settings sit above it, then permissions.json, then IDE settings. A key in permissions.json replaces the IDE allowlist for that type. The docs say the allowlists are not a security guarantee, so do not use this file to stop a command.
{
"mcpAllowlist": ["github:*", "linear:*", "notion:search"],
"terminalAllowlist": ["git", "npm", "cargo build", "cargo test"]
}
Block tool calls with a hook
Hooks are the control in Cursor that can refuse an action outright. A command hook is a script the harness runs for an event, and the script’s exit code or JSON output decides the result. The first section covers writing a blocking hook, and the second covers where hooks are read from on each machine.
Write a blocking hook
- Write a script that prints the JSON the docs describe, with
permission,user_messageandagent_message. Exit code 2 blocks the action, which the docs treat as equivalent to returningpermission: "deny". - Add the script to
hooks.json, using an event such asbeforeShellExecutionfor terminal commands orbeforeMCPExecutionfor MCP calls. - Set
failClosedtotrueon security-critical hooks. The per-script option makes a crash, timeout or non-zero exit block the action instead of letting it through.
{
"version": 1,
"hooks": {
"beforeShellExecution": [
{ "command": "./hooks/block-git.sh", "failClosed": true }
]
}
}
The version key must be 1, and failClosed sits beside command in each script entry.
The hook receives the full command, the working directory and a sandbox flag on stdin. Its output for a deny looks like this:
{
"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."
}
Permission hooks also block when their output is invalid JSON or does not match the expected format, even when failClosed is false. Test the script’s output as well as its exit code.
Deploy hooks to developer machines
Cursor reads hooks from several sources, and every matching hook from every source runs. The highest-priority source is the enterprise file, which MDM places at /Library/Application Support/Cursor/hooks.json on macOS, /etc/cursor/hooks.json on Linux and WSL, and C:\ProgramData\Cursor\hooks.json on Windows. On Enterprise, team hooks are set in the web dashboard and synced to members. Project hooks live at <project-root>/.cursor/hooks.json, user hooks at ~/.cursor/hooks.json, and plugin hooks run after all of these. When hooks disagree, a deny from any hook wins over ask.
The docs do not say whether a user can disable enterprise hooks, so ask Cursor before you treat them as fixed. Test the MDM path on one clean developer machine before a wider rollout, and confirm that a blocked command fails there.
Limit commands and network with the sandbox
Run modes decide when a tool call runs without asking. Auto-review is the recommended default and runs shell commands in the sandbox when it can. Allowlist runs only the actions on your list, and sandboxing is optional there. Run Everything runs every call with no sandbox and no classifier. An admin can override which modes are available, and team settings take precedence over individual and project configuration.
The sandbox reads sandbox.json from ~/.cursor/sandbox.json and from <workspace>/.cursor/sandbox.json. Its networkPolicy defaults to deny, and deny entries win over allow entries. A team-admin policy sits above both files and cannot be weakened by either one.
{
"type": "workspace_readwrite",
"networkPolicy": {
"default": "deny",
"allow": ["registry.npmjs.org"],
"deny": ["*.example.net"]
}
}
On Linux, the sandbox needs kernel 6.2 or later with Landlock v3 support and unprivileged user namespaces. If those are missing, Cursor falls back to asking for approval before commands run. Some commands need full system access and bypass the sandbox, and Cursor asks before running them.
Use the MCP allowlist and per-server network modes
On Enterprise, an admin lists approved MCP servers under MCP Configuration in the team dashboard. Command entries match the full command string, and URL entries match the URL, and both support *. The allowlist approves a configuration and does not install it on developer machines.
Each local server also has a network mode: Allow all, Allowlist, Deny all or No sandbox. Remote URL servers are limited to their URL entry pattern. That limits where Cursor connects. It does not limit where the remote service sends traffic after it runs. The full allowlist procedure is in how to allowlist MCP servers in Cursor, so this post does not repeat the steps.
Gotchas that let an agent through
- An unenforced Team Rule is a suggestion. Members can switch it off in Customize.
- Auto-review is not a security boundary. Its classifier can make mistakes, and the docs say so.
- Run Everything has no sandbox and no classifier. Read access does not apply in that mode either.
- Cloud Agents do not use Run Modes. They run on their own machine and never ask for approval, so check their settings separately.
- Hooks fail open by default. A non-zero exit other than 2 lets the action proceed unless
failClosedis true. askis not enforced forpreToolUse. Usedenyif a rule must hold.- Allowlists match strings. A changed argument or install path stops an entry from matching, and a wildcard can match more than you meant.
A checklist for a policy that holds
Write the policy as a list of decisions, then map each one to a control that blocks, not to a rule that advises.
- Put every “must never happen” decision in a hook with
failClosedset, and test it with a command that should be denied. - Set the team run mode and the sandbox network policy from the dashboard and
sandbox.json, so a developer’spermissions.jsoncannot widen them. - Use Team Rules with “Enforce this rule” for the guidance everyone must read, and do not count it as a control.
- On Enterprise, set the MCP allowlist and record the entries in one written list.
- Test each control on a developer machine with the Cursor build you deploy. The docs name features by version, so note the build next to the result.
Enforcing this with AIControls
AIControls has confirmed support for Claude Code through the gateway, in the CLI and in VS Code, and for Copilot in VS Code, with per-user tokens. Cursor is not on that list, so this post makes no AIControls claim for Cursor. To see what AIControls covers for coding agents, start with the coding agents page, then download AIControls or request a demo. For the Claude Code side of the same policy, read the managed settings guide.
Frequently asked questions
Does a Cursor rule stop an agent from running a command?
No. A rule is text the model reads, so the model can still pick a different action. Admins can make Team Rules mandatory, which only stops a member from switching them off. To stop a specific call, use a hook that denies it, a sandbox network policy, or a run mode an admin sets. Match the control to the call you need to stop.
What does Enforce this rule change in Cursor Team Rules?
It makes the rule required for every team member, and members cannot disable it in Customize. Team Rules need a Team or Enterprise plan. Enforcement changes whether a member can turn the rule off, not whether the model follows it. Team Rules also win over Project and User Rules when guidance conflicts.
Is permissions.json a security control in Cursor?
No. Cursor's permissions reference says the file is not a security boundary and calls its allowlists best-effort convenience. It can list MCP tools and terminal commands that run without an approval prompt. If an admin has set run mode controls in the team dashboard, those take effect, and neither permissions.json nor the IDE allowlist can add entries.
What happens when a hook script crashes or times out?
The action goes ahead unless the hook sets failClosed to true, which makes a crash, timeout or missing output block it. A permission hook behaves differently: invalid JSON or an invalid response blocks the action even when failClosed is false. Test the failure path on purpose before you depend on a hook, and check the hooks reference for the events your Cursor version supports.