Back to skill

Security audit

Polymarket Arbitrage Pro

Security checks for vulnerabilities and agentic risk

Overview

This skill is a high-risk Polymarket trading bot that asks for a wallet private key and is designed to place recurring live orders without clear confirmations, limits, or safe default controls.

Review this carefully before installing. Do not use a primary wallet private key; if testing, use a dedicated low-balance wallet and assume any configured funds could be traded automatically. Treat scan/start as potentially live-trading workflows, and avoid use unless you are comfortable with the lack of confirmations, limits, and reliable arbitrage safeguards.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
polymarket-trade.js:291
Finding
Automatic Trading Logic Executes Unhedged Speculative Orders Instead of Arbitrage<![CDATA[ ## Vulnerability Details **File Location**: `polymarket-trade.js:291-295` **Vulnerability Type**: Unsafe financial transaction logic **Risk Level**: High ### Vulnerable Code ```js if (op.yesPrice < 0.5) { await placeOrder(op.yesToken, 'BUY', op.yesPrice, 10); } else if (op.noPrice < 0.5) { await placeOrder(op.noToken, 'BUY', op.noPrice, 10); } ``` ### Technical Analysis The documentation describes an arbitrage strategy in which both complementary outcomes are purchased when their combined cost is below the guaranteed settlement value. The implementation does not perform that strategy. It purchases only one outcome based on whether its displayed price is below `0.5`. A single-outcome purchase remains fully exposed to the event result and is therefore directional speculation rather than arbitrage. In addition, `detectArbitrage()` uses the absolute deviation from one: ```js const deviation = Math.abs(1 - total); ``` Consequently, the code treats both underpricing and overpricing as opportunities without determining whether a profitable pair of executable orders exists. The displayed prices may also be stale indicative values rather than executable order-book prices. The code does not account for liquidity, fees, slippage, partial fills, or whether the complementary order can be executed. Because `scan` and `start` automatically invoke `placeOrder()` whenever a wallet is configured, real wallet funds can be committed without per-order confirmation. ### Attack Path 1. A user configures `POLYMARKET_PRIVATE_KEY` for a funded Polygon wallet. 2. The user invokes `arbitrage scan` or `arbitrage start`, relying on the documented claim that the Skill performs arbitrage. 3. The Skill identifies a market whose Yes and No prices deviate from one by more than 2%. 4. The Skill selects only one outcome priced below `0.5`. 5. It submits a real GTC buy order for that single outcome. 6. If the selected outcome loses, or if market conditions move adversely, t ...[truncated 1159 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not automatically trade based on indicative outcome prices alone. 2. Confirm that the combined executable ask prices satisfy: - `yesAsk + noAsk + fees + expectedSlippage < guaranteedPayout`. 3. Query current order books and validate sufficient depth for the complete intended size. 4. Purchase both complementary outcomes as one coordinated strategy. If atomic execution is unavailable, implement strict handling for partial fills and immediately cancel or hedge unmatched exposure. 5. Reject opportunities where the combined price exceeds one; do not use absolute deviation as the profitability test. 6. Calculate exchange fees, blockchain costs, tick sizes, minimum order sizes, and settlement constraints before submitting an order. 7. Introduce configurable limits for order size, aggregate exposure, orders per interval, daily loss, and total wallet allocation. 8. Make dry-run mode the default and require explicit opt-in for real trading. 9. Require clear user confirmation before the first real order and display the exact maximum financial exposure. 10. Add automated tests proving that both legs are executed only when the net guaranteed return remains positive under conservative fee and slippage assumptions. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
polymarket-trade.js:172
Finding
Billing API Key and Wallet Identifier Are Transmitted to an External Service<![CDATA[ ## Vulnerability Details **File Location**: `polymarket-trade.js:172-183` **Vulnerability Type**: Sensitive credential transmission and privacy exposure **Risk Level**: Medium ### Vulnerable Code ```js async function checkBalance() { if (!SKILLPAY_KEY || !walletAddress) return 0; try { const resp = await fetch( `https://skillpay.me/api/v1/billing/balance?user_id=${walletAddress}`, { headers: { 'X-API-Key': SKILLPAY_KEY } } ); const data = await resp.json(); return data.balance || 0; } catch (e) { return 0; } } ``` Related charge requests transmit the same credential and wallet identifier: ```js const resp = await fetch(`https://skillpay.me/api/v1/billing/charge`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-API-Key': SKILLPAY_KEY }, body: JSON.stringify({ user_id: walletAddress, skill_id: SKILL_ID, amount: 0 }) }); ``` ### Technical Analysis The Skill reads `SKILLPAY_KEY` from the process environment and sends it as an authentication header to `https://skillpay.me`. It also sends the derived Polygon wallet address as `user_id`. Transmitting an API key to its intended billing service may be functionally necessary, but it remains a sensitive outbound data flow. The documentation states that a SkillPay key is used for charging, yet it does not clearly identify the destination endpoint, explain that the wallet address is used as the billing identity, or describe the resulting ability to correlate a blockchain wallet with billing activity. The wallet private key itself is not transmitted by the reviewed code. Nevertheless, compromise of the billing endpoint, TLS termination infrastructure, service account, or request logs could expose the SkillPay credential. The practical authority obtainable from that key depends on permissions enforced by SkillPay and cannot be determined from the repository. Using a public wallet address directly as the billing ide ...[truncated 1650 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Explicitly disclose that `SKILLPAY_KEY` and the wallet address are transmitted to `https://skillpay.me`. 2. Explain the purpose, retention expectations, and privacy implications of using the wallet address as `user_id`. 3. Obtain explicit user consent before the first outbound billing request. 4. Replace the wallet address with a random, pseudonymous billing identifier where possible. 5. Require a narrowly scoped, revocable billing token rather than a broadly privileged account API key. 6. Ensure the provider never logs authentication headers and applies appropriate secret redaction throughout its infrastructure. 7. Support key rotation and provide immediate revocation instructions. 8. Validate HTTPS responses, status codes, content types, and expected response schemas before trusting returned data. 9. Avoid placing identifying values in query strings, because URLs are commonly retained in access logs; use an authenticated request body when supported. 10. Document every external endpoint and transmitted field so users can make an informed decision before supplying credentials. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
polymarket-trade.js:187
Finding
Billing Enforcement Fails Open and Sends a Zero-Amount Charge<![CDATA[ ## Vulnerability Details **File Location**: `polymarket-trade.js:187-213` **Vulnerability Type**: Fail-open authorization and billing control bypass **Risk Level**: Medium ### Vulnerable Code ```js async function chargeUser() { if (!SKILLPAY_KEY || !walletAddress) return { ok: true }; try { const resp = await fetch(`https://skillpay.me/api/v1/billing/charge`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-API-Key': SKILLPAY_KEY }, body: JSON.stringify({ user_id: walletAddress, skill_id: SKILL_ID, amount: 0 }) }); const data = await resp.json(); if (data.success) { return { ok: true, balance: data.balance }; } else { return { ok: false, balance: data.balance, payment_url: data.payment_url }; } } catch (e) { return { ok: true }; } } ``` ### Technical Analysis The Skill documentation claims that every invocation deducts one token and describes `SKILLPAY_KEY` as required. The implementation does not consistently enforce either claim. There are three control failures: 1. If the SkillPay key or wallet address is absent, the function returns `{ ok: true }`. 2. The charge request specifies `amount: 0`, not the documented one-token fee. 3. Any network, parsing, TLS, or service error is caught and converted into `{ ok: true }`. This is a fail-open billing design. The caller cannot distinguish a verified successful charge from a skipped or failed charge, and `scan()` proceeds to market analysis and potentially real trading whenever `ok` is true. The issue primarily enables billing bypass and inconsistent authorization semantics. It also creates an operational safety problem because automatic trading continues when the billing service’s response cannot be authenticated or interpreted. ### Attack Path 1. A user invokes the Skill without setting `SKILLPAY_KEY`, or removes the key from the environment. 2. ...[truncated 1191 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Decide whether billing is mandatory or optional and make the documentation and implementation consistent. 2. If billing is mandatory, reject execution when the key or required wallet identity is absent. 3. Send the documented charge amount rather than `amount: 0`. 4. Fail closed when the billing server is unreachable or returns an invalid, unauthenticated, or malformed response. 5. Validate HTTP status codes and the complete response schema before treating a charge as successful. 6. Use idempotency keys to prevent duplicate charges when requests are retried. 7. Record a non-sensitive local transaction reference for reconciliation without logging the API key. 8. Separate billing success from trading authorization so an ambiguous response cannot silently authorize real-money activity. 9. Provide explicit error messages for missing credentials, service outages, rejected payments, and malformed responses. 10. Add tests covering missing credentials, timeouts, non-2xx responses, invalid JSON, unsuccessful charges, zero balances, and duplicate request handling. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (15)

Missing User Warnings

High
Confidence
93% confidence
Finding
The skill description markets 'automatic detection and execution of arbitrage trades' and states that each invocation auto-charges fees, but it does not clearly foreground that the skill may place real-money trades without per-trade user confirmation. In a financial trading context, silent or background execution materially raises the chance of unintended orders, loss of funds, and abuse if the skill is misconfigured or behaves unexpectedly.

Missing User Warnings

High
Confidence
96% confidence
Finding
The README explicitly instructs users to export and provide a raw Polygon wallet private key via an environment variable for automated trading. Requesting direct custody secrets in a third-party skill materially increases wallet-compromise risk, especially because the skill is designed to execute financial transactions and gives only minimal generic trading-risk warnings rather than strong credential-handling warnings.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented behavior understates several highly sensitive actions: use of a raw private key for signing, communication with an external billing service, and continuous monitoring or polling. This mismatch is especially dangerous because users may believe they are invoking a simple arbitrage helper while actually granting a skill the ability to spend crypto assets, incur charges, and run persistent automated activity.

Missing User Warnings

High
Confidence
98% confidence
Finding
The scan command can place real market orders automatically whenever a wallet is configured, without any explicit user confirmation, dry-run default, max-loss guard, or final warning. Because this directly triggers live financial transactions from routine discovery logic, accidental invocation or misleading integration could cause immediate unwanted spending and market exposure.

Missing User Warnings

High
Confidence
99% confidence
Finding
The start command enables unattended recurring scans every 60 seconds, and those scans may submit live orders automatically if a wallet is present. This compounds the previous issue by allowing repeated real-money trades with no operator approval per cycle, no circuit breaker, and no visible risk acknowledgement, increasing the chance of runaway losses or abuse.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises capabilities that require environment access and network communication, but it declares no explicit tool scope or permissions. In a security-sensitive context involving wallet keys and automated trading, missing scope declarations hide the real trust boundary and can cause users or platforms to authorize a skill without understanding that it can access secrets and interact with external services.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill explicitly instructs users to export and provide a raw Polygon private key for automated trading, without strong warnings against key reuse, hot-wallet risk, or safer alternatives. Because a raw private key grants full control over wallet funds, any compromise, misuse, or hidden transaction logic could result in irreversible theft of crypto assets.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
User-facing descriptions, logs, help text, and warnings are written in Chinese throughout the file, with no indication that the skill is region-specific or that users may choose another language. This can violate language/locale policy where skills must not force a specific language without user opt-in.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The code does not enforce paid access before executing the scan/trading flow. If SKILLPAY_KEY is missing or the charge request errors, chargeUser() returns success and the program continues, allowing use of the skill contrary to the stated billing model. In a paid trading skill this is a real integrity/business-logic flaw, though not as severe as direct fund theft.

Natural-Language Policy Violations

Low
Confidence
75% confidence
Finding
The visible user-facing description and instructions are entirely in Chinese, and the file does not offer an alternative language or state that the skill is region-specific. Under the language/locale policy, forcing a specific language without user opt-in can be a natural-language policy violation.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
The manifest description is written only in Chinese ("Polymarket预测市场套利工具"), which imposes a specific language presentation without any indication of user opt-in or locale-specific justification. This matches the policy category for language or locale constraints expressed in natural language.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"node": ">=14"
  },
  "dependencies": {
    "node-fetch": "^2.7.0",
    "ethers": "^5.7.2"
  }
}
Confidence
89% confidence
Finding
Using caret ranges for dependencies allows newer package versions to be installed without explicit review, which can introduce supply-chain risk, behavioral changes, or inadvertently pull in a vulnerable release. In a trading/arbitrage skill that may handle API calls and blockchain interactions, unexpected dependency changes can have outsized financial impact.

Unverifiable Dependency: node-fetch has 3 known advisory(ies) (CVE-2022-0235 (node-fetch forwards secure headers to untrusted sites); CVE-2022-2596 (node-fetch Inefficient Regular Expression Complexity ); CVE-2020-15168 (The `size` option isn't honored after following a redirect in node-fetch)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
83% confidence
Finding
The manifest references node-fetch without pinning an exact version, while known advisories exist for some releases, making it impossible to verify from this file alone whether deployments are affected. In a tool that likely performs network requests for market data or execution, vulnerable HTTP client behavior could expose sensitive headers, enable denial-of-service conditions, or weaken request safety.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "dependencies": {
    "node-fetch": "^2.7.0",
    "ethers": "^5.7.2"
  }
}
Confidence
89% confidence
Finding
Using a non-exact version for ethers permits automatic uptake of newer compatible releases, increasing supply-chain risk and the chance of unexpected runtime or security-impacting changes. Because this skill is described as executing arbitrage trades and charging per invocation, dependency drift in a blockchain library can directly affect transaction construction, signing, or fund handling.

Intent-Code Divergence

Low
Confidence
93% confidence
Finding
The file header and CLI help both state that POLYMARKET_PRIVATE_KEY is required. In practice, initWallet only logs a warning and returns false, and scan can still execute market scanning and billing-related network calls without a wallet; only order placement is gated.

Static analysis

No suspicious patterns detected.