Back to skill

Security audit

openclaw-zotero-scholar

Security checks for vulnerabilities and agentic risk

Overview

This Zotero helper is not malicious, but it should be reviewed because it handles a Zotero API key, changes the user's Zotero library, downloads/uploads PDFs with weak URL validation, and resolves an unpinned dependency at runtime.

Install only if you are comfortable giving this skill a Zotero API key that can create items, notes, and attachments in your library. Use a limited Zotero key if possible, avoid passing paper URLs from untrusted sources, and consider pinning dependencies before running it.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/save_paper.py:97
Finding
Insufficient Validation of Remote PDF URLs Enables Arbitrary Network Requests## Vulnerability Details **File Location**: `scripts/save_paper.py`, lines 97–125 **Vulnerability Type**: Improper URL validation and unrestricted remote file retrieval **Risk Level**: Medium ### Vulnerable Code ```python # 下载并附加 PDF if 'arxiv.org' in args.url: try: import urllib.request import tempfile # 将摘要链接转换为 PDF 链接 pdf_url = args.url.replace('/abs/', '/pdf/') if not pdf_url.endswith('.pdf'): pdf_url += '.pdf' print(f"正在下载 PDF...") # 设置 User-Agent 以支持下载 opener = urllib.request.build_opener() opener.addheaders = [('User-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Chrome/120.0.0.0')] urllib.request.install_opener(opener) # 创建安全的文件名 safe_title = "".join(c for c in args.title if c.isalnum() or c in (" ", "-", "_")).strip() safe_title = safe_title[:50] # 限制长度 safe_filename = f"{safe_title}.pdf" # 使用临时目录,但指定文件名 with tempfile.TemporaryDirectory() as td: pdf_path = os.path.join(td, safe_filename) urllib.request.urlretrieve(pdf_url, pdf_path) print(f"正在上传 PDF 附件({safe_filename})...") zot.attachment_simple([pdf_path], item_key) ``` ### Technical Analysis The code determines whether a URL is an arXiv URL by checking whether the untrusted string contains the substring `arxiv.org`. This does not validate the URL's parsed hostname, scheme, port, or redirect destination. For example, both of the following attacker-controlled URLs pass the check even though their effective hosts are not arXiv: ```text https://arxiv.org.attacker.example/payload https://attacker.example/files/arxiv.org/payload ``` ...[truncated 1518 chars]
Remediation
## Remediation Suggestions - Parse the URL using `urllib.parse.urlparse` rather than performing a substring check. - Require the `https` scheme. - Allow only the exact hostname `arxiv.org` and explicitly approved arXiv subdomains. - Reject embedded credentials, unexpected ports, malformed URLs, and non-HTTP schemes. - Derive the PDF URL from a validated arXiv identifier instead of modifying an arbitrary URL. - Disable redirects or validate the hostname, scheme, and port after every redirect. - Stream downloads while enforcing a strict maximum response size. - Require an expected PDF content type and validate the downloaded file signature before uploading it. - Apply connection and read timeouts and delete rejected downloads immediately.

T08 · Insecure Dependencies

Warning
Location
scripts/save_paper.py:2
Finding
Unbounded Runtime Dependency Resolution## Vulnerability Details **File Location**: `scripts/save_paper.py`, lines 2–4 **Vulnerability Type**: Unpinned third-party dependency **Risk Level**: Medium ### Vulnerable Code ```python # /// script # requires-python = ">=3.10" # dependencies = ["pyzotero>=1.6.0"] # /// ``` ### Technical Analysis The script declares `pyzotero` using the open-ended version constraint `>=1.6.0`. When the documented `uv run` workflow resolves this dependency, it may install any future release satisfying that constraint. No lockfile, exact package version, or package integrity hash is present in the audited project. Consequently, the source reviewed during this audit does not fully determine the code that will execute at runtime. A compromised, malicious, or unexpectedly incompatible future package release could execute inside the same process as the skill. This is particularly sensitive because the process has access to `ZOTERO_CREDENTIALS`, which contains the Zotero library identifier and API key. ### Attack Path 1. The user invokes the script through the documented `uv run` command. 2. The package manager resolves `pyzotero>=1.6.0` at runtime or during environment creation. 3. A newer package version satisfying the open-ended constraint is selected. 4. If that selected release or its transitive dependencies have been compromised, their code is installed and imported. 5. The dependency executes in the skill process with access to the process environment, network connectivity, local files available to the user, and the Zotero API credential. ### Impact Assessment A compromised dependency could read and exfiltrate `ZOTERO_CREDENTIALS`, access files available to the invoking user, modify Zotero data within the API key's permissions, and execute arbitrary code with the operating-system privileges of the skill process. No malicious `pyzotero` package behavior was established by this audit. The finding concerns the project's ina ...[truncated 81 chars]
Remediation
## Remediation Suggestions - Pin `pyzotero` to a specific audited version rather than using an open-ended lower bound. - Commit a dependency lockfile containing exact direct and transitive dependency versions. - Use package integrity hashes where supported. - Configure dependency installation to use trusted package indexes only. - Perform dependency updates through a controlled review process that includes changelog review, vulnerability scanning, and testing. - Run the skill with only the minimum required environment variables and filesystem permissions. - Rotate the Zotero API key promptly if dependency compromise is suspected.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (6)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The documented purpose says the skill saves papers to Zotero, but the detected behavior also includes downloading PDFs, uploading attachments, and creating notes containing AI-generated summaries. This mismatch is dangerous because users may consent to a narrower workflow while the skill performs additional external data transfers and persistence operations that affect privacy, storage, and content integrity.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill declares executable behavior that uses environment variables and network access, but it does not explicitly constrain or document those capabilities via a permissions or allowed-tools section. This weakens reviewability and increases the chance that an agent or user will authorize broader access than expected, especially since the skill handles credentials and communicates with external services.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The skill instructs users to store Zotero credentials in an environment variable but gives no guidance on sensitivity, rotation, least privilege, or avoiding accidental exposure in logs and shell history. Because the variable contains an API key in a simple userid:apiKey format, misuse or leakage could allow unauthorized access to the user's Zotero library.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The natural-language strings shown to users are entirely in Chinese, including the command description and argument help text. This imposes a specific language on all users without any opt-in, language selection, or documented region-specific justification.

Natural-Language Policy Violations

Low
Confidence
85% confidence
Finding
The user-facing description and usage instructions are entirely in Chinese, but the file does not indicate that the skill is intended only for Chinese-speaking users or provide an opt-in language choice. This can violate language or locale policy when a skill implicitly forces a specific language without user selection.

Missing User Warnings

Low
Confidence
82% confidence
Finding
This code accesses the sensitive `ZOTERO_CREDENTIALS` environment variable to obtain a library ID and API key. While the script errors if credentials are missing, it does not explicitly disclose in a comment, docstring, or user-facing description that it reads and uses stored credentials for remote API access.

Static analysis

No suspicious patterns detected.