Back to skill

Security audit

Polymarket Music Entertainment Trader

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed trading bot, but its live-trading risk limits are partly inaccurate or unenforced and it gives a trading API key to an unpinned dependency.

Use paper mode first. Before live use, pin and review simmer-sdk, use a narrowly scoped revocable key with no withdrawal authority, set server-side trade and loss limits, and fix or verify the advertised risk controls for volume, per-trade size, and open-position exposure.

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
clawhub.json:2
Finding

Unpinned Third-Party Dependency Receives Trading Credentials and Controls Trade Execution

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:219
Finding

Configured Minimum Market Volume Is Never Enforced

Content
View full analysis
None: mode = "LIVE" if live else "PAPER (sim)" print(f"[polymarket-music-entertainment-trader] mode={mode} max_pos=${MAX_POSITION} min_vol=${MIN_VOLUME} max_spread={MAX_SPREAD:.0%} min_days={MIN_DAYS}") client = get_client(live=live) markets = find_markets(client) print(f"[polymarket-music-entertainment-trader] {len(markets)} candidate markets") placed = 0 for m in markets: if placed >= MAX_POSITIONS: break side, size, reasoning = compute_signal(m) if not side: print(f" [skip] {reasoning}") continue ok, why = context_ok(client, m.id) if not ok: print(f" [skip] {why}") continue try: r = client.trade( market_id=m.id, side=side, amount=size, source=TRADE_SOURCE, skill_slug=SKILL_SLUG, reasoning=reasoning, ) tag = "(sim)" if r.simulated else "(live)" status = "OK" if r.success else f"FAIL:{r.error}" print(f" [trade] {side.upper()} ${size} {tag} {status} — {reasoning[:70]}") if r.success: placed += 1 except Exception as e: print(f" [error] {m.id}: {e}") print(f"[polymarket-music-entertainment-trader] done. {placed} orders placed.") ``` ### Technical Analysis `MIN_VOLUME` is read from the environment and displayed in the startup message, but neither `compute_signal()` nor the trading loop compare ...[truncated 1611 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:180
Finding

Minimum Trade Floor Can Exceed the Maximum Position Limit

Content
View full analysis
= NO_THRESHOLD: conviction = min(1.0, (p - NO_THRESHOLD) / (1 - NO_THRESHOLD) * bias) size = max(MIN_TRADE, round(conviction * MAX_POSITION, 2)) edge = p - NO_THRESHOLD return "no", size, f"NO YES={p:.0%} edge={edge:.0%} bias={bias:.2f}x size=${size} — {q[:65]}" ``` The metadata independently permits `MAX_POSITION` as low as 1 and `MIN_TRADE` as high as 100: ```json { "env": "SIMMER_MAX_POSITION", "default": 15, "range": [ 1, 200 ], "step": 1, "label": "Max position size (USD)", "type": "number" } ``` ```json { "env": "SIMMER_MIN_TRADE", "type": "number", "default": 5, "range": [ 1, 100 ], "step": 1, "label": "Min trade size (USD)" } ``` ### Technical Analysis The order size is calculated with: ```python max(MIN_TRADE, conviction * MAX_POSITION) ``` This enforces only a lower bound. It does not enforce `MAX_POSITION` as an upper bound after applying the minimum trade floor. If `MIN_TRADE` is greater than `MAX_POSITION`, every qualifying order can exceed the configured maximum position. The metadata permits this invalid relationship without validation. For example, `MAX_POSITION=1` and `MIN_TRADE=100` are both individually valid according to the declared ranges, but the resulting order size is $100 rather than the intended $1 cap. Even when `M ...[truncated 1221 chars]
Remediation
View remediation
MAX_POSITION`, zero conviction, extreme environment values, and both YES and NO branches. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:225
Finding

Maximum Open Positions Setting Limits Orders Per Run Instead of Portfolio Exposure

Content
View full analysis
= MAX_POSITIONS: break side, size, reasoning = compute_signal(m) if not side: print(f" [skip] {reasoning}") continue ok, why = context_ok(client, m.id) if not ok: print(f" [skip] {why}") continue try: r = client.trade( market_id=m.id, side=side, amount=size, source=TRADE_SOURCE, skill_slug=SKILL_SLUG, reasoning=reasoning, ) tag = "(sim)" if r.simulated else "(live)" status = "OK" if r.success else f"FAIL:{r.error}" print(f" [trade] {side.upper()} ${size} {tag} {status} — {reasoning[:70]}") if r.success: placed += 1 except Exception as e: print(f" [error] {m.id}: {e}") print(f"[polymarket-music-entertainment-trader] done. {placed} orders placed.") ``` ### Technical Analysis `MAX_POSITIONS` is documented and labeled as a maximum number of concurrent open positions. The implementation instead initializes a local `placed` counter to zero for every invocation and increments it only for successful orders during that invocation. The code never queries the account's existing open positions. It also does not reserve capacity atomically or explicitly prevent new orders in markets where exposure already exists. Therefore, the configured setting is an order-count limit per process run, not a portfolio-level position limit. Repeated manual or scheduled executions can continue adding positions. Concurrent executions are especially problematic because each process starts with an independent zero counter. ### Attack Path 1. The account already has open positions, whether created by a previous Skill run or another client. 2. The ...[truncated 1028 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (2)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The skill references environment-based credentials (SIMMER_API_KEY) and describes live trading behavior, but it does not declare any explicit tool scope or permission boundaries. That omission weakens least-privilege controls because an agent or runtime may expose environment access more broadly than intended, increasing the chance of credential misuse or unauthorized live-trading actions if the surrounding platform permits execution.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The manifest explicitly requires a sensitive API key and configures an automated trading entrypoint, but provides no user-facing disclosure about what the credential can do, how it will be used, or the financial consequences of running the skill. In a trading skill, silent credential use increases the risk of users authorizing live market actions without informed consent, which can lead to unauthorized trades, account exposure, or unexpected financial loss.

Content

No source excerpt is available for this finding.