Back to skill

Security audit

docx-md

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent DOCX review utility that clearly discloses local document reading, editing, and finalization, though users should handle sensitive documents and final outputs carefully.

Install only in an isolated Python environment, use pinned dependencies if possible, and run it on copies of documents. Do not send confidential contracts or business documents to an external LLM unless that provider is approved for the data, and treat finalize as a deliberate cleanup step that removes review metadata from the output file.

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/read_docx.py:195
Finding
Unhardened XML Parsing and Unbounded DOCX Archive Processing<![CDATA[ ## Vulnerability Details **File Location**: `scripts/read_docx.py:195-224`; `scripts/apply_edits_docx.py:410-419` **Vulnerability Type**: Unsafe processing of attacker-controlled XML and ZIP archive members **Risk Level**: Medium ### Vulnerable Code `scripts/read_docx.py:195-224`: ```python def _parse_comments(zipf: zipfile.ZipFile) -> list[dict[str, Any]]: try: data = zipf.read("word/comments.xml") except KeyError: return [] root = etree.fromstring(data) out = [] for c in root.findall(f".//{{{W_NS}}}comment"): out.append({ "id": _attr(c, "id"), "author": _attr(c, "author"), "date": _attr(c, "date"), "text": _text(c).strip(), }) return out def read_docx(path: str | Path) -> dict[str, Any]: """Read docx and return standard structure: { body, comments, path }. Body blocks have type, segments, blockIndex.""" path = Path(path) if not path.exists(): return {"error": f"File not found: {path}"} try: with zipfile.ZipFile(path, "r") as z: doc = z.read("word/document.xml") comments = _parse_comments(z) except Exception as e: return {"error": str(e)} root = etree.fromstring(doc) ``` `scripts/apply_edits_docx.py:410-419`: ```python with zipfile.ZipFile(docx_path, "r") as z_in: doc_bytes = z_in.read("word/document.xml") try: comments_bytes = z_in.read("word/comments.xml") comments_root = etree.fromstring(comments_bytes) except KeyError: comments_root = None comments_bytes = None doc_root = etree.fromstring(doc_bytes) ``` ### Technical Analysis DOCX files are ZIP archives containing XML resources. Both scripts treat the DOCX as potentially arbitrary input, load selected archive members entirely into memory with `ZipFile.read()`, and then parse those bytes through `etree.fromstring()` without explicitly ...[truncated 2389 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create and consistently use an explicitly hardened parser: ```python def secure_xml_parser(): return etree.XMLParser( resolve_entities=False, load_dtd=False, no_network=True, huge_tree=False, recover=False, ) root = etree.fromstring(data, parser=secure_xml_parser()) ``` 2. Reject XML containing a `DOCTYPE` declaration before parsing: ```python if b"<!DOCTYPE" in data.upper(): raise ValueError("DOCTYPE declarations are not permitted") ``` 3. Consider using `defusedxml` where compatible to obtain defensive handling for common XML denial-of-service and entity attacks. 4. Inspect `ZipInfo` metadata before reading members. Enforce conservative limits for: - Maximum individual uncompressed member size - Maximum total uncompressed size - Maximum member count - Maximum compression ratio 5. Read archive members through a bounded streaming routine rather than loading unlimited data with `ZipFile.read()`. 6. Apply document-complexity limits after parsing, including maximum nesting depth, element count, text size, and output size. 7. Process untrusted documents in a sandbox with memory, CPU, execution-time, and filesystem-access restrictions. 8. Add regression tests using oversized archive members, high-compression payloads, deeply nested XML, internal entity expansion, external entity declarations, and malformed XML. ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Mutable and Unhashed Third-Party Dependency Resolution<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-2`; `SKILL.md:71`; `README.md:113` **Vulnerability Type**: Non-reproducible dependency installation without integrity verification **Risk Level**: Low ### Vulnerable Configuration `requirements.txt:1-2`: ```text lxml>=4.9.0 docx-revisions>=0.1.3 ``` `SKILL.md:71`: ```markdown **Requires**: `pip install docx-revisions` (see `requirements.txt`) ``` `README.md:113`: ```markdown - `lxml`: `pip install lxml` ``` ### Technical Analysis The dependency file specifies only minimum versions, while the documentation recommends installing packages by name without a fixed version or package hash. Each installation can therefore resolve to a different future release. This creates a mutable supply-chain boundary: the audited source does not determine which dependency code will execute. A compromised upstream release, compromised package-publishing account, unsafe package-index configuration, or unreviewed incompatible release could be selected automatically. The package names themselves are consistent between the source and documentation, and the audit found no evidence of typosquatting or an intentionally malicious dependency. The weakness is the absence of reproducible version and integrity controls. ### Attack Path 1. A user follows the documented installation command or installs from `requirements.txt`. 2. The package resolver queries the configured Python package index. 3. Because only lower bounds or no versions are specified, the resolver selects the latest compatible releases available at installation time. 4. If a selected release or configured index has been compromised, malicious package code can execute during installation, import, or normal Skill operation. 5. The dependency code runs with the same filesystem and process privileges as the user or service executing the installation and Skill. ### Impact Assessment A compromised dependency could execute arbitrary Python code w ...[truncated 435 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin each dependency to an exact reviewed version: ```text lxml==<reviewed-version> docx-revisions==<reviewed-version> ``` 2. Generate and verify cryptographic hashes for all direct and transitive dependencies. Install with: ```bash python -m pip install --require-hashes -r requirements.txt ``` 3. Maintain a lock file generated by a dependency-management tool such as `pip-tools`, including resolved transitive dependencies and hashes. 4. Configure installation to use a trusted, explicitly selected package index. Avoid untrusted extra indexes that can introduce dependency-confusion risk. 5. Perform routine vulnerability and provenance review of locked releases before updating them. 6. Update `README.md` and `SKILL.md` so installation examples use the reviewed lock or requirements file rather than unversioned package-name commands. 7. Execute dependency installation in an isolated virtual environment and avoid installing packages with elevated system privileges. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (15)

Hidden Instructions

High
Category
Prompt Injection
Content
| Goal | Action |
|------|--------|
| **Get document for AI** | **Read**: run read script → Markdown (default) or JSON. Markdown includes `<!-- b:N -->` blockIndex markers for edit targeting. |
| **Apply AI edits to docx** | **Modify**: run apply script with docx + edits JSON → new docx with track changes and comments. |
| **Deliver final version** | **Finalize**: run finalize script → new docx with no revisions/comments. |
Confidence
70% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Missing User Warnings

High
Confidence
97% confidence
Finding
The finalize guidance explicitly describes irreversible operations: accepting all revisions and removing all comments, which can permanently discard audit history, reviewer annotations, and recoverable context. In a document-review skill that directly modifies DOCX files, lack of any warning, confirmation, or backup expectation increases the chance of accidental or silent destruction of important document metadata.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The README explicitly advertises operations that modify DOCX files and a finalize step that accepts revisions and removes comments, but it does not warn users that these actions can irreversibly alter source documents or erase review metadata. In a document-review workflow, this omission increases the risk of accidental destruction of audit trails, comments, and tracked changes if users run the commands on original files or misunderstand the semantics of finalize.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill documentation describes scripts that read and write local files, including modifying and finalizing DOCX documents, but the manifest declares no explicit tool scope such as permissions or allowed-tools. This creates an overly broad and implicit capability boundary, which can allow downstream agents to invoke file operations without clear least-privilege constraints or policy review.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The document explicitly recommends sending long DOCX content such as contracts to an LLM, but provides no warning about privacy, confidentiality, or data-handling risks. In a document-review skill, this omission can lead users to transmit sensitive legal or business content to external model providers without understanding retention, logging, or cross-border processing implications.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This markdown states that the program converts edit JSON to OOXML and writes back to the DOCX, which can alter user documents. The description does not include any warning about overwriting files, preserving backups, or confirming potentially destructive edits.

Tainted flow: 'cleaned' from pathlib.Path.read_bytes (line 69, file read) → pathlib.Path.write_bytes (file write)

Medium
Category
Data Flow
Content
# Step 2: remove comment markup via regex on raw bytes
        cleaned = _remove_comments_raw(tmp_path.read_bytes())
        out_path.write_bytes(cleaned)
    finally:
        tmp_path.unlink(missing_ok=True)
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Content
args = ap.parse_args()
    out = read_docx(args.docx)
    if "error" in out:
        print(json.dumps(out, ensure_ascii=False), file=__import__("sys").stderr)
        __import__("sys").exit(1)

    if args.format == "json":
Confidence
75% confidence
Finding
Dynamic __import__() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Content
out = read_docx(args.docx)
    if "error" in out:
        print(json.dumps(out, ensure_ascii=False), file=__import__("sys").stderr)
        __import__("sys").exit(1)

    if args.format == "json":
        s = json.dumps(out, ensure_ascii=False, indent=2)
Confidence
75% confidence
Finding
Dynamic __import__() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Natural-Language Policy Violations

Low
Confidence
83% confidence
Finding
Line L75 uses Chinese field values ("原文本", "修改后文本", "修改依据") as the sole example, which can imply a language-specific workflow. The file does not state that the skill is region-specific or that users may choose their preferred language or locale.

Unpinned Dependencies

Low
Category
Supply Chain
Content
lxml>=4.9.0
docx-revisions>=0.1.3
Confidence
94% confidence
Finding
The dependency is specified with only a lower bound, so builds may resolve to different versions over time and pull in unexpected or vulnerable releases. In a document-processing skill, this increases supply-chain and reproducibility risk because parsing untrusted .docx/XML content depends on a native/complex library with a history of security issues.

Unverifiable Dependency: lxml has 14 known advisory(ies) (CVE-2021-43818 (lxml's HTML Cleaner allows crafted and SVG embedded scripts to pass through); CVE-2014-3146 (lxml Cross-site Scripting Via Control Characters); CVE-2021-28957 (lxml vulnerable to Cross-Site Scripting ) +11 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
91% confidence
Finding
lxml has multiple historical advisories, and because the manifest does not pin an exact version, it is impossible to verify from this file alone whether deployment will use a fixed or vulnerable release. This is more concerning in a low-level document conversion tool because lxml processes attacker-controlled document/XML structures, so vulnerable parser-related behavior could be reachable depending on usage.

Unpinned Dependencies

Low
Category
Supply Chain
Content
lxml>=4.9.0
docx-revisions>=0.1.3
Confidence
89% confidence
Finding
The docx-revisions package is also unpinned, which means installs are not reproducible and could silently pick up a compromised or breaking release. Because this skill reads and writes .docx files with revision handling, trust in dependency integrity matters and supply-chain tampering could affect document contents or processing behavior.

Intent-Code Divergence

Low
Confidence
85% confidence
Finding
The docstring for _flatten_paragraph_content says comment nodes are skipped for accept_revision, which is true only for paragraph anchors in document.xml. However, the overall accept flow in this file does not remove corresponding comment entries from comments.xml, so comments are not actually removed from the document package as the broader behavior implies.

Description-Behavior Mismatch

Low
Confidence
92% confidence
Finding
The manifest describes three operations, including a separate finalize capability that accepts revisions and removes comments. In this file, the CLI presents itself only as applying contract-review edits, yet the implementation also supports an "accept_revision" operation that finalizes tracked changes for a paragraph.

Static analysis

No suspicious patterns detected.