Back to skill

Security audit

Sports Betting

Security checks for vulnerabilities and agentic risk

Overview

This real-money betting skill matches its stated purpose, but it needs Review because it can sign and submit wallet transactions with weak validation and includes a confirmation-bypass flag.

Install only if you are comfortable giving this skill access to a wallet private key that can spend real funds. Use a dedicated low-balance betting wallet, avoid storing a raw private key in the skill workspace if possible, review every transaction manually, do not use --yes, and treat Pinwin/Azuro remote payloads as trusted only if you accept the current validation gaps.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (6)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/place-bet.js:188
Finding

Remote Bet Payload Is Signed and Submitted Without Complete Destination and Domain Validation

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/claim-bets.js:92
Finding

Remote Claim Payload Can Specify Unverified Value, Chain, and Contract Calldata

Content
View full analysis
{ rl.question( `\n🛑 CONFIRM CLAIM\n Bet IDs: ${betIds.join(', ')}\n Proceed? (type "yes" to confirm): `, ans => { rl.close() resolve(ans.trim().toLowerCase()) } ) }) const value = payload.value != null ? BigInt(payload.value) : 0n const hash = await walletClient.sendTransaction({ to: payload.to, data: payload.data, value, chainId: Number(payload.chainId), }) ``` ### Technical Analysis The script verifies only that `payload.to` equals the expected claim contract. It does not require: - `payload.chainId` to equal Polygon chain ID `137`. - `payload.value` to equal zero. - `payload.data` to invoke the expected claim function. - The calldata bet IDs to match the IDs displayed to the user. - The calldata to avoid unrelated contract operations. This also conflicts with the Skill documentation, which states that no POL value is sent during a claim. ### Attack Path 1. An attacker compromises or impersonates `https://api.pinwin.xyz/agent/claim`. 2. The service returns a payload whose `to` field is the expected contract. 3. The payload contains a nonzero native-token value, unexpected chain ID, or calldata for an unintended contract operation. 4. The confirmation prompt displays only the locally requested bet IDs and does not decode or explain the ...[truncated 499 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/place-bet.js:46
Finding

Command-Line Flag Bypasses Mandatory Confirmation for Real-Money Bets

Content
View full analysis
{ rl.question( `\n🛑 CONFIRM BET\n ${Number(payloadStake) / 1e6} USDT on ${marketName} — outcome "${selectionLabel}" @ ${currentOdds.toFixed(2)}\n Proceed? (type "yes" to confirm): `, ans => { rl.close() resolve(ans.trim().toLowerCase()) } ) }) if (answer !== 'yes' && answer !== 'y') { console.log('\n❌ Bet cancelled by user.') process.exit(0) } } else { console.log('\n⚠️ [--yes flag] Skipping interactive confirmation.') } ``` ### Technical Analysis The Skill documentation declares that every bet requires fresh user confirmation and that there are no exceptions. The production script nevertheless supports `--yes`, which bypasses the only in-script confirmation gate. The flag is described as intended for CI or testing, but it is not restricted to a mocked wallet, test chain, test environment, or special build. It therefore works with the real Polygon wallet and real USDT. ### Attack Path 1. An Agent, wrapper script, or operator invokes `place-bet.js` with valid bet arguments and `--yes`. 2. The interactive confirmation is skipped. 3. The script may approve USDT for the relayer. 4. It signs and submits the bet using the configured private key. 5. Real funds are committed without contemporaneous in-script user authorization. ### Impact Assessment This can authorize real USDT allowance changes and betting transactions from the configured wallet. The scope is the requested stake plus fees and approval buffer for each invocation. Repeated invocations could place ...[truncated 33 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/place-bet.js:146
Finding

Mandatory Active-Market Verification Is Skipped and Falsely Reported as Successful

Content
View full analysis
Remediation
View remediation

T01 · Skill Instruction Hijacking

Warning
Location
scripts/watch-bets.js:73
Finding

External Match Data Can Be Reintroduced Through the Agent Prompt Channel

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/package-lock.json:5
Finding

Incomplete Lockfile Leaves Wallet-Signing Dependencies Unpinned

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (36)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

There is a material description-behavior mismatch. The code’s primary purpose is limited to discovering and displaying games and main-market odds using HTTPS calls to public Azuro/onchainfeed endpoints and local dictionary helpers. It has no wallet logic, no blockchain RPC interaction, no transaction construction, no EIP-712 signing, no user/account context, and no claim/status handling. The only part of the description that matches is browsing/searching games and odds from the Azuro feed. Because the declared purpose emphasizes placing/claiming on-chain bets and related account actions, while the actual code is just a game/odds fetcher, this should be flagged as a mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code is clearly related to the declared domain (Pinwin/Azuro on-chain sports betting) and does implement bet placement via EIP-712 signing on Polygon, so it is not unrelated. However, the declared description presents a broader multi-capability skill: browsing games and odds, placing bets, checking bets, and claiming winnings. This code chunk only implements the bet placement path and immediate confirmation polling for that new order. It even defines constants related to claims and feed endpoints, but does not use them here. Because the supplied chunk does not actually perform several prominently declared capabilities, the description overstates what this specific code chunk does, making it a description/behavior mismatch.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 61)May include surrounding context.

md
ro-org/dictionaries` is still used by `place-bet.js` for outcomeId resolution. `get-games.js` no longer needs it — the REST API returns human-readable titles di

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 137)May include surrounding context.

