Back to skill

Security audit

Fund Invest Advisor

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a local fund-analysis helper, but it ships an under-documented portfolio script with verified command-injection and data-integrity risks.

Review before installing. The main fund.sh calculator is local and purpose-aligned, but the package includes script.sh with local code-execution and portfolio-data integrity flaws. Avoid running script.sh, especially with untrusted arguments or FUND_DIR values, until it is fixed; treat all investment outputs as educational, not personalized financial advice.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/script.sh:64
Finding
Python Code Injection Through an Attacker-Controlled Portfolio Path<![CDATA[ ## Vulnerability Details **File Location**: `scripts/script.sh`, lines 5–7 and 64–69 **Vulnerability Type**: Python source injection through an unquoted heredoc **Risk Level**: High ### Vulnerable Code ```bash DATA_DIR="${FUND_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/fund-invest-advisor}" PORTFOLIO="$DATA_DIR/portfolio.jsonl" mkdir -p "$DATA_DIR" ``` ```bash cmd_holdings() { [ ! -f "$PORTFOLIO" ] && { echo "No holdings. Use: fund-invest-advisor add <fund> <amount>"; return; } python3 << PYEOF import json holdings = {} with open('$PORTFOLIO') as f: ``` ### Technical Analysis The `FUND_DIR` environment variable influences `PORTFOLIO`. The resulting path is interpolated directly into an unquoted heredoc that is interpreted as Python source code. Although the path appears inside a single-quoted Python string, Bash performs parameter expansion before Python receives the heredoc. A path containing a single quote and additional Python syntax can terminate the string literal and inject arbitrary statements or expressions. Shell quoting of `"$PORTFOLIO"` elsewhere does not protect this operation because the vulnerability occurs when Bash constructs the embedded Python program. The same vulnerable `cmd_holdings` implementation is also reached through the `pnl` command. ### Attack Path 1. An attacker causes the victim to run the script with an attacker-controlled `FUND_DIR` value. 2. The attacker chooses a value containing Python string delimiters and executable Python syntax. 3. The script creates or uses the resulting directory and derives `PORTFOLIO` from it. 4. The victim invokes: - `scripts/script.sh holdings`, or - `scripts/script.sh pnl`. 5. Bash expands `$PORTFOLIO` into the unquoted Python heredoc. 6. The injected path terminates the intended Python string and introduces attacker-controlled Python code. 7. Python executes that code with the privileges and environment of the user running the script. ### Impact Assessment Succes ...[truncated 462 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Do not interpolate filesystem paths or other variable data into generated Python source. Pass the path as a positional argument and quote the heredoc delimiter: ```bash cmd_holdings() { if [[ ! -f "$PORTFOLIO" ]]; then echo "No holdings. Use: fund-invest-advisor add <fund> <amount>" return fi python3 - "$PORTFOLIO" <<'PYEOF' import json import sys portfolio_path = sys.argv[1] holdings = {} with open(portfolio_path, encoding="utf-8") as f: for line in f: d = json.loads(line) fund = d["fund"] amount = float(d["amount"]) if d["action"] == "buy": holdings[fund] = holdings.get(fund, 0) + amount else: holdings[fund] = holdings.get(fund, 0) - amount PYEOF } ``` Additional hardening should include: - Use quoted heredoc delimiters whenever the embedded source must remain literal. - Validate that `FUND_DIR` resolves to an expected user-owned location. - Reject unsafe directory types, including symbolic links where inappropriate. - Create the data directory with restrictive permissions, such as `umask 077` and `mkdir -p -- "$DATA_DIR"`. - Add regression tests using paths containing quotes, backslashes, spaces, newlines, and Python metacharacters. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/script.sh:170
Finding
Shell Command Execution Through Unvalidated Arithmetic Expressions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/script.sh`, lines 170–173 **Vulnerability Type**: Bash arithmetic injection **Risk Level**: High ### Vulnerable Code ```bash cmd_dca() { local amount="${1:?Usage: fund-invest-advisor dca <monthly-amount> <months>}" local months="${2:-12}" local total=$((amount * months)) ``` ### Technical Analysis The `amount` and `months` command-line arguments are accepted as arbitrary strings and then referenced as variables inside Bash arithmetic expansion. Bash arithmetic evaluation is not equivalent to parsing a strictly numeric value. Variable values can be recursively interpreted as arithmetic expressions. Crafted expressions involving array subscripts or command substitutions can cause shell commands to be evaluated while Bash resolves the arithmetic expression. Because neither input is checked against a strict numeric grammar before `$((amount * months))` is evaluated, attacker-controlled command-line data reaches an execution-capable shell context. The later Python conversions do not mitigate this issue because the vulnerable arithmetic expansion occurs first. ### Attack Path 1. An attacker supplies or persuades a victim to use a crafted `amount` or `months` argument. 2. The victim invokes the `dca` command, for example through `scripts/script.sh dca ...`. 3. `cmd_dca` stores the untrusted strings without validation. 4. Bash evaluates `local total=$((amount * months))`. 5. During recursive arithmetic evaluation, attacker-supplied shell constructs are evaluated. 6. Any resulting command executes with the privileges of the user running the script. ### Impact Assessment Successful exploitation can result in arbitrary command execution as the invoking user. This permits access to user-readable files, modification or deletion of portfolio and other user data, execution of local programs, and use of inherited credentials or network permissions. No independent privilege-escalation mechan ...[truncated 176 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Validate both values before using Bash arithmetic expansion: ```bash cmd_dca() { local amount="${1:?Usage: fund-invest-advisor dca <monthly-amount> <months>}" local months="${2:-12}" if [[ ! "$amount" =~ ^[0-9]+$ ]] || [[ ! "$months" =~ ^[0-9]+$ ]]; then echo "Amount and months must be non-negative integers." >&2 return 2 fi local total=$((10#$amount * 10#$months)) # Continue with the calculation. } ``` The `10#` prefix prevents leading-zero values from being treated as octal. Further hardening should include: - Define reasonable upper limits to prevent integer overflow or resource-exhaustion behavior. - If decimal amounts are required, avoid Bash arithmetic and parse the arguments with Python or another numeric parser. - Pass numeric strings through positional arguments or environment variables only after validation. - Add negative tests containing arithmetic operators, variable references, brackets, command substitutions, whitespace, signs, and excessively large values. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/script.sh:46
Finding
Portfolio JSONL Injection Through Unsafe Transaction Serialization<![CDATA[ ## Vulnerability Details **File Location**: `scripts/script.sh`, lines 46–58 **Vulnerability Type**: Improper JSON serialization and persistent record injection **Risk Level**: Medium ### Vulnerable Code ```bash cmd_add() { local fund="${1:?Usage: fund-invest-advisor add <fund> <amount> [date]}" local amount="${2:?}" local date="${3:-$(date +%Y-%m-%d)}" printf '{"action":"buy","fund":"%s","amount":%s,"date":"%s"}\n' "$fund" "$amount" "$date" >> "$PORTFOLIO" echo "[fund] Bought: $fund ¥$amount on $date" _log "buy" "$fund ¥$amount" } cmd_sell() { local fund="${1:?Usage: fund-invest-advisor sell <fund> <amount> [date]}" local amount="${2:?}" local date="${3:-$(date +%Y-%m-%d)}" printf '{"action":"sell","fund":"%s","amount":%s,"date":"%s"}\n' "$fund" "$amount" "$date" >> "$PORTFOLIO" ``` ### Technical Analysis The script manually constructs JSON with `printf` rather than using a JSON serializer: - `fund` and `date` are inserted into quoted JSON strings without escaping quotes, backslashes, control characters, or newlines. - `amount` is inserted as an unquoted JSON token without validation that it is a finite numeric value. - Each generated object is appended to a JSON Lines file, so an embedded newline can create additional attacker-controlled records. A malicious input can therefore corrupt the persistent portfolio file, inject forged transactions, or create content that causes later `json.loads` operations to fail. This affects `holdings`, `pnl`, `history`, and export-related behavior that relies on the same file. ### Attack Path 1. An attacker supplies a crafted fund name, date, or amount to the `add` or `sell` command. 2. The crafted value contains JSON delimiters, escape characters, a newline, or a nonnumeric raw token. 3. `printf` appends the value without JSON encoding or validation. 4. The persistent `portfolio.jsonl` file becomes malformed or contains additional forged transaction records. 5. Subs ...[truncated 728 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Use a JSON serializer instead of manually constructing JSON. Validate and normalize each field before writing it: ```bash write_transaction() { local action="$1" local fund="$2" local amount="$3" local transaction_date="$4" python3 - "$PORTFOLIO" "$action" "$fund" "$amount" "$transaction_date" <<'PYEOF' import datetime import json import math import sys path, action, fund, amount_text, date_text = sys.argv[1:] if action not in {"buy", "sell"}: raise SystemExit("Invalid transaction action") try: amount = float(amount_text) except ValueError: raise SystemExit("Amount must be numeric") if not math.isfinite(amount) or amount <= 0: raise SystemExit("Amount must be a positive finite number") try: datetime.date.fromisoformat(date_text) except ValueError: raise SystemExit("Date must use YYYY-MM-DD format") record = { "action": action, "fund": fund, "amount": amount, "date": date_text, } with open(path, "a", encoding="utf-8") as f: f.write(json.dumps(record, ensure_ascii=False) + "\n") PYEOF } ``` Additional hardening should include: - Restrict fund-name length and reject control characters if they are unnecessary. - Write with restrictive file permissions. - Consider file locking to prevent concurrent writes from interleaving. - Validate every existing JSONL record before calculations and report malformed records safely. - Use structured serialization for logs or escape newlines to prevent log forging. - Add tests for quotes, backslashes, Unicode, embedded newlines, invalid dates, `NaN`, infinity, negative amounts, and malformed numeric input. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (11)

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger text is broad and loosely scoped to generic fund-investment decisions, which can cause the skill to activate in many financial-advice contexts without clear user intent. In a financial domain, accidental invocation is more sensitive because the skill may influence investment choices, asset allocation, and risk-taking based on simplified local calculations and generic heuristics.

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
Nearly all user-facing strings, help text, and instructions are presented only in Chinese, with no opt-in, alternative language support, or explicit statement that the skill is intentionally limited to a Chinese-speaking or region-specific audience. This creates a natural-language locale policy issue because the skill effectively imposes a specific language on all users.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The script provides concrete investment allocation, strategy, risk, and FIRE guidance with prescriptive recommendations but does not present a clear financial-risk disclaimer or state that outputs are educational only. In a skill context, users may over-trust the advice as personalized or reliable, which can lead to real financial harm from unsuitable decisions.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The help text documents `pnl` as a distinct "Profit & loss summary" capability, which implies reporting gains/losses rather than current position totals. In the command dispatcher, `pnl` simply invokes `cmd_holdings`, producing holdings output and not any profit/loss calculation, so the documented intent contradicts the actual behavior.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
This markdown file contains only Chinese-language content from title to footer, with no opt-in, alternative language option, or statement that the skill is intended specifically for a Chinese-speaking audience. Under the policy rule for language/locale constraints, forcing a single language without user choice can be a natural-language policy violation.

Intent-Code Divergence

Low
Confidence
97% confidence
Finding
The help text documents only a subset of implemented commands, while additional commands such as sharpe, maxdd, kelly, monte, fire, var, irr, and cost are callable. This capability mismatch can mislead users, reviewers, or policy controls about what the skill can do, reducing transparency and making oversight weaker even though the hidden commands here are not directly dangerous.

Intent-Code Divergence

Low
Confidence
98% confidence
Finding
The help text describes `rebalance <target-file>` as providing rebalance suggestions, implying a working advisory feature. In reality, the dispatcher only echoes `TODO: rebalance`, so the inline documentation overstates what the code does.

Intent-Code Divergence

Low
Confidence
98% confidence
Finding
The help output lists `screen <criteria>` as a fund screening feature. The corresponding command path only prints `TODO: screening`, which directly conflicts with the documented availability of that functionality.

Intent-Code Divergence

Low
Confidence
98% confidence
Finding
The help text lists `calendar` as a dividend/rebalance calendar feature, suggesting an operational command. The actual handler only prints `TODO: calendar`, so the documented intent does not match the code behavior.

Intent-Code Divergence

Low
Confidence
98% confidence
Finding
The help text presents `import <file>` under Data commands as an available feature for importing holdings. However, the dispatcher for `import` only prints `TODO: import`, meaning the documentation claims a capability the code does not actually provide.

Static analysis

No suspicious patterns detected.