Back to skill

Security audit

Agent Invoice Generator

Security checks for vulnerabilities and agentic risk

Overview

This invoice skill is mostly purpose-aligned, but it handles sensitive billing records with under-scoped file writes, misleading capability claims, and unsafe fallback output behavior.

Review before installing. This is not evidence of theft or a backdoor, but it deals with sensitive invoices and business details. Use it only in a controlled workspace, verify generated output format before sending invoices, avoid untrusted client/item text until HTML escaping is fixed, and do not rely on the advertised recurring or receipt features without additional implementation and approval controls.

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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/invoice.py:291
Finding
Path Traversal in Invoice Payment Status Update<![CDATA[ ## Vulnerability Details **File Location**: `scripts/invoice.py`, lines 291–299 **Vulnerability Type**: Path traversal with unauthorized JSON file modification **Risk Level**: Medium ### Vulnerable Code ```python def mark_paid(args): path = DATA_PATH / f"{args.id}.json" if not path.exists(): print(f"Invoice {args.id} not found.") return inv = json.loads(path.read_text()) inv["status"] = "paid" inv["paidDate"] = datetime.now().strftime("%Y-%m-%d") path.write_text(json.dumps(inv, indent=2)) print(f"Invoice {args.id} marked as paid.") ``` ### Technical Analysis The `paid` command accepts an invoice identifier through the `--id` argument. This value is incorporated directly into a filesystem path without validating that it has the expected invoice-number format or ensuring that the resolved path remains inside `DATA_PATH`. Directory traversal sequences such as `../` can escape the intended invoice directory. An absolute path can also replace the base path under standard `pathlib` path-joining behavior. If the selected target exists and contains a JSON object, the application reads it, adds or replaces the `status` and `paidDate` properties, and rewrites the entire file. Exploitation is constrained to files that are accessible to the current operating-system user and contain JSON that deserializes to an object. Nevertheless, the command crosses the intended invoice-storage boundary and can corrupt unrelated application state. ### Attack Path 1. The attacker or untrusted caller invokes the skill's `paid` command. 2. The attacker supplies a traversal-based identifier, such as: ```bash python3 scripts/invoice.py paid --id ../target ``` 3. The resulting path resolves to `~/.openclaw/target.json` rather than a file beneath `~/.openclaw/invoices`. 4. If the target exists and contains a JSON object, the program loads it. 5. The program inserts or overwrites the `status` and `paidDate` fields. 6. The mo ...[truncated 847 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate invoice identifiers against a strict allowlist pattern before constructing a path: ```python import re if not re.fullmatch(r"INV-\d{4}-\d{3,}", args.id): raise ValueError("Invalid invoice identifier") ``` 2. Resolve both the data directory and candidate path, then verify containment: ```python base = DATA_PATH.resolve() path = (base / f"{args.id}.json").resolve() if path.parent != base: raise ValueError("Invoice path is outside the data directory") ``` 3. Reject identifiers containing path separators, traversal components, or absolute paths. 4. Validate that loaded data is a dictionary before modifying it: ```python inv = json.loads(path.read_text()) if not isinstance(inv, dict): raise ValueError("Invalid invoice record") ``` 5. Add tests for `../`, absolute paths, nested paths, encoded traversal forms, malformed identifiers, invalid JSON, and JSON values that are not objects. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/invoice.py:167
Finding
Stored HTML Injection in Fallback Invoice Generation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/invoice.py`, lines 167–218 **Vulnerability Type**: Stored HTML and script injection through unescaped invoice fields **Risk Level**: Medium ### Vulnerable Code ```python html = f"""<!DOCTYPE html> <html><head><style> body {{ font-family: -apple-system, Arial, sans-serif; max-width: 800px; margin: 40px auto; padding: 20px; }} .header {{ border-bottom: 3px solid #2C3E50; padding-bottom: 20px; margin-bottom: 20px; }} .invoice-num {{ color: #2C3E50; font-size: 24px; }} table {{ width: 100%; border-collapse: collapse; margin: 20px 0; }} th {{ background: #2C3E50; color: white; padding: 10px; text-align: left; }} td {{ padding: 8px 10px; border-bottom: 1px solid #eee; }} .totals td {{ text-align: right; }} .total-row {{ font-weight: bold; border-top: 2px solid #333; }} .notes {{ margin-top: 30px; padding: 15px; background: #f8f9fa; border-radius: 4px; }} </style></head><body> <div class="header"> <h1>{invoice['business']['name']}</h1> <p>{invoice['business'].get('address', '')}<br>{invoice['business'].get('email', '')}</p> </div> <div class="invoice-num">INVOICE {invoice['number']}</div> <p>Date: {invoice['date']}<br>Due: {invoice['dueDate']}</p> <p><strong>Bill To:</strong><br>{invoice['client']}</p> <table> <tr><th>Description</th><th>Qty</th><th>Rate</th><th>Amount</th></tr> """ for item in invoice['items']: html += f"<tr><td>{item['description']}</td><td>{item['quantity']}</td><td>{sym}{item['rate']:.2f}</td><td>{sym}{item['amount']:.2f}</td></tr>\n" html += f"""</table> <table class="totals"> <tr><td colspan="3">Subtotal:</td><td>{sym}{invoice['subtotal']:.2f}</td></tr> """ if invoice.get('discount', 0) > 0: html += f"<tr><td colspan='3'>Discount ({invoice['discount']}%):</td><td>-{sym}{invoice['discountAmount']:.2f}</td></tr>\n" if invoice.get('tax', 0) > 0: html += f"<tr><td colspan='3'>Tax ({invoice['tax']}%):</td><td> ...[truncated 2715 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Escape every dynamic text value before inserting it into HTML: ```python from html import escape business_name = escape(str(invoice["business"]["name"]), quote=True) client = escape(str(invoice["client"]), quote=True) notes = escape(str(invoice.get("notes", "")), quote=True) description = escape(str(item["description"]), quote=True) ``` 2. Use a template engine configured with automatic HTML escaping rather than manually concatenating HTML strings. 3. Treat invoice values as plain text. Do not permit arbitrary markup unless there is an explicit requirement and a robust HTML sanitizer with a restrictive allowlist is used. 4. Escape all business configuration fields as well as invoice-specific fields. Persisted configuration must not be treated as inherently trusted. 5. Consider adding a restrictive Content Security Policy to fallback HTML as defense in depth: ```html <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:"> ``` 6. Add automated tests covering script elements, event-handler attributes, remote images, closing tags, quotes, ampersands, and malformed markup in every text field. ]]>
Vulnerability Patterns
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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
Findings (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The skill advertises capabilities that exceed what the implementation reportedly provides, while omitting persistent local storage behavior. This mismatch can cause agents and users to make unsafe assumptions about natural-language handling, output type, recurring automation, and data retention, increasing the risk of unintended file writes, silent persistence of sensitive business data, and operational misuse.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill documentation indicates file read/write behavior and persistent storage, but it does not declare any explicit tool scope such as allowed-tools or permissions. That creates a transparency and governance gap: an agent may invoke file-capable code without users or policy layers having clear, reviewable boundaries for what filesystem access is expected.

Session Persistence

Medium
Category
Rogue Agent
Content
---
name: invoice-generator
description: Generate professional PDF invoices from natural language or structured data. Use when the user asks to create an invoice, bill a client, generate a receipt, track payments, or manage invoicing. Supports line items, tax calculation, discounts, multiple currencies, recurring invoices, and payment tracking. Outputs clean PDF invoices ready to send.
---

# Invoice Generator
Confidence
90% confidence
Finding
The skill explicitly supports tracking payments and managing invoicing, which implies session or local persistence of financial workflow state. Persistence itself is not inherently malicious, but without clear disclosure, retention limits, and access controls, stored invoice/payment state can create confidentiality and integrity risks for financial records.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The activation text is broad enough to match many common business requests, which can cause the skill to trigger in contexts where invoice generation or local file creation was not the user's intent. Over-broad activation increases the chance of unnecessary access to business data, unintended document generation, or accidental persistence of sensitive information.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The setup section stores business identity and contact details in a local config file under the user's home directory without prominently warning about that persistence. Business metadata may be sensitive, and silent retention can violate user expectations, especially in shared systems or managed environments.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The output section states that invoice files are written to the user's Documents folder but does not warn about this side effect. Writing potentially sensitive invoices to a broadly accessible default location can expose client names, billing amounts, and payment terms to other local users, backups, sync services, or accidental sharing.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill describes automatic invoice delivery and cron-based recurring generation without any safety warning or approval model. Automation involving document creation and delivery can lead to unauthorized or erroneous invoices being generated and sent at scale, especially if configuration or recipient data is wrong.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The function advertises PDF generation but silently degrades to writing an HTML file when reportlab is unavailable. In an invoicing skill, this can mislead users and downstream automation into treating output as a finalized PDF invoice when it is actually editable/rendered HTML, increasing risk of accidental disclosure, tampering, or workflow failure.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The skill metadata promises receipt generation and recurring invoices, but the code only defines CLI arguments for recurring invoices and never implements the functionality; receipt generation is also absent. In this context, capability overstatement is dangerous because users or agent workflows may rely on payment tracking or recurring billing actions that never occur, causing missed invoices, accounting errors, or false assumptions about business state.

Static analysis

No suspicious patterns detected.