Back to skill

Security audit

Personal Expense Tracker

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent expense tracker, but it silently creates extra local files containing personal spending summaries that users are not told about.

Install only if you are comfortable with expense records being stored locally in plaintext. Before using it with real financial data, remove or change the stats side effect that writes data/sample.json, restrict file permissions on the data directory, and make sure the skill asks before saving or exporting summaries.

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

Note
Location
scripts/expense-tracker.py:9
Finding

Financial records are stored in plaintext without enforced restrictive permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/expense-tracker.py, lines 9-10, 23-39, and 136-146
Vulnerability Type: Insecure local storage and insufficient file-permission enforcement
Risk Level: Low

Vulnerable Code

python
DATA_DIR = Path.home() / ".expense-tracker"
DATA_FILE = DATA_DIR / "expenses.json"
python
def init_data():
    """Initialize data directory"""
    DATA_DIR.mkdir(exist_ok=True)
    if not DATA_FILE.exists():
        with open(DATA_FILE, 'w', encoding='utf-8') as f:
            json.dump([], f)

def load_expenses():
    """Load all expenses"""
    init_data()
    with open(DATA_FILE, 'r', encoding='utf-8') as f:
        return json.load(f)

def save_expenses(expenses):
    """Save expenses"""
    with open(DATA_FILE, 'w', encoding='utf-8') as f:
        json.dump(expenses, f, ensure_ascii=False, indent=2)
python
    sample_data = {
        "month": current_month,
        "total": total,
        "count": len(month_expenses),
        "by_category": category_stats
    }

    sample_file = Path(__file__).parent.parent / "data" / "sample.json"
    sample_file.parent.mkdir(exist_ok=True)
    with open(sample_file, 'w', encoding='utf-8') as f:
        json.dump(sample_data, f, ensure_ascii=False, indent=2)

Technical Analysis

The application stores expense descriptions, amounts, categories, dates, timestamps, and monthly aggregates as unencrypted JSON. Neither the private data directory nor the generated files are assigned explicit restrictive permissions.

Path.mkdir(exist_ok=True) and open(..., "w") rely on the process-wide umask. On a system with a permissive umask, the resulting directory or files may be readable by other local users. Existing files with unsafe permissions are also reused without correction.

The stats command creates a second financial-data file at data/sample.json inside the project tree. U ...[truncated 1919 chars]

Remediation
View remediation

Remediation Suggestions

  1. Create the private data directory with mode 0700 and verify or correct its permissions when it already exists.
  2. Create the expense file with mode 0600, rather than relying on the ambient umask.
  3. Write updates atomically to a temporary file in the protected directory, set mode 0600, flush and synchronize the data, and then replace the destination with os.replace.
  4. Refuse to read or overwrite unexpected symbolic links and verify that the resolved data path remains inside the intended protected directory.
  5. Store generated statistics under ~/.expense-tracker rather than inside the project tree, or avoid persisting the summary when it is needed only for display.
  6. Add generated data paths to .gitignore as defense in depth.
  7. Document the data location, plaintext storage behavior, retention policy, and deletion procedure so users can make an informed privacy decision.
  8. If the threat model includes compromised backups or storage media, support encryption at rest using an operating-system credential store or a well-reviewed encryption library with keys kept separately from the data.
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 (9)

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The title and primary description are presented in Chinese only, and the usage examples also rely on Chinese input text. This can constitute a language/locale policy violation because the skill appears to require a specific language without user opt-in or an explicit justification that it is region-specific.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill advertises executable behavior that appears to read and write local data, but it does not declare any explicit tool scope or permissions boundary. That creates an authorization and transparency gap: the agent may invoke file operations without clear user/admin review, increasing the chance of unintended data access or modification.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill description is broad enough to overlap with ordinary finance conversation, which can cause accidental invocation when a user is merely asking for advice or discussing spending. If invoked unintentionally, the skill could perform state-changing actions like recording or analyzing expenses without clear user intent.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The example phrase is highly conversational and resembles normal chat about monthly spending, making accidental matching more likely. In context, this is more dangerous because the skill handles personal-finance data and may infer, analyze, or persist sensitive spending information based on ambiguous requests.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill's natural-language behavior is tied to Chinese-only keywords and Chinese output messages, which effectively forces a specific language/locale. There is no opt-in, configuration, or documented justification that this is intended as a region-specific tool.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The stats command writes real user-derived monthly spending data to a second file under the project tree instead of the declared per-user data store. This creates an unexpected data sink that can expose sensitive financial summaries to other users, backup/sync processes, or packaging workflows without the user's knowledge.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The monthly statistics feature silently persists a user-derived summary to a separate JSON file without warning, consent, or manifest disclosure. In a personal finance skill, even aggregated category totals are sensitive and the hidden persistence expands the privacy and leakage surface beyond what a user would reasonably expect from viewing stats.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The usage scenarios and expected outputs are presented in Chinese-only examples, which suggests a language-specific interaction pattern. Under the language/locale policy, this should either offer opt-in language choice or clearly document that the skill is intentionally limited to a Chinese-speaking context.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The code comment labels the output as 'sample data for reference', but the file actually contains real spending totals and category breakdowns derived from the user's expense history. Mislabeling sensitive output increases the risk that developers or users treat the file as harmless test data and share or publish it inadvertently.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.