Back to skill

Security audit

zotero-enhanced

Security checks for vulnerabilities and agentic risk

Overview

This Zotero skill is a disclosed library-management helper, with expected risks around Zotero credentials, remote library changes, and optional WebDAV file handling.

Install only if you are comfortable giving the skill a Zotero API key and, if used, WebDAV credentials. Prefer a Zotero key with the minimum scopes you need, use dry-run and backup for destructive actions, avoid --no-confirm except in trusted automation with prior consent, and only use WebDAV storage you control and trust.

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/read.sh:85
Finding

Insufficient validation of WebDAV ZIP archives before extraction

Content
View full analysis
/dev/null | grep -qE '(^|/)\.\.(/|$)|^/'; then echo "Error: zip archive contains unsafe path entries; refusing to extract." >&2 exit 1 fi unzip -q "$LOCAL_ZIP_PATH" -d "$TMP_DIR" ``` `scripts/read_universal.sh:146-150`: ```bash if unzip -Z1 "$ZIP_PATH" 2>/dev/null | grep -qE '(^|/)\.\.(/|$)|^/'; then echo "Error: zip archive contains unsafe path entries; refusing to extract." >&2 exit 1 fi unzip -q "$ZIP_PATH" -d "$TMP_DIR" ``` ### Technical Analysis Both readers download ZIP archives from the configured WebDAV server and extract them with `unzip`. The validation only examines entry names for absolute paths and explicit `..` path components. This check does not verify: - Whether entries are symbolic links or other special file types. - Whether a symbolic-link target points outside the temporary directory. - Whether later archive entries are extracted through a previously created link. - The number of archive entries. - The total uncompressed size or compression ratio. - Whether the resolved destination of every extracted file remains under `TMP_DIR`. On `unzip` implementations or configurations that restore symbolic links and subsequently follow them during extraction, a crafted archive may use link semantics to cause files to be written outside the intended temporary directory. Independently of symbolic-link behavior, an archive with a very large expanded size can exhaust local disk space because no extraction quota is enforced. The archive originates from user-configured WebDAV storage. Consequently, exploitation requires compromise or malicious control of that storage, its credentials, or the stored attachment archive. ### Attack Path 1. An attacker obtains ...[truncated 1599 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (62)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The description presents a broader Zotero management skill than this code chunk actually implements. This script only handles adding a PDF into Zotero using a WebDAV-based upload workflow. It does not query Crossref or arXiv, does not search items, and does not manage notes. Although the description says it supports both cloud and WebDAV storage, this script explicitly errors unless WebDAV credentials are provided and directs users to a different script for cloud storage. Reading the local PDF to extract text/title is consistent with adding PDFs, but the main mismatch is that several declared capabilities are absent while storage support is narrower than claimed.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The code substantially matches part of the description: it performs Zotero library insertion, reads a PDF, extracts DOI/arXiv identifiers, fetches metadata from Crossref/arXiv, and uploads attachments via WebDAV. However, the declared description overstates capabilities relative to this specific code chunk. The script explicitly requires WebDAV credentials and exits if they are absent, so it does not support cloud storage here. It also contains no functionality for item search or note management. These are material discrepancies in capability, not merely omitted implementation details.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The code is broadly consistent with the Zotero PDF import and metadata auto-fetch portion of the description: it reads a supplied PDF, extracts DOI/arXiv/title information, calls Crossref/arXiv, and uploads to Zotero with optional WebDAV storage. However, the declared purpose describes a broader skill including item search, file read, and note management, none of which are implemented in this code chunk. More importantly, the script clearly performs creation and upload operations to external services (Zotero API and optionally WebDAV), while the declared permissions are empty, which materially understates the behavior. Therefore this is a description/behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The description presents a multi-feature Zotero management skill, but the supplied code only supports reading a PDF attachment from Zotero via WebDAV. It does not fetch metadata from Crossref/arXiv, search items, manage notes, or perform broader library management actions. WebDAV support is present, but the claimed broader cloud/storage and management capabilities are not evidenced in this code chunk. Therefore the declared description materially overstates and misrepresents the actual behavior shown.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

The supplied code chunk does not exhibit undeclared risky behavior; it only interacts with the Zotero API to fetch and patch an existing note. However, the declared description presents a much broader skill covering library management, PDF metadata retrieval, search, file reading, and storage backends. This code chunk implements only a subset of that description—specifically note management, and even within that, only updating existing notes and tags. Because the declared purpose materially overstates the implemented capabilities in this chunk and the actual primary function is much narrower, this is a description/behavior mismatch.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
92% confidence
Finding

A no-confirm deletion parameter is a classic dangerous tool option because it removes a human checkpoint from an irreversible action. In an LLM-agent environment, parameter abuse can be triggered by prompt mistakes, ambiguous user requests, or malicious task framing, leading to remote data deletion.

Content

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

md
- **Network**: scripts talk only to the Zotero API (`api.zotero.org`), Crossref, and arXiv; WebDAV scripts additionally talk to your configured WebDAV server. `read_universal.sh` sends the API key only to `https://*.zotero.org` download URLs.
- **Credentials**: passed exclusively via environment variables. `ZOTERO_WEBDAV_URL` must be `https://` — plaintext `http://` is refused so credentials never travel unencrypted.
- **Temporary files**: created in unpredictable per‑run `mktemp -d` directories (mode 0700) and removed on exit; downloaded archives are checked for path‑traversal entries before extraction.
- **Deletion is irreversible**: `delete_note.sh --no-confirm` deletes without asking — intended for non‑interactive automation that has already obtained user consent; verify with `--dry-run` first. `--backup` saves note content to `~/.zotero-backup/` (directory 0700, files 0600) at the user's explicit request.

