Back to skill

Security audit

Aoineco Ledger — AI Agent Financial Tracking Engine

Security checks for vulnerabilities and agentic risk

Overview

This financial ledger skill is mostly coherent, but it overstates key protections for sensitive financial data and budget controls.

Review before installing if you plan to rely on this for real spending controls or sensitive financial records. Treat its files as plaintext local records, do not assume encryption, and do not use the advertised budget blocking as an enforcement boundary without fixing the implementation.

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
scripts/ledger_engine.py:204
Finding

Budget limits are not enforced and can be bypassed

Content
View full analysis

Vulnerability Details

File Location: scripts/ledger_engine.py:204-223, scripts/ledger_engine.py:283-304, and scripts/ledger_engine.py:464-477
Related Documentation: SKILL.md:33
Vulnerability Type: Business-logic flaw in budget enforcement
Risk Level: Medium

Technical Analysis

The documentation states that the ledger warns at 80 percent utilization and blocks spending at 100 percent:

markdown
| **Budget Alerts** | Set limits per category/period — warns at 80%, blocks at 100% |

However, log_expense() only prints alerts and adds a warning tag. It unconditionally persists the transaction afterward:

python
def log_expense(self, amount: float, vendor: str, description: str = "",
                category: str = "Other", agent: str = "", 
                currency: str = "USD", account: str = "default",
                context: str = "", tags: list = None) -> Transaction:
    """Log an expense transaction."""
    tx = self._create_tx(
        amount=amount, direction="expense", vendor=vendor,
        description=description, category=category, agent=agent,
        currency=currency, account=account, context=context,
        tags=tags or []
    )
    
    # Check budget
    alerts = self._check_budgets(category, agent)
    if alerts:
        tx.tags.append("BUDGET_WARNING")
        for alert in alerts:
            print(f"⚠️ {alert}")
    
    self._save_tx(tx)
    return tx

The budget check calculates spending from transactions that have already been saved. The pending transaction is not included in spent, so a transaction that crosses the limit may not even generate an immediate warning:

python
def _check_budgets(self, category: str, agent: str) -> list[str]:
    """Check if spending is near/over budget."""
    alerts = []
    
    for budget in self._budgets:
        if budget.category != category:
            continue
     
...[truncated 2915 chars]
Remediation
View remediation

Remediation Suggestions

  1. Calculate projected spending by adding the pending transaction amount before evaluating the threshold:
    python
    spent = self._get_period_spending(category, budget.period, agent)
    projected_spending = spent + amount
    
  2. Change the budget-checking interface to accept the pending amount and return an explicit allow-or-deny result.
  3. Refuse to call _save_tx() when projected spending exceeds a hard limit. Raise a dedicated exception such as BudgetExceededError.
  4. Normalize budget periods to one canonical vocabulary, such as daily, weekly, and monthly.
  5. Reject unsupported periods in set_budget() rather than silently treating them as lifetime periods.
  6. Implement calendar-aligned boundaries where appropriate instead of rolling approximations.
  7. Add tests covering warning thresholds, exact-limit transactions, crossing transactions, already-exceeded budgets, agent-specific budgets, and every supported period.
  8. If budgets are intended to be advisory rather than mandatory, update SKILL.md to state that clearly and avoid claiming that transactions are blocked.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ledger_engine.py:365
Finding

Spreadsheet formula injection in CSV exports

Content
View full analysis

Vulnerability Details

File Location: scripts/ledger_engine.py:365-384
Vulnerability Type: CSV or spreadsheet formula injection
Risk Level: Medium

Technical Analysis

The CSV export writes multiple caller-controlled transaction fields directly into spreadsheet cells:

python
def export_csv(self, period: str = "all") -> str:
    """Export transactions to CSV string."""
    txs = self._filter_by_period(period)
    
    output = io.StringIO()
    writer = csv.writer(output)
    writer.writerow([
        "Date", "Direction", "Amount", "Currency", "Vendor",
        "Description", "Category", "Agent", "Account", "Tags"
    ])
    
    for tx in txs:
        writer.writerow([
            datetime.fromtimestamp(tx.timestamp).strftime("%Y-%m-%d %H:%M"),
            tx.direction, f"{tx.amount:.4f}", tx.currency, tx.vendor,
            tx.description, tx.category, tx.agent, tx.account,
            ";".join(tx.tags)
        ])
    
    return output.getvalue()

Fields such as vendor, description, category, agent, account, currency, and tags may contain strings beginning with spreadsheet formula prefixes such as =, +, -, or @.

The Python CSV writer correctly quotes and delimits cells, but CSV quoting does not prevent spreadsheet applications from interpreting cell content as a formula. Consequently, attacker-controlled ledger data can become active spreadsheet content when an auditor or operator opens the exported file.

Attack Path

  1. An attacker or untrusted integration obtains permission to submit transaction metadata.
  2. The attacker sets a field such as vendor or description to a spreadsheet formula, for example a value beginning with =.
  3. The transaction is persisted in the ledger without formula-prefix validation.
  4. An operator exports the ledger using export_csv().
  5. The operator opens the resulting CSV in a spreadsheet application. 6 ...[truncated 959 chars]
Remediation
View remediation

Remediation Suggestions

  1. Sanitize every text cell before passing it to csv.writer.
  2. Detect cells whose first non-whitespace character is =, +, -, or @.
  3. Prefix dangerous values with an apostrophe or another spreadsheet-safe neutralization character:
    python
    def safe_csv_cell(value):
        text = str(value)
        if text.lstrip().startswith(("=", "+", "-", "@")):
            return "'" + text
        return text
    
  4. Apply sanitization consistently to vendor, description, category, agent, account, currency, tags, and any future caller-controlled fields.
  5. Preserve an unsanitized JSON export for machine-to-machine use when exact raw values are required.
  6. Document that CSV exports are intended for spreadsheet use and are sanitized accordingly.
  7. Add regression tests for formula prefixes, leading whitespace, tabs, carriage returns, multiline values, and ordinary negative numeric values.
Vulnerability Patterns
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (4)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · scripts/ledger_engine.py (reported line 575)May include surrounding context.

python
# Cleanup
    import shutil
    shutil.rmtree("/tmp/aoineco_ledger_test")
    print("\n✅ Aoineco Ledger test complete!")

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The module explicitly claims 'Encrypted storage via VaultCrypto integration', but the implementation writes transactions, budgets, and config as plaintext JSON/JSONL on disk. This mismatch can cause operators or downstream agents to store sensitive financial data under a false assumption of confidentiality, increasing the chance of inadvertent exposure on shared hosts, backups, or compromised user accounts.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

Transaction records are automatically appended to a local file containing financial metadata such as vendors, descriptions, agents, contexts, and tags, with no explicit user-facing disclosure at the point of persistence. In an agent-skill context, silent local persistence is risky because users may provide sensitive operational or financial details assuming they are transient.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

Budget and configuration data are also written to disk without clear disclosure, creating additional silent persistence of operational data. While less sensitive than full transaction logs, these files can still reveal spending limits, usage patterns, and environment-specific configuration that may aid profiling or operational reconnaissance.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.