T09 · Insecure Skill Coding Practices
- Location
scripts/guardian-scan.sh:109- Finding
Arbitrary JavaScript Execution Through Unvalidated Command-Line Arguments
- Content
View full analysis
[chain]}" CHAIN="${2:-base}" CHAIN_LOWER=$(echo "$CHAIN" | tr '[:upper:]' '[:lower:]') ADDR_LOWER=$(echo "$TOKEN_ADDRESS" | tr '[:upper:]' '[:lower:]') API_BASE="${SUPAH_API_BASE:-https://api.supah.ai}" ``` The command-line values are subsequently embedded directly into the source passed to `node -e`: ```javascript console.log('Token: ' + name + ' (\$' + symbol + ')'); console.log('Chain: ' + '${CHAIN}'.charAt(0).toUpperCase() + '${CHAIN}'.slice(1)); console.log('Address: ' + '${TOKEN_ADDRESS}'.substring(0,6) + '...' + '${TOKEN_ADDRESS}'.substring(38)); ``` ### Technical Analysis `CHAIN` and `TOKEN_ADDRESS` are taken directly from positional command-line arguments. They are not validated as a supported chain identifier or a 40-byte hexadecimal EVM address. The script constructs a JavaScript program inside a shell double-quoted string passed to `node -e`. The values are substituted directly into JavaScript single-quoted string literals. Shell quoting does not provide JavaScript escaping. A value containing a single quote followed by valid JavaScript can terminate the intended string and introduce additional statements. Injected JavaScript executes in the Node.js process with the same operating-system privileges and environment as the Skill. Node.js exposes powerful modules such as `child_process`, `fs`, `net`, and `http`, so successful JavaScript injection can be converted into arbitrary local command execution, file access, or additional network communication. Execution of the injected section occurs after the remote request and initial response parsing. Exploitation therefore requires the request path to reach the vulnerable Node.js statements, such as when the configured endpoint returns a u ...[truncated 1728 chars]- Remediation
View remediation
&2 exit 2 fi ``` 2. Normalize and validate the chain against an explicit allowlist derived from the supported-chain documentation: ```bash case "$CHAIN_LOWER" in eth|ethereum|bsc|bnb|polygon|matic|arb|arbitrum|op|optimism|base|avax|avalanche|ftm|fantom|cro|cronos|xdai|gnosis|celo|moonbeam|one|harmony|zksync|era|linea|scroll|mantle|blast|mode|manta) ;; *) echo "Unsupported chain" >&2 exit 2 ;; esac ``` 3. Never interpolate input into executable JavaScript source. Pass data through environment variables or positional arguments instead: ```bash CHAIN_VALUE="$CHAIN" TOKEN_ADDRESS_VALUE="$TOKEN_ADDRESS" node <<'NODE' const chain = process.env.CHAIN_VALUE; const tokenAddress = process.env.TOKEN_ADDRESS_VALUE; console.log( 'Chain: ' + chain.charAt(0).toUpperCase() + chain.slice(1) ); console.log( 'Address: ' + tokenAddress.substring(0, 6) + '...' + tokenAddress.substring(38) ); NODE ``` 4. URL-encode path and query components using a structured URL builder rather than concatenating untrusted values into a URL. 5. Reject all unexpected HTTP status codes before invoking the response parser. Only process explicitly supported success responses. 6. If `SUPAH_API_BASE` remains configurable, require HTTPS and validate the hostname against an approved destination list unless arbitrary endpoints are an intentional administrative feature. ]]>