## Storage Modes

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
92% confidence
Finding

The documented --no-confirm invocation provides a ready-made unsafe parameter set for destructive automation. Because the target system is a user's Zotero library, misuse can silently remove notes and degrade research records.

Content

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

md
bash scripts/delete_note.sh "NOTE_KEY"

# Delete without confirmation (use with caution):
bash scripts/delete_note.sh --no-confirm "NOTE_KEY"

# Backup before deleting:
bash scripts/delete_note.sh --backup "NOTE_KEY"

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

This safety-features section confirms that the tool intentionally supports bypassing confirmation, which is precisely the kind of parameter that increases agent-side misuse risk. The danger is amplified by the irreversible nature of deletion and the possibility of unattended execution.

Content

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

text

#### Safety Features
- **Confirmation prompt**: Requires manual confirmation. `--no-confirm` skips the prompt for non‑interactive automation — the caller is responsible for having user consent; prefer `--dry-run` first
- **Backup option**: Saves note content to `~/.zotero-backup/` before deletion (directory 0700, backup files 0600)
- **Version checking**: Prevents deletion if note was modified by another process
- **Dry-run mode**: Preview deletion without actually deleting

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
_to_zotero_universal.sh`, `read.sh`, `read_universal.sh`, `check_deps.sh`) and `SKILL.md`.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
85% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

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

md
--help, -h        Show this help
  --version         Show version
  --dry-run         Show steps without deleting
  --no-confirm      Skip confirmation prompt
  --backup          Save note content to file before deleting

Environment variables:

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
85% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · scripts/delete_note.sh (reported line 26)May include surrounding context.

sh
--help, -h        Show this help
  --version         Show version
  --dry-run         Show steps without deleting
  --no-confirm      Skip confirmation prompt
  --backup          Save note content to file before deleting

Environment variables:

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
85% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · scripts/delete_note.sh (reported line 34)May include surrounding context.

sh
--help, -h        Show this help
  --version         Show version
  --dry-run         Show steps without deleting
  --no-confirm      Skip confirmation prompt
  --backup          Save note content to file before deleting

Environment variables:

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
85% confidence
Finding

The implemented '--no-confirm' path allows irreversible deletion of a note without an interactive safety check. In an agent or automation context, this increases the risk that an upstream prompt injection, misrouted task, or scripting mistake could trigger destructive actions silently against the user's Zotero data.

Content

Scanner excerpt · scripts/delete_note.sh (reported line 51)May include surrounding context.

sh
DRY_RUN=true
            shift
            ;;
        --no-confirm)
            NO_CONFIRM=true
            shift
            ;;

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill exposes shell-based capabilities and documents operations that read local files, contact external services, and modify/delete Zotero data, but it declares no explicit tool scope or permission boundaries. In an agent ecosystem, missing scope metadata can cause over-broad execution or prevent meaningful policy enforcement, increasing the chance of unintended destructive or networked actions.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
90% confidence
Finding

The --no-confirm flag is a concrete mechanism for autonomous destructive action, not just neutral documentation. In the context of note deletion, the impact is data loss rather than code execution, but the risk is still material because agent systems can chain such flags into unattended workflows.

Content

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

md
- **Network**: scripts talk only to the Zotero API (`api.zotero.org`), Crossref, and arXiv; WebDAV scripts additionally talk to your configured WebDAV server. `read_universal.sh` sends the API key only to `https://*.zotero.org` download URLs.
- **Credentials**: passed exclusively via environment variables. `ZOTERO_WEBDAV_URL` must be `https://` — plaintext `http://` is refused so credentials never travel unencrypted.
- **Temporary files**: created in unpredictable per‑run `mktemp -d` directories (mode 0700) and removed on exit; downloaded archives are checked for path‑traversal entries before extraction.
- **Deletion is irreversible**: `delete_note.sh --no-confirm` deletes without asking — intended for non‑interactive automation that has already obtained user consent; verify with `--dry-run` first. `--backup` saves note content to `~/.zotero-backup/` (directory 0700, files 0600) at the user's explicit request.

