Back to skill

Security audit

Hodl Dance Skill

Security checks for vulnerabilities and agentic risk

Overview

This skill appears purpose-built for HODL.DANCE trading, but it can spend real wallet funds and upload local files with weak safeguards.

Review before installing. Use a dedicated low-value wallet, pin the exact package version, avoid unpinned npx execution, run only read and quote commands unless you explicitly intend to spend funds, and do not let an agent choose logo file paths or trade amounts without strict limits and human approval.

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

Warning
Location
src/commands/create-token.js:68
Finding
Arbitrary Local File Upload Through Unvalidated Logo Path<![CDATA[ ## Vulnerability Details **File Location**: `src/commands/create-token.js:68-95` **Vulnerability Type**: Unrestricted local file read and network upload **Risk Level**: Medium ### Vulnerable Code ```js const resolvedLogo = path.resolve(logoPath); if (!fs.existsSync(resolvedLogo)) { err(`Logo file not found: ${resolvedLogo}`, 'INVALID_ARG'); } (async () => { const wallet = getWallet(); const provider = wallet.provider; // 1. Upload metadata + logo const form = new FormData(); form.append('name', name); form.append('symbol', symbol); form.append('description', description); form.append('website', website); form.append('twitter', twitter); form.append('telegram', telegram); form.append('creator', wallet.address); form.append('category', category); form.append('logo', fs.createReadStream(resolvedLogo), { filename: path.basename(resolvedLogo), }); const uploadRes = await fetch(`${API_BASE}/token/create`, { method: 'POST', body: form, headers: form.getHeaders(), }); ``` ### Technical Analysis The documentation states that `--logo` must identify a PNG, JPEG, or WEBP image no larger than 5 MB. The implementation only verifies that the resolved path exists. It does not verify that the path is a regular file, enforce a size limit, inspect the file signature, restrict MIME types, or reject symbolic links and special files. Any accessible local path can consequently be opened with `fs.createReadStream()` and transmitted to `https://hodl.dance/api/token/create`. Although uploading a logo is necessary for token creation, unrestricted access to arbitrary local files exceeds the minimum filesystem privilege required by that functionality. This issue becomes exploitable when an attacker can influence command arguments, such as through an agent prompt, automation configuration, copied command, or untrusted workflow input. ### Attack Path 1. An attacker persuades an agent or us ...[truncated 1203 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Call `fs.statSync()` or `fs.promises.stat()` and require `isFile()` before opening the path. 2. Enforce the documented 5 MB maximum using the file's stat size before creating the stream. 3. Validate content using trusted file-signature detection rather than relying on the extension or caller-supplied MIME type. 4. Allow only PNG, JPEG, and WEBP signatures and assign the corresponding fixed MIME type. 5. Reject symbolic links, device files, named pipes, sockets, directories, and other special filesystem objects. 6. Consider restricting file selection to an explicitly approved working directory. 7. Display the resolved path, detected type, and size and require confirmation before uploading when the command is used interactively. 8. Apply stream and HTTP timeouts and abort the upload if the transmitted byte count exceeds the limit. 9. Perform all validation before constructing the wallet or making any network request. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/commands/buy-token.js:43
Finding
Buy and Sell Transactions Lack Enforceable Slippage Protection<![CDATA[ ## Vulnerability Details **File Location**: `src/commands/buy-token.js:43-52`; `src/commands/sell-token.js:51-65` **Vulnerability Type**: Unbounded trade execution and transaction-ordering exposure **Risk Level**: High ### Vulnerable Code Buy execution: ```js const { tokensOut, fee } = await simulateBuy(curveAddress, bnbAmount); const tokensEstimated = ethers.formatUnits(tokensOut, 18); const value = ethers.parseEther(String(bnbAmount)); let tx; if (recipient) { tx = await curve.buyTokensFor(recipient, { value, gasLimit: 300_000 }); } else { tx = await curve.buyTokens({ value, gasLimit: 300_000 }); } ``` Sell execution: ```js // Simulate output before sending const { bnbOut, fee } = await simulateSell(curveAddress, tokenAmount); // Step 1: Approve (only if current allowance is insufficient) let approveTxHash = null; const allowance = await tokenContract.allowance(wallet.address, curveAddress); if (allowance < amountWei) { const approveTx = await tokenContract.approve(curveAddress, amountWei, { gasLimit: 100_000 }); const approveReceipt = await approveTx.wait(); approveTxHash = approveReceipt.hash; } // Step 2: Sell const tx = await curve.sellTokens(amountWei, { gasLimit: 300_000 }); const receipt = await tx.wait(); ``` ### Technical Analysis The Skill computes expected output from current bonding-curve reserves, but the estimate is not incorporated into the submitted transaction. The invoked ABI methods accept no minimum-output value or deadline: - `buyTokens()` and `buyTokensFor()` do not include a minimum number of tokens to receive. - `sellTokens()` does not include a minimum amount of BNB to receive. - Neither execution path establishes an expiration deadline. - The Skill does not compare the mined result against a user-approved tolerance before value transfer; after confirmation, any adverse execution is irreversible. The recommendation in `SKILL.md` to run `quote` before trading is informational and cannot prev ...[truncated 1768 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer contract entry points that accept both a minimum output and a deadline, such as: - `buyTokens(minTokensOut, deadline)` - `buyTokensFor(recipient, minTokensOut, deadline)` - `sellTokens(amountIn, minBnbOut, deadline)` 2. Add a required or securely defaulted `--slippage-bps` option and calculate the minimum acceptable output from a fresh on-chain quote. 3. Fetch the quote immediately before transaction construction and include its block number in user-visible output. 4. Reject transactions when the quote is older than a small configured block or time threshold. 5. Require explicit confirmation containing the input amount, minimum output, recipient, contract address, network, fee estimate, and deadline. 6. If the deployed contracts cannot enforce minimum output, clearly state that execution is unprotected and require a high-friction opt-in such as `--allow-unprotected-trade`. 7. Apply configurable maximum BNB and token amounts for autonomous-agent use. 8. Consider private transaction submission where available, but do not treat it as a substitute for contract-enforced slippage limits. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
src/commands/create-token.js:54
Finding
Documented Token-Creation Constraints Are Not Enforced<![CDATA[ ## Vulnerability Details **File Location**: `src/commands/create-token.js:54-66` **Vulnerability Type**: Missing input validation before network and blockchain operations **Risk Level**: Low ### Vulnerable Code ```js const name = flags['name']; const symbol = flags['symbol']; const logoPath = flags['logo']; const category = flags['category'] || 'meme'; const description = flags['description'] || ''; const website = flags['website'] || ''; const twitter = flags['twitter'] || ''; const telegram = flags['telegram'] || ''; const initialBuy = flags['initial-buy'] ? ethers.parseEther(String(flags['initial-buy'])) : 0n; if (!name) err('Usage: create-token --name="..." --symbol=... --logo=./logo.png', 'MISSING_ARG'); if (!symbol) err('Usage: create-token --name="..." --symbol=... --logo=./logo.png', 'MISSING_ARG'); if (!logoPath) err('Usage: create-token --name="..." --symbol=... --logo=./logo.png', 'MISSING_ARG'); ``` ### Technical Analysis The implementation checks only whether `name`, `symbol`, and `logo` were supplied. It does not enforce the constraints declared in `SKILL.md`, including: - Token name length of 3–40 characters. - Symbol length of 1–10 characters. - Description length of at most 500 characters. - Category membership in `meme`, `ai`, `games`, `social`, or `other`. - HTTPS requirements and expected domains for website and social links. - A reasonable, positive, and bounded initial-buy amount. - The documented logo type and size constraints. The unvalidated metadata is uploaded before the on-chain deployment is attempted. Consequently, malformed or excessive content can reach the API even if the blockchain transaction later fails. The absence of strict local validation also makes the command's behavior inconsistent with its published contract. ### Attack Path 1. An attacker or malformed automation input supplies oversized or invalid metadata flags. 2. The command accepts the values b ...[truncated 1067 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate all inputs before calling `getWallet()`, opening a file, uploading metadata, or constructing a transaction. 2. Enforce name and symbol length limits using Unicode-aware character counting. 3. Limit descriptions to 500 characters and impose conservative byte-size limits on every metadata field. 4. Validate categories against a fixed allowlist. 5. Parse URLs with the standard `URL` class and require HTTPS. 6. Restrict Twitter/X and Telegram links to the documented hostnames if those restrictions are part of the product contract. 7. Parse `initial-buy` inside explicit error handling and require a nonnegative value below a configurable maximum. 8. Reject unknown flags and duplicate flags rather than silently accepting them. 9. Return field-specific `INVALID_ARG` errors that identify the rejected constraint. 10. Add unit and integration tests for empty values, boundary lengths, Unicode input, malformed URLs, invalid categories, negative amounts, excessive amounts, and oversized files. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (20)

Known Vulnerable Dependency: form-data==4.0.5 — 1 advisory(ies): CVE-2026-12143 (form-data: CRLF injection in form-data via unescaped multipart field names and f)

High
Category
Supply Chain
Confidence
97% confidence
Finding
form-data is a production dependency, and the cited CRLF injection issue can be dangerous when multipart field names or filenames are attacker-controlled. This can enable malformed request smuggling into downstream parsers, header injection in multipart bodies, or unexpected server-side behavior when the skill uploads user-influenced content to external services.

Known Vulnerable Dependency: nanoid==3.3.12 — 2 advisory(ies): CVE-2026-67214 (nanoid: non-secure generators can loop indefinitely with negative size); CVE-2026-67213 (nanoid: custom generators can loop indefinitely when size is zero)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: postcss==8.5.15 — 2 advisory(ies): CVE-2026-69153 (PostCSS: incomplete fix of GHSA-6g55-p6wh-862q — attacker-controlled sourceMappi); CVE-2026-73646 (PostCSS: Path Traversal in Previous Source Map Auto-Loading (sourceMappingURL) l)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: vite==8.0.14 — 2 advisory(ies): CVE-2026-53571 (vite: `server.fs.deny` bypass on Windows alternate paths); CVE-2026-53632 (launch-editor: NTLMv2 hash disclosure via UNC path handling on Windows)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: ws==8.17.1 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
96% confidence
Finding
ws is a production dependency pulled in through ethers, and the cited advisories include memory disclosure and memory exhaustion conditions affecting WebSocket handling. If the skill connects to untrusted or attacker-influenced WebSocket endpoints, an attacker could potentially trigger denial of service or leak process memory contents through malformed frames/fragments.

Known Vulnerable Dependency: form-data==4.0.5 — 1 advisory(ies): CVE-2026-12143 (form-data: CRLF injection in form-data via unescaped multipart field names and f)

High
Category
Supply Chain
Confidence
87% confidence
Finding
The package includes form-data 4.0.5, which is flagged with a CRLF injection advisory. If the skill constructs multipart requests using attacker-controlled field names or filenames, an attacker may inject crafted headers or manipulate request structure, which is particularly concerning for an agent skill that may broker external HTTP/API interactions.

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding
The README recommends running the skill via `npx @hodl-dance/skill` without pinning a specific version. That causes users and agents to fetch and execute whatever package version is current at runtime, creating a supply-chain risk where a compromised publisher account or malicious update could lead to arbitrary code execution, especially dangerous here because the tool is designed to use `HODL_PRIVATE_KEY` and perform on-chain transactions.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The README documents real `buy-token`, `sell-token`, and `create-token` commands and instructs users to export a private key, but it does not prominently warn that these actions are irreversible on-chain transactions with direct financial risk. In an agent-oriented trading skill, missing safety warnings materially increases the chance of accidental execution, loss of funds, or autonomous misuse by operators who assume examples are safe to run.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding
The skill instructs users to run the package via `npx @hodl-dance/skill` without pinning an exact version, which makes execution dependent on whatever package version is current at install time. Because this skill handles private keys and can submit real on-chain transactions, a compromised publisher account, malicious update, or dependency hijack could lead to credential theft or unauthorized trading.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The documented commands `buy-token`, `sell-token`, and `create-token` send real blockchain transactions, but the skill does not present a clear, explicit warning that these actions are irreversible and can cause immediate financial loss through slippage, scams, wrong addresses, or malicious token behavior. In an agent context, this is especially risky because users may treat the skill as informational while it is actually capable of spending funds and approving token transfers.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This code sends the token name, symbol, description, social links, creator wallet address, and logo file to https://hodl.dance/api via HTTP POST. While the script's purpose implies token creation, there is no runtime confirmation, print/log disclosure, or explicit warning in the file that local file contents and metadata will be transmitted off-system.

Known Vulnerable Dependency: @vitest/mocker==4.1.7 — 1 advisory(ies): CVE-2026-84373 (Vitest: Path Traversal / Arbitrary File Read via @vitest/mocker Redirect Mock)

Low
Category
Supply Chain
Confidence
60% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: vitest==4.1.7 — 1 advisory(ies): CVE-2026-84373 (Vitest: Path Traversal / Arbitrary File Read via @vitest/mocker Redirect Mock)

Low
Category
Supply Chain
Confidence
60% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"author": "HODL.DANCE",
  "license": "MIT",
  "dependencies": {
    "ethers": "^6.13.0",
    "form-data": "^4.0.5",
    "node-fetch": "^2.7.0"
  },
Confidence
95% confidence
Finding
The dependency uses a caret range, allowing newer minor/patch releases to be installed than the exact version reviewed. This creates supply-chain drift and can introduce unreviewed behavior or newly published malicious or breaking updates into the skill environment.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"license": "MIT",
  "dependencies": {
    "ethers": "^6.13.0",
    "form-data": "^4.0.5",
    "node-fetch": "^2.7.0"
  },
  "engines": {
Confidence
98% confidence
Finding
The form-data dependency is not pinned exactly, which is especially risky because this package also has a known advisory in the supplied findings. Version ranges increase the chance of pulling unexpected releases and make incident response and reproducibility harder.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"dependencies": {
    "ethers": "^6.13.0",
    "form-data": "^4.0.5",
    "node-fetch": "^2.7.0"
  },
  "engines": {
    "node": ">=18.0.0"
Confidence
94% confidence
Finding
Using a caret range for node-fetch permits dependency drift across installations. While common in development, it weakens build reproducibility and can expose the skill to unexpected package changes or transitive supply-chain compromise.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"version": "node -e \"console.log(require('./package.json').version)\""
  },
  "devDependencies": {
    "vitest": "^4.1.7"
  }
}
Confidence
90% confidence
Finding
The devDependency vitest is specified with a caret range, so test or CI environments may resolve to newer releases than intended. Although this is less exposed than a runtime dependency, it still presents supply-chain and reproducibility risk, especially in automated pipelines.

Known Vulnerable Dependency: vitest==4.1.7 — 1 advisory(ies): CVE-2026-84373 (Vitest: Path Traversal / Arbitrary File Read via @vitest/mocker Redirect Mock)

Low
Category
Supply Chain
Confidence
80% confidence
Finding
Vitest 4.1.7 is reported as affected by a path traversal/arbitrary file read issue through mocking features. Because this is a devDependency, the primary exposure is in developer or CI environments rather than production runtime, but it can still matter if untrusted tests, fixtures, or PR workflows are executed.

Natural-Language Policy Violations

Low
Confidence
80% confidence
Finding
The comment "znajdź nowy token" forces a non-English phrase without any user opt-in or documented locale context. This is a minor natural-language policy inconsistency because the rest of the skill is written in English and no regional justification is provided.

Missing User Warnings

Low
Confidence
84% confidence
Finding
This code performs an HTTP/API request using the user-provided address, which is a network operation covered by the missing-warnings rule for code files. There is no visible confirmation prompt, log/print disclosure, or explanatory comment/docstring in this file indicating that the address will be sent to a remote API.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/build.js:39