Back to skill

Security audit

Docxtpl

Security checks for vulnerabilities and agentic risk

Overview

The skill is generally a document-generation helper, but its batch script can let CSV data control output paths and write outside the chosen folder.

Review this skill before installing if you plan to batch-render from CSV or TSV files you did not create. Avoid using --overwrite with untrusted data, inspect ID columns for path characters, and run the scripts in a limited working directory or sandbox. Pin docxtpl in controlled environments if reproducible installs matter.

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

Error
Location
scripts/docxtpl-render-batch.py:80
Finding

CSV-Controlled Path Traversal in Batch Output Filenames

Content
View full analysis

Vulnerability Details

File Location: scripts/docxtpl-render-batch.py, lines 80–100
Vulnerability Type: Path traversal and arbitrary file write
Risk Level: High

Vulnerable Code

python
for i, row in enumerate(rows):
    base_name = row[args.id_column].strip()
    if not base_name:
        print(f"Warning: Row {i} has empty {args.id_column}, skipping", file=sys.stderr)
        skipped += 1
        continue

    output_path = out_dir / f"{base_name}{args.suffix}.docx"
    if output_path.exists() and not args.overwrite:
        print(f"Skip (exists): {output_path}", file=sys.stderr)
        skipped += 1
        continue

    if args.dry_run:
        print(f"[DRY RUN] Would generate: {output_path}")
        generated += 1
        continue

    tpl.render(row, autoescape=args.autoescape)
    tpl.save(str(output_path))
    tpl.reset_replacements()

Technical Analysis

The value used as base_name comes directly from the attacker-controllable CSV or TSV ID column. It is combined with the output directory without validating filename separators, parent-directory components, or absolute paths.

A value such as ../../outside/report causes the resulting path to escape the intended output directory. An absolute identifier can cause pathlib to disregard the configured output directory entirely. The application does not resolve the final path and verify that it remains beneath out_dir.

The --overwrite option increases the impact by allowing an existing writable target to be replaced. Without that option, the script can still create a new file at an unintended writable location if the destination and its parent directories are available.

Attack Path

  1. An attacker creates or modifies the CSV/TSV data file used for batch rendering.
  2. The attacker places a traversal path or absolute path in the configured ID column, for example:
    csv
    id,name
    ../../outside/report,Alice
    
  3. A user runs docxtpl-render-batch.py with ...[truncated 965 chars]
Remediation
View remediation

Remediation Suggestions

  1. Treat the ID column as a filename identifier rather than a path:
    • Reject absolute paths.
    • Reject /, \, .., null bytes, and platform-specific path separators.
    • Allow only a conservative character set such as letters, digits, underscores, and hyphens.
  2. Resolve and validate the final output path before writing:
    python
    import re
    
    safe_id_pattern = re.compile(r"^[A-Za-z0-9_-]+$")
    
    base_name = row[args.id_column].strip()
    if not safe_id_pattern.fullmatch(base_name):
        raise ValueError(f"Unsafe output identifier: {base_name!r}")
    
    root = out_dir.resolve()
    output_path = (root / f"{base_name}{args.suffix}.docx").resolve()
    
    try:
        output_path.relative_to(root)
    except ValueError:
        raise ValueError("Output path escapes the configured output directory")
    
  3. Validate args.suffix under the same filename policy because it also contributes to the resulting path.
  4. Perform containment validation regardless of whether --overwrite is enabled.
  5. Prefer exclusive creation for non-overwrite operations and avoid relying solely on a separate exists() check, which can introduce a time-of-check/time-of-use race.
  6. Add tests covering parent traversal, absolute paths, alternate separators, symbolic links, and valid identifiers.

T08 · Insecure Dependencies

Note
Location
SKILL.md:16
Finding

Unpinned Third-Party Dependency Installation

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 16–19
Vulnerability Type: Unpinned dependency and supply-chain exposure
Risk Level: Low

Vulnerable Code

bash
pip install docxtpl
# For subdocuments support:
pip install "docxtpl[subdoc]"

Technical Analysis

The installation instructions request docxtpl without a fixed version, constraints file, lock file, or package hashes. Consequently, installation resolves whatever compatible release and transitive dependencies are available from the configured Python package index at installation time.

This makes installations non-reproducible and permits code that was not part of the audited project state to be installed later. The reviewed package name appears consistent with the declared project, so there is no evidence of deliberate typosquatting or dependency confusion. The risk arises from unrestricted future dependency resolution and possible upstream compromise or unexpected breaking changes.

Attack Path

  1. A user follows the documented installation command.
  2. pip queries the configured package index and resolves the latest available docxtpl package and its transitive dependencies.
  3. A future compromised, malicious, or unexpectedly incompatible release is selected because no audited version or hash is required.
  4. Package installation hooks or imported package code execute with the privileges of the user running the installation or rendering scripts.

This path depends on compromise or unsafe modification of an upstream package or package index; no such compromise was identified in the audited repository.

Impact Assessment

If an upstream dependency is compromised, malicious package code could execute with the permissions of the installing or invoking user. Potential impact could include access to files, environment variables, and network resources available to that user.

The repository itself contains no evidence of a malicious dependency name, custom packag ...[truncated 179 chars]

Remediation
View remediation

Remediation Suggestions

  1. Pin docxtpl to a reviewed version:
    bash
    python3 -m pip install "docxtpl==0.20.1"
    
    The selected version should be verified against the version actually tested by the project.
  2. Maintain a lock or constraints file that pins transitive dependencies.
  3. Use hash verification, for example through a requirements file generated with trusted tooling:
    text
    docxtpl==<reviewed-version> --hash=sha256:<verified-hash>
    
  4. Install dependencies in an isolated virtual environment rather than into the system Python environment.
  5. Configure a trusted package index explicitly in controlled deployments and review dependency updates before changing pins.
  6. Add automated dependency vulnerability and integrity scanning to the release process.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (3)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The CLI example includes -o with an inline # overwrite note, but it does not clearly warn that this may replace an existing output document and affect user data. Because this is markdown guidance for a potentially destructive file operation, a more explicit warning is warranted.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · references/official-docs.md (reported line 293)May include surrounding context.

optional arguments: -h, --help Show help and exit -o, --overwrite Overwrite existing output without confirmation -q, --quiet Suppress unnecessary messages

text

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill documentation instructs users to save rendered output with doc.save("output.docx") and presents a workflow ending in doc.save(path), which affects user files. In a markdown skill description, file-writing behavior should be disclosed with a brief warning when it could affect user data or existing documents.

Content

No source excerpt is available for this finding.