Back to skill

Security audit

Polymarket Copy Profit Taker Trader

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed paper-by-default trading skill, but its live-trading risk controls and rotation logic do not fully match the safety claims users would rely on.

Install only if you are comfortable reviewing the live-trading behavior first. Keep it in paper mode unless the SDK is pinned and audited, the rotation-window bug is fixed, and live orders fail closed whenever portfolio position checks cannot be verified. Use a tightly scoped, revocable SIMMER_API_KEY with low account limits.

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

T08 · Insecure Dependencies

Error
Location
clawhub.json:3
Finding
Unpinned Third-Party Trading SDK Creates a Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Location**: `clawhub.json:3`; `SKILL.md:144` **Vulnerability Type**: Unpinned executable dependency **Risk Level**: High ### Complete Code Snippet From `clawhub.json:3`: ```json "requires": {"env": ["SIMMER_API_KEY"], "pip": ["simmer-sdk"]}, ``` From `SKILL.md:144`: ```markdown Requires `simmer-sdk` (pip install simmer-sdk) and a valid `SIMMER_API_KEY`. ``` ### Technical Analysis The project installs `simmer-sdk` without an exact version, package hash, or lock file. Consequently, installation can resolve to a package version that was not part of this audit. This dependency is security-sensitive because `trader.py` imports `SimmerClient` from it and provides the `SIMMER_API_KEY` directly to the client. The SDK also mediates position retrieval, market discovery, and trade submission. A compromised or unexpectedly modified package version would therefore execute within the Skill process and could access its environment and trading operations. The reviewed project does not demonstrate dependency confusion or prove that the current package is malicious. The vulnerability is the absence of controls ensuring that future installations obtain the specific audited artifact. ### Attack Path 1. An attacker compromises the package registry account, publication process, or upstream source for `simmer-sdk`. 2. The attacker publishes a malicious release under the expected package name. 3. A new Skill installation executes `pip install simmer-sdk` without a version constraint or integrity hash. 4. The package installer selects and installs the attacker-controlled release. 5. Import-time code or malicious `SimmerClient` methods execute with the privileges of the Skill process. 6. When the Skill constructs the client, the malicious package receives `SIMMER_API_KEY`. 7. The package can exfiltrate the credential, alter authenticated requests, submit unauthorized trades, or access other data available to the process. ### Impact As ...[truncated 479 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `simmer-sdk` to a specific audited version, such as an exact `==` constraint. 2. Maintain a lock file containing all transitive dependency versions. 3. Require cryptographic package hashes during installation, for example with pip's `--require-hashes`. 4. Verify that the package originates from the expected publisher and documented source repository. 5. Review each dependency update before changing the pinned version. 6. Run the Skill in an isolated environment with minimal filesystem and network permissions. 7. Scope `SIMMER_API_KEY` to only the operations and account limits required by this Skill. 8. Prefer short-lived or readily revocable credentials where supported. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:412
Finding
Position-Limit Guard Fails Open and Is Not Enforced Before Every Rotation Trade<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:412-422`, `trader.py:505-506`, and `trader.py:574-580` **Vulnerability Type**: Fail-open financial risk control and incomplete limit enforcement **Risk Level**: High ### Complete Code Snippet The position check treats an exception as approval: ```python def context_ok(client: SimmerClient) -> bool: """Check portfolio context to prevent excessive positions.""" try: positions = client.get_positions() open_count = len([p for p in positions if float(getattr(p, "size", 0)) > 0]) if open_count >= MAX_POSITIONS: safe_print(f" [GATE] Already at {open_count}/{MAX_POSITIONS} positions, skipping") return False except Exception as exc: safe_print(f" [WARN] Could not check positions: {exc}") return True ``` The rotation phase performs the check only once before processing all targets: ```python if not context_ok(client): safe_print("\nAborted: portfolio context check failed.") return executed = 0 skipped = 0 seen_markets: set[str] = set() ``` Each rotation target can then submit a trade without another position-limit check: ```python try: r = client.trade( market_id=m.id, side=side, amount=size, source=TRADE_SOURCE, ) safe_print(f" TRADE: {side.upper()} ${size:.2f} on {m.question[:60]}") safe_print(f" -> {r}") executed += 1 ``` ### Technical Analysis `context_ok()` is intended to enforce `MAX_POSITIONS`, but it returns `True` whenever `client.get_positions()` raises an exception. This is a fail-open design: inability to establish that the portfolio is safe is interpreted as permission to continue trading. The rotation-target loop also checks the portfolio only once. If the account starts below the limit, several rotation targets may each open a position and cause the portfolio to exceed `MAX_POSITIONS`. Unlike the fallback loop, the rotation loop does not invoke `c ...[truncated 1733 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Fail closed in live mode whenever positions cannot be retrieved: ```python except Exception as exc: safe_print(f" [ERROR] Could not verify positions: {exc}") return False ``` 2. Invoke `context_ok(client)` immediately before every rotation and fallback trade. 3. Maintain a local count of successful orders during the run and stop when the remaining position allowance is exhausted. 4. Refresh portfolio state after each successful live order when the API supports consistent reads. 5. Treat ambiguous trade responses and timeouts carefully to avoid duplicate orders; reconcile order status before retrying. 6. Add a separate maximum total amount per run in addition to the per-order and position-count limits. 7. Validate all risk-related environment variables and reject negative, non-finite, or out-of-policy values. 8. Add automated tests covering position API failure, a portfolio one position below the cap, and multiple simultaneous rotation targets. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:255
Finding
Configured Rotation Time Window Is Not Enforced<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:255-307` **Vulnerability Type**: Incorrect temporal validation in trading-signal generation **Risk Level**: Medium ### Complete Code Snippet ```python def detect_rotations( all_wallet_positions: dict[str, list[tuple[str, str, float, float, float, str]]], rotation_window_hours: int, ) -> list[tuple[str, str, str, str, float]]: """Detect rotation patterns: whale exits market A AND enters market B. For each wallet: find markets where they're TAKING_PROFIT and markets where they're ACCUMULATING. If same wallet does both within the rotation window -> rotation detected. Args: all_wallet_positions: wallet -> list of position change tuples rotation_window_hours: time window for rotation detection Returns: list of (wallet, exit_market, entry_market, entry_side, entry_conviction) entry_conviction = how much they're putting into the new position relative to what they're taking out """ rotations = [] for wallet, positions in all_wallet_positions.items(): exits = [] entries = [] for title, side, buy_total, sell_total, sell_ratio, status in positions: if status == "TAKING_PROFIT": exits.append((title, sell_total, sell_ratio)) elif status == "ACCUMULATING": entries.append((title, side, buy_total)) if not exits or not entries: continue # Each exit/entry pair is a potential rotation total_exit_value = sum(sell_total for _, sell_total, _ in exits) for exit_title, exit_sold, exit_ratio in exits: for entry_title, entry_side, entry_bought in entries: # entry_conviction: how much of the exit capital went to this entry if total_exit_value > 0: entry_conviction = min(1.0, entry_bought / total_exit_value) else: ...[truncated 2799 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Preserve relevant temporal data during position analysis, including at least the most recent qualifying buy timestamp and sell timestamp for each market. 2. Extend the rotation data model to include those timestamps. 3. Enforce an explicit temporal relationship before emitting a rotation: - Both timestamps must be present and timezone-aware. - Their absolute difference must not exceed `rotation_window_hours`. - If the strategy requires capital to move from exit to entry, require the exit to occur before the entry. 4. Reject activity with invalid or unparseable timestamps instead of silently using it for rotation inference. 5. Define whether aggregated historical trades should be limited to the same analysis window and filter them consistently. 6. Add tests for events inside the window, outside the window, in reverse order, missing timestamps, and boundary timestamps. 7. Log the timestamps and calculated interval for every accepted rotation so users can verify the signal. ]]>
Vulnerability Patterns
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (4)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared purpose frames the skill as detection/identification of whale exits and rotations, but the content clearly describes autonomous trade execution, optional live trading, fallback trading beyond explicit rotation targets, and broader market selection logic. This mismatch is dangerous because operators may approve or invoke the skill expecting passive analytics, while it can place trades and take financial actions with real capital under a less restrictive trust model.

Intent-Code Divergence

High
Confidence
98% confidence
Finding
The code claims to detect rotations only when a whale exits one market and enters another within a configured time window, but the implementation never checks exit and entry timestamps when pairing them. This can generate false 'rotation' signals from unrelated historical trades, causing the bot to trade live on fabricated smart-money flow and undermining the core safety logic of the strategy.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises network access and use of an environment variable (`SIMMER_API_KEY`) but does not declare any explicit tool scope such as permissions or allowed-tools. In an agent framework, undeclared env/network capabilities reduce transparency and policy enforcement, making it easier for a skill to access secrets or external endpoints beyond what reviewers expect.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The skill advertises a strategy centered on copying whale profit-taking rotations, but the fallback branch places trades on any 'ACCUMULATING' market that passes generic threshold checks, even when no rotation signal exists. In an automated trading skill, this strategy drift is dangerous because operators may enable live mode expecting one constrained behavior while the code executes a broader, materially different trading policy.

Static analysis

No suspicious patterns detected.