Back to skill

Security audit

Polymarket World Cup Player Goal Value

Security checks for vulnerabilities and agentic risk

Overview

This is a real-money trading skill whose main purpose is coherent, but it includes an explicit SDK integrity-check bypass and under-scoped account/trading safeguards that warrant Review before installation.

Install only after reviewing the trading and credential implications. Start in sim/dry-run, use a restricted Simmer API key if available, avoid --live unless you accept real-money order risk, and do not rely on the local daily budget/cooldown files as hard limits. The SDK integrity-check bypass should be removed or justified before trusting this for live funds.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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 (2)

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/client_factory.py:3
Finding

Deliberate Bypass of SDK Entrypoint Integrity Verification

Content
View full analysis
SimmerClient: return SimmerClient(api_key=api_key, venue=venue, live=live) ``` ```python # player_goal_value.py:111-120 def get_client(live: bool, venue: str): cache_key = (venue, bool(live)) if cache_key not in _clients: from scripts.client_factory import create_simmer_client key = os.environ.get("SIMMER_API_KEY") if not key: print("Error: SIMMER_API_KEY not set") sys.exit(1) _clients[cache_key] = create_simmer_client(api_key=key, venue=venue, live=live) return _clients[cache_key] ``` ### Technical Analysis The helper explicitly states that it constructs `SimmerClient` from a directory without `SKILL.md` so that the SDK does not validate the trading entrypoint against its published hash. The trading engine then delegates authenticated client construction to this helper. Entrypoint verification is a supply-chain and code-integrity boundary. Its purpose is to ensure that code exercising authenticated trading capabilities matches the reviewed or published Skill. Moving client construction to a metadata-free directory makes modified code appear outside the verification c ...[truncated 1449 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
player_goal_value.py:627
Finding

Non-Atomic Budget and Cooldown Accounting Can Permit Duplicate or Excessive Live Orders

Content
View full analysis
= int(_config["max_trades_per_run"]): break if spend["spent"] + run_spent >= float(_config["daily_budget_usd"]): break mid = m.id last = float(cooldown.get(mid, 0.0)) if tnow - last < float(_config["cooldown_hours"]) * 3600: continue ``` ```python # player_goal_value.py:740-787 for px, amt, off_c in rung_orders: if spend["spent"] + run_spent + amt > float(_config["daily_budget_usd"]): continue note = ( f"WCPGV edge={edge:.3f} fair={fair:.3f} px={px:.3f} off={off_c:.1f}c " f"player={player}" ) if live: signal_data = { "player": player, "fair_yes": round(fair, 5), "ask_yes": round(ask_yes, 5), "edge": round(edge, 5), "spread": None if spread is None else round(spread, 5), "slippage_pct": round(slip, 5), "entry_price": px, "goals_per90": round(float(model_inputs["goals_per90"]), 5), "expected_minutes": round(float(model_inputs["expected_minutes"]), 2), "role_multiplier": round(float(model_inputs["role_multiplier"]), 5), "penalty_goal_share": round(float(model_inputs["penalty_goal_share"]), 5), "expected_market_matches": round(float(model_inputs["expected_m ...[truncated 4187 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (21)

Tainted flow: 'req' from os.environ.get (line 150, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · player_goal_value.py (reported line 155)May include surrounding context.

python
headers={"Authorization": f"Bearer {key}", "Accept": "application/json"},
    )
    try:
        with urllib.request.urlopen(req, timeout=20) as r:
            data = json.loads(r.read().decode())
    except Exception:
        return []

Tainted flow: 'req' from os.environ.get (line 150, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · player_goal_value.py (reported line 180)May include surrounding context.

python
headers={"Authorization": f"Bearer {key}", "Accept": "application/json"},
    )
    try:
        with urllib.request.urlopen(req, timeout=20) as r:
            data = json.loads(r.read().decode())
    except Exception:
        return []

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

If the skill accesses authenticated account or portfolio endpoints while declaring itself as a market-selection/trading skill and omitting explicit permissions, users may unknowingly expose sensitive account context. This is dangerous because hidden or poorly disclosed authenticated access can leak financial data, enable profiling of balances/positions, or normalize broader account access than necessary.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

If the skill accesses authenticated account or portfolio endpoints while declaring itself as a market-selection/trading skill and omitting explicit permissions, users may unknowingly expose sensitive account context. This is dangerous because hidden or poorly disclosed authenticated access can leak financial data, enable profiling of balances/positions, or normalize broader account access than necessary.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

If the skill accesses authenticated account or portfolio endpoints while declaring itself as a market-selection/trading skill and omitting explicit permissions, users may unknowingly expose sensitive account context. This is dangerous because hidden or poorly disclosed authenticated access can leak financial data, enable profiling of balances/positions, or normalize broader account access than necessary.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

