T09 · Insecure Skill Coding Practices
- Location
scripts/shared/storage/keys.js:43- Finding
Private Keys Stored in Plaintext Without Enforced Restrictive Permissions
- Content
View full analysis
Vulnerability Details
File Location:
scripts/shared/storage/keys.js:43-59;scripts/shared/storage/base.js:9-12, 26-31
Vulnerability Type: Plaintext sensitive-data storage and unsafe file permissions
Risk Level: HighVulnerable Code
scripts/shared/storage/keys.js:43-59:js _encodeEntry({ alias, privateKeyHex, createdAt }) { const masterKey = getMasterKey(); if (masterKey) { return { version: 1, provider: "encrypted", data: { alias, key: encryptKey(privateKeyHex, masterKey), createdAt }, }; } return { version: 1, provider: "plain", data: { alias, key: privateKeyHex, createdAt }, }; }scripts/shared/storage/base.js:9-12:js async ensureDirectory() { const dir = path.dirname(this.filePath); await fs.mkdir(dir, { recursive: true }); }scripts/shared/storage/base.js:26-31:js 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
The key store silently selects the
plainprovider wheneverBILLIONS_NETWORK_MASTER_KMS_KEYis absent or rejected. This writes the raw identity private key into$HOME/.openclaw/billions/kms.json.The storage implementation does not explicitly create the containing directory with mode
0700or the temporary key file with mode0600. Effective permissions therefore depend on the process umask and existing directory permissions. The temporary file is also sensitive because it contains the complete serialized key store before being renamed.Encryption is optional according to the documented behavior, but plaintext private-key storage is not necessary for the identity functionality and creates a substantial local credential-exposure risk.
...[truncated 1199 chars]
- Remediation
View remediation
Remediation Suggestions
- Make encrypted key storage mandatory instead of silently falling back to plaintext.
- Fail safely with a clear setup error when no valid master key or secure OS keystore is available.
- Create
$HOME/.openclaw/billionswith mode0700. - Create both temporary and final sensitive files with mode
0600, and verify existing permissions before reading or writing. - Open the temporary file using exclusive creation semantics to reduce symlink and replacement risks.
- Prefer an operating-system keychain, hardware-backed keystore, or secret-management service over a JSON key file.
- If plaintext compatibility must remain, require explicit informed opt-in and display a prominent warning before key generation.
- Provide a migration utility that encrypts existing plaintext entries and securely replaces the old file.