md
ro-org/dictionaries` is still used by `place-bet.js` for outcomeId resolution. `get-games.js` no longer needs it — the REST API returns human-readable titles di

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 138)May include surrounding context.

md
ro-org/dictionaries` is still used by `place-bet.js` for outcomeId resolution. `get-games.js` no longer needs it — the REST API returns human-readable titles di

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 139)May include surrounding context.

md
ro-org/dictionaries` is still used by `place-bet.js` for outcomeId resolution. `get-games.js` no longer needs it — the REST API returns human-readable titles di

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 140)May include surrounding context.

md
ro-org/dictionaries` is still used by `place-bet.js` for outcomeId resolution. `get-games.js` no longer needs it — the REST API returns human-readable titles di

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 143)May include surrounding context.

md
ro-org/dictionaries` is still used by `place-bet.js` for outcomeId resolution. `get-games.js` no longer needs it — the REST API returns human-readable titles di

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 144)May include surrounding context.

md
ro-org/dictionaries` is still used by `place-bet.js` for outcomeId resolution. `get-games.js` no longer needs it — the REST API returns human-readable titles di

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 145)May include surrounding context.

md
ro-org/dictionaries` is still used by `place-bet.js` for outcomeId resolution. `get-games.js` no longer needs it — the REST API returns human-readable titles di

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 157)May include surrounding context.

md
ro-org/dictionaries` is still used by `place-bet.js` for outcomeId resolution. `get-games.js` no longer needs it — the REST API returns human-readable titles di

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 272)May include surrounding context.

md
ro-org/dictionaries` is still used by `place-bet.js` for outcomeId resolution. `get-games.js` no longer needs it — the REST API returns human-readable titles di

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 275)May include surrounding context.

md
ro-org/dictionaries` is still used by `place-bet.js` for outcomeId resolution. `get-games.js` no longer needs it — the REST API returns human-readable titles di

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 578)May include surrounding context.

md
ro-org/dictionaries` is still used by `place-bet.js` for outcomeId resolution. `get-games.js` no longer needs it — the REST API returns human-readable titles di

Credential Access

High
Category
Privilege Escalation
Confidence
97% confidence
Finding

The skill explicitly instructs storing a live wallet private key in a .env file inside the skill workspace, while the skill metadata declares environment access and the document itself discusses model-readable config risks. In this context, any compromise of the workspace, scripts, or agent runtime could expose a high-value credential capable of authorizing irreversible on-chain transactions.

Content

Scanner excerpt · SKILL.md (reported line 68)May include surrounding context.

The recommended approach is a .env file with restricted permissions — the key is read only by the Node process and never stored in any config file the model can read:

bash
# Create .env in the skill workspace
echo "BETTOR_PRIVATE_KEY=0xyour_private_key_here" > ~/.openclaw/workspace/skills/sports-betting/.env
chmod 600 ~/.openclaw/workspace/skills/sports-betting/.env

Credential Access

High
Category
Privilege Escalation
Confidence
97% confidence
Finding

The instruction that scripts load the .env automatically increases secret exposure by normalizing local plaintext key storage and automated secret consumption. For a betting skill that can sign blockchain transactions, compromise of that file means direct fund loss and account takeover for all supported actions.

Content

Scanner excerpt · SKILL.md (reported line 69)May include surrounding context.

bash
# Create .env in the skill workspace
echo "BETTOR_PRIVATE_KEY=0xyour_private_key_here" > ~/.openclaw/workspace/skills/sports-betting/.env
chmod 600 ~/.openclaw/workspace/skills/sports-betting/.env

# Scripts load it automatically at runtime — no manual export needed

Credential Access

High
Category
Privilege Escalation
Confidence
96% confidence
Finding

This finding reinforces that the workspace .env contains a wallet private key used for real-money transactions. Even with chmod 600, plaintext local storage remains dangerous because the threat model includes agent-accessible files, compromised scripts, backups, logs, or other local processes.

Content

Scanner excerpt · SKILL.md (reported line 70)May include surrounding context.

bash
# Create .env in the skill workspace
echo "BETTOR_PRIVATE_KEY=0xyour_private_key_here" > ~/.openclaw/workspace/skills/sports-betting/.env
chmod 600 ~/.openclaw/workspace/skills/sports-betting/.env

# Scripts load it automatically at runtime — no manual export needed

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · SKILL.md (reported line 447)May include surrounding context.

md
1. **Check allowance** — if `allowance(bettor, relayer) >= stake + fee`, approval was already sent. Skip Step 5.
2. **Check for pending bets** — query the bets subgraph for `status: "Accepted"` bets from this wallet placed in the last 10 minutes. If found, poll that order instead of creating a new one.
3. If no pending bet found and no clear state, start the flow from Step 1 with a fresh game fetch.

---

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill declares high-risk capabilities including environment-variable access and unrestricted network interaction, yet does not define an explicit tool scope such as allowed-tools or permissions. In a skill that can access a private key and initiate on-chain activity, lack of least-privilege scoping increases the blast radius if the skill is invoked unexpectedly or modified later.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · SKILL.md (reported line 53)May include surrounding context.

md
| **clientCore** (bet payload verification) | `0xF9548Be470A4e130c90ceA8b179FCD66D2972AC7` |
| **claimContract** (LP claim, redeem won/canceled bets) | `0x0FA7FB5407eA971694652E6E16C12A52625DE1b8` |
| **environment** | `PolygonUSDT` |
| **data-feed URL** | `https://api.onchainfeed.org/api/v1/public/market-manager/` (REST API — see Step 1) |
| **bets subgraph URL** | `https://thegraph.onchainfeed.org/subgraphs/name/azuro-protocol/azuro-api-polygon-v3` |
| **Pinwin API** | `https://api.pinwin.xyz` |
| **Polygonscan** | `https://polygonscan.com/tx/{txHash}` |

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · SKILL.md (reported line 296)May include surrounding context.

