T09 · Insecure Skill Coding Practices
Error
- Location
- trader.py:55
- Finding
- Cached Client Can Retain Live-Trading Mode During a Paper-Trading Run<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:37, 55-76` **Vulnerability Type**: `T09: Insecure Skill Coding Practices` **Risk Level**: High ### Vulnerable Code ```python _client: SimmerClient | None = None def get_client(live: bool = False) -> SimmerClient: """ live=False -> venue="sim" (paper trades -- safe default). live=True -> venue="polymarket" (real trades, only with --live flag). """ global _client, MAX_POSITION, MIN_VOLUME, MAX_SPREAD, MIN_DAYS, MAX_POSITIONS global YES_THRESHOLD, NO_THRESHOLD, MIN_TRADE, MARU_THRESHOLD if _client is None: venue = "polymarket" if live else "sim" _client = SimmerClient( api_key=os.environ["SIMMER_API_KEY"], venue=venue, ) if live: _client.live = True try: _client.apply_skill_config(SKILL_SLUG) except AttributeError: pass # apply_skill_config only available in Simmer runtime # Re-read params in case apply_skill_config updated os.environ. MAX_POSITION = float(os.environ.get("SIMMER_MAX_POSITION", str(MAX_POSITION))) MIN_VOLUME = float(os.environ.get("SIMMER_MIN_VOLUME", str(MIN_VOLUME))) MAX_SPREAD = float(os.environ.get("SIMMER_MAX_SPREAD", str(MAX_SPREAD))) MIN_DAYS = int(os.environ.get( "SIMMER_MIN_DAYS", str(MIN_DAYS))) MAX_POSITIONS = int(os.environ.get( "SIMMER_MAX_POSITIONS", str(MAX_POSITIONS))) YES_THRESHOLD = float(os.environ.get("SIMMER_YES_THRESHOLD", str(YES_THRESHOLD))) NO_THRESHOLD = float(os.environ.get("SIMMER_NO_THRESHOLD", str(NO_THRESHOLD))) MIN_TRADE = float(os.environ.get("SIMMER_MIN_TRADE", str(MIN_TRADE))) MARU_THRESHOLD = float(os.environ.get("SIMMER_MARU_THRESHOLD", str(MARU_THRESHOLD))) return _client ``` ### Technical Analysis The module stores a single `SimmerClient` instance in the global `_client` variable ...[truncated 1842 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Do not use one unqualified singleton for both venues. Maintain separate clients indexed by mode or venue: ```python _clients: dict[str, SimmerClient] = {} def get_client(live: bool = False) -> SimmerClient: venue = "polymarket" if live else "sim" if venue not in _clients: _clients[venue] = SimmerClient( api_key=os.environ["SIMMER_API_KEY"], venue=venue, ) client = _clients[venue] if getattr(client, "venue", venue) != venue: raise RuntimeError("Trading client venue does not match requested mode") return client ``` - Alternatively, recreate the client whenever the requested mode differs from the cached client's mode. - Immediately before every call to `client.trade()`, enforce that the client's effective venue matches the requested run mode. - Avoid relying on a mutable SDK-specific `live` attribute as the only safety control. - Add regression tests that execute a live initialization followed by a paper run in the same process and verify that the second run cannot use the live venue. - Make the mode part of the immutable execution context and include the verified venue in trade logs. ]]>
