T09 · Insecure Skill Coding Practices
- Location
scripts/shared/storage/base.js:9- Finding
Plaintext Private Keys Are Stored Without Enforced Filesystem Permissions
- Content
View full analysis
entry.alias === args.alias); if (index >= 0) { keys[index].privateKeyHex = args.key; } else { keys.push({ alias: args.alias, privateKeyHex: args.key }); } await this.writeFile(keys); } ``` `scripts/shared/storage/base.js:9-12, 27-33`: ```js async ensureDirectory() { const dir = path.dirname(this.filePath); await fs.mkdir(dir, { recursive: true }); } async writeFile(data) { await this.ensureDirectory(); const json = JSON.stringify(data, null, 2); const tempPath = `${this.filePath}.tmp`; await fs.writeFile(tempPath, json, "utf-8"); await fs.rename(tempPath, this.filePath); } ``` ### Technical Analysis `KeysFileStorage.importKey()` stores raw private keys in the `privateKeyHex` field and passes them directly to the generic JSON storage implementation. The resulting `$HOME/.openclaw/billions/kms.json` file is unencrypted. The storage implementation does not explicitly set mode `0700` on the containing directory or mode `0600` on the temporary and final files. Access therefore depends entirely on the process's ambient `umask` and pre-existing directory permissions. Under a permissive configuration, the temporary file or final key store may be readable by other local users or processes. The temporary-file approach also uses a predictable `.tmp` path and does not use exclusive creation. Although the subsequent rename reduces partial-write risks, it does not establish confidentiality or protect against unsafe pre-existing filesystem objects. This implementation conflicts with the README statement that private keys are “ ...[truncated 1361 chars]- Remediation
View remediation
