Back to skill

Security audit

Agent Chat Nostr

Security checks for vulnerabilities and agentic risk

Overview

AgentChat is a coherent Nostr messaging CLI, but it handles a long-lived private key unsafely enough that users should review it before installing.

Install only if you are comfortable giving this tool a dedicated Nostr key and sending messages through public relays. Do not use a valuable or primary Nostr identity with the current login flow; the key is passed on the command line and then persisted in plaintext under your home directory. Treat relay metadata and small file events as potentially visible to relay operators and recipients, even when message bodies are encrypted.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

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. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/index.ts:282
Finding
Nostr Private Key Accepted as a Command-Line Argument<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:22-24`, `src/index.ts:282-289` **Vulnerability Type**: Secret exposure through process arguments and shell history **Risk Level**: Medium ### Vulnerable Code ```markdown # Login with private key agent-chat login <nsec> ``` ```ts case "login": if (!args[1]) { console.error("Usage: agent-chat login <nsec>"); process.exit(1); } login(args[1]); break; ``` ### Technical Analysis The documented login workflow requires users to provide a long-lived private key directly as a command-line argument. The implementation obtains that value from `process.argv` and passes it to `login`. Command-line secrets may be exposed through: - Shell history files. - Process-listing utilities while the command is running. - Process accounting or monitoring systems. - Terminal logging and session recording. - Automation logs that record complete commands. - Shell completion, debugging, or audit infrastructure. The secret is a Nostr identity key, not a narrowly scoped or readily revocable access token. Exposure therefore enables cryptographic impersonation. ### Attack Path 1. The victim follows the documented syntax and executes `agent-chat login <nsec>`. 2. The shell records the complete command in history, or the operating system exposes its arguments through process inspection or monitoring. 3. An attacker with access to the history file, process metadata, terminal logs, or automation logs extracts the `nsec`. 4. The attacker decodes or imports the private key into a Nostr client. 5. The attacker signs events and sends messages as the victim and may decrypt accessible encrypted messages. ### Impact Assessment The attacker initially requires access to command history, process metadata, or command logs. Once the private key is recovered, the attacker gains control of the corresponding Nostr identity, including the ability to: - Cryptographically impersonate the victim. - Publish unauthorized signed ...[truncated 321 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the private key from the documented command syntax. 2. Read the key through an interactive hidden prompt with terminal echo disabled. 3. Support standard input or a file descriptor for non-interactive environments, while clearly warning users against insecure pipelines and logged environment variables. 4. Prefer integration with an operating-system credential manager so subsequent commands do not require the key. 5. Do not place the key in environment variables, URLs, diagnostic messages, or exception output. 6. Update `SKILL.md` and CLI usage text to document the secure login workflow. 7. Minimize the lifetime of plaintext key material in memory and clear mutable buffers when practical. 8. Add tests confirming that login does not require or accept a private key in `process.argv`. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (18)

Known Vulnerable Dependency: vitest==1.6.1 — 1 advisory(ies): CVE-2026-47429 (When Vitest UI server is listening, arbitrary file can be read and executed)

Critical
Category
Supply Chain
Confidence
90% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: vitest==1.6.1 — 1 advisory(ies): CVE-2026-47429 (When Vitest UI server is listening, arbitrary file can be read and executed)

Critical
Category
Supply Chain
Confidence
98% confidence
Finding
The package includes vitest 1.6.1, which is flagged as affected by a critical advisory allowing arbitrary file read and code execution when the Vitest UI server is listening. Even though vitest is a devDependency, vulnerable developer tooling can still be exploited in local, CI, or shared environments, and this skill context includes executable scripts that may lead users to run the toolchain.

Known Vulnerable Dependency: nanoid==3.3.11 — 3 advisory(ies): CVE-2026-67214 (nanoid: non-secure generators can loop indefinitely with negative size); CVE-2026-67213 (nanoid: custom generators can loop indefinitely when size is zero); CVE-2026-73086 (nanoid: Integer Overflow or Wraparound)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: postcss==8.5.6 — 4 advisory(ies): CVE-2026-45623 (PostCSS: Arbitrary file read and information disclosure via attacker-controlled ); CVE-2026-69153 (PostCSS: incomplete fix of GHSA-6g55-p6wh-862q — attacker-controlled sourceMappi); CVE-2026-41305 (PostCSS has XSS via Unescaped </style> in its CSS Stringify Output) +1 more

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: rollup==4.57.1 — 1 advisory(ies): CVE-2026-27606 (Rollup 4 has Arbitrary File Write via Path Traversal)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: vite==5.4.21 — 3 advisory(ies): CVE-2026-39365 (Vite Vulnerable to Path Traversal in Optimized Deps `.map` Handling); CVE-2026-53571 (vite: `server.fs.deny` bypass on Windows alternate paths); CVE-2026-53632 (launch-editor: NTLMv2 hash disclosure via UNC path handling on Windows)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The documentation promotes sending private messages and small files over public Nostr relays but does not warn users that metadata, relay visibility, retention, and protocol-level limitations may still expose sensitive information. Even if message bodies are encrypted, users may wrongly assume full privacy and transmit secrets or files that should not traverse public infrastructure.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The command example instructs users to pass an nsec private key directly as a CLI argument, which can leak through shell history, process listings, logs, screenshots, and telemetry. This creates a concrete credential exposure risk that could allow full compromise of the user's Nostr identity.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The send operation publishes encrypted direct messages to remote relays, which is a network action involving user data. While sending messages is the tool's purpose, the code provides no explicit disclosure about relay transmission or relay destinations at the point of use; only a success message is shown after transmission.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The login flow stores the user's Nostr private key directly in a JSON config file under the home directory, with no encryption, file-permission hardening, or explicit warning to the user. Because this application is a messaging client, compromise of that key allows full impersonation, message decryption/signing, and persistent account takeover across relays.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
Most of the file is written in English, but line L18 switches to Chinese with no opt-in, translation, or stated locale scope. This can violate language/locale policy expectations when a skill imposes or assumes a language without user choice.

Known Vulnerable Dependency: esbuild==0.27.3 — 1 advisory(ies): GHSA-g7r4-m6w7-qqqr (esbuild allows arbitrary file read when running the development server on Window)

Low
Category
Supply Chain
Confidence
60% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: esbuild==0.21.5 — 1 advisory(ies): GHSA-67mh-4wv8-2f99 (esbuild enables any website to send any requests to the development server and r)

Low
Category
Supply Chain
Confidence
60% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unpinned Dependencies

Low
Category
Supply Chain
Content
],
  "license": "MIT",
  "dependencies": {
    "nostr-tools": "^2.0.0"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"nostr-tools": "^2.0.0"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "tsx": "^4.0.0",
    "typescript": "^5.0.0",
    "vitest": "^1.0.0"
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "devDependencies": {
    "@types/node": "^20.0.0",
    "tsx": "^4.0.0",
    "typescript": "^5.0.0",
    "vitest": "^1.0.0"
  }
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"devDependencies": {
    "@types/node": "^20.0.0",
    "tsx": "^4.0.0",
    "typescript": "^5.0.0",
    "vitest": "^1.0.0"
  }
}
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"@types/node": "^20.0.0",
    "tsx": "^4.0.0",
    "typescript": "^5.0.0",
    "vitest": "^1.0.0"
  }
}
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Static analysis

No suspicious patterns detected.