Back to skill

Security audit

Zotero Ingest

Security checks for vulnerabilities and agentic risk

Overview

This Zotero skill mostly matches paper-ingestion use, but it under-discloses password-manager credential access and includes unsafe library deletion capability.

Review before installing. This skill should only be used if you intentionally want an agent to write to Zotero account 10425097 using the named 1Password secret, and you trust it with collection changes and item deletion. Prefer a version that reads ZOTERO_API_KEY from the environment, lets you configure the Zotero user ID, and removes or strongly confirms deletion operations.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (2)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
SKILL.md:50
Finding

Undeclared Password-Manager Credential Access

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 8–12, 23–26, and 50–51
Vulnerability Type: Undeclared credential retrieval that exceeds the documented environment-variable requirement
Risk Level: Medium

Vulnerable Code

yaml
primaryEnv: ZOTERO_API_KEY
requires:
  env:
  - ZOTERO_API_KEY
markdown
## Credentials

- **API Key:** `op read "op://OpenClaw/Zotero API Credentials/credential"`
- **User ID:** `10425097`
- **Base URL:** `https://api.zotero.org/users/10425097`
python
def get_zotero_key():
    return subprocess.check_output(['op', 'read', 'op://OpenClaw/Zotero API Credentials/credential']).decode().strip()

Technical Analysis

The skill metadata declares ZOTERO_API_KEY as its required credential, creating the expectation that the skill will use only a caller-supplied environment variable. The implementation instead invokes the local 1Password CLI and retrieves a credential from a fixed vault path.

This behavior expands the access boundary from a single explicitly supplied environment variable to the user's password-manager integration. It also binds the skill to a hard-coded Zotero account and credential record. If the local op session is authorized, the skill can obtain a write-capable key without using the credential the operator intentionally provided.

The credential is passed only to the official Zotero API in the documented code, and no evidence of credential exfiltration to an unrelated endpoint was found. Nevertheless, accessing an undeclared password-manager record violates least-privilege and transparent capability declaration principles.

