Back to skill

Security audit

Million Bit Homepage NFTs

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches its blockchain minting purpose, but its shell and Node scripts expose user-controlled inputs to local code execution risks.

Review before installing. Only run this skill on trusted image paths and plain numeric coordinates, and do not let untrusted users supply filenames or coordinate strings. Before submitting any prepared transaction, verify the Base chain ID, contract address, ETH value, URL, image, and permanence of the on-chain publication in your wallet.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/prepare_mint.sh:59
Finding
Shell Command Injection Through Unsafe Arithmetic Evaluation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/prepare_mint.sh:59-63`; `scripts/config.sh:67-92`; `scripts/check_price.sh:27-35`; `scripts/find_available_plots.sh:38-50` **Vulnerability Type**: Command injection through unvalidated Bash arithmetic expressions **Risk Level**: High ### Vulnerable Code ```bash # scripts/prepare_mint.sh:59-63 WIDTH=$((X2 - X1)) HEIGHT=$((Y2 - Y1)) # Step 1: Validate coordinates echo "Validating coordinates..." >&2 validate_coords "$X1" "$Y1" "$X2" "$Y2" || exit 1 ``` ```bash # scripts/config.sh:67-92 validate_grid_aligned() { local val="$1" local name="$2" if (( val % GRID_UNIT != 0 )); then echo "Error: $name ($val) must be a multiple of $GRID_UNIT" >&2 return 1 fi if (( val < 0 || val > CANVAS_SIZE )); then echo "Error: $name ($val) must be between 0 and $CANVAS_SIZE" >&2 return 1 fi return 0 } validate_coords() { local x1="$1" y1="$2" x2="$3" y2="$4" validate_grid_aligned "$x1" "x1" || return 1 validate_grid_aligned "$y1" "y1" || return 1 validate_grid_aligned "$x2" "x2" || return 1 validate_grid_aligned "$y2" "y2" || return 1 if (( x2 <= x1 )); then echo "Error: x2 ($x2) must be greater than x1 ($x1)" >&2 return 1 fi if (( y2 <= y1 )); then echo "Error: y2 ($y2) must be greater than y1 ($y1)" >&2 return 1 fi return 0 } ``` ```bash # scripts/check_price.sh:27-35 else X1="$1" Y1="$2" X2="$3" Y2="$4" WIDTH=$((X2 - X1)) HEIGHT=$((Y2 - Y1)) fi ``` ```bash # scripts/find_available_plots.sh:38-50 if (( WIDTH % GRID_UNIT != 0 )); then echo "Error: Width ($WIDTH) must be a multiple of $GRID_UNIT" >&2 exit 1 fi if (( HEIGHT % GRID_UNIT != 0 )); then echo "Error: Height ($HEIGHT) must be a multiple of $GRID_UNIT" >&2 exit 1 fi if (( WIDTH < GRID_UNIT || HEIGHT < GRID_UNIT )); then echo "Error: Minimum size is ${GRID_UNIT}x${GRID_UNIT}" >&2 ...[truncated 2531 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate every numeric argument before it appears in any arithmetic context: ```bash validate_decimal_integer() { local value="$1" local name="$2" if [[ ! "$value" =~ ^[0-9]+$ ]]; then printf 'Error: %s must be an unsigned decimal integer\n' "$name" >&2 return 1 fi } ``` 2. Invoke lexical validation immediately after parsing arguments and before calculating width, height, modulo, comparisons, or ranges: ```bash validate_decimal_integer "$X1" x1 || exit 1 validate_decimal_integer "$Y1" y1 || exit 1 validate_decimal_integer "$X2" x2 || exit 1 validate_decimal_integer "$Y2" y2 || exit 1 WIDTH=$((10#$X2 - 10#$X1)) HEIGHT=$((10#$Y2 - 10#$Y1)) ``` 3. Use the `10#` prefix after validation to force base-10 interpretation and avoid leading-zero octal behavior. 4. Apply the same validation to `WIDTH`, `HEIGHT`, `LIMIT`, and every other value later used in an arithmetic expression. 5. Reject missing values after options such as `--limit` rather than evaluating an absent or unrelated argument. 6. Add regression tests containing command substitutions, array syntax, operators, whitespace, signs, hexadecimal notation, leading zeros, newlines, and nonnumeric characters. Tests should verify that rejection occurs before any arithmetic evaluation or side effect. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/prepare_mint.sh:78
Finding
Arbitrary Node.js Code Execution Through Image Path Interpolation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/prepare_mint.sh:78-83` **Vulnerability Type**: JavaScript source injection through `node -e` **Risk Level**: High ### Vulnerable Code ```bash # Get image dimensions using sharp (via node one-liner) IMG_INFO=$(node -e " const sharp = require('sharp'); sharp('$IMAGE_PATH').metadata().then(m => { console.log(JSON.stringify({width: m.width, height: m.height})); }).catch(e => { console.error(e.message); process.exit(1); }); ") ``` The preceding existence check does not make the value safe: ```bash if [ ! -f "$IMAGE_PATH" ]; then echo "Error: Image file not found: $IMAGE_PATH" >&2 exit 1 fi ``` ### Technical Analysis `IMAGE_PATH` is inserted directly into JavaScript source enclosed by single quotes and passed to `node -e`. Shell quoting around the overall script does not escape the value for the JavaScript grammar. A filename can legally contain quotes, parentheses, semicolons, comment markers, and other JavaScript syntax characters. If an attacker can create or select an existing file with such a name, the `-f` check succeeds. A single quote in the filename can then terminate the `sharp('...')` argument, append arbitrary JavaScript, and comment out the remaining generated source. This is a source-code injection vulnerability. Node.js code can access the filesystem, environment, child-process APIs, and network APIs with the same permissions as the skill process. ### Attack Path 1. An attacker creates, uploads, or otherwise causes an image file to exist under a filename containing JavaScript syntax. 2. The attacker asks the Agent to prepare a mint using that path. 3. `prepare_mint.sh` verifies only that the path points to an existing regular file. 4. The path is concatenated into the source string passed to `node -e`. 5. The crafted quote terminates the intended string literal and injects additional JavaScript statements. 6. Node.js executes the injected statements while evaluating the ...[truncated 1023 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Do not place file paths or any other external values inside source code passed to `node -e`. Pass the path as a positional argument: ```bash IMG_INFO=$(node - "$IMAGE_PATH" <<'NODE' const sharp = require('sharp'); const imagePath = process.argv[2]; sharp(imagePath).metadata() .then((metadata) => { console.log(JSON.stringify({ width: metadata.width, height: metadata.height })); }) .catch((error) => { console.error(error.message); process.exit(1); }); NODE ) ``` Alternatively, move metadata inspection into a dedicated checked-in helper script and invoke it as: ```bash node "$HELPERS_DIR/image_metadata.js" "$IMAGE_PATH" ``` Additional hardening should include: 1. Treat all paths as opaque data and pass them only through `process.argv`, never through generated JavaScript. 2. Use `--` where supported when passing user-controlled paths to command-line utilities. 3. Validate that image decoding succeeds and enforce input-size and pixel-count limits to reduce image-processing denial-of-service risks. 4. Keep temporary output in a securely created directory and continue quoting all shell path expansions. 5. Add tests using filenames containing single quotes, double quotes, semicolons, spaces, newlines, parentheses, backticks, dollar signs, and comment markers. The path must remain data and must never alter executed source. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (7)

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill invokes shell scripts and dependency installation but does not declare any explicit tool scope or allowed-tools boundary. In an agent environment, this can cause the skill to run with broader shell access than the operator expects, increasing the risk of unintended command execution, filesystem access, or network actions through the referenced scripts.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill guides users through preparing and submitting a blockchain mint transaction without a clear, prominent warning that the action spends real funds and permanently publishes image/link data on-chain. This increases the chance of accidental financial loss or irreversible disclosure, especially in agent-driven workflows where users may treat the process as routine automation.

