T09 · Insecure Skill Coding Practices
- Location
index.js:42- Finding
Plaintext Storage of Nostr Private Keys with Unrestricted Default Permissions
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This Nostr skill is mostly purpose-aligned, but it needs review because it stores and exports account-controlling private keys in plaintext and falls back to a shared default identity when user context is missing.
Review carefully before installing. Use only throwaway or low-value Nostr identities unless the skill is updated to encrypt keys or use a keychain, require authenticated user context, add confirmations for key export and network-visible actions, verify relay events, and refresh the dependency lockfile. Treat anything posted or followed through this skill as relay-distributed public activity, and treat DMs as encrypted content with observable metadata.
index.js:42Plaintext Storage of Nostr Private Keys with Unrestricted Default Permissions
index.js:542Missing Authentication Context Falls Back to a Shared Privileged Identity
index.js:253Unverified Events from Nostr Relays Are Treated as Trusted Data
package-lock.json:19Cryptographic Dependency Tree Is Locked to a Third-Party npm Registry Mirror
The skill description presents normal Nostr assistant features, but the file also indicates that users can export private keys and that private keys are stored locally in data/identities.json. Handling and exporting long-term private keys is highly sensitive; if users are not clearly warned up front, they may invoke the skill without understanding that account-compromising secret material will be stored and can be revealed.
Private keys are stored in a local JSON file in plaintext, which is a sensitive-secret storage weakness. Any local compromise, backup leakage, container escape, shared-host access, or accidental file exposure would immediately disclose all user identities and enable account takeover and DM decryption.
The skill exposes a direct private-key export capability even though the described skill purpose does not clearly establish key backup/export as a required operation. In this context, the private key is the user's full Nostr identity; any caller able to invoke this action can permanently impersonate the user, decrypt future/archived DMs available to that key, and take over the account.
Although the DM content is encrypted, sending private messages to third-party relays still exposes metadata such as sender, recipient, timing, and event IDs, and places sensitive communication onto external infrastructure. Without explicit warning/consent in the action path, users may wrongly assume stronger privacy guarantees than Nostr relays actually provide.
The lockfile pins ws to 8.19.0, and the supplied finding identifies known advisories for that exact version involving memory disclosure and memory-exhaustion denial of service. In this skill, WebSocket handling is central to Nostr relay communication, so an exposed vulnerable ws client/library increases risk because the skill processes untrusted network traffic from external relays.
The skill depends on ws 8.19.0, which is flagged with advisories for uninitialized memory disclosure and memory-exhaustion denial of service. In the context of a Nostr client/agent that communicates over WebSockets and processes untrusted remote data, these issues are more dangerous because an attacker-controlled relay or peer could potentially trigger information leakage or exhaust process memory.
The README advertises generation and management of Nostr keys but provides no warning that these are highly sensitive credentials whose disclosure would allow account takeover and impersonation. In an agent skill context, users may ask the assistant to create or handle keys conversationally, increasing the chance that private keys are exposed in logs, chat history, screenshots, or insecure storage.
The README describes sending content to the Nostr network without warning that posts may be public, replicated across relays, and difficult or impossible to retract. In this skill's context, users may treat the agent like a private assistant and unknowingly instruct it to publish sensitive, personal, or regulated information to a public network.
The skill performs sensitive operations around Nostr identity management and private-key handling, yet the manifest does not declare an explicit tool scope or permissions boundary. Missing scope makes it harder for users and the host to understand what resources the skill may access, increasing the risk of overbroad execution or unintended access to environment-backed secrets.
The skill advertises posting and private messaging features but does not clearly warn in the main description that content will be transmitted to external relays. In this context, omission is more dangerous because Nostr posts and relay-delivered messages can be widely replicated or metadata-exposed, and users may not realize they are sending data off-device to third-party infrastructure.
The trigger examples include broad everyday phrases like '有什么新消息', which can overlap with ordinary conversation and unintentionally activate the skill. Because this skill can post content, manage identities, and send direct messages, accidental invocation could lead to unintended external actions or disclosure of social-graph and message data.
User-facing strings throughout the skill are consistently in Chinese, and the header brands the tool in Chinese, but there is no opt-in language selection or explanation that the skill is intended only for a Chinese-language audience. This can violate a language/locale policy requiring user choice or explicit locale justification.
This function publishes user-provided content to multiple external WebSocket relays, which is a network transmission of user data. The code does not show a confirmation prompt, visible log/print, or warning in this path that the content will be sent to third-party relays.
Following a user creates and publishes a social graph event to external relays. There is no prompt, visible logging, or explanatory warning in this action path informing the user that the follow event will be broadcast publicly to third-party servers.
The manifest says the skill can read a timeline, which normally implies the user's Nostr timeline or feed. The implementation fetches recent kind-1 events from relays without using the user's follow list, so it behaves like a global public feed reader rather than a user-specific timeline.
The relay fetch helper opens WebSocket connections to external Nostr relays and sends query filters, which is network activity involving user-driven lookups. There is no visible prompt, log, or explanatory comment indicating to the user that external relay access occurs for reads as well as writes.
The inline documentation describes '发送私信并等待回复(模拟对话)', implying conversational reply handling. In reality, the function just delegates to the send operation and returns immediately with a note to use another function to view replies, which contradicts the stated intent of waiting for a response.
The dependency uses a caret range, which permits automatic installation of newer minor/patch releases instead of a fully fixed version. This weakens supply-chain reproducibility and can unexpectedly introduce vulnerable or malicious upstream changes, though by itself it is a lower-severity hygiene issue.
"author": "",
"license": "ISC",
"dependencies": {
"https-proxy-agent": "^7.0.6",
"nostr-tools": "^2.23.1",
"ws": "^8.19.0"
}
The nostr-tools dependency is specified with a caret range, allowing non-identical builds over time as upstream minor/patch releases are pulled in. For a skill that manages identities, timelines, and private messaging, this increases supply-chain risk because cryptographic or protocol-handling behavior could change without review.
"license": "ISC",
"dependencies": {
"https-proxy-agent": "^7.0.6",
"nostr-tools": "^2.23.1",
"ws": "^8.19.0"
}
}
The ws package is not only unpinned via a caret range but is also the WebSocket library likely exposed to untrusted network traffic in this Nostr skill. Unpinned network-facing dependencies raise supply-chain risk and make it easier for insecure or unexpected upstream changes to reach production unnoticed.
"dependencies": {
"https-proxy-agent": "^7.0.6",
"nostr-tools": "^2.23.1",
"ws": "^8.19.0"
}
}
The manifest description is written entirely in Chinese and presents the skill as a general-purpose "smart assistant" for Nostr, with no indication that the skill is China-specific or that users can choose another language. This can conflict with language/locale policy expectations when a skill is not explicitly scoped to a Chinese-speaking audience.
No suspicious patterns detected.