## Storage Modes

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
90% confidence
Finding

The --no-confirm flag is a concrete mechanism for autonomous destructive action, not just neutral documentation. In the context of note deletion, the impact is data loss rather than code execution, but the risk is still material because agent systems can chain such flags into unattended workflows.

Content

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

md
- **Network**: scripts talk only to the Zotero API (`api.zotero.org`), Crossref, and arXiv; WebDAV scripts additionally talk to your configured WebDAV server. `read_universal.sh` sends the API key only to `https://*.zotero.org` download URLs.
- **Credentials**: passed exclusively via environment variables. `ZOTERO_WEBDAV_URL` must be `https://` — plaintext `http://` is refused so credentials never travel unencrypted.
- **Temporary files**: created in unpredictable per‑run `mktemp -d` directories (mode 0700) and removed on exit; downloaded archives are checked for path‑traversal entries before extraction.
- **Deletion is irreversible**: `delete_note.sh --no-confirm` deletes without asking — intended for non‑interactive automation that has already obtained user consent; verify with `--dry-run` first. `--backup` saves note content to `~/.zotero-backup/` (directory 0700, files 0600) at the user's explicit request.

## Storage Modes

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
88% confidence
Finding

The example explicitly teaches deletion without confirmation, which facilitates autonomous destructive behavior. In a skill that interfaces with remote library data, reducing friction for irreversible deletion raises the chance of accidental or unauthorized data removal by an agent or automation wrapper.

Content

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

md
ZOTERO_API_KEY="<api_key>" \
bash scripts/delete_note.sh "NOTE_KEY"

# Delete without confirmation (use with caution):
bash scripts/delete_note.sh --no-confirm "NOTE_KEY"

# Backup before deleting:

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
88% confidence
Finding

The direct example of delete_note.sh --no-confirm lowers the barrier to autonomous deletion and may be copied verbatim into automation. Because deletion is irreversible, misuse or prompt-induced invocation can remove user notes without an opportunity to intervene.

