T09 · Insecure Skill Coding Practices
- Location
scripts/transfer.sh:50- Finding
Unsafe JSON Construction in Authenticated Financial Transactions
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This YNAB skill appears purpose-aligned, but it needs Review because it can read and write a live budget while being declared prompt-only and triggered by broad finance requests.
Install only if you intentionally want an agent to access your YNAB account and potentially create transactions or transfers. Prefer environment variables or a protected config file, verify the skill only contacts the official YNAB API, require confirmation before any write action, and be careful with generic finance requests because the skill may activate even when you did not explicitly mention YNAB.
scripts/transfer.sh:50Unsafe JSON Construction in Authenticated Financial Transactions
scripts/transfer.sh:38jq Program Injection Through Untrusted Search and Account Names
scripts/transfer.sh:19Insufficient Validation of Transaction-Critical Amounts and Dates
skill.toml:97Runtime Metadata Understates Executable and Financial Capabilities
The description claims a full-featured YNAB management skill that can add transactions, create transfers, track goals, monitor spending, and generate reports, and says it should be used for a wide range of finance intents such as 'add an expense' or 'upcoming bills'. The actual code only implements one narrow capability: a scheduled, read-only daily budget check/report. It accesses YNAB budget/month and scheduled transaction data and formats a morning summary, but contains no write operations, no transaction creation, no transfer support, and no general-purpose interaction handling. While parts of the description related to spending analysis, goal tracking, Age of Money, and budget reports are consistent, the declared scope materially overstates the implemented behavior.
The code is clearly YNAB-related and does support one declared capability: adding transactions, plus limited spending lookup. However, the description substantially overstates the skill’s scope. The script does not implement goal tracking, transfers, budget reports, Age of Money, upcoming bills, or general personal-finance management. Its actual behavior is a small CLI helper around a few YNAB endpoints, not a full-featured budget management skill matching the broad trigger guidance. There are no obvious undeclared malicious capabilities, but there is a material description-versus-behavior mismatch due to the claimed breadth of functionality.
The invocation guidance is extremely broad, telling the system to use this skill whenever the user mentions general budgeting or personal finance, even without naming YNAB. In context, this is dangerous because the skill includes write-capable financial actions such as adding transactions and creating transfers, so over-triggering can cause unintended access to financial data or accidental state-changing operations.
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).
| Accounts | `GET /v1/budgets/{id}/accounts` |
| Categories | `GET /v1/budgets/{id}/categories` |
| Transactions | `GET/POST /v1/budgets/{id}/transactions` |
| Single transaction | `GET/PUT/DELETE /v1/budgets/{id}/transactions/{id}` |
| Month summary | `GET /v1/budgets/{id}/months/current` |
| Scheduled | `GET /v1/budgets/{id}/scheduled_transactions` |
| Payees | `GET /v1/budgets/{id}/payees` |
The manifest advertises a prompt-only skill with no tools or capabilities, yet the embedded instructions direct use of curl, jq, local config files, environment variables, and a remote API. This mismatch can bypass platform safety gating and mislead orchestrators into invoking a skill that expects network access, credential handling, and filesystem interaction without explicit declaration.
The trigger language is extremely broad, covering generic phrases like 'add an expense', 'how much did I spend', and 'check my budget' even when YNAB is not mentioned. In context, this is risky because the skill supports financial record modification, so over-triggering could route unrelated finance requests into a workflow that assumes access to a live personal budget and may prompt or perform sensitive actions.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
# YNAB API Skill for Claude Code
Manage your [YNAB](https://www.ynab.com/) budget directly from Claude Code. Add transactions, track goals, monitor spending, create transfers, and generate reports.
## Installation
The skill declares shell-capable behavior but does not define any explicit tool scope such as allowed tools or permissions. That creates an authorization gap where the agent may invoke shell functionality more broadly than intended, increasing the chance of unintended command execution or access to sensitive local data and environment variables.
The skill encourages broad reuse across ongoing personal-finance interactions, which increases the chance that sensitive budget context, account identifiers, or prior transaction assumptions persist across requests. In a financial-management context, over-retention or over-application of session context can lead to mistaken actions, privacy leakage, or unauthorized use of stale financial state.
---
name: ynab-api
description: "YNAB (You Need A Budget) budget management via API. Add transactions, track goals, monitor spending, create transfers, and generate budget reports. Use this skill whenever the user mentions YNAB, budget tracking, spending analysis, budget goals, Age of Money, or wants to manage their personal finances -- even if they just say 'add an expense', 'how much did I spend', 'check my budget', or 'upcoming bills' without naming YNAB explicitly. Also use for automated budget reports and financial summaries."
user-invocable: true
metadata: {"requiredEnv": ["YNAB_API_KEY", "YNAB_BUDGET_ID"]}
---
The skill is designed to send budget and transaction data to an external service endpoint, which is a real external-transmission behavior. In context this is expected for a YNAB integration, but it still carries confidentiality risk because personal financial data and API credentials are involved, and accidental invocation or overbroad use could expose sensitive information to the third-party API.
YNAB_API="https://api.ynab.com/v1"
# Add a transaction
# POST \/budgets/\/transactions
Several guidance and troubleshooting lines are written as mandatory instructions in Italian, including 'riprovare silenziosamente' and 'NON assumere subito', without offering the user a language choice or documenting a justified locale restriction. This creates a natural-language policy issue because the skill imposes a specific language/locale in its operational instructions without opt-in.
The skill instructs users to configure a live YNAB API token and use scripts that can create transactions and transfers, but it does not clearly warn that these operations affect a real budget. In a personal-finance skill, silent state-changing behavior increases the risk of unintended financial record modification, duplicate entries, or accidental transfers if the agent invokes scripts without explicit confirmation.
The skill recommends persistent storage of the API key in ~/.config/ynab/config.json, which creates session/credential persistence on disk. While common, this becomes a security issue if the file is left world-readable, synced insecurely, or exposed via backups or local compromise, especially because the token can authorize modifications to financial records.
Set environment variables YNAB_API_KEY and YNAB_BUDGET_ID, or create ~/.config/ynab/config.json:
{
The script supports long-lived storage of an API key in a local JSON file under ~/.config/ynab/config.json, which can expose sensitive financial API credentials if file permissions are weak, the host is shared, or backups/logging capture the file. In the context of a finance skill, compromise of this token could allow unauthorized access to budget and transaction data and potentially modification via the API.
API_KEY=$(jq -r .api_key "${YNAB_CONFIG:-$HOME/.config/ynab/config.json}")
BUDGET_ID=$(jq -r '.budget_id // "last-used"' "${YNAB_CONFIG:-$HOME/.config/ynab/config.json}")
else
echo "Error: YNAB config not found. Set YNAB_API_KEY+YNAB_BUDGET_ID or create ~/.config/ynab/config.json" >&2
exit 1
fi
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
exit 1
fi
YNAB_API="https://api.ynab.com/v1"
TODAY=$(date -u '+%Y-%m-%d')
TOMORROW=$(date -u -d "+1 day" '+%Y-%m-%d')
END_7_DAYS=$(date -u -d "+7 days" '+%Y-%m-%d')
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
exit 1
fi
YNAB_API="https://api.ynab.com/v1"
TODAY=$(date -u '+%Y-%m-%d')
TOMORROW=$(date -u -d "+1 day" '+%Y-%m-%d')
END_7_DAYS=$(date -u -d "+7 days" '+%Y-%m-%d')
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
exit 1
fi
YNAB_API="https://api.ynab.com/v1"
TODAY=$(date -u '+%Y-%m-%d')
TOMORROW=$(date -u -d "+1 day" '+%Y-%m-%d')
END_7_DAYS=$(date -u -d "+7 days" '+%Y-%m-%d')
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
exit 1
fi
YNAB_API="https://api.ynab.com/v1"
TODAY=$(date -u '+%Y-%m-%d')
TOMORROW=$(date -u -d "+1 day" '+%Y-%m-%d')
END_7_DAYS=$(date -u -d "+7 days" '+%Y-%m-%d')
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
exit 1
fi
YNAB_API="https://api.ynab.com/v1"
TODAY=$(date -u '+%Y-%m-%d')
TOMORROW=$(date -u -d "+1 day" '+%Y-%m-%d')
END_7_DAYS=$(date -u -d "+7 days" '+%Y-%m-%d')
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
exit 1
fi
YNAB_API="https://api.ynab.com/v1"
TODAY=$(date -u '+%Y-%m-%d')
TOMORROW=$(date -u -d "+1 day" '+%Y-%m-%d')
END_7_DAYS=$(date -u -d "+7 days" '+%Y-%m-%d')
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
exit 1
fi
YNAB_API="https://api.ynab.com/v1"
TODAY=$(date -u '+%Y-%m-%d')
TOMORROW=$(date -u -d "+1 day" '+%Y-%m-%d')
END_7_DAYS=$(date -u -d "+7 days" '+%Y-%m-%d')
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
exit 1
fi
YNAB_API="https://api.ynab.com/v1"
TODAY=$(date -u '+%Y-%m-%d')
TOMORROW=$(date -u -d "+1 day" '+%Y-%m-%d')
END_7_DAYS=$(date -u -d "+7 days" '+%Y-%m-%d')
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
exit 1
fi
YNAB_API="https://api.ynab.com/v1"
TODAY=$(date -u '+%Y-%m-%d')
TOMORROW=$(date -u -d "+1 day" '+%Y-%m-%d')
END_7_DAYS=$(date -u -d "+7 days" '+%Y-%m-%d')
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
exit 1
fi
YNAB_API="https://api.ynab.com/v1"
TODAY=$(date -u '+%Y-%m-%d')
TOMORROW=$(date -u -d "+1 day" '+%Y-%m-%d')
END_7_DAYS=$(date -u -d "+7 days" '+%Y-%m-%d')
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
exit 1
fi
YNAB_API="https://api.ynab.com/v1"
TODAY=$(date -u '+%Y-%m-%d')
TOMORROW=$(date -u -d "+1 day" '+%Y-%m-%d')
END_7_DAYS=$(date -u -d "+7 days" '+%Y-%m-%d')