T09 · Insecure Skill Coding Practices
- Location
send.js:6- Finding
Long-Lived Nostr Private Key Stored in a Plaintext File Without Explicit Access Restrictions
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill appears to be a real Nostr messaging tool, but it stores a long-lived private identity key in plaintext and some security properties are under-disclosed.
Install only if you are comfortable with a Nostr identity key being created under your home directory and reused across runs. Do not use the public chat path for secrets, review or restrict permissions on ~/.clawdzap_keys.json, consider a dedicated throwaway identity, and treat relay traffic and metadata as visible to relay operators and public subscribers.
send.js:6Long-Lived Nostr Private Key Stored in a Plaintext File Without Explicit Access Restrictions
receive.js:39Relay Events Are Consumed Without Cryptographic Signature or Structural Verification
package.json:6Unpinned Dependencies and Missing Lockfile Make Installations Non-Reproducible
Persisting secret identity key material locally while also connecting to an external public relay increases the attack surface, especially when the capability is marketed as secure P2P messaging. Users may not realize that long-lived private keys are stored on disk and that messages are sent through a third-party relay, creating confidentiality and key-management risks.
Persisting secret identity key material locally while also connecting to an external public relay increases the attack surface, especially when the capability is marketed as secure P2P messaging. Users may not realize that long-lived private keys are stored on disk and that messages are sent through a third-party relay, creating confidentiality and key-management risks.
Persisting secret identity key material locally while also connecting to an external public relay increases the attack surface, especially when the capability is marketed as secure P2P messaging. Users may not realize that long-lived private keys are stored on disk and that messages are sent through a third-party relay, creating confidentiality and key-management risks.
Persisting secret identity key material locally while also connecting to an external public relay increases the attack surface, especially when the capability is marketed as secure P2P messaging. Users may not realize that long-lived private keys are stored on disk and that messages are sent through a third-party relay, creating confidentiality and key-management risks.
The skill advertises encrypted P2P messaging, but it actually subscribes to and publishes Nostr kind-1 public notes. This creates a confidentiality failure: operators may believe messages are private when they are broadcast to a public relay and readable by anyone, which is especially dangerous in an agent-to-agent messaging context where sensitive prompts, identifiers, or coordination data may be exposed.
The skill is described as encrypted P2P messaging, but this receiver subscribes to public kind-1 events tagged with a global hashtag and prints their plaintext content directly. That creates a serious security mismatch: users or downstream agents may assume confidentiality or peer scoping when messages are actually publicly broadcast and readable by anyone on the relay.
The skill metadata claims encrypted P2P messaging, but the implementation constructs a standard Nostr kind:1 event and publishes the raw message content to a public relay. This creates a serious trust mismatch: users or agents may send sensitive information believing it is encrypted when it is actually publicly readable and attributable to the sender key.
The skill advertises and instructs use of code that accesses environment-derived capabilities and external resources, but the manifest declares no permissions or allowed-tools scope. Missing scope declarations weaken review and sandbox expectations, making it easier for a skill to perform network or key-related actions without explicit operator awareness.
The documentation encourages network messaging and local key storage without warning users about privacy, persistence, or exposure risks. In a messaging skill, lack of disclosure is dangerous because operators may unintentionally expose message contents, metadata, or private key material while assuming safe defaults.
The inline comment claims the code is subscribing to DMs, but the filter uses kinds: [1], which is public chat, not direct messaging. This misleading documentation increases the chance that developers deploy or extend the skill under a false privacy assumption, leading to accidental disclosure of message contents and unsafe downstream integrations.
The code automatically signs and publishes a network event immediately upon connection, without any user confirmation or clear warning. In an agent skill, this can cause unintended outbound communication, metadata leakage, spam-like behavior, or unreviewed publication under the agent's identity, especially if the behavior is triggered simply by loading or testing the skill.
A passive listener for public messages does not need the stored secret key, yet this code accesses it anyway. In an agent skill context, unnecessary secret material in memory is especially risky because agent integrations, plugins, logs, and future modifications can accidentally expose or misuse that key, turning a read-only component into a credential-bearing target.
The code comment states only the public key is needed, yet the receiver reads the secret key from disk and derives the public key from it. Unnecessary secret access expands the exposure surface: any compromise, logging bug, crash dump, dependency issue, or later code change in this passive listener now has access to material that should not have been loaded at all.
The code's identity-management section silently creates or loads a long-term secret key from a predictable file in the user's home directory, but the description does not make this persistence explicit. In an agent skill context, hidden long-lived credential storage increases the risk of unintended identity reuse, correlation, and secret exposure through backups, logs, or local compromise.
The secret key is written to disk automatically without warning, consent, or any visible permission hardening. In a skill advertised around messaging, this is risky because the key represents the sender identity and can be stolen or reused by other local processes, enabling impersonation and deanonymization.
The script generates a long-lived private key and writes it to a predictable location in the user's home directory without warning, consent, or any permission hardening. In an agent setting, this creates persistent credential material on disk that may be readable by other local processes, included in backups, or exfiltrated later, enabling impersonation of the user's Nostr identity and decryption/signing misuse.
The dependency version for nostr-tools is specified with a caret range, which allows newer compatible releases to be installed automatically. This can introduce supply-chain risk because future upstream releases may contain malicious code, regressions, or newly introduced vulnerabilities, and this skill handles decentralized messaging where dependency trust is security-relevant.
"description": "Decentralized P2P Messaging for Agents",
"main": "send.js",
"dependencies": {
"nostr-tools": "^2.1.0",
"websocket": "^1.0.34"
}
}
The websocket dependency is also declared with a caret range, permitting automatic installation of later patch/minor versions. Because this package provides network-facing functionality for P2P messaging, an unintended dependency update could introduce vulnerable or malicious code into a component directly exposed to untrusted network input.
"main": "send.js",
"dependencies": {
"nostr-tools": "^2.1.0",
"websocket": "^1.0.34"
}
}
No suspicious patterns detected.