T09 · Insecure Skill Coding Practices
- Location
src/index.ts:38- Finding
Nostr Private Key Stored in a Plaintext Configuration File Without Restrictive Permissions
- Content
View full analysis
Vulnerability Details
File Location:
src/index.ts:38-55, with the sensitive value populated atsrc/index.ts:129-134
Vulnerability Type: Plaintext storage of sensitive authentication material
Risk Level: HighVulnerable Code
ts const CONFIG_PATH = join(homedir(), ".agent-chat", "config.json"); function loadConfig(): Config { if (existsSync(CONFIG_PATH)) { return JSON.parse(readFileSync(CONFIG_PATH, "utf-8")); } throw new Error("Config not found. Run: agent-chat login <nsec>"); } 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)); }); }The configuration written by this function contains the supplied private key:
ts const config: Config = { npub, nsec, relays: [], }; saveConfig(config);Technical Analysis
The
logincommand places the complete Nostr private key (nsec, or a hexadecimal equivalent) in the configuration object.saveConfigserializes that object directly to~/.agent-chat/config.json.Neither the configuration directory nor the file is created with an explicit restrictive permission mode. Their effective permissions therefore depend on the process umask and any pre-existing filesystem objects. On a permissively configured multi-user system, another local account or process may be able to read the private key.
The implementation also does not inspect or reject a pre-existing symbolic link at the configuration path. Consequently, local filesystem manipulation may redirect the write to another user-writable target when the CLI executes with greater privileges.
Attack Path
- A victim runs
agent-chat login <nsec>. - The CLI serializes the supplied private key into `~/.agent-chat/config.j ...[truncated 1141 chars]
- A victim runs
- Remediation
View remediation
Remediation Suggestions
- Prefer an operating-system credential store or hardware-backed secret provider instead of a plaintext JSON file.
- If file storage is unavoidable, create
~/.agent-chatwith mode0700and the configuration file with mode0600. - Open the destination using flags that prevent following symbolic links where supported, and verify that the destination is a regular file owned by the current user.
- Write through a securely created temporary file in the same protected directory, set its permissions explicitly, and atomically rename it into place.
- Validate and reject insecure permissions on pre-existing directories and files.
- Separate public configuration such as relay URLs from private key material.
- Document key rotation and revocation procedures for users whose configuration file may have been exposed.
- Add automated tests that verify directory permissions, file permissions, symbolic-link handling, and failure behavior.
