Back to skill

Security audit

Personal Finance

Security checks for vulnerabilities and agentic risk

Overview

This finance CSV tool mostly does what it says, but its optional export can overwrite user-writable files and is less safely scoped than its docs claim.

Review this skill before installing. It appears offline and purpose-aligned, but only run it on CSVs you trust, choose export paths carefully, avoid opening generated CSVs in spreadsheets when input rows may be attacker-controlled, and do not rely on its current output-path safety claim to prevent overwrites.

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
personal-finance.sh:250
Finding
Output Path Validation Can Be Bypassed to Overwrite Arbitrary Writable Files<![CDATA[ ## Vulnerability Details **File Location**: `personal-finance.sh`, lines 250-260 **Vulnerability Type**: Improper path validation and unsafe file overwrite **Risk Level**: Medium ### Vulnerable Code ```python out_path = Path(OUTPUT_PATH) if out_path.exists(): print(f"Warning: {out_path} already exists and will be overwritten.", file=sys.stderr) if out_path.is_absolute() and not str(out_path).startswith(str(Path.home())): raise FinanceError(f"Output path must be in user home directory for safety") out_path.parent.mkdir(parents=True, exist_ok=True) with out_path.open("w", newline="", encoding="utf-8") as ostream: writer = csv.DictWriter(ostream, fieldnames=headers) ``` ### Technical Analysis The output-path security check is applied only when the supplied path is absolute. A relative path containing parent-directory components, such as `../../target`, is accepted even when its resolved destination is outside the user's home directory. The string-prefix comparison also does not provide a reliable directory-containment check. For example, a path whose textual prefix resembles the home path is not necessarily inside that directory. Furthermore, the code does not resolve symlinks before validation. A path inside the allowed directory can therefore be a symbolic link to a file elsewhere. The file is opened in `"w"` mode, which truncates an existing file. The warning shown for an existing path does not require confirmation and does not prevent the overwrite. ### Attack Path 1. An attacker influences the `--output` argument supplied to the skill. 2. The attacker supplies a relative traversal path, for example: ```sh ./personal-finance.sh categorize \ --csv transactions.csv \ --output ../../target-file ``` 3. Because the path is relative, the absolute-path restriction is skipped. 4. `mkdir()` creates missing parent directories where permitted. 5. `open("w")` creates or truncates the resolved target file. Alternatively, ...[truncated 672 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve both the home directory and requested output path before performing validation: ```python home = Path.home().resolve() out_path = Path(OUTPUT_PATH).expanduser().resolve(strict=False) try: out_path.relative_to(home) except ValueError as exc: raise FinanceError("Output path must be inside the user home directory") from exc ``` 2. Reject symbolic-link output files and validate existing parent directories for symlinks before writing. 3. Use exclusive creation mode (`"x"`) by default so existing files cannot be silently truncated. 4. If overwriting is required, introduce an explicit `--force` option and require it whenever the destination exists. 5. Create the file with restrictive permissions appropriate for financial data, such as mode `0600`, and ensure newly created directories are not broadly accessible. 6. Add tests covering relative traversal, similarly prefixed directories, symbolic links, nonexistent nested directories, and attempted overwrites. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
personal-finance.sh:261
Finding
Generated CSV Files Are Vulnerable to Spreadsheet Formula Injection<![CDATA[ ## Vulnerability Details **File Location**: `personal-finance.sh`, lines 261-266 **Vulnerability Type**: CSV/spreadsheet formula injection **Risk Level**: Medium ### Vulnerable Code ```python with out_path.open("w", newline="", encoding="utf-8") as ostream: writer = csv.DictWriter(ostream, fieldnames=headers) writer.writeheader() for row in categorized: if "account_number" in row: row["account_number"] = mask_account(row["account_number"]) writer.writerow(row) ``` ### Technical Analysis Transaction fields originating in the input CSV are written to the generated CSV without spreadsheet-specific sanitization. This includes attacker-controlled values such as `description`, `merchant`, and preexisting `category` fields. Categories loaded from a custom rules file can also reach the export. When a cell begins with a formula-triggering character such as `=`, `+`, `-`, or `@`, spreadsheet applications may interpret the value as a formula rather than plain text. Standard CSV quoting performed by `csv.DictWriter` does not reliably neutralize formula interpretation. The precise consequences depend on the spreadsheet application and its security configuration. Potential effects include external network requests, disclosure of spreadsheet data, misleading displayed content, and invocation of dangerous legacy spreadsheet functionality. ### Attack Path 1. An attacker supplies or modifies a transaction CSV so that a textual field contains a spreadsheet formula, for example: ```csv date,description,merchant,amount,account_number 2025-01-01,"=HYPERLINK(""https://attacker.example/"",""Open receipt"")",Merchant,-10.00,12345678 ``` 2. A user processes the file and exports categorized results: ```sh ./personal-finance.sh categorize \ --csv attacker-controlled.csv \ --output categorized.csv ``` 3. The payload is copied unchanged into the output file by `writer.writerow(row)`. 4. The user op ...[truncated 725 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Sanitize every textual cell before writing spreadsheet-compatible CSV output. 2. Treat cells beginning with `=`, `+`, `-`, `@`, tab, carriage return, or line feed as potentially dangerous after removing or accounting for leading whitespace. 3. Prefix dangerous textual values with an apostrophe or another spreadsheet-safe neutralization character. Apply this consistently to all user-controlled fields, including descriptions, merchants, categories, dates, and account identifiers. 4. Keep numeric transaction amounts as validated numeric values rather than applying text sanitization indiscriminately to legitimate negative amounts. 5. Clearly document whether an export is intended for machine processing or interactive spreadsheet use. Consider providing a dedicated hardened spreadsheet-safe export mode. 6. Add regression tests using formula payloads in every exported textual column and verify behavior in supported spreadsheet applications. ]]>
Vulnerability Patterns
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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
Findings (3)

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
for rule in rules:
        for keyword in rule["keywords"]:
            if keyword in text:
                return rule["category"]
    return "Uncategorized"
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill advertises file read and write behavior but does not declare an explicit tool scope such as permissions or allowed-tools. That creates an authorization ambiguity: an agent or runtime may permit broader filesystem access than users expect, especially because `--output` enables writing arbitrary paths. In a personal-finance context, undeclared file access is more sensitive because inputs contain financial data and account information.

Vague Triggers

Low
Confidence
90% confidence
Finding
This JSON manifest defines activation-like matching keywords for categories. The keyword "deposit" is overly broad everyday language and could match many unrelated transactions, making the trigger scope insufficiently specific.

Static analysis

No suspicious patterns detected.