Back to skill

Security audit

TokenDraft

Security checks for vulnerabilities and agentic risk

Overview

This skill is review-worthy because it can use a Solana private key to sign server-supplied transactions and can set up recurring paid tournament entries without spending limits.

Install only if you are comfortable giving the agent access to a Solana wallet key for TokenDraft. Use a wallet with limited funds, avoid enabling paid auto-join, and require decoded transaction review, spend limits, and explicit approval before any buy-in transaction is signed.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
SKILL.md:139
Finding

Server-Supplied Solana Transaction Is Signed Without Local Validation

Content
View full analysis
", "walletPublicKey": ""}' ``` Returns `{ transaction (base64), expectedSignature, tournamentInfo }`. **2. Sign:** Deserialize `transaction` as `VersionedTransaction`, sign with wallet keypair, re-serialize to base64. **3. Send signed transaction:** ```bash curl -X POST "https://tokendraft-production.up.railway.app/api/v2/buyIn/sendSignedTransaction" \ -H "Content-Type: application/json" \ -d '{ "signedTransactionBase64": "", "tournamentId": "", "expectedSignature": "", "walletPublicKey": "" }' ``` ``` ### Technical Analysis The remote TokenDraft API controls the serialized Solana transaction that the Skill instructs the Agent to sign. The instructions do not require the Agent to decode and validate the transaction before applying the wallet signature. In particular, the workflow does not verify: - The invoked Solana program IDs - The instruction types and instruction data - Recipient and destination accounts - The exact SOL or token amount transferred - The fee payer and required signer set - The selected Solana network - The relationship between the transaction and the requested tournament - The absence of additional or unrelated instructions - The expected post-transaction balance changes The API also supplies `expectedSignature`, so that field is not an independent integrity check. Verifying that the wallet has enough SOL for the advertised buy-in does not prove that the serialized transaction contains only that buy-in. This creat ...[truncated 1581 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
SKILL.md:226
Finding

Persistent Auto-Join Job Can Repeatedly Execute Paid Tournament Entries

Content
View full analysis
. If > 0, check SOL balance and follow buy-in flow. 4. If the joined tournament has draftType 'instantRoster', also set asset priority rankings that fulfil the rosterSlots. 5. Report results for each tournament (joined or error reason)." ``` ```markdown Manage with `openclaw cron list`, `openclaw cron remove `, `openclaw cron edit --enabled false/true`. ``` ### Technical Analysis The Skill creates a scheduled task that survives the initiating session and runs every 30 minutes. It directs the Agent to join all open tournaments, explicitly including paid tournaments. The scheduled workflow has no: - Per-tournament maximum buy-in - Per-run or cumulative spending cap - Tournament allowlist - Maximum number of entries - Expiration time or automatic disablement - Transaction-specific user confirmation - Restriction limiting unattended operation to free tournaments A balance check only determines whether sufficient funds are available; it does not establish authorization to spend those funds. The persistent task therefore holds broader and longer-lived authority than is minimally necessary to query tournaments or join a user-selected event. ### Attack Path 1. The user authorizes or is induced to create the auto-join cron job. 2. The job persists across sessions and runs every 30 minutes. 3. The TokenDraft API reports one or more paid tournaments as open. 4. The scheduled A ...[truncated 1112 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:68
Finding

Remote Authentication Challenge Is Signed Without Validating Its Meaning

Content
View full analysis
r.json()); // Step 2 const messageBytes = new TextEncoder().encode(message); const signature = nacl.sign.detached(messageBytes, keyPair.secretKey); const signatureBase58 = bs58.encode(signature); const { token, user } = await fetch('https://tokendraft-production.up.railway.app/api/v2/agents/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ walletPublicKey, nonce, signature: signatureBase58 }), }).then(r => r.json()); ``` ### Technical Analysis The TokenDraft server supplies the complete `message`, and the Agent signs it using the Solana wallet key without validating a canonical authentication format. The Skill does not require checking that the message contains: - A fixed TokenDraft authentication domain and purpose - The expected API origin - The requesting wallet public key - The returned nonce - The intended Solana network - An issuance timestamp and short expiration - An explicit statement that the signature is only for authentication and not a transaction Nonce expiration and single-use handling by the server do not independently protect the client from signing a different statement. If the endpoint is compromised, it can return arbitrary text and obtain a valid Ed25519 signature from the wallet. This is a generic message-signing oracle rather than a narrowly constrained authentication operation. The reviewed example does not send the private key over the network. The sensitive output is the signature produced with that key. ### A ...[truncated 1270 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (5)

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 · SKILL.md (reported line 22)May include surrounding context.

Step 1: Request a Nonce

bash
curl -X POST https://tokendraft-production.up.railway.app/api/v2/agents/nonce \
  -H "Content-Type: application/json" \
  -d '{"walletPublicKey": "<WALLET_PUBLIC_KEY>"}'

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 · SKILL.md (reported line 69)May include surrounding context.

md
const walletPublicKey = bs58.encode(keyPair.publicKey);

// Step 1
const { nonce, message } = await fetch('https://tokendraft-production.up.railway.app/api/v2/agents/nonce', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ walletPublicKey }),

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 · SKILL.md (reported line 80)May include surrounding context.

md
const signature = nacl.sign.detached(messageBytes, keyPair.secretKey);
const signatureBase58 = bs58.encode(signature);

const { token, user } = await fetch('https://tokendraft-production.up.railway.app/api/v2/agents/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ walletPublicKey, nonce, signature: signatureBase58 }),

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 · SKILL.md (reported line 135)May include surrounding context.

If buyInAmountSol is 0:

bash
curl -X POST "https://tokendraft-production.up.railway.app/api/v2/tournaments/join/<TOURNAMENT_ID>" \
  -H "Authorization: Bearer $TOKENDRAFT_JWT"

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The auto-join cron explicitly instructs the agent to join all open tournaments every 30 minutes, including paid tournaments that require signing and sending on-chain buy-in transactions, but it does not require an explicit user opt-in or a clear warning about financial risk. In this skill’s context, that means a background task can repeatedly spend funds from a configured Solana wallet with limited user awareness, which is materially dangerous for a wallet-enabled agent.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.