Back to skill

Security audit

Polymarket Arbitrage

Security checks for vulnerabilities and agentic risk

Overview

This skill is a Polymarket market scanner, but it overstates trading capabilities and contains unsafe command execution and credential-handling patterns users should review before installing.

Review this before installing. Use a virtual environment, pin dependencies, avoid passing untrusted or unusual --data-dir values, and do not put real Telegram or webhook tokens on the command line. Treat the tool as a read-only paper-trading scanner, not an automated trading or execution system.

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)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/monitor.py:24
Finding
Arbitrary Command Execution Through Shell Injection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/monitor.py:24-44`, with exploitable command construction at `scripts/monitor.py:137-143` and `scripts/monitor.py:196-201` **Vulnerability Type**: OS command injection through user-controlled path interpolation **Risk Level**: High ### Vulnerable Code ```python def run_command(cmd, description=""): """Run a shell command and return the output.""" if description: print(f"[{datetime.now().strftime('%H:%M:%S')}] {description}", file=sys.stderr) try: result = subprocess.run( cmd, shell=True, capture_output=True, text=True, timeout=60 ) if result.returncode != 0: print(f"Error: {result.stderr}", file=sys.stderr) return None return result.stdout except subprocess.TimeoutExpired: print(f"Timeout running: {cmd}", file=sys.stderr) return None ``` The continuous-monitoring path constructs commands using the user-controlled data directory: ```python script_dir = Path(__file__).parent fetch_cmd = f"python3 {script_dir}/fetch_markets.py --output {markets_file} --min-volume 50000" run_command(fetch_cmd, "Fetching markets...") detect_cmd = f"python3 {script_dir}/detect_arbitrage.py {markets_file} --min-edge {min_edge} --output {arbs_file}" run_command(detect_cmd, "Detecting arbitrage...") ``` The one-time execution path uses the same unsafe pattern: ```python run_command(f"python3 {script_dir}/fetch_markets.py --output {markets_file}") run_command(f"python3 {script_dir}/detect_arbitrage.py {markets_file} --min-edge {args.min_edge} --output {arbs_file}") ``` ### Technical Analysis The `--data-dir` command-line argument is accepted as an unrestricted string and converted into a `Path`. Derived paths such as `markets_file` and `arbs_file` are then inserted directly into shell command strings. Because `run_command()` invokes `subproce ...[truncated 1881 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `shell=True` entirely. - Pass commands as argument arrays so paths and numeric values cannot be interpreted as shell syntax. - Use `sys.executable` instead of relying on the `python3` command resolved from `PATH`. - Resolve and validate the data directory before using it. - Restrict output to an approved directory if arbitrary output locations are not required. - Check each subprocess result before proceeding to load generated files. A safer implementation is: ```python def run_command(cmd, description=""): if description: print( f"[{datetime.now().strftime('%H:%M:%S')}] {description}", file=sys.stderr ) try: result = subprocess.run( cmd, shell=False, capture_output=True, text=True, timeout=60, check=False ) if result.returncode != 0: print(f"Error: {result.stderr}", file=sys.stderr) return None return result.stdout except subprocess.TimeoutExpired: print("Child process timed out", file=sys.stderr) return None ``` Commands should then be constructed as lists: ```python fetch_cmd = [ sys.executable, str(script_dir / "fetch_markets.py"), "--output", str(markets_file), "--min-volume", "50000", ] detect_cmd = [ sys.executable, str(script_dir / "detect_arbitrage.py"), str(markets_file), "--min-edge", str(min_edge), "--output", str(arbs_file), ] ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/monitor.py:81
Finding
Telegram Bot Token Exposure Through Command-Line Arguments and Logging<![CDATA[ ## Vulnerability Details **File Location**: `scripts/monitor.py:81-105`; insecure usage is documented at `SKILL.md:213-217` **Vulnerability Type**: Plaintext credential exposure and sensitive-data logging **Risk Level**: Medium ### Vulnerable Code The alert function prints the entire supplied webhook URL: ```python def send_alert(arb, webhook_url=None): """Send alert for arbitrage opportunity.""" message = f""" 🚨 ARBITRAGE OPPORTUNITY DETECTED {arb['title'][:80]} Type: {arb['type']} Net Profit: {arb['net_profit_pct']:.2f}% (after fees) Volume: ${arb['volume']:,} Risk Score: {arb['risk_score']}/100 Action: {arb['action']} URL: {arb['url']} Probabilities: {arb['probabilities']} Sum: {arb['prob_sum']}% """ print(message, file=sys.stderr) # TODO: Implement webhook alerts (Telegram, Discord, etc.) if webhook_url: print(f"[ALERT] Would send to webhook: {webhook_url}", file=sys.stderr) ``` The documentation instructs users to place a Telegram bot token directly in the command line: ```bash python scripts/monitor.py --alert-webhook "https://api.telegram.org/bot<token>/sendMessage?chat_id=<id>" ``` ### Technical Analysis Telegram bot API URLs contain the bot token in the URL path. Supplying that URL as a command-line argument exposes the credential to shell history and potentially to process-inspection tools available to other local users or monitoring software. The monitor then writes the complete URL to standard error. If stderr is redirected, collected by a service manager, captured by CI/CD infrastructure, or forwarded to centralized logging, the token is copied into additional persistent systems. The implementation does not actually send a webhook request. The option only prints the sensitive URL, meaning the credential exposure is unnecessary for the currently implemented functionality and exceeds minimum privilege and data-handling requirements. ### Attack Path 1. A user follows `SKILL.md` and invoke ...[truncated 1192 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not accept secret-bearing webhook URLs directly as command-line arguments. - Load tokens from a protected environment variable, operating-system credential store, or dedicated secrets manager. - Prefer separate non-secret configuration fields, such as a provider name and chat identifier. - Never print a complete webhook URL. - Redact credentials before logging; for example, retain only the provider hostname. - Update `SKILL.md` to state that webhook delivery is not implemented until a secure implementation exists. - If webhook delivery is implemented, enforce HTTPS, define an explicit destination allowlist, apply request timeouts, and avoid automatic redirects to untrusted hosts. - Ensure deployment logs and shell-history files are reviewed for previously exposed credentials. - Revoke and rotate any token that may already have been entered using the documented command. A minimal safe interim behavior is to reject the unsupported option without displaying its value: ```python if webhook_url: print( "[ALERT] Webhook delivery is not implemented; supplied value was ignored.", file=sys.stderr ) ``` ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:16
Finding
Unpinned Runtime Dependency Installation<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:16-19`, also repeated at `SKILL.md:252` and `references/getting_started.md:13-18` **Vulnerability Type**: Unconstrained third-party dependency installation **Risk Level**: Medium ### Vulnerable Code The primary installation instructions install mutable package versions: ```bash cd skills/polymarket-arbitrage pip install requests beautifulsoup4 python scripts/monitor.py --once --min-edge 3.0 ``` The fetch script repeats the same recommendation when imports fail: ```python try: import requests from bs4 import BeautifulSoup except ImportError: print("Error: Missing dependencies. Install with:", file=sys.stderr) print(" pip install requests beautifulsoup4", file=sys.stderr) sys.exit(1) ``` ### Technical Analysis Neither dependency has a version constraint or an integrity hash. The project also contains no reviewed lock file in the audited directory. As a result, each installation resolves whatever versions the configured Python package index serves at that time. The effective dependency code can therefore change after this Skill has been reviewed. A compromised upstream release, package-index account, mirror, or dependency in the transitive dependency graph could introduce arbitrary code into the runtime environment. The documented command also does not require an isolated virtual environment. Users may consequently install packages into a shared interpreter or user-level Python environment, increasing the scope of dependency conflicts and compromise. No evidence was found that `requests` or `beautifulsoup4` is currently malicious. The vulnerability is the unsafe, non-reproducible installation process rather than a confirmed malicious package. ### Attack Path 1. A user follows the Skill's installation instructions. 2. `pip` resolves the latest available versions from the user's configured package index or mirror. 3. A compromised or malicious release, mirror response, or ...[truncated 884 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Add a dependency manifest with exact, reviewed versions. - Generate and commit a lock file that captures transitive dependencies. - Require cryptographic hashes for all resolved packages, such as through a hashed requirements file and `pip install --require-hashes`. - Review and update pinned versions through a controlled dependency-update process. - Install dependencies inside a dedicated virtual environment with least-privileged ownership. - Avoid installation as root or into a shared system interpreter. - Document the expected package index and use trusted TLS-protected repositories. - Consider removing `beautifulsoup4` if the parser can be implemented without it; minimizing dependencies reduces supply-chain exposure. The documentation should use a reproducible command such as: ```bash python3 -m venv .venv . .venv/bin/activate python -m pip install --require-hashes -r requirements.txt ``` The checked-in `requirements.txt` should contain exact versions and validated hashes for both direct and transitive dependencies. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The description substantially overstates the implemented functionality. The code does match part of the declared purpose: it detects one form of Polymarket arbitrage (math arbitrage) and includes a basic risk score. However, several prominent declared capabilities are absent. There is no market monitoring loop or live data access, no trade execution, no order placement, no alerting, and no P&L tracking. Cross-market arbitrage is explicitly marked TODO and returns an empty list, while orderbook arbitrage is only mentioned in comments/docstring and not implemented at all. The script operates only on a supplied local markets JSON file and produces an output file, so its actual primary purpose is offline math-arbitrage screening rather than a broader monitor-and-execute arbitrage system.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description promises a full arbitrage monitoring and execution capability for Polymarket, including several specific analysis modes and operational features. The provided code does not implement any of those core behaviors. It only retrieves the Polymarket homepage, parses visible market-related text from HTML, extracts basic fields such as title, probabilities, and volume strings, filters by minimum volume, deduplicates by market ID, and writes the results to a JSON file. While fetching market data could be a supporting component for an arbitrage system, this chunk by itself does not detect arbitrage, compare markets, inspect orderbooks, execute trades, manage risk, track P&L, or send alerts. Therefore the actual behavior is materially narrower and different from the declared purpose.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
print(f"[{datetime.now().strftime('%H:%M:%S')}] {description}", file=sys.stderr)
    
    try:
        result = subprocess.run(
            cmd,
            shell=True,
            capture_output=True,
Confidence
97% confidence
Finding
Using shell=True in a tool-like monitoring script materially increases abuse risk because this skill accepts runtime parameters such as --data-dir and --min-edge that flow into command strings. In this context, an attacker or unsafe caller could supply crafted values to break out of the intended command and run arbitrary shell commands, compromising the host running the trading monitor.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill documentation describes capabilities that inherently use network, shell, and file read/write behaviors, but it does not declare any explicit tool scope or permissions boundary. This creates a governance and least-privilege problem: an agent may invoke the skill without clear authorization constraints, increasing the chance of unintended file modification, command execution, or outbound requests.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The manifest description says the skill will 'monitor and execute arbitrage opportunities' and includes trading strategies, risk management, and P&L tracking. However, the file repeatedly describes only market scraping, arbitrage detection, monitoring, deduplicated alerting, and saving JSON outputs, while explicitly stating automation/execution is a future phase and that current trading should remain manual.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The manifest states the skill detects 'cross-market arbitrage' and 'orderbook inefficiencies' as present capabilities. In contrast, the documentation says cross-market arbitrage is 'not yet implemented' and orderbook arbitrage requires real-time orderbook data that the current source does not provide, indicating the advertised behavior exceeds actual functionality.

External Transmission

Medium
Category
Data Exfiltration
Content
Pass webhook URL to monitor script for alerts:

```bash
python scripts/monitor.py --alert-webhook "https://api.telegram.org/bot<token>/sendMessage?chat_id=<id>"
```

### Position Sizing
Confidence
80% confidence
Finding
The skill supports sending alerts to an arbitrary webhook URL, with the example using Telegram's bot API. Outbound transmission can leak market data, operational metadata, or secrets embedded in URLs, and if the webhook target is user-controlled or misconfigured it becomes an exfiltration channel.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The module docstring claims support for math, cross-market, and orderbook arbitrage, but the implementation only performs math arbitrage detection. In a trading skill, this can mislead users or downstream agents into believing broader coverage exists, causing missed risk checks and incorrect trading decisions based on incomplete analysis.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The code advertises cross-market arbitrage detection, but the corresponding function is a TODO stub that always returns an empty list. In this skill context, silent non-detection is dangerous because users may rely on the tool for automated market monitoring or trading and assume an important class of opportunities or inconsistencies is being checked.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
print(f"[{datetime.now().strftime('%H:%M:%S')}] {description}", file=sys.stderr)
    
    try:
        result = subprocess.run(
            cmd,
            shell=True,
            capture_output=True,
Confidence
95% confidence
Finding
The code executes shell commands with subprocess.run(..., shell=True) using command strings built from variables such as script paths, output file paths, and user-controlled CLI arguments. If any of those values contain shell metacharacters, an attacker could achieve command injection and execute arbitrary commands with the privileges of the monitoring process.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This code file invokes shell commands using subprocess.run with shell=True, which is a safety-relevant operation under the audit criteria. Although progress messages are printed, there is no explicit disclosure in the function docstring or surrounding comments that the skill will execute external scripts/commands on the user's system.

Description-Behavior Mismatch

Low
Confidence
85% confidence
Finding
The manifest says the skill includes 'P&L tracking,' which implies recording realized or hypothetical profit/loss over time. The documented files and workflow mention markets.json, arbs.json, and alert_state.json, plus manual spreadsheet tracking by the user, but no built-in P&L ledger or tracking mechanism is described.

Intent-Code Divergence

Low
Confidence
92% confidence
Finding
The function docstring says "Send alert for arbitrage opportunity," and the CLI exposes an `--alert-webhook` option, but when a webhook URL is provided the implementation merely prints "Would send to webhook" and performs no network delivery. This is an intent-code divergence because the inline documentation and interface imply actual alert dispatch while the code only emits local stderr output.

Missing User Warnings

Low
Confidence
85% confidence
Finding
This file creates a data directory and persists markets, arbitrage results, and alert state, which affects user data on disk. While the top-level docstring mentions 'Persistence of results,' it does not clearly disclose what files are written or that alert history is retained, so the storage behavior is only partially described.

Static analysis

No suspicious patterns detected.