md
| **clientCore** (bet payload verification) | `0xF9548Be470A4e130c90ceA8b179FCD66D2972AC7` |
| **claimContract** (LP claim, redeem won/canceled bets) | `0x0FA7FB5407eA971694652E6E16C12A52625DE1b8` |
| **environment** | `PolygonUSDT` |
| **data-feed URL** | `https://api.onchainfeed.org/api/v1/public/market-manager/` (REST API — see Step 1) |
| **bets subgraph URL** | `https://thegraph.onchainfeed.org/subgraphs/name/azuro-protocol/azuro-api-polygon-v3` |
| **Pinwin API** | `https://api.pinwin.xyz` |
| **Polygonscan** | `https://polygonscan.com/tx/{txHash}` |

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · SKILL.md (reported line 410)May include surrounding context.

md
| **clientCore** (bet payload verification) | `0xF9548Be470A4e130c90ceA8b179FCD66D2972AC7` |
| **claimContract** (LP claim, redeem won/canceled bets) | `0x0FA7FB5407eA971694652E6E16C12A52625DE1b8` |
| **environment** | `PolygonUSDT` |
| **data-feed URL** | `https://api.onchainfeed.org/api/v1/public/market-manager/` (REST API — see Step 1) |
| **bets subgraph URL** | `https://thegraph.onchainfeed.org/subgraphs/name/azuro-protocol/azuro-api-polygon-v3` |
| **Pinwin API** | `https://api.pinwin.xyz` |
| **Polygonscan** | `https://polygonscan.com/tx/{txHash}` |

Session Persistence

Medium
Category
Rogue Agent
Confidence
93% confidence
Finding

Persisting BETTOR_PRIVATE_KEY in a workspace .env creates long-lived sensitive state on disk, increasing the window for theft and misuse across sessions. Because the credential directly authorizes on-chain transactions, session persistence here materially raises the chance of unauthorized betting or fund exfiltration if the environment is later accessed.

Content

Scanner excerpt · SKILL.md (reported line 68)May include surrounding context.

The recommended approach is a .env file with restricted permissions — the key is read only by the Node process and never stored in any config file the model can read:

bash
# Create .env in the skill workspace
echo "BETTOR_PRIVATE_KEY=0xyour_private_key_here" > ~/.openclaw/workspace/skills/sports-betting/.env
chmod 600 ~/.openclaw/workspace/skills/sports-betting/.env

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · SKILL.md (reported line 70)May include surrounding context.

bash
# Create .env in the skill workspace
echo "BETTOR_PRIVATE_KEY=0xyour_private_key_here" > ~/.openclaw/workspace/skills/sports-betting/.env
chmod 600 ~/.openclaw/workspace/skills/sports-betting/.env

# Scripts load it automatically at runtime — no manual export needed

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · SKILL.md (reported line 75)May include surrounding context.

bash
# Create .env in the skill workspace
echo "BETTOR_PRIVATE_KEY=0xyour_private_key_here" > ~/.openclaw/workspace/skills/sports-betting/.env
chmod 600 ~/.openclaw/workspace/skills/sports-betting/.env

# Scripts load it automatically at runtime — no manual export needed

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access, suspicious.exposed_secret_literal

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/place-bet.js:386

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/claim-bets.js:36

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/claim-bets.js:41