Attack Path

  1. A user installs or invokes the skill based on metadata stating that only ZOTERO_API_KEY is required.
  2. The agent follows the provided implementation and calls get_zotero_key().
  3. subprocess.check_output() invokes the local op executable.
  4. The command requests `op:// ...[truncated 848 chars]
Remediation
View remediation

Remediation Suggestions

  1. Retrieve the declared environment variable directly:

    python
    import os
    
    def get_zotero_key():
        return os.environ["ZOTERO_API_KEY"]
    
  2. Remove the fixed 1Password vault reference from the skill.

  3. If optional 1Password integration is necessary:

    • Declare the op CLI and password-manager access explicitly in metadata.
    • Require affirmative user consent before invoking it.
    • Make the vault path configurable rather than account-specific.
    • Prefer an environment variable populated externally through 1Password secret references.
  4. Avoid hard-coding the Zotero user ID. Require an explicit ZOTERO_USER_ID configuration value and validate it before performing writes.

  5. Use a narrowly scoped Zotero API key granting only the permissions required for ingestion.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
SKILL.md:216
Finding

Destructive Zotero Item Deletion Exceeds the Skill's Declared Ingestion Scope

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 216–225
Vulnerability Type: Unrestricted destructive operation outside the declared paper-ingestion purpose
Risk Level: Medium

Vulnerable Code

python
def delete_item(item_key: str):
    key = get_zotero_key()
    base = "https://api.zotero.org/users/10425097"
    headers = {"Zotero-API-Key": key}
    req = urllib.request.Request(f"{base}/items/{item_key}", headers=headers)
    with urllib.request.urlopen(req) as r:
        version = json.loads(r.read())["version"]
    del_headers = {**headers, "If-Unmodified-Since-Version": str(version)}
    req = urllib.request.Request(f"{base}/items/{item_key}", headers=del_headers, method="DELETE")
    with urllib.request.urlopen(req) as r:
        return r.status

The skill's declared purpose is limited to adding papers:

yaml
description: Add papers (arXiv, DOI, URL) to Zotero via the Zotero REST API. Requires
  Zotero to be open for sync. Use when ingesting research papers into the Zotero library
  for citation management.

Technical Analysis

Item deletion is not required to ingest papers. The delete_item() function accepts an arbitrary item key, retrieves its current version, and immediately submits an authenticated HTTP DELETE request.

Fetching the current version provides concurrency protection but does not provide authorization or safety protection. The function does not:

  • Ask the user to confirm the deletion.
  • Check whether the item was created by the current operation.
  • Verify an expected auto-ingested tag.
  • Restrict deletion to a temporary or recoverable scope.
  • Validate the item type or ownership context.
  • Provide a dry-run mode or display the selected item before deletion.

Consequently, an erroneous instruction, a manipulated item key, or inappropriate agent tool selection can cause deletion of an existing library record unrelated to the in ...[truncated 1195 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove delete_item() from the ingestion skill because deletion is unnecessary for its declared purpose.
  2. If deletion must be supported, place it in a separate administrative skill with independently declared permissions.
  3. Require explicit, operation-specific user confirmation that displays the item key, title, type, and account before issuing DELETE.
  4. Restrict deletion to items created during the current execution, using a trusted in-memory set of returned item keys.
  5. As an additional safeguard, verify that the target contains an expected ingestion marker, while not relying on a mutable tag as the sole authorization control.
  6. Add a dry-run mode and make it the default.
  7. Use a Zotero API key that lacks deletion or broader library-management permissions when the API supports sufficiently granular access controls.
  8. Log destructive requests and their confirmed target metadata without logging the API key.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • 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 (11)

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The stated purpose is adding papers to Zotero, but the file also documents broader modification capabilities such as listing collections, moving items, creating collections, and deleting items. This scope expansion increases the risk that an agent granted permission for ingestion could perform unexpected library-management or destructive actions outside the user's intended trust boundary.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The manifest says the skill requires ZOTERO_API_KEY, but the examples actually fetch the key from 1Password using op read. This mismatch undermines operator understanding of what credentials and tools the skill will access, making review, sandboxing, and least-privilege enforcement harder.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill is documented as requiring a Zotero API key via environment variable, but the implementation instead invokes the 1Password CLI to retrieve credentials. Introducing a separate secret-management tool expands the skill's privilege surface and allows access to secrets beyond the declared capability, which is dangerous in an agent setting where tool use should be minimal and transparent.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · SKILL.md (reported line 27)May include surrounding context.

md
- **API Key:** `op read "op://OpenClaw/Zotero API Credentials/credential"`
- **User ID:** `10425097`
- **Base URL:** `https://api.zotero.org/users/10425097`

## Routing Rule (mandatory)

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · SKILL.md (reported line 65)May include surrounding context.

md
- **API Key:** `op read "op://OpenClaw/Zotero API Credentials/credential"`
- **User ID:** `10425097`
- **Base URL:** `https://api.zotero.org/users/10425097`

## Routing Rule (mandatory)

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · SKILL.md (reported line 134)May include surrounding context.

md
- **API Key:** `op read "op://OpenClaw/Zotero API Credentials/credential"`
- **User ID:** `10425097`
- **Base URL:** `https://api.zotero.org/users/10425097`

## Routing Rule (mandatory)

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · SKILL.md (reported line 161)May include surrounding context.

md
- **API Key:** `op read "op://OpenClaw/Zotero API Credentials/credential"`
- **User ID:** `10425097`
- **Base URL:** `https://api.zotero.org/users/10425097`

## Routing Rule (mandatory)

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · SKILL.md (reported line 175)May include surrounding context.

md
- **API Key:** `op read "op://OpenClaw/Zotero API Credentials/credential"`
- **User ID:** `10425097`
- **Base URL:** `https://api.zotero.org/users/10425097`

## Routing Rule (mandatory)

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · SKILL.md (reported line 205)May include surrounding context.

md
- **API Key:** `op read "op://OpenClaw/Zotero API Credentials/credential"`
- **User ID:** `10425097`
- **Base URL:** `https://api.zotero.org/users/10425097`

## Routing Rule (mandatory)

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · SKILL.md (reported line 218)May include surrounding context.

md
- **API Key:** `op read "op://OpenClaw/Zotero API Credentials/credential"`
- **User ID:** `10425097`
- **Base URL:** `https://api.zotero.org/users/10425097`

## Routing Rule (mandatory)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill includes a fully documented delete operation with no warning, dry-run mode, or requirement for explicit user confirmation. In an agent workflow, this creates a realistic risk of accidental or unauthorized permanent changes to the user's Zotero library.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.