T09 · Insecure Skill Coding Practices
Error
- Location
- src/index.ts:49
- Finding
- Plaintext Nostr Private Key Stored Without Enforced Restrictive Permissions<![CDATA[ ## Vulnerability Details **File Location**: `src/index.ts:49-53`, `src/index.ts:149-153` **Vulnerability Type**: Plaintext sensitive-data storage **Risk Level**: High ### Vulnerable Code ```ts function saveConfig(config: Config): void { const dir = join(homedir(), ".agent-chat"); import("fs").then(fs => { if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2)); }); } ``` ```ts const config: Config = { npub, nsec, relays: [], }; saveConfig(config); ``` ### Technical Analysis The `login` operation places the complete Nostr private key (`nsec` or hexadecimal equivalent) into the configuration object. `saveConfig` then serializes that object directly to `~/.agent-chat/config.json` in plaintext. Neither `mkdirSync` nor `writeFileSync` specifies an access mode. Consequently, effective permissions depend on the user's process umask and any permissions already present on the directory or file. Under permissive or commonly used configurations, another local account or process may be able to read the private key. Existing insecure permissions are also not corrected when the file is overwritten. Encryption of direct messages does not mitigate this issue because the exposed key is the cryptographic identity used to sign events and decrypt messages. ### Attack Path 1. A user runs the documented `agent-chat login` command with a valid private key. 2. The application stores that private key inside the `nsec` property of the configuration object. 3. `saveConfig` writes the object in plaintext to `~/.agent-chat/config.json`. 4. A malicious local user, compromised process, backup collector, or other software with access to the user's home directory reads the configuration file. 5. The attacker imports the recovered key into another Nostr client. 6. The attacker signs events as the victim and decrypts NIP-04 messages available to the attacker. ### Impact Assessment Com ...[truncated 682 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Prefer an operating-system credential manager, hardware-backed keystore, or dedicated secret-storage service instead of a JSON configuration file. 2. Store only non-sensitive configuration such as the public key and relay list in `config.json`. 3. If file-based key storage is unavoidable: - Create `~/.agent-chat` with mode `0700`. - Create the key file atomically with mode `0600`. - Reject symbolic links and validate that the destination is a regular file owned by the current user. - Check and correct permissions on every load and save. - Avoid temporary plaintext copies. 4. Consider encrypting the private key at rest with a user-supplied passphrase and a modern password-based key derivation function. 5. Add a logout or key-removal command that securely removes stored credentials where supported. 6. Warn users that this is a long-lived identity key and recommend using a dedicated, low-value key for the application. 7. Add automated tests that verify directory and file permissions and ensure the private key is absent from ordinary configuration output. ]]>
