Skip to content

Commit 1c90c12

Browse files
committed
fix(hooks): harden Cursor policy controls
Close shell and file-read guard bypasses, bound audit logging, and add regression coverage. Run hook and skill tooling through locked Python 3.14 CI.
1 parent f84761c commit 1c90c12

10 files changed

Lines changed: 1163 additions & 252 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
name: Python Tooling and Skill Eval Checks
3+
4+
# yamllint disable-line rule:truthy
5+
on:
6+
push:
7+
branches:
8+
- main
9+
paths:
10+
- ".github/workflows/ci-skill-evals.yaml"
11+
- "evals/**"
12+
- "hooks/cursor/**"
13+
- "pyproject.toml"
14+
- "skills/**"
15+
- "uv.lock"
16+
pull_request:
17+
branches:
18+
- main
19+
paths:
20+
- ".github/workflows/ci-skill-evals.yaml"
21+
- "evals/**"
22+
- "hooks/cursor/**"
23+
- "pyproject.toml"
24+
- "skills/**"
25+
- "uv.lock"
26+
27+
permissions:
28+
contents: read
29+
30+
concurrency:
31+
group: skill-evals-${{ github.workflow }}-${{ github.ref }}
32+
cancel-in-progress: true
33+
34+
jobs:
35+
deterministic:
36+
name: Validate Python tools and skill evals
37+
runs-on: ubuntu-latest
38+
timeout-minutes: 5
39+
40+
steps:
41+
- name: Checkout code
42+
uses: actions/checkout@v7
43+
44+
- name: Set up Python
45+
uses: actions/setup-python@v7
46+
with:
47+
python-version: "3.14"
48+
49+
- name: Set up uv
50+
uses: astral-sh/setup-uv@v9
51+
52+
- name: Install locked development tools
53+
run: uv sync --frozen --dev
54+
55+
- name: Lint and format-check Python
56+
run: |
57+
uv run ruff check evals hooks/cursor
58+
uv run ruff check hooks/cursor/*.py --select D --config "lint.pydocstyle.convention='google'"
59+
uv run ruff format --check evals hooks/cursor
60+
uv run pylint hooks/cursor/*.py
61+
62+
- name: Validate skills and eval definitions
63+
run: uv run python -m evals.skill_eval validate
64+
65+
- name: Run Python unit tests
66+
run: |
67+
uv run python -m unittest discover -s evals/tests -v
68+
uv run python -m unittest discover -s hooks/cursor/tests -v

hooks/cursor/README.md

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ This directory contains **optional** hook scripts and example `hooks.json` confi
44

55
Hooks are deterministic programs that run at defined points in the agent loop and can block, allow, or modify actions.
66

7+
### Requirements
8+
9+
- [uv](https://docs.astral.sh/uv/) available on `PATH`
10+
- Python 3.14 or newer, managed through `uv`
11+
- Hook scripts copied with executable mode
12+
13+
The scripts use only the Python standard library at runtime. Repository development tools are declared in [`pyproject.toml`](../../pyproject.toml) and locked in [`uv.lock`](../../uv.lock).
14+
715
### Files
816

917
- `guard_before_shell.py`: Intended for `beforeShellExecution`
@@ -12,7 +20,8 @@ Hooks are deterministic programs that run at defined points in the agent loop an
1220
- `guard_before_read_file.py`: Intended for `beforeReadFile`
1321
- Denies reading common secret files (for example `.env`, private keys)
1422
- `audit_log.py`: Intended for `preToolUse` (or other events)
15-
- Writes a redacted JSONL audit record to `.cursor/hooks/state/hook-audit.jsonl` (project) or `~/.cursor/hooks/state/hook-audit.jsonl` (user)
23+
- Writes a bounded, redacted JSONL audit record to `.cursor/hooks/state/hook-audit.jsonl` (project) or `~/.cursor/hooks/state/hook-audit.jsonl` (user)
24+
- `hook_io.py`: Shared fail-safe JSON input/output helpers used by both guard scripts
1625
- `hooks.project.example.json`: Example project config (paths like `.cursor/hooks/...`)
1726
- `hooks.user.example.json`: Example user config (paths like `./hooks/...`)
1827

@@ -26,3 +35,25 @@ cp -R /path/to/agent-engineering-handbook/hooks/cursor/*.py .cursor/hooks/
2635
cp /path/to/agent-engineering-handbook/hooks/cursor/hooks.project.example.json .cursor/hooks.json
2736
chmod +x .cursor/hooks/*.py
2837
```
38+
39+
The example configuration executes each script directly. Its `uv run` shebang selects the declared Python runtime without requiring a project-specific virtual environment.
40+
41+
### Security model
42+
43+
These hooks are defense-in-depth controls, not a sandbox:
44+
45+
- Guard scripts reject malformed input instead of silently allowing it.
46+
- Audit records redact common secret keys and values, bound untrusted data, and use `0700` directory and `0600` file permissions.
47+
- Example configurations retain `"failClosed": false` so a missing runtime or broken optional hook does not disable Cursor. Organizations that treat hooks as mandatory policy enforcement should test the scripts in their environment and deliberately change this setting.
48+
- Shell parsing is conservative but cannot prove arbitrary shell code safe. Remote-write authorization and normal code review remain required.
49+
50+
### Development
51+
52+
```bash
53+
uv sync --dev
54+
uv run ruff check hooks/cursor
55+
uv run ruff check hooks/cursor/*.py --select D --config "lint.pydocstyle.convention='google'"
56+
uv run ruff format --check hooks/cursor
57+
uv run pylint hooks/cursor/*.py
58+
uv run python -m unittest discover -s hooks/cursor/tests -v
59+
```

0 commit comments

Comments
 (0)