Back to skill

Security audit

PolyGuard Martin Pro

Security checks for vulnerabilities and agentic risk

Overview

This skill is a real Polymarket auto-trader, but it can repeatedly place live financial orders with a stored API key and without clear built-in limits or confirmation.

Review carefully before installing. Only use narrowly scoped, revocable Polymarket credentials, avoid committing config.yaml after adding a real key, and do not run it with funds you cannot risk. The current artifact should be treated as live repeated trading automation, not a one-time alert or one-shot order tool.

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
main.py:174
Finding
Unbounded Repeated Financial Transactions<![CDATA[ ## Vulnerability Details **File Location**: `main.py:174-194` **Vulnerability Type**: Unbounded automated order submission **Risk Level**: High ### Vulnerable Code ```python while True: try: price = get_polymarket_price(cfg) if price is None: logger.info("No price available; retrying later.") else: logger.info("Current price: %s", price) if should_place_order(cfg, price): logger.info("Price condition met; placing order.") try: result = place_polymarket_order(cfg, price) logger.info("Order placed: %s", json.dumps(result)) except RuntimeError as e: logger.error("Order failed: %s", e) else: logger.debug("Condition not met; no order placed.") except requests.RequestException as e: logger.warning("Network error: %s", e) except Exception as e: logger.exception("Unexpected error: %s", e) time.sleep(cfg.poll_interval_seconds) ``` ### Technical Analysis The trading loop continues after a successful order. If the configured price condition remains true, the Skill submits another GTC order after every polling interval. There is no one-shot termination, duplicate-order detection, open-order check, position limit, cumulative spending limit, order-count limit, or post-trade cooldown. Consequently, the implementation can create substantially more financial exposure than a user may infer from the documentation's singular description of placing an order when a condition is met. This behavior exceeds the minimum privilege and transaction scope necessary for a basic threshold-triggered order unless repeated trading is explicitly requested and bounded. ### Attack Path 1. A user configures a market, order size, and reachable price threshold. 2. The user starts the Skill with a trading-capable API credential. 3. The market price satisfies ...[truncated 940 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Stop execution after one successful order by default. - Require an explicit configuration option, such as `repeat_trading: true`, before permitting repeated orders. - Check existing open orders and current positions before submitting another order. - Generate and persist an idempotency key or transaction identifier to prevent duplicate submissions. - Add configurable limits for: - Maximum number of orders per run. - Maximum cumulative order size. - Maximum monetary exposure. - Maximum position per market. - Minimum cooldown after a successful order. - Pause or terminate after repeated API errors rather than retrying indefinitely. - Clearly disclose repeat-order behavior and require explicit user confirmation for unbounded automation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
config.yaml:1
Finding
Trading Credential Stored in Plaintext Configuration<![CDATA[ ## Vulnerability Details **File Location**: `config.yaml:1`, `main.py:39-44`, `SKILL.md:20-29` **Vulnerability Type**: Plaintext sensitive credential storage **Risk Level**: Medium ### Vulnerable Code `config.yaml:1`: ```yaml api_key: "YOUR_API_KEY_HERE" ``` `main.py:39-44`: ```python def load_config(path: str = CONFIG_PATH) -> SkillConfig: """Load and validate config from YAML. Reads only top-level keys.""" with open(path, "r", encoding="utf-8") as f: raw = yaml.safe_load(f) or {} api_key = str(raw.get("api_key") or "").strip() ``` The installation instructions explicitly direct users to place their Polymarket API key in this file. ### Technical Analysis The Skill expects a reusable trading credential to be stored in an ordinary plaintext file inside the project workspace. It provides no supported environment-variable or secret-manager mechanism, does not verify restrictive file permissions, and provides no source-control exclusion guidance. Although the repository contains only a placeholder rather than a live secret, following the documented installation procedure creates a sensitive local file. Workspaces are commonly copied, archived, synchronized, included in support bundles, or committed to source control. Any process or person able to read the workspace can recover the credential without defeating encryption or access controls. The credential's transmission to the hardcoded Polymarket API host is necessary for the declared remote-trading functionality. The storage method, however, is not the least-risk means of supplying that credential. ### Attack Path 1. The user follows the documentation and replaces the placeholder in `config.yaml` with a live trading credential. 2. The configuration file remains readable as plaintext in the project workspace. 3. The workspace is committed, shared, backed up, synchronized, included in logs or support archives, or read by another local user or process. 4. The exposed creden ...[truncated 563 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `api_key` from the ordinary YAML configuration schema. - Load the credential from an environment variable, operating-system keychain, or dedicated secret manager. - Keep secret files outside the project and source tree. - If file-based secrets must be supported: - Use a separate secret file. - Require restrictive owner-only permissions. - Reject startup when permissions are overly broad. - Add the secret file to `.gitignore`. - Provide an example file containing placeholders only. - Recommend narrowly scoped, revocable credentials with the minimum required trading permissions. - Document credential rotation and immediate revocation procedures. - Avoid exposing credential values in exceptions, debug output, or diagnostics. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
main.py:52
Finding
Missing Validation of Financial and Polling Parameters<![CDATA[ ## Vulnerability Details **File Location**: `main.py:52-59`, `main.py:159-166` **Vulnerability Type**: Insufficient validation of transaction configuration **Risk Level**: Medium ### Vulnerable Code `main.py:52-59`: ```python return SkillConfig( api_key=api_key, market_id=market_id, side=str(raw.get("side") or "buy").lower(), size=float(raw.get("size", 0)), max_price=float(raw.get("max_price", 0)), poll_interval_seconds=float(raw.get("poll_interval_seconds", 5)), ) ``` `main.py:159-166`: ```python def should_place_order(cfg: SkillConfig, price: float) -> bool: """True if current price meets the configured threshold for the chosen side.""" if cfg.side == "buy": return price <= cfg.max_price return price >= cfg.max_price ``` ### Technical Analysis Configuration values are converted to strings or floating-point numbers but are not checked against safe semantic constraints. Specifically: - `side` is not restricted to `buy` or `sell`. - Any value other than exactly `buy` follows the sell-side threshold branch. - `size` is not required to be positive or bounded. - `max_price` is not checked against the market's valid price range. - `poll_interval_seconds` is not required to be positive and has no safe minimum. - The manifest schema declares broad string and number types but does not define a side enumeration or numeric bounds. As a result, malformed, accidentally edited, or externally supplied configuration can reach financial decision logic and order construction. A zero or negative polling interval can also cause immediate failures or excessive request frequency. ### Attack Path 1. A user, deployment process, or party capable of modifying `config.yaml` supplies an invalid value, such as a misspelled side, an excessive size, an out-of-range threshold, or an unsafe polling interval. 2. `load_config` accepts the value because it performs type conversion without semantic validation. 3. A misspelled side ...[truncated 1038 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Validate all configuration before entering the monitoring loop: - Restrict `side` to an explicit enumeration containing only `buy` and `sell`. - Require `size` to be finite, positive, and no greater than a configurable safety limit. - Require `max_price` to be finite and within the valid range for the relevant Polymarket contract. - Require `poll_interval_seconds` to be finite and positive, with a conservative minimum to prevent excessive polling. - Reject `NaN` and infinite floating-point values explicitly. - Add equivalent constraints to `manifest.json`, including: - An `enum` for `side`. - `exclusiveMinimum` and `maximum` constraints for numeric fields. - Fail closed with a clear configuration error instead of entering fallback transaction logic. - Consider requiring explicit confirmation when order size or total exposure exceeds a conservative threshold. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (5)

Missing User Warnings

High
Confidence
95% confidence
Finding
The main loop continuously monitors prices and automatically places live orders as soon as the threshold is met, with no confirmation prompt, dry-run mode, kill switch, or prominent warning that real funds may be used. In an agent-skill context, this is especially dangerous because users may invoke the skill expecting analysis or monitoring, but the code can execute irreversible financial actions immediately and repeatedly.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The docstring makes a trust-building claim that the skill performs 'No data collection; no hidden backdoors,' yet the code sends API credentials in Authorization headers and transmits trading/order data to external Polymarket endpoints. Even if those transmissions are functionally required, the misleading assurance can cause users to underestimate the sensitivity of what the skill sends off-host and approve use without informed consent.

External Transmission

Medium
Category
Data Exfiltration
Content
url = f"{POLYMARKET_BASE_URL}/v1/trading/orders"
    try:
        resp = requests.post(url, headers=headers, json=order_body, timeout=15)
    except requests.RequestException as e:
        raise RuntimeError(f"Order request failed: {e}") from e
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The manifest states 'No data collection' while the skill explicitly requires an API key, which is sensitive credential material. Even if the key is only used locally for trading, the claim is misleading and can cause users to underestimate privacy and account-risk implications of supplying credentials.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This manifest advertises automated trading but omits clear warnings that the skill can place potentially irreversible trades using the user's API key. In a trading context, missing risk disclosure is more dangerous because users may enable automation without understanding financial loss, account misuse, or credential exposure risks.

Static analysis

No suspicious patterns detected.