Back to skill

Security audit

Portfolio

Security checks for vulnerabilities and agentic risk

Overview

This is a local portfolio-tracking skill with disclosed file storage and no evidence of hidden network, credential, or persistence behavior beyond its own data files.

Install only if you are comfortable with a skill that can create and modify persistent local portfolio files under ~/.portfolio. Review commands before running remove operations, keep backups of important portfolio data, and avoid importing untrusted ticker values into CSV exports until validation and CSV escaping are improved.

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/script.sh:201
Finding
Spreadsheet Formula Injection in CSV Export<![CDATA[ ## Vulnerability Details **File Location**: `scripts/script.sh:35` and `scripts/script.sh:201-204` **Vulnerability Type**: CSV/spreadsheet formula injection caused by insufficient output neutralization **Risk Level**: Medium ### Vulnerable Code ```bash ticker=$(echo "$1" | tr '[:lower:]' '[:upper:]') ``` ```python elif fmt == 'csv': print('ticker,quantity,avg_price,value,date_added') for h in holdings: val = round(h['quantity'] * h['avg_price'], 2) print(f"{h['ticker']},{h['quantity']},{h['avg_price']},{val},{h.get('date_added','')}") ``` ### Technical Analysis The `add` command accepts an arbitrary ticker string and only converts lowercase characters to uppercase. It does not enforce a valid ticker syntax or reject spreadsheet control prefixes such as `=`, `+`, `-`, and `@`. The `list --format csv` command then constructs CSV records with direct string interpolation. It neither uses a standards-compliant CSV encoder nor neutralizes formula-like fields. Consequently, a ticker beginning with a formula prefix is written into the first CSV column unchanged. Spreadsheet applications may interpret such fields as formulas when a user opens the exported file. Quoting a field alone is not sufficient protection in all spreadsheet applications; formula-prefix neutralization is also required. ### Attack Path 1. An attacker, untrusted caller, or agent-mediated input supplies a formula-like ticker: ```bash bash scripts/script.sh add '=1+1' 10 100 ``` 2. The ticker is stored unchanged except for uppercase conversion. 3. A user exports the portfolio: ```bash bash scripts/script.sh list --format csv > portfolio.csv ``` 4. The resulting file contains a formula-like cell: ```csv ticker,quantity,avg_price,value,date_added =1+1,10.0,100.0,1000.0,2026-09-12 ``` 5. When the CSV file is opened in compatible spreadsheet software, the ticker cell may be evaluated as a formula. 6. A more capable spreadsheet formula ...[truncated 856 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce an explicit allowlist for ticker symbols before storing them. For example: ```bash if [[ ! "$ticker" =~ ^[A-Z0-9][A-Z0-9._-]{0,15}$ ]]; then echo "Invalid ticker format." >&2 return 1 fi ``` 2. Generate CSV output with Python's standard `csv` module instead of manual string interpolation: ```python import csv import sys writer = csv.writer(sys.stdout) writer.writerow(['ticker', 'quantity', 'avg_price', 'value', 'date_added']) ``` 3. Neutralize spreadsheet formulas in every textual CSV field. Prefix values beginning with `=`, `+`, `-`, or `@` with an apostrophe: ```python def spreadsheet_safe(value): text = str(value) if text.startswith(('=', '+', '-', '@')): return "'" + text return text ``` 4. Apply neutralization to all attacker-controlled textual fields, including values loaded from pre-existing or manually modified JSON files. 5. Add regression tests covering formula prefixes, commas, quotation marks, line breaks, and ordinary ticker symbols. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/script.sh:63
Finding
Missing Numeric Validation Allows Portfolio Data Corruption<![CDATA[ ## Vulnerability Details **File Location**: `scripts/script.sh:63-64`, `scripts/script.sh:77-83`, and `scripts/script.sh:145-148` **Vulnerability Type**: Improper input validation of financial quantities and prices **Risk Level**: Medium ### Vulnerable Code ```python ticker = os.environ["TICKER"] qty = float(os.environ["QUANTITY"]) px = float(os.environ["PRICE"]) dt = os.environ["DATE"] holdings = json.load(open(holdings_file)) transactions = json.load(open(transactions_file)) # Update holdings — merge if ticker exists found = False for h in holdings: if h['ticker'] == ticker: old_qty = h['quantity'] old_cost = h['avg_price'] * old_qty new_cost = px * qty h['quantity'] = old_qty + qty h['avg_price'] = round((old_cost + new_cost) / (old_qty + qty), 4) found = True break ``` ```python if qty_to_remove and float(qty_to_remove) < h['quantity']: removed = float(qty_to_remove) h['quantity'] -= removed new_holdings.append(h) ``` ### Technical Analysis The script converts user-supplied quantities and prices with `float()` but does not ensure that the resulting values are finite and strictly positive. This permits values such as: - Negative quantities. - Negative prices. - Zero quantities or prices. - `NaN`. - Positive or negative infinity. The removal logic is particularly vulnerable to negative quantities. Any negative value is less than a positive current quantity, so it enters the partial-removal branch. Subtracting a negative value increases the holding: ```python h['quantity'] -= removed ``` For example, removing `-100` shares from a holding of 10 shares produces a holding of 110 shares while recording a sell transaction with a quantity of `-100`. The addition logic can also fail or corrupt records. If the new quantity is the negative of the existing quantity, this expression divides by zero: ```python (old_cost + new_cost) / (old_qty + qty) ``` Non-finite floating-poin ...[truncated 2018 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse numeric input with explicit exception handling and reject non-finite values: ```python import math try: qty = float(os.environ["QUANTITY"]) px = float(os.environ["PRICE"]) except ValueError: raise SystemExit("Quantity and price must be numeric.") if not math.isfinite(qty) or qty <= 0: raise SystemExit("Quantity must be a finite number greater than zero.") if not math.isfinite(px) or px <= 0: raise SystemExit("Price must be a finite number greater than zero.") ``` 2. Validate removal quantities independently: ```python try: removed = float(qty_to_remove) except ValueError: raise SystemExit("Removal quantity must be numeric.") if not math.isfinite(removed) or removed <= 0: raise SystemExit("Removal quantity must be finite and greater than zero.") if removed > h['quantity']: raise SystemExit("Removal quantity exceeds the current holding.") ``` 3. Define clear behavior for a removal quantity equal to the current holding, treating it as a complete removal without relying on an ambiguous fallback branch. 4. Check the weighted-average denominator before division and reject any operation that would produce a non-positive resulting quantity. 5. Consider using `decimal.Decimal` instead of binary floating-point values for financial quantities and prices. 6. Validate all records loaded from the JSON files before performing calculations so manually modified or previously corrupted records cannot propagate invalid values. 7. Write updates atomically by serializing to a securely created temporary file in the same directory, flushing it, and replacing the destination with `os.replace()` only after all validation and calculations succeed. 8. Add tests for zero, negative, non-numeric, `NaN`, infinity, oversized removal quantities, and quantities that exactly cancel an existing position. ]]>
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (17)

Ae1

High
Category
analysis-evasion
Content
bash scripts/script.sh add AAPL 100 150.50 --date 2024-01-15
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/script.sh add AAPL 100 150.50 --date 2024-01-15
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/script.sh add AAPL 100 150.50 --date 2024-01-15
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/script.sh add AAPL 100 150.50 --date 2024-01-15
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/script.sh add AAPL 100 150.50 --date 2024-01-15
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/script.sh add AAPL 100 150.50 --date 2024-01-15
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/script.sh add AAPL 100 150.50 --date 2024-01-15
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/script.sh add AAPL 100 150.50 --date 2024-01-15
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/script.sh add AAPL 100 150.50 --date 2024-01-15
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/script.sh add AAPL 100 150.50 --date 2024-01-15
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/script.sh add AAPL 100 150.50 --date 2024-01-15
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/script.sh add AAPL 100 150.50 --date 2024-01-15
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/script.sh add AAPL 100 150.50 --date 2024-01-15
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/script.sh add AAPL 100 150.50 --date 2024-01-15
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
bash scripts/script.sh add AAPL 100 150.50 --date 2024-01-15
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill describes filesystem write behavior (`~/.portfolio/` JSON storage) and implies use of environment/filesystem capabilities, but it does not declare any `permissions` or `allowed-tools` scope. This creates a least-privilege gap: an agent may invoke a skill that can read environment context and write local files without any explicit tool contract, making review, sandboxing, and policy enforcement weaker.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The remove command performs destructive changes to the portfolio state immediately, with no confirmation prompt, dry-run mode, or safety interlock. In an agent-driven or automated context, a mistaken ticker, bad parsing, or unintended invocation can silently delete or reduce holdings data, causing integrity loss and potentially incorrect downstream portfolio analysis or advice.

Static analysis

No suspicious patterns detected.