T09 · Insecure Skill Coding Practices
- 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, andscripts/ledger_engine.py:464-477
Related Documentation:SKILL.md:33
Vulnerability Type: Business-logic flaw in budget enforcement
Risk Level: MediumTechnical 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 txThe 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
- 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 - Change the budget-checking interface to accept the pending amount and return an explicit allow-or-deny result.
- Refuse to call
_save_tx()when projected spending exceeds a hard limit. Raise a dedicated exception such asBudgetExceededError. - Normalize budget periods to one canonical vocabulary, such as
daily,weekly, andmonthly. - Reject unsupported periods in
set_budget()rather than silently treating them as lifetime periods. - Implement calendar-aligned boundaries where appropriate instead of rolling approximations.
- Add tests covering warning thresholds, exact-limit transactions, crossing transactions, already-exceeded budgets, agent-specific budgets, and every supported period.
- If budgets are intended to be advisory rather than mandatory, update
SKILL.mdto state that clearly and avoid claiming that transactions are blocked.
- Calculate projected spending by adding the pending transaction amount before evaluating the threshold:
