T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/utils.ts:195
- Finding
- Wallet Mnemonics and LLM API Keys Are Persisted in Plaintext<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils.ts:195-203`; sensitive values originate from `scripts/create_agents.ts:72-99` **Vulnerability Type**: Plaintext storage of wallet recovery secrets and API credentials **Risk Level**: High ### Vulnerable Code `scripts/create_agents.ts:72-99`: ```ts const mnemonic = await generateMnemonic() const newAgentAddressIndex = parseAddressIndexFromPath(DEFAULT_PATH) const addresses = await getAllAddress(mnemonic, { addressIndex: newAgentAddressIndex }) const publicKey = await getPublicKey('mvc', mnemonic, { addressIndex: newAgentAddressIndex }) const pathStr = getPath({ defaultPath: DEFAULT_PATH }) const newAccount: Account = { mnemonic, mvcAddress: addresses.mvcAddress, btcAddress: addresses.btcAddress, dogeAddress: addresses.dogeAddress, publicKey, userName: '', path: pathStr, llm: [ { provider: llmFromEnv.provider, apiKey: llmFromEnv.apiKey, baseUrl: llmFromEnv.baseUrl, model: llmFromEnv.model, temperature: llmFromEnv.temperature, maxTokens: llmFromEnv.maxTokens, }, ], } ``` `scripts/utils.ts:195-203`: ```ts export function writeAccountFile(data: AccountData): void { try { const filtered = data.accountList.filter( (account) => account.mnemonic && account.mnemonic.trim() !== '' ) filtered.forEach(normalizeAccountLLM) const filteredData: AccountData = { accountList: filtered } fs.writeFileSync(ACCOUNT_FILE, JSON.stringify(filteredData, null, 4), 'utf-8') ``` ### Technical Analysis A generated wallet recovery mnemonic and an LLM API key loaded from environment configuration are inserted directly into an account object. The entire object is then serialized as readable JSON in the shared root-level `account.json` file. No encryption, operating-system credential store, secret reference, or explicit restrictive file mode is used. The effective permissions therefore depend on the process umask and any pre-existi ...[truncated 2168 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Do not store recovery mnemonics or raw API keys in `account.json`. 2. Store wallet secrets in an OS keychain, hardware wallet, encrypted vault, or dedicated secret manager. 3. Persist only an opaque secret identifier in account records and retrieve the secret only for the duration of a signing operation. 4. Keep LLM API keys in a secret store or environment configuration rather than copying them into each account. 5. If file-based storage is unavoidable, use authenticated encryption with a user-supplied key that is not stored beside the ciphertext. 6. Create sensitive files with mode `0600`, verify ownership, reject unsafe permissions, and use atomic writes. 7. Separate public account metadata from signing secrets so other Skills can consume addresses and profile data without receiving wallet authority. 8. Add `account.json`, encrypted secret files, backups, and local environment files to ignore rules for version control and packaging. 9. Instruct existing users to rotate exposed API keys and migrate funds to newly generated wallets after secure storage is implemented. ]]>
