Back to skill

Security audit

Bitstamp Trader

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed Bitstamp trading helper, but its live-trading safety controls have material weaknesses that warrant review before installation.

Install only if you understand this can place real Bitstamp trades with your API key. Use an API key with no withdrawal permission, enable IP allowlisting, pin and review the ccxt dependency, and do not rely on the daily cap or kill switch as fully reliable until the locking, cancellation reporting, and numeric validation issues are fixed.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (4)

T08 · Insecure Dependencies

Warning
Location
scripts/bitstamp.py:39
Finding
Unpinned privileged CCXT dependency creates supply-chain risk<![CDATA[ ## Vulnerability Details **File Location**: `scripts/bitstamp.py:39-44` **Vulnerability Type**: Unpinned third-party dependency **Risk Level**: Medium ### Vulnerable Code ```python try: import ccxt except ImportError: print("ERROR: ccxt not installed. Run:") print(f" source {SKILL_DIR}/.venv/bin/activate && pip install ccxt") sys.exit(1) ``` ### Technical Analysis The installation guidance invokes `pip install ccxt` without a pinned version, lock file, or package hash. The effective dependency can therefore change after the Skill has been reviewed. CCXT is security-sensitive in this project because it receives the Bitstamp API key and secret and implements authenticated account and order operations: ```python return ccxt.bitstamp({ "apiKey": api_key, "secret": api_secret, "enableRateLimit": True, }) ``` An upstream compromise, malicious package-index response, or incompatible future release could execute arbitrary Python code under the invoking user's account. It could also intercept exchange credentials or alter authenticated trading requests. The audit found no evidence that the current project intentionally installs a malicious package. The issue is the absence of controls ensuring that users install the same reviewed dependency version. ### Attack Path 1. CCXT is absent from the local environment. 2. The application displays an instruction to execute `pip install ccxt`. 3. The user installs whatever release the configured package index currently resolves. 4. A compromised or unexpectedly modified CCXT package is imported by `scripts/bitstamp.py`. 5. The package executes with the permissions of the invoking user. 6. For authenticated commands, it receives the Bitstamp API key and secret and can observe or manipulate account and trading requests. ### Impact Assessment A compromised dependency could: - Read the Bitstamp API key and secret available to the process. - Submit, cancel, or modify trades within th ...[truncated 428 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin CCXT to a reviewed exact version, for example through a locked requirements file. 2. Require package hashes, such as with `pip install --require-hashes -r requirements.txt`. 3. Commit an auditable dependency lock file and review dependency changes before updating it. 4. Document use of the official Python package index or an organization-controlled package mirror. 5. Add automated dependency vulnerability and provenance scanning. 6. Continue requiring Bitstamp API keys without withdrawal permission and with IP allowlisting. 7. Consider running the CLI in an isolated virtual environment with minimal filesystem permissions. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/bitstamp.py:136
Finding
Race condition allows concurrent orders to exceed the daily trading limit<![CDATA[ ## Vulnerability Details **File Location**: `scripts/bitstamp.py:136-152, 224-230, 334-340, 372-378` **Vulnerability Type**: Non-atomic financial limit enforcement **Risk Level**: High ### Vulnerable Code The daily total is read and updated without locking: ```python def get_daily_volume() -> float: today = datetime.now(timezone.utc).strftime("%Y-%m-%d") if DAILY_VOLUME_FILE.exists(): with open(DAILY_VOLUME_FILE) as f: data = json.load(f) if data.get("date") == today: return data.get("volume_usd", 0.0) return 0.0 def add_daily_volume(amount_usd: float): today = datetime.now(timezone.utc).strftime("%Y-%m-%d") current = get_daily_volume() CONFIG_DIR.mkdir(parents=True, exist_ok=True) with open(DAILY_VOLUME_FILE, "w") as f: json.dump({"date": today, "volume_usd": current + amount_usd}, f) ``` The safety check is separate from order submission and accounting: ```python daily_vol = get_daily_volume() if daily_vol + est_usd > cfg["max_daily_volume_usd"]: print(f"🚫 Daily volume limit would be exceeded.") print(f" Used today: ${daily_vol:,.2f} / ${cfg['max_daily_volume_usd']:,.2f}") print(f" This order: ${est_usd:,.2f}") ``` For a live buy, the exchange order is created before the local total is updated: ```python if args.price: order = exchange.create_limit_buy_order(market, args.amount, args.price) else: order = exchange.create_market_buy_order(market, args.amount) add_daily_volume(est_usd) ``` The live sell path uses the same pattern: ```python if args.price: order = exchange.create_limit_sell_order(market, args.amount, args.price) else: order = exchange.create_market_sell_order(market, args.amount) add_daily_volume(est_usd) ``` ### Technical Analysis Daily-volume enforcement is a time-of-check/time-of-use operation. Reading the current volume, testing the limit, creating an exchange order, and updating the JSON file are independent op ...[truncated 1766 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Serialize the complete check-reserve-submit-reconcile sequence with an inter-process lock. 2. Prefer a transactional datastore such as SQLite with an immediate transaction rather than an unlocked JSON file. 3. Atomically reserve the estimated order value before contacting the exchange. 4. Roll back the reservation if submission definitively fails. 5. Reconcile reservations against returned order status and actual fills. 6. Use atomic file replacement if a file-based design is retained. 7. Recover pending reservations after process termination by querying Bitstamp for authoritative order and trade data. 8. Test concurrent live-order workflows to verify that the aggregate cap cannot be exceeded. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/bitstamp.py:452
Finding
Kill switch falsely reports that failed order cancellations succeeded<![CDATA[ ## Vulnerability Details **File Location**: `scripts/bitstamp.py:452-476` **Vulnerability Type**: Silent failure in emergency order cancellation **Risk Level**: High ### Vulnerable Code ```python def cmd_kill_switch(args): if args.deactivate: deactivate_kill_switch() elif args.status: if is_kill_switch_active(): data = json.loads(KILL_SWITCH_FILE.read_text()) print(f"🛑 Kill switch ACTIVE since {data['activated']}") print(f" Reason: {data['reason']}") else: print("✅ Kill switch is NOT active. Trading is enabled.") else: reason = args.reason or "Manual activation" activate_kill_switch(reason) # Also cancel all open orders try: exchange = get_exchange(live=True) orders = exchange.fetch_open_orders() for o in orders: try: exchange.cancel_order(o["id"], o["symbol"]) except Exception: pass print(f"Cancelled {len(orders)} open orders.") except Exception: print("(Could not cancel orders — no API keys or connection issue)") ``` ### Technical Analysis The kill switch correctly creates a local file that blocks new buy and sell commands, but its remote cancellation behavior is unreliable. Every exception raised while cancelling an individual order is discarded with `except Exception: pass`. The code then prints the number of fetched orders as the number cancelled, regardless of how many cancellation requests succeeded. Cancellation can fail because of network errors, authentication failures, insufficient API permissions, rate limiting, stale order state, or exchange-side errors. The success message can therefore cause an operator to believe that market exposure has been removed when orders remain active on Bitstamp. This conflicts with the documented claim that the kill switch cancels all open orders. ...[truncated 1215 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the blanket suppression of cancellation exceptions. 2. Count successful and failed cancellations separately. 3. Record every failure in the audit log with the order ID, symbol, exception class, and sanitized error message. 4. Re-fetch open orders after cancellation attempts to verify the final exchange state. 5. Print an explicit warning listing orders that remain open. 6. Exit with a nonzero status if any order cannot be confirmed cancelled. 7. Implement bounded retries with backoff for transient network and rate-limit failures. 8. Preserve the local kill-switch file even when remote cancellation fails. 9. Avoid printing an aggregate success message unless all cancellation results have been verified. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/bitstamp.py:200
Finding
Non-finite and non-positive values can bypass local trading guardrails<![CDATA[ ## Vulnerability Details **File Location**: `scripts/bitstamp.py:200-230, 488-499, 574-584` **Vulnerability Type**: Insufficient numeric input and configuration validation **Risk Level**: Medium ### Vulnerable Code Order values are calculated and compared without first validating finiteness or positivity: ```python if price: order_price = price else: order_price = current_price # For buy orders, amount is in base currency est_usd = amount * order_price if "/EUR" in market: est_usd *= 1.08 # Rough EUR→USD for limit checks print(f"💰 Estimated order value: ${est_usd:,.2f}") # Max order size if est_usd > cfg["max_order_size_usd"]: print(f"🚫 Order exceeds max size (${cfg['max_order_size_usd']:,.2f}). Rejected.") ``` The daily-limit comparison has the same weakness: ```python daily_vol = get_daily_volume() if daily_vol + est_usd > cfg["max_daily_volume_usd"]: print(f"🚫 Daily volume limit would be exceeded.") ``` Configuration values are converted to floats but not validated: ```python default_val = DEFAULT_CONFIG[key] if isinstance(default_val, float): value = float(value) elif isinstance(default_val, list): value = [v.strip() for v in value.split(",")] cfg[key] = value save_config(cfg) ``` The CLI similarly accepts any value understood by Python's `float()` conversion: ```python p = sub.add_parser("buy", help="Place a buy order") p.add_argument("amount", type=float, help="Amount to buy (in base currency)") p.add_argument("--price", "-p", type=float, help="Limit price (omit for market order)") p = sub.add_parser("sell", help="Place a sell order") p.add_argument("amount", type=float, help="Amount to sell (in base currency)") p.add_argument("--price", "-p", type=float, help="Limit price (omit for market order)") ``` ### Technical Analysis Python accepts values such as `nan`, `inf`, negative numbers, and zero through `float()`. The application does not enforce that order amounts, prices, or safety limits are fini ...[truncated 1810 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate all amounts, prices, thresholds, and limits with `math.isfinite()`. 2. Require order amounts and explicit prices to be strictly greater than zero. 3. Require maximum order size and maximum daily volume to be positive finite values. 4. Require deviation and confirmation thresholds to be finite and nonnegative. 5. Reject invalid configuration before writing it to disk. 6. Validate loaded configuration as well as command-line input, because the JSON file can be edited externally. 7. Reject non-finite ticker values returned by the exchange. 8. Use a custom `argparse` type function that fails early with a clear validation message. 9. Add tests covering `nan`, positive and negative infinity, zero, negative values, and extremely large finite values. 10. Serialize JSON with strict non-finite handling so `NaN` and infinity cannot be persisted. ]]>
Vulnerability Patterns
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • 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
Findings (27)

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
All commands use the script at `scripts/bitstamp.py`. Run via:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill exposes sensitive capabilities related to environment access and file read/write, but it does not declare any explicit tool scope or permissions boundaries. In a trading skill, this is especially dangerous because environment variables are expected to contain Bitstamp API credentials, and file access could enable unauthorized reading or persistence of sensitive data, making the agent's effective privileges broader than the user can easily assess.

Tainted flow: 'LOG_FILE' from os.environ.get (line 51, credential/environment) → open (file write)

Medium
Category
Data Flow
Content
"action": action,
        **details,
    }
    with open(LOG_FILE, "a") as f:
        f.write(json.dumps(entry) + "\n")
    return entry
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Static analysis

No suspicious patterns detected.