If the skill accesses authenticated account or portfolio endpoints while declaring itself as a market-selection/trading skill and omitting explicit permissions, users may unknowingly expose sensitive account context. This is dangerous because hidden or poorly disclosed authenticated access can leak financial data, enable profiling of balances/positions, or normalize broader account access than necessary.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The module docstring asserts the runner operates only in dry-run/paper-preview mode unless a future persisted non-SIM venue is added, but the argument parser already accepts arbitrary --venue values and passes them through to the strategy. In a trading skill, this discrepancy is especially risky because operators may rely on the documented safety guarantees and accidentally trigger real-money actions.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The code intentionally strips output lines that would warn about live trading while comments claim the child strategy is a dry-run, yet the runner forwards user-controlled --venue and sets TRADING_VENUE accordingly. If invoked with --venue polymarket, this runner can launch real-funds trading while suppressing messaging that would alert an operator, creating a dangerous mismatch between behavior and operator expectations.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · adaptive_game_runner.py (reported line 170)May include surrounding context.

python
print(f"- {label}: {ctx.name} — {ctx.detail} — score {ctx.score}")
    print()

    env = os.environ.copy()
    env["TRADING_VENUE"] = args.venue
    env.setdefault("SIMMER_WCPGV_PLAYER_DATA_FILE", "data/wc_players_filtered.csv")
    env.setdefault("SIMMER_WCPGV_MIN_PLAYER_MINUTES", "300")

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill manifest describes trading Polymarket player-goal YES markets using a specific valuation strategy, but this file is a standalone 'Simmer Account Status' tool that authenticates to api.simmer.markets and fetches portfolio/positions data. Monitoring a different platform's account state is not an obvious implementation detail of the declared Polymarket trading strategy.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill advertises and documents capabilities that imply environment access, filesystem reads/writes, network access, and shell execution, but it declares no explicit tool scope or permissions. This creates an authorization ambiguity where a reviewer or runtime may permit broader access than users expect, increasing the risk of unintended data access, file modification, or command execution.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill documents a live trading mode for Polymarket but does not include a prominent warning that this can place real-money orders. In a trading context, insufficient warning materially increases the risk of accidental financial loss, especially when users may copy-paste commands or assume dry-run semantics from nearby examples.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · adaptive_game_runner.py (reported line 183)May include surrounding context.

python
print(f"Polymarket event filter: {env['SIMMER_WCPGV_COMBO_EVENT_SLUGS']}")

    if should_refresh_player_data(args, env):
        refresh = subprocess.run([sys.executable, str(ROOT / "scripts" / "fetch_wc_stats.py")], cwd=str(ROOT), env=env, text=True, capture_output=True)
        if refresh.returncode != 0:
            if refresh.stdout:
                print(refresh.stdout, end="")

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · adaptive_game_runner.py (reported line 192)May include surrounding context.

python
return refresh.returncode

    cmd = [sys.executable, str(ROOT / "player_goal_value.py"), "--venue", args.venue, "--quiet"]
    proc = subprocess.run(cmd, cwd=str(ROOT), env=env, text=True, capture_output=True)
    out = filtered(proc.stdout)
    err = filtered(proc.stderr)
    if out:

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · player_goal_value.py (reported line 149)May include surrounding context.

python
"limit": max(1, min(limit, 1000)),
        }
    )
    url = f"https://api.simmer.markets/api/sdk/markets?{params}"
    req = urllib.request.Request(
        url,
        headers={"Authorization": f"Bearer {key}", "Accept": "application/json"},

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/fetch_wc_stats.py (reported line 84)May include surrounding context.

python
def _fetch(url: str) -> Optional[dict]:
    result = subprocess.run(
        ["curl", "-s", "--max-time", "12", url],
        capture_output=True, text=True
    )

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module docstring explicitly says 'Simmer Account Status' and 'Shows wallet balance, positions, and recent activity,' which conflicts with the surrounding skill's declared purpose of trading Polymarket player-goal YES markets. This is an intent-level contradiction in documentation, not merely an omitted detail.

Content

No source excerpt is available for this finding.

Scope Creep

Low
Category
Excessive Agency
Confidence
70% confidence
Finding

Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Content

Scanner excerpt · LICENSE (reported line 12)May include surrounding context.

text
permit persons to whom the Software is furnished to do so.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE

Vague Triggers

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

This JSON manifest-style data includes a natural-language note saying "Check if Polymarket has active markets for him," but it does not specify who should perform that check, when it should occur, or under what constraints. In a manifest/data context, this kind of open-ended instruction is overly vague and could lead to unintended activation or inconsistent behavior.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · player_goal_value.py (reported line 578)May include surrounding context.

python
for attr in ("yes_ask", "ask_yes", "best_ask_yes", "ask", "best_ask"):
        try:
            v = getattr(market_obj, attr, None)
            if v is not None:
                fv = float(v)
                if 0.0 < fv < 1.0:

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · player_goal_value.py (reported line 603)May include surrounding context.

python
for attr in ("yes_ask", "ask_yes", "best_ask_yes", "ask", "best_ask"):
        try:
            v = getattr(market_obj, attr, None)
            if v is not None:
                fv = float(v)
                if 0.0 < fv < 1.0: