Back to skill

Security audit

Freelance Invoice Generator

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent local invoice generator, but its HTML output can include unescaped invoice data, which could let malicious content run or alter the displayed invoice if the JSON input is untrusted.

Review before installing if you plan to generate HTML invoices from client-supplied, downloaded, or shared JSON files. Markdown output is lower risk, but the HTML renderer should escape all invoice fields before the skill is used for untrusted inputs or sent to recipients.

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
scripts/invoice_gen.py:94
Finding

Unescaped User-Controlled Data in Generated HTML Invoices

Content
View full analysis
str: subtotal, discount, tax, total = build_totals(job) currency = job.get("currency", "USD") f = job.get("from", {}) t = job.get("to", {}) rows = "".join( f"{i['description']}{i['quantity']}{currency} {i['rate']:,.2f}{currency} {i['quantity'] * i['rate']:,.2f}" f"" f"" for i in job["items"] ) extra_rows = "" if discount: extra_rows += f"Discount-{currency} {discount:,.2f}" if job.get("tax_rate_pct"): extra_rows += f"Tax ({job['tax_rate_pct']}%){currency} {tax:,.2f}" return f""" Invoice {job.get('invoice_number', '')} body {{ font-family: -apple-system, Helvetica, Arial, sans-serif; max-width: 720px; margin: 40px auto; color: #1a1a1a; }} table {{ width: 100%; border-collapse: collapse; margin-top: 16px; }} th, td {{ text-align: left; padding: 8px; border-bottom: 1px solid #ddd; }} th {{ background: #f5f5f5; }} .total {{ font-size: 1.2em; font-weight: bold; }} .meta {{ color: #555; }}

Invoice {job.get('invoice_number', '')}

Date: {job.get('date', datetime.now().date().isoformat())}  |  Due: {job.get('due_date', 'On receipt')}


From: {f.get('name', '')}
{f.get('email', '')}
{f.get('address', '')}

Bill To: {t.get('name', '')}
{t.get('email', '')}
{t.get('address', '')}

DescriptionQtyRateAmount {rows} {extra_rows}
Remediation
View remediation
`, `&`, single quotes, and double quotes are encoded and cannot create elements or attributes. 6. Consider adding a restrictive Content Security Policy to generated HTML as defense in depth. Output escaping remains mandatory because a policy alone does not prevent deceptive markup injection. ]]>
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • 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 (1)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill instructs users to run a local script that writes output files (--out invoice.md / invoice.html), but the manifest declares no permissions or allowed-tools scope. This creates an undeclared file-write capability, which weakens transparency and policy enforcement because an agent or reviewer cannot accurately tell from the skill metadata that filesystem modification is expected.

Content

No source excerpt is available for this finding.