External Transmission

Medium
Category
Data Exfiltration
Content
'{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"to":$to,"data":$data},"latest"]}')

    local response
    response=$(curl -s -X POST "$RPC_URL" \
        -H "Content-Type: application/json" \
        -d "$payload")
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"description": "OpenClaw bot skill for minting plots on the Million Bit Homepage",
  "private": true,
  "dependencies": {
    "pako": "^1.0.10",
    "ethers": "^6.13.0",
    "sharp": "^0.33.0"
  }
Confidence
95% confidence
Finding
The dependency uses a caret range (^1.0.10), which allows automatic installation of newer minor/patch releases rather than a single reviewed version. This weakens build reproducibility and can unintentionally pull in a compromised or vulnerable upstream release through the software supply chain.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"private": true,
  "dependencies": {
    "pako": "^1.0.10",
    "ethers": "^6.13.0",
    "sharp": "^0.33.0"
  }
}
Confidence
95% confidence
Finding
The ethers dependency is specified with a caret range (^6.13.0), so installs are not guaranteed to be reproducible across environments or over time. In a blockchain transaction-preparation skill, unexpected dependency changes can affect transaction construction logic or introduce supply-chain risk.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"dependencies": {
    "pako": "^1.0.10",
    "ethers": "^6.13.0",
    "sharp": "^0.33.0"
  }
}
Confidence
98% confidence
Finding
The sharp dependency is unpinned (^0.33.0), which is more concerning because sharp is a native/image-processing package with a larger attack surface and a history of security advisories in underlying components. Allowing version drift can silently introduce vulnerable binaries or behavior changes into image handling for untrusted user-supplied content.

Unverifiable Dependency: sharp has 4 known advisory(ies) (GHSA-54xq-cgqr-rpm3 (sharp vulnerability in libwebp dependency CVE-2023-4863); GHSA-f88m-g3jw-g9cj (sharp inherited vulnerabilities in libvips: CVE-2026-33327, CVE-2026-33328, CVE-); CVE-2022-29256 (sharp vulnerable to Command Injection in post-installation over build environmen) +1 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
87% confidence
Finding
The manifest does not pin sharp, and sharp has known advisories affecting some versions and underlying libraries, so the actual installed package may resolve to an affected release without clear visibility. Because this skill processes images before blockchain-related actions, vulnerable image parsing/native code could increase the risk of denial of service or code-execution paths depending on the resolved version and environment.

Static analysis

No suspicious patterns detected.