Back to skill

Security audit

cashbook

Security checks for vulnerabilities and agentic risk

Overview

This is mostly a coherent local bookkeeping skill, but it needs review because its reset command can delete an arbitrary user-writable file if the database path is changed.

Install only if you are comfortable with a local Chinese-first bookkeeping tool. Do not run init.py --force with a custom CASHBOOK_DB value unless you have verified the exact path and have a backup; avoid using imported CSVs or extracted screenshot values until amounts and dates are checked carefully.

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

Error
Location
scripts/init.py:84
Finding
Arbitrary File Deletion Through CASHBOOK_DB and Forced Initialization## Vulnerability Details **File Location**: `scripts/init.py:84-89`, with attacker-controlled path resolution in `scripts/db.py:9-12` **Vulnerability Type**: Arbitrary file deletion **Risk Level**: High ### Technical Analysis The database path is accepted directly from the `CASHBOOK_DB` environment variable without restriction, canonicalization, ownership validation, or verification that the target is a cashbook SQLite database: ```python def get_db_path(): """Return the database path, preferring CASHBOOK_DB.""" return os.environ.get("CASHBOOK_DB", os.path.expanduser("~/.local/share/cashbook/cashbook.db")) ``` Forced initialization deletes whatever filesystem object exists at that path before attempting to create a database: ```python def init_db(force=False): db_path = get_db_path() if force and os.path.exists(db_path): os.remove(db_path) print(f"Deleted old database: {db_path}") ``` The `--force` option is exposed without an interactive confirmation or a check that the path is within the normal cashbook data directory: ```python def main(): parser = argparse.ArgumentParser(description="Initialize cashbook database") parser.add_argument("--force", action="store_true", help="Delete and recreate database") args = parser.parse_args() init_db(force=args.force) ``` Consequently, any actor able to influence the environment and cause the initialization command to run with `--force` can select an arbitrary file writable by the process and delete it. The deletion occurs before SQLite validates the target, so the file does not need to be a database. This is especially relevant in an agent environment, where tool instructions or user-supplied command parameters may influence environment variables and command invocation. ### Attack Path 1. The attacker identifies a file writable by the user running the skill, such as a document ...[truncated 1266 chars]
Remediation
## Remediation Suggestions - Do not pass the unrestricted `CASHBOOK_DB` value directly to a destructive operation. - Resolve the path with `os.path.realpath()` and, by default, require it to remain under a dedicated directory such as `~/.local/share/cashbook/`. - If custom database paths must be supported, separate database selection from database deletion. Require an explicit, independently supplied confirmation of the canonical path. - Before deletion, verify that the target is a regular file, is not a symbolic link, is owned by the current user, contains a valid SQLite header, and has the expected cashbook schema or application marker. - Refuse special files, directories, and paths outside an approved storage boundary. - Create a timestamped backup before resetting an existing database. - Require interactive confirmation unless a separately protected administrative automation mode is explicitly enabled. - Prefer clearing known cashbook tables inside a validated database rather than deleting an arbitrary path.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/add_tx.py:142
Finding
Unvalidated Transaction Amounts Permit Balance and Ledger Corruption## Vulnerability Details **File Location**: `scripts/add_tx.py:142-152` and `scripts/add_tx.py:198-211` **Vulnerability Type**: Improper numeric input validation **Risk Level**: Medium ### Technical Analysis Transaction amounts are parsed as Python floating-point values, but the code does not require them to be positive and finite: ```python def main(): parser = argparse.ArgumentParser(description="Record a transaction") parser.add_argument("--amount", type=float, required=True, help="Amount") parser.add_argument("--type", required=True, choices=["expense", "income", "transfer"], help="Type") parser.add_argument("--category", required=True, help="Category name") parser.add_argument("--account", help="Account nickname; default account if omitted") parser.add_argument("--date", default="today", help="Date: today/yesterday/YYYY-MM-DD") parser.add_argument("--note", help="Note") parser.add_argument("--merchant", help="Merchant") parser.add_argument("--source", default="nlp", help="Source, default nlp") args = parser.parse_args() ``` The unchecked value is then inserted into the ledger and applied directly to the account balance: ```python # Write transaction conn.execute( "INSERT INTO transactions (amount, type, category_id, account_id, note, merchant, date, source) " "VALUES (?, ?, ?, ?, ?, ?, ?, ?)", (args.amount, args.type, category_id, account_id, args.note, args.merchant, tx_date, args.source), ) # Update account balance if args.type == "expense": conn.execute("UPDATE accounts SET balance = balance - ? WHERE id = ?", (args.amount, account_id)) elif args.type == "income": conn.execute("UPDATE accounts SET balance = balance + ? WHERE id = ?", (args.amount, account_id)) conn.commit() ``` For example, recording an expense of `-100` executes `balance = balance - (-100)`, increasing the ...[truncated 2398 chars]
Remediation
## Remediation Suggestions - Validate every monetary amount before any database operation: ```python import math if not math.isfinite(args.amount) or args.amount <= 0: parser.error("--amount must be a finite number greater than zero") ``` - Apply equivalent validation to account balances, budgets, and all values parsed during CSV import. - Use `decimal.Decimal` rather than binary floating point for currency, or store amounts as integer minor units such as cents. - Add database constraints, for example `CHECK(amount > 0)`, so invalid values cannot be inserted through another code path. - Add constraints for transaction types, account types, budget periods, and finite monetary ranges where supported. - Parse and validate all CSV rows before beginning database mutations. Reject the import or clearly report invalid rows. - Perform transaction insertion and balance adjustment in one explicit SQLite transaction, rolling back on any validation or update failure. - Add tests for zero, negative, extremely large, `NaN`, positive infinity, and negative infinity values.
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 (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description presents a full-featured bookkeeping skill handling expense entry, receipt screenshots, budgets, reports, CSV import/export, and multilingual trigger phrases. The actual code shown only implements account administration via CLI: creating, listing, editing, deleting accounts, and setting a default account in SQLite. Account management is one subset of the declared functionality, but the chunk does not demonstrate the broader primary capabilities described. This is a description-behavior mismatch because the supplied code materially underdelivers relative to the declared purpose and exposes only a narrow CLI account-management behavior rather than the advertised all-purpose bookkeeping skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The supplied code is narrowly focused on budget management: it defines argparse subcommands for setting budgets, querying budget progress, listing budgets, and deleting budgets, and reads from/writes to SQLite tables such as budgets, categories, and transactions. This partially overlaps with the declared description's 'budget tracking' feature, but it does not implement the broader declared purpose of a general bookkeeping skill that supports recording expenses from natural language or screenshots, account management, reports, CSV import/export, or entry deletion. The primary behavior of this chunk is therefore materially narrower than the declared description. While this may be one component of a larger bookkeeping system, based on this code chunk alone the description overstates what the code actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code chunk only implements read-only querying and reporting against a database via command-line arguments. It supports detailed transaction listing and summary statistics over date ranges, categories, and accounts. While this is related to bookkeeping, it does not implement most of the declared core capabilities: recording expenses, handling screenshots, parsing natural-language triggers, managing accounts, tracking budgets, importing/exporting CSV, or deleting entries. Therefore the declared description substantially overstates the behavior of this specific code chunk.

Ae1

High
Category
analysis-evasion
Content
如需重置:`python3 scripts/init.py --force`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding
The skill declares no explicit tool scope while its documented behavior relies on access to environment-controlled database paths and local files such as CSV imports/exports and screenshots. Missing permission boundaries can let the runtime grant broader-than-necessary file or environment access, increasing the chance of unintended data exposure or misuse if the skill or surrounding agent is compromised.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger set is broad and conversational, including generic phrases and screenshot uploads, which can cause the skill to activate on loosely related user inputs. Overbroad activation increases the risk of unintended processing of sensitive financial screenshots or accidental execution of destructive bookkeeping actions such as deletion or imports under the wrong context.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The documentation advertises a destructive `delete --id 1` command with no warning, confirmation step, or mention of recovery/backup implications. In a personal bookkeeping skill, deleting an account can destroy financial history, orphan related transactions, or cause silent data integrity problems if users or downstream agents invoke it casually.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This markdown file presents the schema and labels entirely in Chinese, including the title and inline comments, with no indication that users may choose another language. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is clearly documented and justified.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The script's docstrings, CLI descriptions, status/error messages, and canonical category outputs are all fixed in Chinese, while only accepting some multilingual aliases as input. This imposes a specific output language/locale without giving the user any option to select another language, which matches the language/locale policy concern.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This Python file contains the skill description, help text, comments, and CLI output entirely in Chinese, including the argparse description and user-visible status/error messages. Because the skill does not offer user language selection or explain that it is intentionally region- or locale-specific, it violates the language/locale policy criterion.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This code's natural-language strings, including the module docstring, error messages, success messages, and argparse descriptions, are all fixed to Chinese. Under the language/locale policy, forcing a specific language without user opt-in or clear justification is a policy concern.

Natural-Language Policy Violations

Medium
Confidence
85% confidence
Finding
The module docstrings are written only in Chinese, including descriptions of core behavior such as database path resolution and automatic initialization. If this skill is intended for general use, Chinese-only user/developer-facing language can violate locale-choice policy because it does not offer any language choice or state a justified locale restriction.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This Python file contains user-facing natural-language strings exclusively in Chinese, including the module docstring, status messages, and CLI help text. The skill does not indicate that it is region-specific or offer any language/locale selection, which can violate language/locale policy requirements for user-facing skills.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This code file contains natural-language descriptions, help text, docstrings, and console output entirely in Chinese, including the CLI description and argument help. That imposes a specific language on users without any opt-in or documented justification, which matches the language/locale policy violation criteria.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The module docstrings, CLI description, help text, and all user-facing output strings are written only in Chinese. This imposes a specific language on users without any visible opt-in, selection mechanism, or documented justification that the skill is intended only for a Chinese-speaking or region-specific context.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
The user-facing description, help text, and status/error messages are all hard-coded in Chinese throughout the script. This imposes a single locale on all users without offering any language choice or documenting a justified region-specific constraint.

Missing User Warnings

Low
Confidence
91% confidence
Finding
This code automatically creates the database directory, opens the SQLite file, and triggers schema/data initialization on first use. While comments/docstrings describe the behavior for developers, there is no user-facing prompt, logging, or visible disclosure that local user data will be written under ~/.local/share/cashbook/cashbook.db.

Static analysis

No suspicious patterns detected.