This repo provides Cursor agent hooks that check dependencies against Endor Labs’ malware intelligence. It’s designed to catch malicious packages whether they’re introduced via manifest edits or package manager install commands.
- afterFileEdit: scan edited dependency manifests and record findings
- beforeShellExecution: block installation commands that include known malware
- stop: summarize any detections back to the agent at the end of the session
This follows the Cursor hooks spec: https://cursor.com/docs/agent/hooks.
| File | Hook | Purpose |
|---|---|---|
.cursor/hooks/malware-audit-hook.sh |
afterFileEdit |
Parse edited manifests and query Endor Labs |
.cursor/hooks/shell-malware-audit-hook.sh |
beforeShellExecution |
Detect install commands and deny known-malware packages |
.cursor/hooks/session-end.sh |
stop |
Report a session summary to the agent |
.cursor/hooks.json |
config | Example Cursor hooks configuration |
- Cursor with hooks enabled
- Bash +
jq endorctlinstalled and authenticated (Endor Labs)
Install endorctl and authenticate per Endor Labs docs: https://docs.api.endorlabs.com/endorctl/install-and-configure/
- npm: npm / yarn / pnpm / bun
- PyPI: pip / pip3 / poetry / pipenv / uv
- Go:
go get,go install - Rust:
cargo add,cargo install - Packagist:
composer require - Maven:
mvn ... -Dartifact=...
Endor Labs supports additional ecosystems, but these hooks do not currently parse/handle them:
- Ruby: RubyGems (
gem) - .NET: NuGet (
nuget)
All checks call Endor Labs’ QueryMalware API via endorctl, using ecosystem-qualified package version names like npm://express@4.18.0:
endorctl api create -r QueryMalware -n oss \
-d '{"spec":{"package_version_names":{"names":["npm://package@version"]}}}'- When it runs: after edits to supported dependency manifests.
- What it does: extracts dependencies from the edited content, queries Endor Labs, and records any malware hits to:
./malware_detected_packages_<generation_id>.txt
- Notes: this hook runs after the edit has already happened; it’s primarily used to detect and report, not to prevent the edit.
Support qualifications:
- File qualification: only dependency manifests are scanned; all other files are skipped.
- Best-effort parsing: unexpected formats (or invalid JSON/XML/TOML) may yield partial extraction; on errors, the hook fails open.
Supported manifests (current):
| Manifest | Ecosystem | Extracted |
|---|---|---|
package.json |
npm | dependencies, devDependencies |
package-lock.json |
npm | limited parsing |
requirements.txt |
pypi | name==version lines |
go.mod |
go | module deps |
Cargo.toml |
cargo | crate deps (best-effort) |
composer.json |
packagist | require |
pom.xml |
maven | supported when pom.xml is at repo root |
Not yet supported (manifests / lock files):
- Lock files:
yarn.lock,pnpm-lock.yaml,poetry.lock,Cargo.lock,go.sum,composer.lock,gradle.lockfile - Python:
Pipfile,Pipfile.lock,pyproject.toml - Java/Gradle:
build.gradle,build.gradle.kts
- When it runs: before every shell command; the hook only acts when it detects an installation pattern.
- What it does: extracts the package + version and denies the command if Endor Labs returns
status: MALWARE. - Output: returns Cursor’s allow/deny JSON payload.
Support qualifications:
- Command qualification: only recognized install/add patterns are scanned; non-installation commands are allowed (no-op).
- Version required: detectors currently key off explicit versions (e.g.
<pkg>@<ver>or<pkg>==<ver>), so unversioned installs may not be intercepted. - Single-package focus: patterns are intended for commands specifying one package/version at a time.
Supported package managers (current):
- JavaScript/Node.js: npm, yarn, pnpm, bun
- Python: pip, pip3, poetry, pipenv, uv
- Go:
go get,go install - Rust:
cargo add,cargo install(also supports--version <version>) - PHP:
composer require - Java/Maven:
mvn ... -Dartifact=<groupId>:<artifactId>:<version>
Supported command patterns (current):
| Package manager | Pattern (examples) |
|---|---|
| npm | npm install <pkg>@<ver> / npm i <pkg>@<ver> / npm add <pkg>@<ver> |
| yarn | yarn add <pkg>@<ver> |
| pnpm | pnpm add <pkg>@<ver> / pnpm install <pkg>@<ver> / pnpm i <pkg>@<ver> |
| bun | bun add <pkg>@<ver> / bun install <pkg>@<ver> / bun i <pkg>@<ver> |
| pip | pip install <pkg>==<ver> / pip3 install <pkg>==<ver> / python -m pip install <pkg>==<ver> |
| poetry | poetry add <pkg>@<ver> or poetry add <pkg>==<ver> |
| pipenv | pipenv install <pkg>==<ver> |
| uv | uv pip install <pkg>==<ver> / uv add <pkg>==<ver> |
| go | go get <module>@<ver> / go install <module>@<ver> |
| cargo | cargo add <crate>@<ver> / cargo install <crate>@<ver> / cargo add <crate> --version <ver> |
| composer | composer require <pkg>:<ver> |
| maven | mvn ... -Dartifact=<groupId>:<artifactId>:<ver> |
Example input:
{ "command": "npm install lodash@4.17.21" }Example deny output:
{
"permission": "deny",
"user_message": "Malware detected in command. Command blocked. npm://nyc-config@0.9.0",
"agent_message": "Malware detected in command. Command blocked. npm://nyc-config@0.9.0"
}- When it runs: at the end of the agent session.
- What it does: reads
./malware_detected_packages_<generation_id>.txt(if present) and returns afollowup_messageto the agent, then removes the file.
Support qualifications:
- Working directory matters: the summary is read from the current working directory (
./malware_detected_packages_<generation_id>.txt). If hooks run under different CWDs, the stop hook may not find the file.
- Blocks:
beforeShellExecutionblocks when Endor Labs reportsstatus: MALWARE. - Fail-open: if
endorctlis missing, auth is misconfigured, or the API call fails, hooks allow the operation (and emit a warning).
Cursor will only run hooks that are declared in a hooks.json. The scripts alone are not enough — config is required.
- Ensure hook scripts are executable:
chmod +x ./.cursor/hooks/*.sh-
In Cursor, configure hooks using the repo’s
.cursor/hooks.json(commands are relative and reference./.cursor/hooks/...).This is the file Cursor uses to know which scripts to run:
.cursor/hooks.json
- Copy scripts:
mkdir -p ~/.cursor/hooks
cp ./.cursor/hooks/*.sh ~/.cursor/hooks/
chmod +x ~/.cursor/hooks/*.sh- Create
~/.cursor/hooks/hooks.json(adjust paths as needed):
{
"version": 1,
"hooks": {
"beforeShellExecution": [{ "command": "~/.cursor/hooks/shell-malware-audit-hook.sh" }],
"afterFileEdit": [{ "command": "~/.cursor/hooks/malware-audit-hook.sh" }],
"stop": [{ "command": "~/.cursor/hooks/session-end.sh" }]
}
}- Restart Cursor.
# afterFileEdit: clean
echo '{"file_path":"package.json","generation_id":"test-123","edits":[{"new_string":"{\"dependencies\":{\"express\":\"^4.18.0\"}}"}]}' \
| ./.cursor/hooks/malware-audit-hook.sh
# beforeShellExecution: blocks known malware example
echo '{"command":"npm install nyc-config@0.9.0"}' \
| ./.cursor/hooks/shell-malware-audit-hook.sh
# stop: summarizes detections (expects the malware_detected_packages_* file in CWD)
echo '{"generation_id":"test-123"}' \
| ./.cursor/hooks/session-end.shjq: command not found: installjqand restart Cursor.endorctlmissing / auth failures: runendorctl initand verify your org credentials.- Hooks not triggering: confirm your
hooks.jsonis in the correct location and paths are executable. - API error logs: the shell hook writes stderr from
endorctlto/tmp/endorctl.log.
┌─────────────────────────────────────────────────────────────────┐
│ Cursor Agent Session │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌──────────────────┐ ┌──────────────┐ │
│ │ File Edit │───▶│ afterFileEdit │───▶│ Endor Labs │ │
│ │ (manifest) │ │ Hook │ │ API │ │
│ └─────────────┘ └──────────────────┘ └──────────────┘ │
│ │ │ │
│ ▼ │ │
│ ┌──────────────┐ │ │
│ │ Block/Allow │◀──────────────┘ │
│ └──────────────┘ │
│ │
│ ┌─────────────┐ ┌──────────────────┐ ┌──────────────┐ │
│ │ pkg install │───▶│ beforeShellExec │───▶│ Endor Labs │ │
│ │ command │ │ Hook │ │ API │ │
│ └─────────────┘ └──────────────────┘ └──────────────┘ │
│ │ │ │
│ ▼ │ │
│ ┌──────────────┐ │ │
│ │ Deny/Allow │◀──────────────┘ │
│ └──────────────┘ │
│ │
│ ┌─────────────┐ ┌──────────────────┐ │
│ │ Session End │───▶│ sessionEnd │───▶ Followup Message │
│ │ │ │ Hook │ │
│ └─────────────┘ └──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
This hook suite is provided as-is for security auditing purposes. Please ensure compliance with your organization's security policies and Endor Labs' terms of service.