T09 · Insecure Skill Coding Practices
Error
- Location
- src/cli.ts:259
- Finding
- Unnecessary Collection and Plaintext Storage of Cryptocurrency Private Keys<![CDATA[ ## Vulnerability Details **File Location**: `src/cli.ts:29-31, 46-50, 225-230, 259-286`; documentation at `SKILL.md:64-67` **Vulnerability Type**: Sensitive credential exposure and violation of least privilege **Risk Level**: High ### Vulnerable Code ```ts interface Config { wallet?: string; privateKey?: string; } ``` ```ts function saveConfig(config: Config): void { ensureDir(); fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2)); fs.chmodSync(CONFIG_FILE, 0o600); } ``` ```ts const config = loadConfig(); if (!config.privateKey) { console.log('❌ No wallet configured. Run:'); console.log(' virtuals config --wallet <address> --private-key <key>'); return; } ``` ```ts program .command('config') .description('Configure wallet for trading') .option('--wallet <address>', 'Wallet address') .option('--private-key <key>', 'Private key (stored securely)') .option('--show', 'Show current config') .action(async (options) => { if (options.show) { const config = loadConfig(); console.log('\n⚙️ Virtuals Configuration'); console.log('═══════════════════════════════════════'); console.log(` Wallet: ${config.wallet || 'Not set'}`); console.log(` Key: ${config.privateKey ? '••••••••' : 'Not set'}`); console.log('═══════════════════════════════════════'); return; } const config = loadConfig(); if (options.wallet) { config.wallet = options.wallet; } if (options.privateKey) { config.privateKey = options.privateKey; } saveConfig(config); console.log('✅ Configuration saved'); }); ``` The documented invocation also directs users to expose the key through a command-line argument: ```bash virtuals config --wallet <address> --private-key <key> ``` ### Technical Analysis The Skill requests a cryptocurrency private key as a command-line argument and persists it as unencrypted JSON in `~/.openclaw/virtuals/config.json`. ...[truncated 3057 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Remove private-key collection immediately** - Delete the `privateKey` configuration property and the `--private-key` option until transaction signing is actually implemented. - Remove the private-key presence check from `create`, because the command currently performs no transaction. - Remove existing documentation that instructs users to place private keys on the command line. 2. **Use external wallet signing** - Prefer a hardware wallet, browser wallet, WalletConnect-compatible provider, or other external signer. - Require the wallet to display and approve each transaction. - Do not give the Skill persistent access to raw private-key material. 3. **Avoid command-line secret arguments** - If local key import is unavoidable, collect it through a hidden interactive prompt or protected standard input. - Never include secrets in argv, logs, exceptions, telemetry, or shell examples. 4. **Use platform credential storage** - Store secrets in an operating-system credential vault rather than a plaintext JSON file. - Examples include macOS Keychain, Windows Credential Manager, or a Linux Secret Service implementation. - Encrypt secrets at rest and narrowly scope access to the required application identity. 5. **Require explicit transaction controls** - Display the chain ID, destination contract, function, token amounts, gas estimate, and maximum financial impact before signing. - Require affirmative user confirmation for every transaction. - Reject chain or contract mismatches. 6. **Correct the network documentation** - Reconcile the “testnet-only” statement with the Base Mainnet RPC and contract configuration. - Use explicit chain IDs and environment-specific contract allowlists. - Prevent mainnet signing when testnet mode is selected. 7. **Support secure cleanup and migration** - Warn existing users that `~/.openclaw/virtuals/config.json` may contain a plaintext private k ...[truncated 210 chars]
