Back to skill

Security audit

zotero-docx

Security checks for vulnerabilities and agentic risk

Overview

This skill appears to do the disclosed Zotero DOCX rewriting and validation work, with manageable risks when handling untrusted or very large documents.

Install only if you are comfortable with a local Python skill reading and rewriting the full DOCX you provide. Use it on trusted documents where possible, review outputs before relying on them, avoid very large or untrusted DOCX files, and understand that optional external validation may run LibreOffice/pdfinfo on the document.

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
zotero_docx.py:614
Finding

Unbounded DOCX Decompression Enables Memory-Exhaustion Denial of Service

Content
View full analysis

Vulnerability Details

File Location: zotero_docx.py, lines 614–616
Vulnerability Type: Unbounded decompression and memory consumption
Risk Level: Medium

Vulnerable Code

python
with zipfile.ZipFile(src) as zf:
    infos = zf.infolist()
    items = {i.filename: zf.read(i.filename) for i in infos}
    before_inv = _field_inventory(zf, ns)
    before_prefs = _prefs_of(zf)

Additional archive-controlled reads occur during precheck and validation without limits, including zf.read(...) calls at lines 138, 144, 149, and 163.

Technical Analysis

A DOCX file is a ZIP archive. The implementation accepts a user-supplied DOCX and reads every archive member into memory through ZipFile.read(). The decompressed contents are retained simultaneously in the items dictionary.

No limits are enforced on:

  • The number of ZIP members
  • The uncompressed size of an individual member
  • The aggregate uncompressed archive size
  • The ratio between compressed and uncompressed sizes
  • The amount of data read into process memory

Consequently, a small but highly compressible DOCX can expand to a very large size when processed. The initial precheck also decompresses selected XML members before the full archive-loading operation, but it does not apply size limits either.

This flaw does not provide code execution or additional system privileges. It enables resource exhaustion in the security context of the process handling the document.

Attack Path

  1. An attacker creates a ZIP archive with a .docx extension.
  2. The archive includes the minimum valid OOXML and Zotero field structures required to pass early format checks.
  3. The attacker adds one or more members containing extremely large, highly compressible data.
  4. The crafted document is supplied to process() as the source file.
  5. The code enumerates all members and executes zf.read(i.filename) for each entry.
  6. The decompressed data for every member is retained in the items dictiona ...[truncated 729 chars]
Remediation
View remediation

Remediation Suggestions

Apply archive limits before calling ZipFile.read():

  1. Inspect every ZipInfo entry and reject archives that exceed a defined member-count limit.
  2. Reject any individual member whose file_size exceeds a safe maximum.
  3. Sum all declared uncompressed sizes and reject archives exceeding an aggregate limit.
  4. Reject suspicious compression ratios, accounting safely for zero-byte compressed entries.
  5. Apply the checks before precheck functions read archive members.
  6. Read only the OOXML parts needed for the requested operation instead of loading every member into a dictionary.
  7. Use bounded streaming for large non-XML members where preservation is necessary.
  8. Run document processing under explicit memory and execution-time limits as defense in depth.
  9. Add regression tests covering:
    • A single high-compression-ratio member
    • Excessive aggregate uncompressed size
    • Excessive member count
    • Misleading declared archive structures
    • Boundary values at and immediately above each configured limit

A centralized validation function should be called immediately after opening the ZIP archive and before any member is decompressed.

Vulnerability Patterns
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared purpose is DOCX rewriting and Zotero-style preservation, but the skill also documents external program invocation for validation and comparison using LibreOffice and pdfinfo. This behavior mismatch is dangerous because operators may approve the skill expecting document-only processing, while it can trigger additional local execution paths and environment dependencies that expand the attack surface beyond what the description makes clear.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill describes Python usage plus external verification steps that imply file writes, shell execution, and potentially network access, but it does not declare any tool scope or allowed-tools constraints. In an agent setting, undeclared capabilities weaken sandboxing and user consent, increasing the chance that the skill writes files or invokes external components in ways the platform did not explicitly authorize.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The entire skill description and usage guidance are written only in Chinese, and there is no indication that users may choose another language or that the skill is intentionally limited to a Chinese-language audience for compliance or regional reasons. Under the stated policy, forcing a specific language without opt-in is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The entire skill guidance is written in Chinese, including imperative instructions such as '写代码前先读这里', with no indication that the user may choose another language or that the skill is intentionally limited to a Chinese-speaking context. This is a natural-language locale policy concern because it imposes a specific language without visible opt-in or justification in the file.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The document describes a mismatch between documented safety guarantees and actual behavior: users may rely on the claim that text inside revision marks will not be rewritten, while the implementation still modifies it. In a document-rewriting skill that must preserve Zotero fields and editorial structure, this can cause unintended alteration of tracked changes, undermine review integrity, and lead users to approve unsafe transformations based on false assumptions.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The top-level natural-language instructions are entirely in Chinese and explicitly tell the user how to run and interpret the test, without offering a language choice or documenting a justified locale restriction. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · verify_external.py (reported line 33)May include surrounding context.

python
return None, 0, None, '本机没有 LibreOffice,跳过'
    tmp = tempfile.mkdtemp()
    try:
        r = subprocess.run(
            [soffice, '-env:UserInstallation=file://%s/profile' % tmp,
             '--headless', '--convert-to', 'pdf', '--outdir', tmp, str(path)],
            capture_output=True, timeout=300)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · verify_external.py (reported line 44)May include surrounding context.

python
size = pdfs[0].stat().st_size
        pages = None
        if shutil.which('pdfinfo'):
            out = subprocess.run(['pdfinfo', str(pdfs[0])],
                                 capture_output=True, timeout=60).stdout.decode()
            for line in out.splitlines():
                if line.lower().startswith('pages:'):

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The file-level docstring and later user-facing exception messages are written exclusively in Chinese, indicating the skill presents its behavior and refusals in a fixed language. Under the policy rule, forcing a specific language without offering user choice or documenting a justified locale constraint is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.