Back to skill

Security audit

Polymarket Arbitrage Cn

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly aligned with Polymarket monitoring, but its monitor script can execute shell-injected commands through user-controlled paths and can expose webhook tokens in logs.

Review before installing. Run it only in a constrained environment, do not pass untrusted values to --data-dir or other monitor parameters, avoid using real Telegram bot tokens on the command line, and prefer a fixed virtual environment with pinned dependencies. This skill does not appear to place trades or manage wallet keys, but its monitor script should be fixed before routine use.

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:30
Finding
OS Command Injection Through User-Controlled Data Directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/monitor.py:25-47`, `scripts/monitor.py:137-142`, and `scripts/monitor.py:198-199` **Vulnerability Type**: OS command injection through unsafe shell command construction **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 except Exception as e: print(f"Exception: {e}", file=sys.stderr) return None ``` The continuous-monitoring path constructs commands using the user-controlled data directory: ```python # Step 1: Fetch markets 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...") # Step 2: Detect arbitrage 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-shot path is affected as well: ```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 converted to a `Path`, incorporated into `markets_file` and `arbs_file`, and then interpolated directly into shell command str ...[truncated 1991 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Eliminate shell interpretation and pass each subprocess argument as a separate list element: ```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("Subprocess timed out", file=sys.stderr) return None ``` Construct commands without string interpolation: ```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), ] ``` Additional hardening should include: 1. Validate that `--data-dir` resolves to an approved directory or permitted base path. 2. Reject control characters and unexpected path forms. 3. Use `sys.executable` rather than relying on a `python3` executable resolved through `PATH`. 4. Apply the same argument-array construction in both continuous and one-shot modes. 5. Add tests using directory names containing spaces and shell metacharacters to verify that they are handled only as literal path data. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/monitor.py:103
Finding
Webhook Credential Exposure Through Command-Line Arguments and Logging<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:216-219` and `scripts/monitor.py:81-105` **Vulnerability Type**: Exposure of sensitive webhook credentials in process arguments, shell history, and logs **Risk Level**: Medium ### Vulnerable Code The documentation instructs users to place a Telegram bot token directly in a command-line URL: ```bash python scripts/monitor.py --alert-webhook "https://api.telegram.org/bot<token>/sendMessage?chat_id=<id>" ``` The monitor then writes the full supplied webhook URL to standard error: ```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) ``` ### Technical Analysis Telegram webhook URLs commonly contain the bot token as part of the URL path. Passing such a URL through `--alert-webhook` exposes the token in the process argument vector. Depending on the host configuration, command-line arguments may be visible through process-monitoring tools, diagnostic systems, orchestration interfaces, audit records, or shell history. The code compounds this exposure by printing the entire URL to standard error. Standard error is frequently redirected to persistent terminal recordings, service logs, CI logs, or centralized logging systems. Anyone with access to those records may recover the token. The documentation describes webhook alerting as a feature, but the implementation does not perform a network request. It only logs the supplied URL. Thus, c ...[truncated 1451 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not accept secret-bearing webhook URLs directly through command-line arguments. 2. Load the token from a protected environment variable, operating-system credential store, or deployment secret manager. 3. Accept non-secret configuration, such as a chat identifier, separately from the secret token. 4. Never print the complete webhook URL. Redact credentials before any diagnostic output: ```python print("[ALERT] Webhook configured; credential omitted", file=sys.stderr) ``` 5. Ensure exception messages from future HTTP integrations do not include authorization headers or credential-bearing URLs. 6. Mark webhook functionality as unimplemented in the documentation until an actual, securely designed sender exists. 7. If webhook transmission is implemented, use an API endpoint with credentials supplied through an authorization mechanism where supported, enforce HTTPS, set short timeouts, and restrict allowed webhook destinations to trusted hosts. 8. Advise users who have run the documented command with a real token to rotate that token and remove it from shell histories and retained logs. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:17
Finding
Unpinned Third-Party Dependencies Create Supply-Chain and Reproducibility Risk<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:17-22`, `SKILL.md:252-255`, `references/getting_started.md:12-18`, and `references/getting_started.md:186-190` **Vulnerability Type**: Unpinned dependency installation from a mutable package index **Risk Level**: Low ### Vulnerable Code The primary installation guidance installs dependencies without version constraints or integrity hashes: ```bash cd skills/polymarket-arbitrage pip install requests beautifulsoup4 python scripts/monitor.py --once --min-edge 3.0 ``` The setup reference repeats the same instruction: ```bash # Install dependencies pip install requests beautifulsoup4 ``` It is repeated again as a recommended next step: ```markdown 1. **Install dependencies:** `pip install requests beautifulsoup4` ``` ### Technical Analysis The installation commands ask `pip` to resolve the latest package releases and their transitive dependencies at installation time. No lock file, exact version constraints, package hashes, or reviewed dependency manifest is supplied. This means two users installing the same Skill at different times may receive different code. A future compromised release, malicious transitive dependency, dependency-account takeover, or incompatible package update could alter installation-time or runtime behavior without any change to the audited Skill package. The packages named in the instructions are legitimate, commonly used projects; the audit found no evidence that the Skill intentionally uses a typosquatted or known-malicious package. The finding concerns the unsafe and non-reproducible installation method rather than confirmed malicious dependency content. ### Attack Path 1. A user follows the documented `pip install requests beautifulsoup4` instruction. 2. `pip` queries its configured package index and resolves the versions available at that time. 3. If a selected direct or transitive package release has been compromised, the user downloads and installs attacker-con ...[truncated 958 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a reviewed dependency lock file containing exact direct and transitive versions. 2. Generate and verify cryptographic hashes for every permitted distribution. 3. Install dependencies in an isolated virtual environment using: ```bash python -m pip install --require-hashes -r requirements.txt ``` 4. Use a trusted package index and disable unexpected extra indexes to reduce dependency-confusion exposure. 5. Review dependency updates before regenerating the lock file. 6. Add automated vulnerability and provenance scanning for locked dependencies. 7. Document the supported Python version and periodically test the locked environment. 8. Prefer invoking pip as `python -m pip` so installation targets the intended Python interpreter. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (7)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The documented behavior claims automatic arbitrage discovery, but the skill text itself mainly describes scraping, monitoring, and writing output files while key detection behavior is deferred to scripts not shown here. This mismatch can mislead operators about what the skill actually does and hide data-handling or execution side effects that should be explicitly disclosed.

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
This is a true tool-parameter abuse issue because the subprocess invocation delegates parsing to the shell while incorporating variable content into the command string. In an agent skill context, parameters may come from user prompts, orchestration layers, or filesystem paths, making exploitation more realistic and enabling arbitrary local command execution under the agent's privileges.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill instructs use of shell commands, network access, and local file writes, but it does not declare any tool scope or permissions. That makes the operational capabilities broader and less auditable than the metadata suggests, increasing the chance that an agent or user invokes networked/scripted behavior without clear consent boundaries.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger phrases include very broad terms like 'Polymarket', '预测市场', '套利', and 'arbitrage', which can activate the skill during ordinary discussion rather than explicit user intent to run monitoring or trading-related workflows. Overbroad activation raises the risk of unintended execution of shell/network instructions in contexts where the user only wanted information.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The documentation encourages sending alerts to a Telegram webhook but does not prominently disclose that opportunity data and potentially market-monitoring metadata will be transmitted to a third-party service. Users may expose trading signals, chat identifiers, and bot tokens without understanding the privacy and operational risks.

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
89% confidence
Finding
The Telegram API endpoint is an external transmission sink, and using it can send monitored opportunity data off-platform to a third party. Even if intended for user alerts, this creates confidentiality and token-exposure risk if messages, URLs, or logs are mishandled.

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 helper executes a string command with shell=True, which allows shell metacharacter interpretation if any part of the command string becomes attacker-controlled. In this file, command strings are built using runtime values such as script paths, output paths, and CLI parameters like min_edge/data_dir, so a crafted value can lead to command injection and arbitrary command execution.

Static analysis

No suspicious patterns detected.