T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/shared/storage/keys.js:47
- Finding
- Private Keys Stored in Plaintext Without Enforced Restrictive Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shared/storage/keys.js:47-60`; `scripts/shared/storage/base.js:9-12,26-31` **Vulnerability Type**: Plaintext storage of cryptographic keys and insufficient filesystem permission enforcement **Risk Level**: High ### Vulnerable Code `scripts/shared/storage/keys.js:47-60`: ```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,26-31`: ```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 The key-storage implementation makes encryption optional. If `BILLIONS_NETWORK_MASTER_KMS_KEY` is absent, whitespace-only, or shorter than the accepted minimum, `_encodeEntry` silently writes the raw private key to `kms.json` using the `plain` provider. The generic storage implementation creates the containing directory and temporary file without specifying secure modes. Consequently, access permissions depend on the process umask. Common umask settings can result in a directory readable or traversable by other local users and a file created with permissions such as `0644`. The temporary file `${this.filePath}.tmp` also contains the complete serialized key material while a write is in progress. It is created without `O_EXCL`, a restrictive mode, or validation that it is a regular file ...[truncated 1116 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Make encrypted key storage mandatory. Fail closed if no valid master key or protected operating-system key store is available. 2. Create `$HOME/.openclaw/billions` with mode `0700`. 3. Create `kms.json` and temporary files with mode `0600`, and explicitly enforce these permissions after replacement. 4. Use a uniquely named temporary file in the same directory, opened with exclusive creation semantics such as `O_CREAT | O_EXCL`. 5. Reject symbolic links and verify that both the destination and temporary path are regular files owned by the current user. 6. Flush the temporary file before atomic replacement where durability is required. 7. Prefer an operating-system credential vault, hardware-backed key store, or dedicated encrypted KMS instead of application-managed plaintext files. 8. Detect legacy plaintext entries and require an explicit, secure migration to encrypted storage. 9. Document key rotation procedures and advise users to rotate keys that may already have been stored with permissive access. ]]>
