Back to skill

Security audit

Finance Tracker

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent local personal finance tracker, but users should understand it stores sensitive financial records locally and has some security hardening gaps.

Reasonable to install for single-user local finance tracking. Before using it for sensitive records, know that data is stored in plaintext under ~/.finance-tracker; tighten file permissions or avoid use on shared machines, review due recurring items before automating heartbeat processing, and be careful opening exported CSV files in spreadsheet apps if descriptions could contain untrusted text.

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
lib/storage.py:25
Finding

Sensitive Financial Records Are Created Without Restrictive Permissions

Content
View full analysis
Dict[str, Any]: try: with open(self.income_file) as f: return json.load(f) except: return {"currency": "UZS", "income": []} def _save_income(self, data: Dict[str, Any]): with open(self.income_file, 'w') as f: json.dump(data, f, indent=2, ensure_ascii=False) ``` ```python # lib/goals.py:22-26, 44-46 if data_dir is None: data_dir = Path.home() / ".finance-tracker" self.data_dir = Path(data_dir) self.data_dir.mkdir(parents=True, exist_ok=True) def _save(self, data: Dict[str, Any]): with open(self.goals_file, 'w') as f: js ...[truncated 2380 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
lib/storage.py:176
Finding

Spreadsheet Formula Injection in CSV Export

Content
View full analysis
str: """Export all transactions as CSV.""" data = self._load_json() lines = ["date,category,amount,description"] for tx in data["transactions"]: date = datetime.fromisoformat(tx["date"]).strftime("%Y-%m-%d %H:%M") lines.append(f'{date},{tx["category"]},{tx["amount"]},"{tx["description"]}"') return "\n".join(lines) ``` ### Technical Analysis The transaction description and category are included in CSV output without spreadsheet formula neutralization. The description is user-controlled through commands such as `finance add` and `finance edit`. Cells beginning with formula-significant characters such as `=`, `+`, `-`, or `@` may be interpreted as formulas when the exported content is opened in spreadsheet software. Wrapping the description in double quotes does not prevent formula evaluation. The implementation also constructs CSV records manually instead of using a standards-compliant CSV writer. Embedded double quotes, carriage returns, or newlines in a description are therefore not escaped correctly and can alter the exported row structure. This can help place attacker-controlled content into additional spreadsheet cells. Actual formula capabilities depend on the spreadsheet application and its security configuration. Potential effects include external network requests, disclosure of spreadsheet data through formula-generated URLs, misleading rendered content, and use of dangerous legacy formula features where enabled. ### Attack Path 1. An attacker causes a crafted description to be recorded. This may occur through an integration that converts chat messages into Finance Tracker commands or by persuading the victim to import or enter supplied text. 2. The crafted value begins with a spreadsheet f ...[truncated 1313 chars]
Remediation
View remediation
str: data = self._load_json() output = io.StringIO(newline="") writer = csv.writer(output, quoting=csv.QUOTE_ALL) writer.writerow(["date", "category", "amount", "description"]) for tx in data["transactions"]: date = datetime.fromisoformat(tx["date"]).strftime("%Y-%m-%d %H:%M") writer.writerow([ safe_spreadsheet_cell(date), safe_spreadsheet_cell(str(tx["category"])), tx["amount"], safe_spreadsheet_cell(str(tx["description"])), ]) return output.getvalue() ``` 2. Neutralize every attacker-controlled field that starts with a spreadsheet formula marker: ```python def safe_spreadsheet_cell(value: str) -> str: if value and value[0] in ("=", "+", "-", "@"): return "'" + value return value ``` 3. Consider leading whitespace, tabs, carriage returns, and line feeds when implementing the policy, because some spreadsheet programs ignore such characters before detecting formulas. 4. Apply neutralization before CSV serialization. CSV quoting alone is insufficient because spreadsheet applications can evaluate formulas in quoted cells. 5. Add tests for values containing: - Leading `=`, `+`, `-`, and `@` - Double quotes - Commas - Carriage returns and newlines - Leading tabs or spaces followed by formula characters 6. Document whether CSV exports are intended to preserve literal text or formulas. For financial transaction exports, literal text should be the enforced default. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (18)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill explicitly recommends automated execution of finance recurring process, which creates new transaction records without clearly warning the user that this command performs state-changing writes. In an agent or heartbeat context, hidden automatic writes can lead to unintended financial logs, confusing audit trails, and repeated duplicate entries if the automation is misconfigured or triggered unexpectedly.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill prints income amounts with a fixed 'UZS' currency label regardless of user preference, and the help/examples repeatedly assume UZS as the default locale. This imposes a specific regional format rather than consistently offering user choice, which is a natural-language locale policy concern.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · lib/currency.py (reported line 99)May include surrounding context.

