Back to skill

Security audit

Zotero MCP

Security checks for vulnerabilities and agentic risk

Overview

This skill is a straightforward Zotero integration that reads local library data for its stated purpose, with some install and privacy cautions but no evidence of hidden or malicious behavior.

Install only if you trust the zotero-mcp npm package and are comfortable enabling Zotero's local API. Use it on a trusted machine, consider pinning or locally installing a reviewed package version, and disable Zotero API access when you no longer need agent access to your library.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T08 · Insecure Dependencies

Warning
Location
SKILL.md:18
Finding
Unpinned Global npm Dependency Introduces Supply-Chain Risk## Vulnerability Details **File Location**: `SKILL.md`, lines 18–21 **Vulnerability Type**: `T08: Insecure Dependencies` **Risk Level**: Medium ### Vulnerable Code ```markdown 2. **Install zotero-mcp package**: ```bash npm install -g zotero-mcp ``` ``` ### Technical Analysis The installation instruction retrieves the latest available version of `zotero-mcp` from the configured npm registry and installs it globally. It does not pin an exact reviewed version or provide lockfile or integrity metadata. npm packages can execute lifecycle scripts during installation. Consequently, a compromised package release, registry account, package source, or transitive dependency could execute code with the privileges of the user running the installation. Global installation also increases the scope of the installed package and exposes a mutable executable through the user's command search path. The installed `zotero-mcp-server` executable is subsequently invoked by examples in `SKILL.md` and by `scripts/zotero-mcp-client.py`. Because the dependency source is not included in this project, its runtime behavior could not be verified during this audit. The server is also expected to interact with Zotero's local API, potentially exposing reference metadata, collections, annotations, and PDF content to a compromised dependency. ### Attack Path 1. An attacker compromises the `zotero-mcp` publisher, package release, registry delivery path, or one of its dependencies. 2. The attacker publishes malicious package code or an npm lifecycle script under a version satisfying the unpinned installation command. 3. A user follows the documented instruction: ```bash npm install -g zotero-mcp ``` 4. npm downloads the attacker-controlled release and may execute its lifecycle scripts during installation. 5. The package is installed globally as `zotero-mcp-server`. 6. The Skill later invokes that executable while Zotero's local ...[truncated 984 chars]
Remediation
## Remediation Suggestions 1. Pin the dependency to an exact, reviewed version rather than installing the latest release: ```bash npm install --save-exact zotero-mcp@<reviewed-version> ``` 2. Prefer a project-local installation over `npm install -g` to reduce system-wide exposure. 3. Commit a lockfile containing resolved versions and integrity hashes, and use `npm ci` for reproducible installation. 4. Review the package's source, publisher identity, transitive dependencies, and lifecycle scripts before approval. 5. Where compatible, install with lifecycle scripts disabled: ```bash npm ci --ignore-scripts ``` 6. Execute the MCP server under a restricted account or sandbox with access limited to the required Zotero API and files. 7. Apply outbound network restrictions if the server only needs localhost access. 8. Document the expected package checksum, source repository, publisher, and verified executable path. 9. Consider vendoring or otherwise distributing a reviewed implementation through a trusted, integrity-verified release process.
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (8)

External Script Fetching

High
Category
Supply Chain
Content
### 1. Find Papers on a Topic
```bash
SEARCH_QUERY="gravitational waves"
curl -s "http://127.0.0.1:23119/api/users/7120115/items?q=$SEARCH_QUERY&limit=10" | python3 -c "
import sys, json
data = json.load(sys.stdin)
for item in data:
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
### 2. List All Collections
```bash
curl -s http://127.0.0.1:23119/api/users/7120115/collections | python3 -c "
import sys, json
data = json.load(sys.stdin)
for c in data:
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
### 3. Get Items from a Collection
```bash
COLLECTION_KEY="U4PZ3XNP"  # Pending collection
curl -s "http://127.0.0.1:23119/api/users/7120115/collections/$COLLECTION_KEY/items" | python3 -c "
import sys, json
data = json.load(sys.stdin)
print(f'Found {len(data)} items'
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
### 4. Get PDF Text
```bash
ITEM_KEY="VEFDJC2X"
curl -s "http://127.0.0.1:23119/api/items/$ITEM_KEY" | python3 -c "
import sys, json
data = json.load(sys.stdin)
d = data.get('data', {})
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill demonstrates shell execution via npm, node, echo, and curl examples but does not declare any tool scope or allowed-tools restrictions. This increases the chance that an agent using the skill may invoke shell commands without explicit sandboxing or user visibility, which is risky even though the documented commands target localhost and appear intended for legitimate Zotero access.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill instructs users to enable Zotero's local API and shows direct access to library contents, but it does not warn that doing so exposes potentially sensitive papers, notes, annotations, and metadata to any local process that can reach 127.0.0.1:23119. In this context, the omission matters because the skill is specifically about querying and extracting library and PDF content, so users may unknowingly weaken privacy on systems with other untrusted local software.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The examples instruct users to query a locally exposed Zotero API and enumerate library items, collections, item details, PDF text, and annotations, but they provide no warning that this can reveal sensitive research interests, notes, or full-text content. In the context of a reference-library skill, this data access is expected functionality, but omitting privacy and access-scope warnings increases the chance of inadvertent disclosure when users copy and run the commands.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
}
    
    # Start the MCP server and communicate via stdin/stdout
    proc = subprocess.Popen(
        ["node", "-e", f"""
const {{ spawn }} = require('child_process');
const cp = spawn('zotero-mcp-server', {{ stdio: ['pipe', 'pipe', 'pipe'] }});
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.