Back to skill

Security audit

Polymarket Twitter Cadence Model Trader

Security checks for vulnerabilities and agentic risk

Overview

This skill is upfront that it can paper trade by default and place real Polymarket trades with --live, but several documented trading safeguards are not actually enforced in the code.

Install only if you are comfortable giving this skill a Simmer API key and keeping it in paper mode unless you have independently reviewed the code and dependency. Do not provide a live-capable key or run with --live until the market-volume check, account-wide position limit, order-size bounds, and live/paper client handling are fixed.

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)

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:266
Finding

Minimum Market-Volume Safeguard Is Not 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: ...[truncated 1549 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:266
Finding

Maximum Open-Position Limit Only Counts 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[:70]}") if r.success: placed += 1 ``` ### Technical Analysis `MAX_POSITIONS` is documented as the maximum number of concurrent open positions. The implementation instead initializes a local counter to zero on every invocation and increments it only for successful orders placed during that invocation. The code never retrieves existing account positions, does not count unresolved exposure from earlier runs, and does not determine whether the account already has a position in the selected market. Therefore, `MAX_POSITIONS` is a per-run order limit rather than an account-wide open-position limit. Repeated invocations can continuously add exposure while each individual run remains within the local counter. ### Attack Path 1. The account already holds `MAX_POSITIONS` unresolved positions. 2. The operator, automation, or another process invokes the Skill again. 3. The local `placed` counter is reset to zero. 4. Market discovery identifies additional qualifying markets. 5. The Skill submits up to `MAX_POSITIONS` further successful orders. 6. Repeating the process causes the actual number of open positio ...[truncated 460 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:204
Finding

Minimum Trade Size Can Override the Configured Maximum Position Size

Content
View full analysis
= NO_THRESHOLD: # We'd sell NO — does our model agree this bin is overpriced? if model_p >= p * 1.5: return None, 0, f"Model disagrees: model={model_p:.1%} > mkt={p:.1%}, skip NO" bias = min(2.0, max(0.5, max(p, 0.01) / max(model_p, 0.01))) conviction = min(1.0, (p - NO_THRESHOLD) / (1 - NO_THRESHOLD) * bias) size = max(MIN_TRADE, round(conviction * MAX_POSITION, 2)) edge = p - model_p ``` The allowed configuration ranges overlap unsafely: `SIMMER_MAX_POSITION` can be as low as `1`, while `SIMMER_MIN_TRADE` can be as high as `100`. ### Technical Analysis The calculation uses `max(MIN_TRADE, calculated_size)`. If `MIN_TRADE` is greater than `MAX_POSITION`, the resulting order size exceeds the configured maximum position. For example, a valid configuration of `MAX_POSITION=10` and `MIN_TRADE=100` results in a `$100` order. A maximum exposure control must be an absolute upper bound. A minimum execution amount should cause undersized signals to be skipped, not cause an order to be increased beyond the maximum. ### Attack Path 1. Skill configuration or environment variables set `SIMMER_MIN_TRADE` to a value greater than `SIMMER_MAX_ ...[truncated 733 chars]
Remediation
View remediation
MAX_POSITION`. - Treat `MAX_POSITION` as an unconditional upper bound. - Calculate the conviction amount first; if it is below `MIN_TRADE`, skip the trade rather than increasing it. - Apply a final bound before submission, for example: ```python calculated_size = round(conviction * MAX_POSITION, 2) if calculated_size < MIN_TRADE: return None, 0, "Calculated order is below the minimum trade size" size = min(calculated_size, MAX_POSITION) ``` - Validate all numeric settings as finite and non-negative. - Add tests for equal limits, inverted limits, boundary values, zero conviction, and both YES and NO paths. ]]>

T08 · Insecure Dependencies

Note
Location
clawhub.json:5
Finding

Trading Dependency Is Installed Without a Version or Integrity Pin

Content
View full analysis
Remediation
View remediation
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)

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

The skill is presented as a Twitter cadence modeling template, but the content also describes trading Truth Social markets and executing real Polymarket trades when --live is supplied. This mismatch can mislead users or orchestration systems about the skill's real operational scope, increasing the chance of enabling live financial actions or unsupported market coverage without informed consent.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill declares access to an environment variable containing trading authority (SIMMER_API_KEY) but does not define any explicit tool scope such as permissions or allowed-tools. In an agent ecosystem, missing scope declarations weaken least-privilege controls and can let the skill access sensitive data or capabilities without clear operator visibility.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The manifest description limits the skill to pricing and trading Twitter post-count bins, but the implementation explicitly searches for and models both Twitter/X and Truth Social markets. This broadens the operational scope beyond the manifest's stated purpose in a way that is semantically significant, not just an implementation detail.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The global cached client in get_client() ignores subsequent live/paper mode changes after the first initialization. If the process first creates a live client, later calls to run(live=False) may still reuse the live client and place real-money trades despite the code and docstring claiming paper mode by default, which is dangerous in an automated trading skill.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

This manifest declares a required environment variable named SIMMER_API_KEY, which indicates the skill depends on sensitive credentials. In this file there is no accompanying user-facing warning or explanatory text about credential handling, which can leave users unaware that they must supply and trust the skill with an API key.

Content

No source excerpt is available for this finding.