python
# Try backup API
        try:
            url = "https://api.exchangerate.host/latest?base=USD"
            req = urllib.request.Request(url, headers={"User-Agent": "FinanceTracker/1.0"})
            with urllib.request.urlopen(req, timeout=5) as response:
                data = json.loads(response.read().decode())

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill stores financial goal information under ~/.finance-tracker/goals.json, which affects user data persistence and privacy. While file storage is part of the functionality, there is no visible confirmation prompt or explicit disclosure in nearby docstrings/comments that user financial data will be saved locally.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The remove_goal method irreversibly removes a user's saved financial goal and writes the updated file immediately. There is no confirmation prompt, warning message, or documentation indicating that this action is destructive.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill creates persistent storage under the user's home directory and later stores portfolio and income records there, which can contain highly sensitive personal financial information. Although the code performs file writes and the docstrings describe tracking functionality, there is no explicit user-facing warning, comment, or disclosure that this data will be saved locally in plaintext JSON files.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The default portfolio and income data are initialized with currency set to "UZS", which imposes a specific locale-related default on all users. There is no natural-language explanation that the skill is region-specific and no visible opt-in or configuration path presented here for users who need a different currency.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The class initializes a default data directory under the user's home folder and creates it automatically, then writes recurring expense data to recurring.json. There is no confirmation prompt, visible log/print, or inline warning to disclose that persistent financial data will be stored locally.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

process_due() automatically logs every due recurring expense by calling log_recurring() without any confirmation, dry-run, or authorization check at the point of execution. In a finance tool, automatic creation of transaction records can corrupt financial history or be triggered unintentionally by another component, making integrity loss the primary risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This module automatically creates a hidden directory in the user's home folder and stores transaction data in JSON and Markdown files. Although the code has developer-facing docstrings, there is no user-facing prompt, confirmation, or disclosure that potentially sensitive financial data will be persisted locally.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The undo_last method removes stored transaction data and updates both backing files, and the operation is potentially destructive from a user perspective. The code does not include any confirmation prompt, warning, or user-visible notice before performing the deletion.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The delete_transaction method permanently removes a transaction from persistent JSON and Markdown storage. There is no built-in confirmation, warning, or visible disclosure in this code path to alert the user that stored financial data will be deleted.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The skill advertises live exchange-rate refreshes and auto-conversion behavior but does not disclose that this may require outbound network access. In agent environments, undocumented network calls can violate user expectations, leak metadata such as timing or locale-related usage, and break sandbox or policy assumptions.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The categorization logic embeds English and Uzbek keywords across categories, which implicitly privileges those languages for correct detection. There is no natural-language indication that the skill is limited to these locales or that users can choose another language, which can violate a language/locale policy requiring opt-in or explicit justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The generated report uses UZS currency formatting and an example command with a specific monetary convention, which implicitly fixes the skill to one locale. The file does not indicate that this is optional, configurable, or justified as a region-specific tool.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The confirmation formatter defaults to the currency code "UZS", which embeds a locale-specific assumption into user-facing output. The policy allows locale constraints when they are explicitly justified or user-selectable, but this file provides neither.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The storage initialization sets the currency to "UZS" by default, and the Markdown file also presents UZS as the active currency. This imposes a locale-specific default in user-facing behavior without offering an explicit choice or explaining why the skill is region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The generated Markdown log explicitly displays "Currency: UZS" as the default user-facing format. This is a locale-specific assumption that is not presented as an opt-in choice and is not justified in the file as a region-limited tool.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.