T09 · Insecure Skill Coding Practices
Error
- Location
- src/config.ts:65
- Finding
- Wallet Private Key and Pinata JWT Are Persisted Insecurely<![CDATA[ ## Vulnerability Details **File Location**: `src/commands/config.ts:8-18`, `src/config.ts:65-79` **Vulnerability Type**: Plaintext credential storage and insecure command-line secret handling **Risk Level**: High ### Vulnerable Code `src/commands/config.ts:8-18` accepts sensitive credentials directly as command-line arguments: ```ts export function registerConfigCommand(program: Command): void { program .command("config") .description("Save or view CLI configuration") .option("-k, --key <privateKey>", "Set private key") .option("-o, --operator <address>", "Set operator address") .option("-a, --arbiter-url <url>", "Set arbiter server URL") .option("-c, --court-url <url>", "Set court UI URL (for independent verification)") .option("-n, --network <networkId>", "Set network ID (e.g., eip155:84532)") .option("-r, --rpc <url>", "Set RPC URL") .option("--pinata-jwt <jwt>", "Set Pinata JWT token") ``` `src/config.ts:65-79` merges those credentials into the configuration and writes them to disk as plaintext without explicitly restricting permissions: ```ts export function saveConfigFile(config: CliConfigFile): void { if (!fs.existsSync(CONFIG_DIR)) { fs.mkdirSync(CONFIG_DIR, { recursive: true }); } // Merge with existing config const existing = loadConfigFile(); const merged = { ...existing, ...config }; // Remove undefined/null values for (const key of Object.keys(merged)) { if (merged[key as keyof CliConfigFile] === undefined || merged[key as keyof CliConfigFile] === null) { delete merged[key as keyof CliConfigFile]; } } fs.writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2)); } ``` The documented setup command also encourages passing the private key on the command line: ```bash npx --yes @x402r/cli config --key <private-key> --arbiter-url https://www.moltarbiter.com/arbiter ``` ### Technical Analysis The configuration model includes both `privateKey` and `pinataJwt`. T ...[truncated 2560 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Do not accept wallet private keys through ordinary command-line arguments. - Use an interactive hidden prompt when manual entry is unavoidable. - Support input through protected standard input rather than process arguments. - Prefer hardware wallets, external signers, wallet-agent integrations, or operating-system keychains. 2. Avoid long-term private-key persistence where possible. - Retain only a signer reference or keychain identifier. - If environment-variable support remains, document that it is preferable to command-line arguments but may still leak through misconfigured process environments or logs. 3. Store secrets separately from non-sensitive configuration. - Create `~/.x402r` with mode `0700`. - Create secret files with mode `0600`. - Explicitly correct permissions on existing files rather than relying on the process umask. 4. Use secure file replacement: - Write to an owner-only temporary file in the same directory. - Flush and atomically rename it into place. - Prevent symlink-following where supported. 5. Add startup permission checks. - Refuse to load a private key from a group-readable or world-readable file. - Display a clear remediation command without printing any secret. 6. Update `SKILL.md` so examples do not place private keys directly in shell history. ]]>
