Back to skill

Security audit

Polymarket 24h Nba Game Structure Trader

Security checks for vulnerabilities and agentic risk

Overview

The skill is clearly a trading bot and is not deceptive, but its live-trading safeguards are under-enforced in ways that could expose more funds than users expect.

Review this carefully before installing for live use. It is acceptable for paper trading, but live trading should only be used with a tightly limited API key, small balances, dependency pinning, and fixes for the volume, position-count, and fail-closed context checks.

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

Error
Location
clawhub.json:6
Finding

Unpinned Third-Party Trading Dependency

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:439
Finding

Declared Minimum-Volume Trading Safeguard Is Not Enforced

Content
View full analysis
tuple[bool, str]: """Check basic market quality gates.""" p = getattr(market, "current_probability", None) if not isinstance(p, (int, float)): return False, "missing probability" spread_cents = getattr(market, "spread_cents", None) if isinstance(spread_cents, (int, float)) and spread_cents / 100 > MAX_SPREAD: return False, f"Spread {spread_cents/100:.1%} > {MAX_SPREAD:.1%}" resolves_at = getattr(market, "resolves_at", None) if resolves_at: try: resolves = datetime.fromisoformat(resolves_at.replace("Z", "+00:00")) days = (resolves - datetime.now(timezone.utc)).days if days < MIN_DAYS: return False, f"Only {days} days to resolve" except Exception: pass return True, "ok" ``` ### Technical Analysis `SIMMER_MIN_VOLUME` is exposed as a tunable and documented as the minimum acceptable market volume. Despite being parsed into `MIN_VOLUME`, that value is not referenced by `valid_market`, signal generation, or order execution. As a result, users and operators may reasonably believe that markets below the configured liquidity threshold are rejected when no such protection exists. A low-volume market can pass validation as long as its probability, reported spread, and resolution date pass the implemented checks. Low-liquidity prediction markets are more susceptible to manipulation, stale prices, excessive execution slippage, and an inability to unwind ...[truncated 1301 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:561
Finding

Concurrent Position Limit Resets on Every Invocation

Content
View full analysis
= MAX_POSITIONS: break market = opp[0] side, size, reasoning = compute_signal(market, opp) if not side: print(f" [skip] {reasoning}") continue ok, why = context_ok(client, market_id) if not ok: print(f" [skip] {why}") continue try: r = client.trade( market_id=market_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[:110]}") if r.success: placed += 1 except Exception as e: print(f" [error] {market_id}: {e}") ``` ### Technical Analysis `SIMMER_MAX_POSITIONS` is described as the maximum number of concurrent open positions. The implementation instead initializes `placed` to zero on every run and increments it only for successful orders submitted during that invocation. The skill never queries the account's existing open positions. Therefore, the limit is a per-run order-count limit rather than a concurrent-position limit. Existing positions, positions opened by previous invocations, and potentially positions opened by other tools using the same account are excluded. Repeated live execution can consequently accumulate substantially more exposure than the documented control permits. ### Attack Path 1. The account already has ope ...[truncated 983 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:496
Finding

Trading Context Safeguards Fail Open on Errors

Content
View full analysis
tuple[bool, str]: """Check flip-flop and slippage safeguards.""" try: ctx = client.get_market_context(market_id) if not ctx: return True, "no context" if ctx.get("discipline", {}).get("is_flip_flop"): reason = ctx["discipline"].get("flip_flop_reason", "recent reversal") return False, f"Flip-flop: {reason}" slip = ctx.get("slippage", {}) if isinstance(slip, dict) and slip.get("slippage_pct", 0) > 0.15: return False, f"Slippage {slip['slippage_pct']:.1%}" for w in ctx.get("warnings", []): print(f" [warn] {w}") except Exception as e: print(f" [ctx] {market_id}: {e}") return True, "ok" ``` The approving result is subsequently used to permit trading: ```python ok, why = context_ok(client, market_id) if not ok: print(f" [skip] {why}") continue try: r = client.trade( market_id=market_id, side=side, amount=size, source=TRADE_SOURCE, skill_slug=SKILL_SLUG, reasoning=reasoning, ) ``` ### Technical Analysis The function is intended to reject flip-flop behavior and excessive slippage. However, both an empty context and any exception result in approval: - `if not ctx: return True, "no context"` - The exception handler logs the error and falls through to `return True, "ok"` Exceptions may arise from network failures, authentication errors, timeouts, malformed responses, unexpected SDK return types, or changes to the response schema. For example, a non-dictionary `ctx` can fail at `ctx.get`, while a nonnumeric `slippage_pct` can fail during comparison or forma ...[truncated 1302 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
85% confidence
Finding

The skill references a high-value credential (SIMMER_API_KEY) and describes trading behavior, but it does not declare an explicit tool/permission scope such as allowed tools or environment access boundaries. That omission weakens least-privilege guarantees: an agent runtime may expose environment access more broadly than intended, increasing the chance that credentials are read or misused by skill logic or adjacent components.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This manifest requires the SIMMER_API_KEY environment variable, which indicates the skill depends on sensitive credentials. In this file there is no accompanying disclosure about credential handling, storage, or transmission, which can leave users unaware that a secret is needed and may be used by the skill.

Content

No source excerpt is available for this finding.