T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/wallet-xray.sh:98
- Finding
- User-Controlled Input Injected into Dynamically Generated Node.js Code<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wallet-xray.sh:98-99, 119` **Vulnerability Type**: JavaScript code injection through unsafe source-code interpolation **Risk Level**: High ### Vulnerable Code ```bash echo "$BODY" | node -e " const fs = require('fs'); const raw = fs.readFileSync('/dev/stdin', 'utf8'); let d; try { d = JSON.parse(raw); } catch(e) { console.log('⚠️ Unexpected response'); process.exit(1); } if (d.status === 'error') { console.log('⚠️ Scan error: ' + (d.error?.message || 'Unknown')); process.exit(1); } const r = d.data || d; const addr = '${ADDRESS}'; const ensName = '${ENS_NAME}'; ``` The chain argument is interpolated into the same dynamically generated program: ```bash console.log('Chain: ' + '${CHAIN}'.charAt(0).toUpperCase() + '${CHAIN}'.slice(1)); ``` The affected values originate from command-line arguments: ```bash INPUT="${1:?Usage: wallet-xray.sh <address_or_ens> [chain]}" CHAIN="${2:-ethereum}" ``` ### Technical Analysis The script constructs a JavaScript program for `node -e` and directly inserts the address, ENS name, and chain values into single-quoted JavaScript string literals. These values are not escaped for JavaScript syntax and are not validated against restrictive formats. A value containing a single quote can terminate the intended string literal. Additional JavaScript can then be inserted into the generated program. Because the generated code runs under Node.js, injected code can access powerful built-in modules such as `fs`, `child_process`, `http`, and `https`. Shell quoting around the outer `node -e` invocation does not make this safe. The shell first expands `${ADDRESS}`, `${ENS_NAME}`, and `${CHAIN}`, after which Node.js parses the resulting text as source code. The address validation is particularly insufficient. The script only uses the following condition to distinguish a direct address from a name: ```bash if [[ "$INPUT" == *.eth ]] || [[ "$INPUT" == *.xyz ]] || [[ ! "$IN ...[truncated 1828 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Validate direct EVM addresses before making any request: ```bash if [[ "$INPUT" =~ ^0x[0-9a-fA-F]{40}$ ]]; then ADDRESS="$INPUT" else # Perform explicitly validated ENS resolution. fi ``` 2. Validate the resolved address using the same strict expression before using it. 3. Restrict chains to an explicit allowlist rather than accepting arbitrary strings: ```bash case "$CHAIN_LOWER" in ethereum|base|bsc|polygon|arbitrum|optimism|avalanche|fantom) ;; *) echo "Unsupported chain" >&2 exit 1 ;; esac ``` 4. Never place data inside dynamically generated JavaScript source. Pass values as process arguments or environment variables: ```bash ADDRESS_VALUE="$ADDRESS" ENS_VALUE="$ENS_NAME" CHAIN_VALUE="$CHAIN" \ node -e ' const addr = process.env.ADDRESS_VALUE; const ensName = process.env.ENS_VALUE; const chain = process.env.CHAIN_VALUE; ' ``` 5. Prefer placing the parser in a static `.js` file and invoke it with data arguments. This removes source generation entirely. 6. Add regression tests using values containing quotes, backslashes, newlines, semicolons, URL fragments, and JavaScript expressions. The tests should confirm that malformed inputs are rejected and never evaluated. ]]>
