Back to skill

Security audit

Pdf Invoice Parser

Security checks for vulnerabilities and agentic risk

Overview

This invoice parser is purpose-aligned, but it needs review because it recommends system-level dependency installation and writes untrusted invoice text into spreadsheet-ready files without formula-safety handling.

Install only in an isolated virtual environment, avoid --break-system-packages, review or pin dependencies before use, and treat generated CSV/JSON files as containing sensitive untrusted invoice data. Open CSV output with formula execution disabled or sanitize cells before sharing or importing into spreadsheets.

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/parse-invoice.py:82
Finding

Spreadsheet Formula Injection Through Unsanitized Invoice Data

Content
View full analysis

Vulnerability Details

File Location: scripts/parse-invoice.py:82-86, scripts/parse-invoice.py:180-205, and scripts/parse-invoices.py:69-91
Vulnerability Type: CSV/spreadsheet formula injection
Risk Level: Medium

Technical Analysis

Text extracted from an untrusted PDF is assigned directly to invoice fields. In particular, the first extracted line becomes the vendor name without any validation or spreadsheet-specific escaping:

python
# Vendor name (usually at top)
lines = text.strip().split('\n')
if lines:
    data['vendor_name'] = lines[0].strip()

The single-invoice writer then places the attacker-controlled fields directly into CSV cells:

python
def write_csv(data, output_path):
    """Write invoice data to CSV."""
    fieldnames = ['vendor_name', 'invoice_number', 'invoice_date', 'due_date',
                  'description', 'quantity', 'unit_price', 'line_total',
                  'subtotal', 'tax', 'total', 'currency']

    with open(output_path, 'w', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()

        for item in data['line_items']:
            row = {
                'vendor_name': data['vendor_name'],
                'invoice_number': data['invoice_number'],
                'invoice_date': data['invoice_date'],
                'due_date': data['due_date'],
                'description': item.get('description', ''),
                'quantity': item.get('quantity', ''),
                'unit_price': item.get('unit_price', ''),
                'line_total': item.get('line_total', ''),
                'subtotal': data['subtotal'],
                'tax': data['tax'],
                'total': data['total'],
                'currency': data['currency']
            }
            writer.writerow(row)

The consolidated writer has the same vulnerable data flow:

py
...[truncated 2629 chars]
Remediation
View remediation

Remediation Suggestions

  • Introduce a centralized sanitizer for every textual value written to CSV.
  • Treat cells beginning with =, +, -, or @ as unsafe after removing or examining leading whitespace and control characters.
  • Neutralize unsafe values according to the target spreadsheet's documented import behavior, commonly by prefixing them with an apostrophe.
  • Apply sanitization at the output boundary to all string fields, including vendor names, invoice numbers, descriptions, dates, and currency values.
  • Preserve an unsanitized structured format such as JSON only when consumers explicitly require original extracted text and understand that it is untrusted.
  • Add regression tests covering all formula prefixes, leading spaces, tabs, carriage returns, and line feeds.
  • Warn users that generated files contain untrusted invoice content and should be imported with formula execution disabled.

Example hardening pattern:

python
FORMULA_PREFIXES = ('=', '+', '-', '@')

def sanitize_spreadsheet_cell(value):
    value = '' if value is None else str(value)
    inspected = value.lstrip(' \t\r\n')
    if inspected.startswith(FORMULA_PREFIXES):
        return "'" + value
    return value

Apply this function to every value in row before calling writer.writerow(row).

T08 · Insecure Dependencies

Warning
Location
SKILL.md:24
Finding

Unpinned Dependencies Installed Into the System Python Environment

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:24-27
Vulnerability Type: Unsafe and non-reproducible dependency installation
Risk Level: Medium

Technical Analysis

The documented installation procedure resolves mutable package versions from the configured Python package index and explicitly bypasses operating-system package-management protections:

bash
# Install dependencies
pip install --break-system-packages PyPDF2 pymupdf pillow pytesseract

None of the packages are pinned to reviewed versions or protected with package hashes. Consequently, separate installations can retrieve materially different dependency versions. Python package installation may execute package build or installation logic, so compromise of a named package, its dependency chain, the configured index, or a future release could result in code execution during installation.

The --break-system-packages option also permits pip to modify an externally managed system Python environment. This expands the potential effect of dependency conflicts and can replace or destabilize packages used by other system applications. The exact privileges available depend on the account used to execute the command; the documented command does not itself invoke sudo.

Attack Path

  1. A user follows the dependency installation command from SKILL.md.
  2. Pip resolves the latest package versions and transitive dependencies available from its configured index because no versions or hashes are pinned.
  3. A compromised, substituted, or unexpectedly changed package is downloaded.
  4. Package build or installation logic executes with the privileges of the user running pip.
  5. Because system-package protections are disabled, installed files or dependency changes may affect the externally managed Python environment and other applications that use it.

Impact Assessment

A malicious dependency could execute arbitrary code with the privileges of the ...[truncated 523 chars]

Remediation
View remediation

Remediation Suggestions

  • Remove --break-system-packages from the installation instructions.
  • Require installation inside a dedicated virtual environment or another isolated runtime.
  • Pin all direct and transitive dependencies to reviewed versions.
  • Use a lock file or hash-verified requirements file with --require-hashes.
  • Configure pip to use an explicitly trusted package index and secure HTTPS certificate validation.
  • Regularly scan locked dependencies for known vulnerabilities and update them through a controlled review process.
  • Document supported Python and operating-system versions to improve reproducibility.

Example isolated installation workflow:

bash
python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install --require-hashes -r requirements.txt

The referenced requirements.txt should contain exact versions and cryptographic hashes for every direct and transitive dependency.

Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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 (4)

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

The code substantially matches the core declared purpose: it extracts structured invoice data from PDFs, supports scanned PDFs through OCR, and outputs CSV with vendor, invoice number, dates, line items, and totals. However, there are notable discrepancies. First, the description claims CSV/Excel output, but the code only writes CSV or prints JSON; there is no Excel generation. Second, the code has an undeclared JSON output mode. Third, the implementation is specifically for a single PDF invoice and uses invoice-specific heuristics, which is somewhat narrower than the broader description of handling 'PDF invoices and documents.' These differences are material enough to flag a mismatch, though the primary purpose is largely aligned.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This skill processes invoices, which commonly contain sensitive financial and personal data, yet it does not warn users that such data will be extracted and written to local output files. That omission increases the risk of accidental exposure, mishandling, or storage of regulated data in insecure locations, especially in shared environments or automated workflows.

Content

No source excerpt is available for this finding.

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 88)May include surrounding context.

Install system dependencies:

bash
# Ubuntu/Debian
sudo apt-get install -y tesseract-ocr

# macOS
brew install tesseract

Static analysis

No suspicious patterns detected.