Back to skill

Security audit

Polymarket Youtube Channel Trader

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed paper-by-default trading skill, but its live-trading path has under-enforced risk controls that could expose users to more financial risk than expected.

Install only if you are comfortable with a trading automation skill that uses a high-value API key. Keep it in paper mode unless you have reviewed the code and SDK, use a narrowly scoped or segregated key where possible, and be aware that live mode may trade broader YouTube/creator markets and may not enforce the documented volume, position-size, and portfolio-limit safeguards as written.

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 (5)

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:307
Finding

Minimum Market Volume Safeguard Is Configured but Never Enforced

Content
View full analysis
= MAX_POSITIONS: break side, size, reasoning = compute_signal(m) if not side: safe_print(f" [skip] {reasoning}") continue ok, why = context_ok(client, m.id) if not ok: safe_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, ) ``` The documented setting is: ```markdown | `SIMMER_MIN_VOLUME` | `2000` | Min market volume — lower bar for niche YouTube markets | ``` ### Technical Analysis The application loads `SIMMER_MIN_VOLUME` from the environment and advertises it as a market-liquidity safeguard. However, neither `compute_signal()` nor the execution loop compares a market's volume against this threshold. Consequently, any discovered market that passes the probability, spread, resolution, and context checks may be traded regardless of its liquidity. Merely loading a risk parameter does not enforce the associated policy. Low-volume prediction ...[truncated 1308 chars]
Remediation
View remediation
tuple[bool, str]: raw_volume = getattr(market, "volume", None) try: volume = float(raw_volume) except (TypeError, ValueError): return False, "Missing or invalid market volume" if not math.isfinite(volume) or volume < 0: return False, "Invalid market volume" if volume < MIN_VOLUME: return False, f"Volume ${volume:,.2f} below ${MIN_VOLUME:,.2f}" return True, "ok" ``` Call this check before `compute_signal()` or `client.trade()`. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:505
Finding

Minimum Trade Floor Can Override 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 [{channel['display_name']}] YES={p:.0%} edge={edge:.0%} " f"bias={bias:.2f}x size=${size} — {q[:55]}" ) ``` The metadata permits `SIMMER_MAX_POSITION` to be as low as 1 and `SIMMER_MIN_TRADE` to be as high as 50: ```json { "env": "SIMMER_MAX_POSITION", "type": "number", "default": 30, "range": [1, 200] } ``` ```json { "env": "SIMMER_MIN_TRADE", "type": "number", "default": 5, "range": [1, 50] } ``` ### Technical Analysis The calculated size is bounded only from below: ```python size = max(MIN_TRADE, calculated_size) ``` It is not subsequently bounded by `MAX_POSITION`. If `MIN_TRADE` is larger than `MAX_POSITION`, the minimum-trade floor becomes the final order size and violates the declared maximum. The configuration metadata independently allows values that create this invalid relationship. Runtime environment variables are also parsed directly without enforcing metadata ranges, finite-number checks, positivity, or cross-field constraints. For example, wi ...[truncated 1034 chars]
Remediation
View remediation
MAX_POSITION: raise ValueError( "SIMMER_MIN_TRADE must not exceed SIMMER_MAX_POSITION" ) ``` Apply both lower and upper bounds when sizing: ```python calculated = round(conviction * MAX_POSITION, 2) size = min(MAX_POSITION, max(MIN_TRADE, calculated)) ``` Also: 1. Enforce the documented metadata ranges at runtime. 2. Validate the relationship between all threshold and sizing fields. 3. Add tests for `MIN_TRADE == MAX_POSITION`, `MIN_TRADE < MAX_POSITION`, and invalid inverted settings. 4. Refuse to start live mode when any risk configuration is invalid. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:657
Finding

Concurrent Position Limit Counts Only Orders Placed During the Current Run

Content
View full analysis
= MAX_POSITIONS: break side, size, reasoning = compute_signal(m) if not side: safe_print(f" [skip] {reasoning}") continue ok, why = context_ok(client, m.id) if not ok: safe_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}" safe_print(f" [trade] {side.upper()} ${size} {tag} {status} -- {reasoning[:75]}") if r.success: placed += 1 ``` The documented setting is: ```markdown | `SIMMER_MAX_POSITIONS` | `10` | Max concurrent open positions | ``` ### Technical Analysis The local `placed` counter starts at zero every time the process runs and increases only after successful orders during that invocation. The application does not query the account's existing open positions or pending orders. As a result, this code implements “maximum successful orders per process run,” not “maximum concurrent open positions.” Existing exposure is ignored. Repeated manual, scheduled, or managed executions can therefore add up to `MAX_POSITIONS` additional orders on every run. The logic may also place another order in a market where a position already exists unless the external context API independently blocks it. That API is not used to calculate the declared portfolio-wide limit. ### Attack Path 1. The account already has one or more open positions from an earlier execution. 2. The script starts again and resets `placed` to zero. 3 ...[truncated 615 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
trader.py:491
Finding

Invalid Resolution Timestamps Bypass the Minimum-Days Policy

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
clawhub.json:5
Finding

Trading SDK Dependency Is Not Version-Pinned

Content
View full analysis
Remediation
View remediation
\ --hash=sha256: ``` ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (5)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill explicitly requires the high-value SIMMER_API_KEY environment variable but does not declare any tool scope or permission boundary for environment access. That creates an unnecessary trust gap: a runtime may expose environment variables more broadly than intended, and users/reviewers cannot easily verify the minimum privileges the skill needs before enabling it.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The manifest requires a live trading credential (SIMMER_API_KEY) but the manifest itself provides no user-facing warning that the skill connects to an external trading service and may place trades when configured for live mode. In a financial trading skill, unclear credential use increases the risk of users supplying sensitive API keys without understanding scope, external transmission, or the consequences of enabling real-money actions.

Content

No source excerpt is available for this finding.

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding

Suspicious Unicode normalization or mixed-script content

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The market discovery logic expands beyond the stated 'YouTube channel' scope into broader creator and streaming terms such as streamers, content creators, watch hours, and non-top-channel personalities. In a trading agent, this scope drift is dangerous because it can cause autonomous trades in markets the operator did not intend to authorize, increasing financial and policy risk even if paper trading is the default.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

The docstring says the function returns a matching profile only when 'no top-10 channel is referenced,' implying matching is limited to top-10 channels. In reality, CHANNEL_PROFILES includes numerous additional channels beyond the top 10, and the function matches against all of them, so the inline documentation misstates the function's behavior.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.