T09 · Insecure Skill Coding Practices
Error
- Location
- trader.py:27
- Finding
- Market-volume and portfolio-exposure safeguards are not enforced<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:27-35`, `trader.py:220-258` **Vulnerability Type**: Missing financial risk-control enforcement **Risk Level**: High ### Vulnerable Code ```python # Risk parameters — declared as tunables in clawhub.json, tunable from Simmer UI. # Named SIMMER_* so apply_skill_config() can load automaton-managed overrides. MAX_POSITION = float(os.environ.get("SIMMER_MAX_POSITION", "25")) MIN_VOLUME = float(os.environ.get("SIMMER_MIN_VOLUME", "1000")) MAX_SPREAD = float(os.environ.get("SIMMER_MAX_SPREAD", "0.12")) MIN_DAYS = int(os.environ.get( "SIMMER_MIN_DAYS", "0")) MAX_POSITIONS = int(os.environ.get( "SIMMER_MAX_POSITIONS", "8")) # Signal thresholds — buy YES below YES_THRESHOLD, sell NO above NO_THRESHOLD. # Position size scales with conviction, further boosted/dampened by seasonal alignment. YES_THRESHOLD = float(os.environ.get("SIMMER_YES_THRESHOLD", "0.42")) NO_THRESHOLD = float(os.environ.get("SIMMER_NO_THRESHOLD", "0.58")) MIN_TRADE = float(os.environ.get("SIMMER_MIN_TRADE", "5")) ``` ```python def run(live: bool = False) -> None: mode = "LIVE" if live else "PAPER (sim)" print(f"[polymarket-climate-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-climate-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=TR ...[truncated 2636 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Enforce the volume threshold before signal generation or trade submission: ```python volume = getattr(m, "volume", None) if volume is None or volume < MIN_VOLUME: print(f" [skip] Volume {volume!r} below minimum ${MIN_VOLUME}") continue ``` Confirm the authoritative volume property and units in the pinned SDK version rather than assuming the field name. 2. Retrieve the account's current open positions before processing markets. 3. Calculate remaining capacity from the actual portfolio: ```python remaining_slots = max(0, MAX_POSITIONS - len(open_positions)) ``` 4. Refuse new orders when the market already has exposure unless intentional position increases are explicitly supported and bounded. 5. Add account-wide and per-market notional limits, including pending orders. 6. Recheck portfolio state immediately before trade submission to reduce race conditions between concurrent runs. 7. Use server-side limits or idempotency keys where supported so multiple processes cannot bypass client-side checks. 8. Add automated tests proving that: - Markets below `MIN_VOLUME` are rejected. - Existing positions count toward `MAX_POSITIONS`. - Repeated and concurrent invocations cannot exceed the configured exposure limits. ]]>
