This is a purely technical guide on how to set up some monitoring in Cursor IDE
Purpose: Set up two Cursor hooks that record file access and shell commands reaching outside the currently open project. The log remains local to the machine and applies across workspaces.
1. Confirm jq is installed
Both scripts parse JSON sent by Cursor, so jq must be available on your PATH.
which jq || brew install jq
If which jq prints a path, jq is already installed and the installation command will not run.
2. Create the file-watcher script
Create the hooks directory:
mkdir -p ~/.cursor/hooks
Create ~/.cursor/hooks/log-external.sh with the following contents:
vim ~/.cursor/hooks/log-external.sh
#!/bin/bash input=$(cat) file_path=$(echo "$input" | jq -r '.file_path // .path // empty') project_dir="${CURSOR_PROJECT_DIR:-}" log_file="$HOME/.cursor/external-activity.log" if [ -n "$file_path" ]; then abs=$(realpath "$file_path" 2>/dev/null || echo "$file_path") case "$abs" in "$project_dir"*) ;; *) printf '[%s] %s -> file: %s\n' \ "$(date '+%F %T')" "$project_dir" "$abs" >> "$log_file" ;; esac fi echo '{"permission":"allow"}'
This hook runs when Cursor reads or edits a file. It resolves the path and logs it only when it falls outside the active project directory.
Press Esc to leave insert mode.
Type :wq and hit Enter to save and quit.
3. Create the command-watcher script
Create ~/.cursor/hooks/log-external-cmd.sh:
vim ~/.cursor/hooks/log-external-cmd.sh
#!/bin/bash input=$(cat) cmd=$(echo "$input" | jq -r '.command // empty') project_dir="${CURSOR_PROJECT_DIR:-}" log_file="$HOME/.cursor/external-activity.log" if [ -n "$cmd" ]; then if echo "$cmd" | grep -qE '(~|/Users/|/home/)' \ && ! echo "$cmd" | grep -qF "$project_dir"; then printf '[%s] %s -> command: %s\n' \ "$(date '+%F %T')" "$project_dir" "$cmd" >> "$log_file" fi fi echo '{"permission":"allow"}'
Press Esc to leave insert mode.
Type :wq and hit Enter to save and quit.
This hook uses a heuristic. It records commands that reference common home-directory paths, such as ~, /Users/, or /home/, unless the current project path also appears in the command.
4. Make both scripts executable
chmod +x ~/.cursor/hooks/log-external.sh chmod +x ~/.cursor/hooks/log-external-cmd.sh
5. Configure Cursor hooks
Create or edit the user-level file ~/.cursor/hooks.json. User-level configuration ensures the hooks apply regardless of the repository or worktree currently open.
vim ~/.cursor/hooks.json
{ "version": 1, "hooks": { "afterFileEdit": [ { "command": "$HOME/.cursor/hooks/log-external.sh" } ], "beforeReadFile": [ { "command": "$HOME/.cursor/hooks/log-external.sh" } ], "beforeShellExecution": [ { "command": "$HOME/.cursor/hooks/log-external-cmd.sh" } ] } }
Press Esc to leave insert mode.
Type :wq and hit Enter to save and quit.
If hooks.json already contains other configuration, merge these entries into the existing "hooks" object. Do not replace unrelated hooks.
6. Restart and verify
Cursor reads hooks during startup. Reload the window with Cmd+Shift+P → Developer: Reload Window, or fully restart Cursor.
Ask the agent to perform an operation outside the open project, such as listing the Desktop directory:
ls ~/Desktop
Monitor the activity log:
tail -f ~/.cursor/external-activity.log
A matching entry resembles:
[2026-09-16 10:42:07] /Users/example/project -> command: ls ~/Desktop
What the hooks capture
Hook | Event | Detection method | Output |
|---|---|---|---|
afterFileEdit | Agent edits a file | Resolved file path is compared with the project directory | File activity entry |
beforeReadFile | Agent reads a file | Resolved file path is compared with the project directory | File activity entry |
beforeShellExecution | Agent runs a shell command | Heuristic search for home-directory paths outside the project | Command activity entry |
Known limitations
Command detection is heuristic. It may miss indirect access, including git push, uploads with curl, or Docker volume mounts.
File detection is the reliable half. It compares a resolved path with the project root for each file event.
The log is local. There is no synchronization between devices. Each machine maintains its own ~/.cursor/external-activity.log.
Only Cursor is covered. Other coding agents use separate approval or hook systems.
The log does not rotate automatically. Archive or clear it periodically to prevent unbounded growth.
Log maintenance
To clear the log after reviewing it:
echo -n > ~/.cursor/external-activity.log
For audit retention, archive the file first, preferably with a date in the filename.
Optional next step: blocking instead of logging
After running in log-only mode and reviewing false positives, the command watcher can be adapted to block matched commands by returning {"permission":"deny"} on the matched branch. Start with logging because the command pattern is intentionally broad and may identify legitimate operations.
No comments:
Post a Comment