Back to skill

Security audit

zotero-pdf-upload

Security checks for vulnerabilities and agentic risk

Overview

This Zotero skill appears purpose-built for Zotero uploads, but its setup can expose a powerful API key in command history and plaintext config.

Install only if you are comfortable granting Zotero API access to this skill. Prefer setting ZOTERO_API_KEY or using a separate restricted secret file instead of the documented one-line setup, create the least-privilege Zotero key you can, avoid broad group write permissions unless needed, and review config.json permissions if it contains a key.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/setup.py:40
Finding

Zotero API Key Exposed Through Command-Line Arguments and Plaintext Configuration

Content
View full analysis
" "" ``` ### Technical Analysis The setup workflow receives the Zotero API key through `sys.argv`. Command-line arguments can be exposed through shell history, process inspection utilities, operating-system auditing, terminal recordings, and command telemetry. The key is subsequently placed in the `apiKey` configuration field and written in plaintext to `config.json`. The code does not explicitly create or change the file to owner-only permissions. For a newly created file, effective permissions therefore depend on the process umask. For an existing file, `write_text()` generally retains its existing permissions. Although `.gitignore` excludes `config.json`, this only reduces accidental source-control commits. It does not protect the key from other local accounts, processes, backups, endpoint monitoring, or overly broad file permissions. The documentation recommends a key with library access, notes access, write access, and potentiall ...[truncated 1614 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • 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
Findings (9)

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file presents all operational instructions in Chinese and does not state that the user can choose another language. Under the policy criteria, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is clearly documented and justified.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
77% confidence
Finding

The documentation states at L061 that all write operations require explicit confirmation, suggesting a safety boundary. However, the setup instructions explicitly tell users to create an API key with write access, and this README section does not describe any enforcement mechanism for confirmation at the API credential level; this creates a contradiction between the safety claim and the documented operational setup.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill performs privileged actions involving environment access, local file reads/writes, and network requests, but it does not declare any explicit tool scope or permission boundaries. This increases the risk of overbroad execution in agent runtimes, because users and orchestrators cannot easily see or enforce what capabilities the skill is expected to use before it handles secrets and performs write operations against Zotero.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
95% confidence
Finding

The skill explicitly documents a workflow that sends the Zotero API key in the request URL path (/keys/{apiKey}), which can expose the secret to intermediary logs, proxies, browser history equivalents, and server access logging. Even though the file warns about this, the behavior still creates a real secret-leakage risk if the key is later harvested and reused to access or modify the user's Zotero library.

Content

Scanner excerpt · SKILL.md (reported line 66)May include surrounding context.

md
Never print full API keys in output.

Security note: when resolving a personal library URL (username-based, no numeric ID),
the skill calls `GET https://api.zotero.org/keys/{apiKey}` to look up the userID.
This is the standard Zotero API pattern — the key appears in the URL and may be
visible in server access logs. Use a least-privilege key and prefer env/file loading
over inline config.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The setup script takes a Zotero API key from the command line and writes it directly into config.json, creating persistent plaintext secret storage on disk. This increases the exposure surface because the key may be recovered from the file itself, backups, shell history, or accidental repository commits, and it is not necessary given the template already supports apiKeyEnv and apiKeyPath.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The script writes user-supplied credentials to disk without warning the user that the API key will be stored in plaintext in config.json. In this skill context, the key grants access to a Zotero personal or group library, so silent local persistence can lead to unauthorized library access if the file is exposed through local compromise, syncing, backups, or accidental sharing.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/zotero_client.py (reported line 144)May include surrounding context.

python
The Zotero API returns the userID associated with the given key.
    This is used when a personal URL contains a username instead of an ID.
    """
    url = f"https://api.zotero.org/keys/{api_key}"
    req = urllib.request.Request(
        url,
        method="GET",

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This code sends the Zotero API key to a remote endpoint as part of the request URL when resolving a user ID. Although the function has an internal docstring, there is no user-facing prompt, log, or warning that credentials will be used in a network call, which matches the missing-warning criterion for code handling sensitive credentials and outbound transmission.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The function reads a local PDF file and posts its bytes to a remote upload URL, transmitting user data off-system. The code contains no confirmation prompt, user-visible log/print, or nearby disclosure warning that local file contents and metadata will be uploaded, so this safety-relevant behavior lacks user warning within the code.

Content

No source excerpt is available for this finding.