Back to skill

Security audit

Local Budget

Security checks for vulnerabilities and agentic risk

Overview

This skill is a local budgeting helper that reads user-provided financial CSVs and writes user-directed local JSON or Markdown reports, with privacy and Markdown-rendering caveats users should understand.

Install only if you are comfortable processing bank or card exports through your agent and storing generated local files. Keep outputs in a private local folder, avoid synced/shared vaults unless intended, and review generated Markdown before opening it in renderers that auto-load remote images.

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/report.py:196
Finding

Unescaped Transaction Data Allows Markdown Content Injection

Content
View full analysis
0: remaining = monthly_budget - spent pct = spent / monthly_budget * 100 pct_str = f"{pct:.0f}%" remaining_str = fmt_currency(remaining) if remaining >= 0 else f"**-{fmt_currency(abs(remaining))}**" alert = "⚠️" if spent > monthly_budget else ("🔶" if pct >= 80 else "✅") bar = bar_chart(spent, monthly_budget) lines.append(f"| {category} | {fmt_currency(spent)} | {fmt_currency(monthly_budget)} | {remaining_str} | {pct_str} | {alert} {bar} |") else: lines.append(f"| {category} | {fmt_currency(spent)} | — | — | — | — |") else: pct = fmt_pct(spent, stats["total_spent"]) lines.append(f"| {category} | {fmt_currency(spent)} | {pct} |") ``` ```python for i, (merchant, amount) in enumerate(stats["top_merchants"], 1): # Truncate long merchant names name = merchant[:50] + "…" if len(merchant) > 50 else merchant lines.append(f"| {i} | {name} | {fmt_currency(amount)} |") ``` Unknown bank categories are also propagated without validation: ```python mapped = bank_category_map.get(original_category.lower()) if mapped: return (mapped, "medium") # Unknown bank category — include it but flag as low confidence return (original_category, "low") ``` ### Technical Analysis Merchant descriptions originate from imported CSV records, and unknown bank categories are retained as supplied. These fields are inserted directly into Markdown tables without escaping Markdo ...[truncated 2001 chars]
Remediation
View remediation
str: text = str(value) text = text.replace("\\", "\\\\") text = text.replace("|", "\\|") text = text.replace("\r", " ").replace("\n", " ") return text ``` 2. Apply the function to every user-controlled value before inserting it into a Markdown table: ```python safe_category = escape_markdown_table_cell(category) safe_merchant = escape_markdown_table_cell(name) lines.append( f"| {safe_category} | {fmt_currency(spent)} | {pct} |" ) lines.append( f"| {i} | {safe_merchant} | {fmt_currency(amount)} |" ) ``` 3. If links, images, and inline HTML are not required, additionally neutralize or reject relevant Markdown constructs. Escaping must match the behavior of the intended Markdown renderer. 4. Normalize all carriage returns and line feeds in CSV-derived fields before categorization or report generation. 5. Validate category names against an explicit allowlist. Unknown bank categories should map to `Other` while retaining the original value only as non-rendered metadata, or they should be safely escaped before display. 6. Safely serialize YAML frontmatter rather than interpolating values directly, particularly values derived from file names or input data. 7. Add regression tests covering: - Pipe characters in merchant and category values. - Embedded CR/LF sequences. - Markdown links and image syntax. - Inline HTML. - Backslashes and other escaping edge cases. ]]>
Vulnerability Patterns
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (5)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The code is related to the declared domain of bank/credit card CSV handling and is fully local, so it is not unrelated or suspicious. However, the declared description claims a broader finance-analysis skill: spending tracking, categorization with LLM reasoning, budget comparison, and markdown report generation. This code chunk implements only a CSV ingestion/parsing utility that normalizes transactions into JSON. That is a materially narrower behavior than the declared primary purpose. Therefore this is a description-to-behavior mismatch, likely because the provided chunk is just one component of a larger skill, but on its own it does not fulfill the claimed analysis/reporting capabilities.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill instructs the agent to generate JSON and markdown outputs on disk, including saving reports into an Obsidian vault, but it does not declare an explicit tool scope such as file-write permissions. That mismatch weakens least-privilege controls and can allow broader file writes than users or the platform may expect, especially given the sensitive financial nature of the data.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The trigger phrases are broad enough to match ordinary finance conversations, increasing the chance the skill activates when a user did not intend to process sensitive bank or credit-card data. In context, accidental invocation is more serious because the skill deals with highly sensitive financial records and can create local artifacts containing that data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The instructions repeatedly direct the user to save parsed transactions, categorized JSON, and markdown reports to disk or directly into an Obsidian vault without warning that these files contain sensitive financial data. This can lead to unintentional long-term storage, syncing, backup exposure, or indexing of bank and credit-card details in locations the user considers convenient but not secure.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The manifest says categorization uses LLM reasoning, but this reference file describes categorize.py as regex-based and then states that the LLM should review certain transactions before report generation. In this file, that LLM-review instruction appears as intent/documentation without corresponding operational detail, creating a documentation-level divergence from the concrete process described here.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.