Content

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

md
bash scripts/delete_note.sh "NOTE_KEY"

# Delete without confirmation (use with caution):
bash scripts/delete_note.sh --no-confirm "NOTE_KEY"

# Backup before deleting:
bash scripts/delete_note.sh --backup "NOTE_KEY"

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
86% confidence
Finding

Although framed as a safety feature, this section still affirms support for skipping confirmation in non-interactive automation. In agent settings, that can normalize destructive autonomy and undermine user intent verification.

Content

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

text

#### Safety Features
- **Confirmation prompt**: Requires manual confirmation. `--no-confirm` skips the prompt for non‑interactive automation — the caller is responsible for having user consent; prefer `--dry-run` first
- **Backup option**: Saves note content to `~/.zotero-backup/` before deletion (directory 0700, backup files 0600)
- **Version checking**: Prevents deletion if note was modified by another process
- **Dry-run mode**: Preview deletion without actually deleting

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

Installation (Debian/Ubuntu)

bash
sudo apt-get update
sudo apt-get install -y curl jq poppler-utils zip

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

Installation (Debian/Ubuntu)

bash
sudo apt-get update
sudo apt-get install -y curl jq poppler-utils zip

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
83% confidence
Finding

This changelog entry documents the risk of --no-confirm rather than introducing it, so it is less dangerous than the live usage sections. However, it still evidences the presence of a destructive non-interactive path in the skill.

Content

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

md
- **Security fixes** (scan findings T09/TM1/EA2 on v1.3.12):
  - `delete_note.sh --backup`: backup directory now created with mode 0700 and backup files set to 0600 (were default umask).
  - `add_to_zotero.sh` / `add_to_zotero_enhanced.sh`: no longer claim “file stored in Zotero cloud” in cloud mode — **these scripts never had a cloud upload path**. They now fail fast with a clear error when WebDAV is not fully configured, before creating any item; use `add_to_zotero_universal.sh` for Zotero cloud storage.
  - SKILL.md/SECURITY.md: `--no-confirm` documented as a risk note (unattended deletion), added a Safety & Security Notes section, corrected the false “supports Zotero cloud” claims for both legacy adder scripts, and qualified the backup exception in SECURITY.md.
- Script versions synced to v1.3.13.

### v1.3.12 (2026‑09‑16)

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% 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/add_to_zotero.sh (reported line 87)May include surrounding context.

sh
PARENT_PAYLOAD="[{\"itemType\": \"journalArticle\", \"title\": $TITLE_ESCAPED, \"creators\": []}]"
API_URL="https://api.zotero.org/users/$ZOTERO_USER_ID/items"

PARENT_RESPONSE=$(curl -s -f \
  -H "Zotero-API-Key: $ZOTERO_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$PARENT_PAYLOAD" \

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% 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/add_to_zotero.sh (reported line 101)May include surrounding context.

sh
PARENT_PAYLOAD="[{\"itemType\": \"journalArticle\", \"title\": $TITLE_ESCAPED, \"creators\": []}]"
API_URL="https://api.zotero.org/users/$ZOTERO_USER_ID/items"

PARENT_RESPONSE=$(curl -s -f \
  -H "Zotero-API-Key: $ZOTERO_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$PARENT_PAYLOAD" \

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% 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/add_to_zotero_enhanced.sh (reported line 330)May include surrounding context.

sh
PARENT_PAYLOAD="[{\"itemType\": \"journalArticle\", \"title\": $TITLE_ESCAPED, \"creators\": []}]"
API_URL="https://api.zotero.org/users/$ZOTERO_USER_ID/items"

PARENT_RESPONSE=$(curl -s -f \
  -H "Zotero-API-Key: $ZOTERO_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$PARENT_PAYLOAD" \

Static analysis

No suspicious patterns detected.