T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/save_config.sh:27
- Finding
- JavaScript Code Injection Through the Wallet Owner Argument<![CDATA[ ## Vulnerability Details **File Location**: `scripts/save_config.sh`, lines 27-34 **Vulnerability Type**: Untrusted input embedded in executable JavaScript **Risk Level**: High ### Vulnerable Code ```bash # Update config with wallet owner using a portable approach TMP_FILE=$(mktemp) node -e " const fs = require('fs'); const cfg = JSON.parse(fs.readFileSync('$CONFIG_DIR/config.json', 'utf8')); cfg.walletOwner = '$WALLET_OWNER'; fs.writeFileSync('$CONFIG_DIR/config.json', JSON.stringify(cfg, null, 2)); " ``` ### Technical Analysis The value supplied through `--wallet-owner` is directly interpolated into JavaScript source passed to `node -e`: ```javascript cfg.walletOwner = '$WALLET_OWNER'; ``` No escaping or syntactic validation occurs before interpolation. An attacker-controlled value containing a single quote followed by JavaScript statements can terminate the intended string literal and inject arbitrary JavaScript. Any public-key validation performed after interpolation would be insufficient because the injected source is parsed and executed by Node.js first. In this script, the value is not validated as a Solana public key at all. The script also creates a temporary file with `mktemp` but never uses or removes it. This is not the primary vulnerability, but the unnecessary operation should be removed. ### Attack Path 1. An attacker influences the value passed to `save_config.sh --wallet-owner`. 2. The crafted value closes the JavaScript string assigned to `cfg.walletOwner`. 3. The value appends arbitrary JavaScript and neutralizes the remaining expected syntax. 4. Bash expands the argument into the source passed to `node -e`. 5. Node.js parses and executes the injected statements with the privileges of the user running the skill. 6. The injected code can read or modify files available to that user, including `.safeflow/agent-keypair.json` and `.safeflow/config.json`. ### Impact Assessment Successful exploitation provides arbitrar ...[truncated 588 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Move the JavaScript implementation into a checked-in `.js` file and pass the wallet owner as a normal process argument: ```bash node scripts/save_config.js --wallet-owner "$WALLET_OWNER" ``` Read the value from `process.argv` rather than constructing JavaScript source code from it. Validate it before updating the configuration: ```javascript const { PublicKey } = require('@solana/web3.js'); const walletOwner = getArgument('--wallet-owner'); const validatedOwner = new PublicKey(walletOwner).toBase58(); cfg.walletOwner = validatedOwner; ``` Additional hardening should include: - Never interpolate user input into `node -e`, `eval`, or similar executable source strings. - Write the updated configuration to a securely created temporary file and atomically rename it, or remove the unused `mktemp` call. - Set restrictive permissions on `.safeflow`, the keypair, and configuration files. - Reject missing, malformed, or unexpectedly long argument values. ]]>
