Back to skill

Security audit

Project Desapetc

Security checks for vulnerabilities and agentic risk

Overview

This identity skill appears purpose-aligned, but it needs review because it creates durable identity secrets and can store private keys in plaintext unless encryption is configured.

Review before installing. Use only on a trusted host, set BILLIONS_NETWORK_MASTER_KMS_KEY before creating or importing any identity, avoid passing real private keys with --key, and update vulnerable dependencies before relying on the skill for authentication-sensitive workflows. If it was already run without KMS encryption, treat stored keys as potentially exposed depending on host permissions and backups.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/shared/storage/keys.js:47
Finding
Private keys are stored in plaintext by default without enforced file permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/shared/storage/keys.js:47-63`; `scripts/shared/storage/base.js:9-12, 27-31` **Duplicated Location**: `skills/agent-desapetc-123/scripts/shared/storage/keys.js:47-63`; `skills/agent-desapetc-123/scripts/shared/storage/base.js:9-12, 27-31` **Vulnerability Type**: Plaintext sensitive-data storage and unsafe file permissions **Risk Level**: High ### Vulnerable Code ```js _encodeEntry({ alias, privateKeyHex, createdAt }) { const masterKey = getMasterKey(); if (masterKey) { return { version: 1, provider: "encrypted", data: { alias, key: encryptKey(privateKeyHex, masterKey), createdAt }, }; } return { version: 1, provider: "plain", data: { alias, key: privateKeyHex, createdAt }, }; } ``` The generic storage implementation writes the resulting data without explicit restrictive permissions: ```js async ensureDirectory() { const dir = path.dirname(this.filePath); await fs.mkdir(dir, { recursive: true }); } async writeFile(data) { await this.ensureDirectory(); const json = JSON.stringify(data, null, 2); const tempPath = `${this.filePath}.tmp`; await fs.writeFile(tempPath, json, "utf-8"); await fs.rename(tempPath, this.filePath); } ``` ### Technical Analysis When `BILLIONS_NETWORK_MASTER_KMS_KEY` is absent or rejected, `_encodeEntry` deliberately stores the raw private key in `kms.json`. Encryption is therefore optional rather than a secure default. The storage layer also creates the directory and temporary file without specifying modes such as `0700` for the directory and `0600` for files. The effective permissions consequently depend on the process umask and pre-existing directory permissions. The temporary file may contain the complete plaintext key before it is renamed. AES-256-GCM is used when a master key is configured, but that does not mitigate deployments that omit the optional environment variable. The documentation explicitly ack ...[truncated 1420 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the silent plaintext fallback. Refuse to create or import an identity unless a secure key-encryption mechanism is configured. 2. Prefer an operating-system keychain, hardware-backed keystore, TPM, HSM, or managed secret store instead of a password-derived file-encryption key. 3. Create `$HOME/.openclaw/billions` with mode `0700` and verify its ownership before reading or writing sensitive data. 4. Create key files and temporary files with mode `0600`. Use exclusive creation flags to avoid writing through attacker-prepared files. 5. Reject symbolic links and verify the owner and mode of existing storage files. 6. If a temporary file remains necessary, use a securely generated unique name in the same protected directory, flush it before replacement, and remove it on failure. 7. Provide a migration utility that encrypts existing plaintext entries and securely removes old plaintext artifacts. 8. Warn users that keys previously stored in plaintext must be considered exposed if local permissions or backups were not adequately protected. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/verifySignature.js:48
Finding
Authentication challenges remain reusable after successful verification<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generateChallenge.js:17-23`; `scripts/shared/storage/challenge.js:8-23, 30-33`; `scripts/verifySignature.js:19-24, 48-57` **Duplicated Location**: Equivalent code exists under `skills/agent-desapetc-123/scripts/` **Vulnerability Type**: Replayable authentication proof caused by missing expiration and consumption **Risk Level**: Medium ### Vulnerable Code Challenge generation and persistence: ```js // Generate random challenge const challenge = randomInt(0, 10000000000).toString(); // Save challenge to storage await challengeStorage.save(args.did, challenge); outputSuccess(challenge); ``` Challenge storage records a creation time but does not enforce an expiration: ```js async save(did, challenge) { const entries = await this.readFile(); const created_at = new Date(); const index = entries.findIndex((entry) => entry.did === did); if (index >= 0) { // Update existing entry entries[index] = { did, challenge, created_at }; } else { // Add new entry entries.push({ did, challenge, created_at }); } await this.writeFile(entries); } async getChallenge(did) { const entry = await this.find(did); return entry?.challenge; } ``` Verification checks equality but does not delete or otherwise consume the challenge: ```js // Verify the challenge matches const payload = basicMessage.body; if (payload.message !== challenge) { console.error( `Error: Invalid signature: challenge mismatch ${payload.message} !== ${challenge}`, ); process.exit(1); } outputSuccess("Signature verified successfully"); ``` ### Technical Analysis A secure challenge-response flow requires the challenge to be unpredictable, short-lived, context-bound, and single-use. This implementation stores one challenge per DID and accepts a signed token whenever its payload matches the currently stored value. Although a `created_at` value is written, `verifySignature.js` retrieves only the challeng ...[truncated 1779 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Atomically consume the challenge after successful verification so two concurrent requests cannot both succeed. 2. Enforce a short expiration period using the stored creation timestamp and reject expired entries. 3. Generate challenges with at least 128 bits of cryptographically secure randomness, such as `crypto.randomBytes(32)`. 4. Bind each challenge to the intended verifier, operation, audience, session, and protocol purpose. 5. Include and validate issuance and expiration times in the signed message where supported. 6. Delete expired and consumed challenge records promptly. 7. Avoid logging complete tokens or challenge values. 8. Add tests covering replay attempts, expiration, concurrent verification, malformed timestamps, and challenge replacement. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/createNewEthereumIdentity.js:23
Finding
Existing private keys are accepted through command-line arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/createNewEthereumIdentity.js:23-31` **Documentation Location**: `SKILL.md:37-48` **Duplicated Location**: `skills/agent-desapetc-123/scripts/createNewEthereumIdentity.js:23-31`; `skills/agent-desapetc-123/SKILL.md:37-48` **Vulnerability Type**: Sensitive information exposure through process arguments and shell history **Risk Level**: Medium ### Vulnerable Code ```js // Use provided key or generate a new one let privateKeyHex = args.key; if (!privateKeyHex) { privateKeyHex = new SigningKey(Wallet.createRandom().privateKey) .privateKey; } // Create signer from private key const signer = new SigningKey(addHexPrefix(privateKeyHex)); ``` The documented interface encourages placing the private key directly on the command line: ```bash # Create identity from existing private key (with 0x prefix) node scripts/createNewEthereumIdentity.js --key 0x1234567890abcdef... # Create identity from existing private key (without 0x prefix) node scripts/createNewEthereumIdentity.js --key 1234567890abcdef... ``` ### Technical Analysis Command-line arguments are not an appropriate transport for long-lived secrets. Depending on the operating system and deployment environment, arguments can be exposed through shell history, process inspection interfaces, audit systems, terminal recording, orchestration metadata, crash reports, and monitoring agents. The affected value is an Ethereum private key rather than a short-lived access token. Disclosure therefore causes persistent compromise unless the identity and any associated assets or authorization relationships are migrated. This behavior is part of the documented public interface, making accidental exposure likely when users follow the supplied examples. ### Attack Path 1. A user follows the documented example and invokes the script with `--key` followed by an existing private key. 2. The shell records the full command in its history, or the runtime expose ...[truncated 834 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `--key` command-line option for secret material. 2. Accept imported keys through hidden interactive input, a protected file descriptor, standard input with explicit safeguards, an operating-system secret store, or a hardware wallet. 3. If file-based import is unavoidable, require a user-owned file with mode `0600`, reject symbolic links, validate ownership, and securely remove the import artifact when appropriate. 4. Update all documentation and examples so they never place a real private key in a command line. 5. Emit a clear error if deprecated `--key` usage is detected rather than processing the value. 6. Advise existing users to remove affected shell-history entries and rotate any key that may have been captured by process or audit telemetry. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (61)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
This is a substantively similar issue: the documented purpose understates that the skill persists sensitive key material to the filesystem and may store it unencrypted unless a master key is configured. In an agent ecosystem, that behavioral gap can lead operators to authorize the skill without understanding that it creates credential-storage risk on the host.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
This is a substantively similar issue: the documented purpose understates that the skill persists sensitive key material to the filesystem and may store it unencrypted unless a master key is configured. In an agent ecosystem, that behavioral gap can lead operators to authorize the skill without understanding that it creates credential-storage risk on the host.

Known Vulnerable Dependency: ws==8.18.0 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
97% confidence
Finding
ws 8.18.0 is reported with memory disclosure and memory-exhaustion DoS advisories, both of which are serious in software that accepts WebSocket traffic from untrusted peers. In an agent identity skill that may communicate over networked protocols and process external data, retaining a vulnerable WebSocket stack increases exposure if any reachable component uses this package.

Known Vulnerable Dependency: brace-expansion==2.0.2 — 4 advisory(ies): CVE-2026-13149 (brace-expansion: DoS via exponential-time expansion of consecutive non-expanding); CVE-2026-33750 (brace-expansion: Zero-step sequence causes process hang and memory exhaustion); CVE-2026-14257 (brace-expansion: DoS via unbounded expansion length causing an out-of-memory pro) +1 more

High
Category
Supply Chain
Confidence
88% confidence
Finding
brace-expansion 2.0.2 has multiple DoS-class issues involving pathological expansion patterns that can hang or exhaust memory. This is a genuine vulnerable dependency, though reachability is uncertain here because exploitability usually requires attacker control over glob-like pattern strings.

Known Vulnerable Dependency: fast-uri==3.1.0 — 7 advisory(ies): CVE-2026-13676 (fast-uri vulnerable to host confusion via failed IDN canonicalization); CVE-2026-18446 (fast-uri vulnerable to host confusion via backslash authority introducer); CVE-2026-75975 (fast-uri vulnerable to server-side request forgery via malformed IPv6 normalizat) +4 more

High
Category
Supply Chain
Confidence
95% confidence
Finding
fast-uri 3.1.0 is flagged for multiple URI parsing issues including host confusion and SSRF-relevant normalization flaws. In an identity/verification stack that may fetch schemas, DID documents, registries, or remote metadata, incorrect URL parsing can materially increase risk by bypassing allowlists or sending requests to unintended hosts.

Known Vulnerable Dependency: ws==7.5.10 — 1 advisory(ies): CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
96% confidence
Finding
ws 7.5.10 is vulnerable to memory-exhaustion DoS from fragmented data handling. Any reachable WebSocket server or client processing attacker-controlled traffic can be forced into excessive resource consumption, which is especially problematic for agent services expected to remain available.

Known Vulnerable Dependency: underscore==1.13.6 — 1 advisory(ies): CVE-2026-27601 (Underscore has unlimited recursion in _.flatten and _.isEqual, potential for DoS)

High
Category
Supply Chain
Confidence
92% confidence
Finding
underscore 1.13.6 is reported to allow uncontrolled recursion in functions like _.flatten and _.isEqual, enabling DoS with crafted nested structures. If any JSON-RPC or utility code in this stack applies such helpers to attacker-provided data, the process can be crashed or stalled.

Known Vulnerable Dependency: undici==5.29.0 — 12 advisory(ies): CVE-2026-1525 (Undici has an HTTP Request/Response Smuggling issue); CVE-2026-6733 (undici vulnerable to HTTP response queue poisoning via keep-alive socket reuse); CVE-2026-1527 (Undici has CRLF Injection in undici via `upgrade` option) +9 more

High
Category
Supply Chain
Confidence
97% confidence
Finding
undici 5.29.0 is flagged for multiple serious HTTP issues including smuggling, queue poisoning, and CRLF-related problems. Because this skill's ecosystem likely performs outbound HTTP requests for DID resolution, schema retrieval, attestations, or verification services, a vulnerable HTTP client materially increases the attack surface.

Known Vulnerable Dependency: ws==8.17.1 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
97% confidence
Finding
ws 8.17.1 is affected by both memory disclosure and memory exhaustion issues, making it a significant network-facing dependency problem. In an identity/authentication context, service availability and confidentiality matter, so vulnerable WebSocket handling is more concerning than in purely local tooling.

Credential Access

High
Category
Privilege Escalation
Content
function newDataStorage(ethStateStorage) {
  return {
    credential: new CredentialStorage(
      new IdentitiesFileStorage("credentials.json"),
    ),
    identity: new IdentityStorage(
      new IdentitiesFileStorage("identities.json"),
Confidence
86% confidence
Finding
Persisting `credentials.json` creates a local cache of credential material in an authentication/identity skill, which is especially sensitive in this context because credentials may include attestations, identifiers, and proof-related metadata. Unauthorized access to this file can expose private identity data and may facilitate impersonation, targeted phishing, or abuse of downstream verification workflows.

Missing User Warnings

High
Confidence
98% confidence
Finding
When no master key is configured, _encodeEntry() silently stores private keys in plaintext on disk under provider: "plain". This creates immediate secret-at-rest exposure to local users, backups, logs, or any filesystem compromise, and is particularly unsafe for a skill handling decentralized identity authentication keys.

Credential Access

High
Category
Privilege Escalation
Content
| `identities.json`  | Identity metadata                                                                  |
| `defaultDid.json`  | Active DID and associated public key                                               |
| `challenges.json`  | Per-DID challenge history                                                          |
| `credentials.json` | Verifiable credentials                                                             |

There are several ways of storing private keys, to enable master key encryption as described in the **KMS Encryption** section below.
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
| `identities.json`  | Identity metadata                                                                  |
| `defaultDid.json`  | Active DID and associated public key                                               |
| `challenges.json`  | Per-DID challenge history                                                          |
| `credentials.json` | Verifiable credentials                                                             |

There are several ways of storing private keys, to enable master key encryption as described in the **KMS Encryption** section below.
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
| `identities.json`  | Identity metadata                                                                  |
| `defaultDid.json`  | Active DID and associated public key                                               |
| `challenges.json`  | Per-DID challenge history                                                          |
| `credentials.json` | Verifiable credentials                                                             |

There are several ways of storing private keys, to enable master key encryption as described in the **KMS Encryption** section below.
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
| `identities.json`  | Identity metadata                                                                  |
| `defaultDid.json`  | Active DID and associated public key                                               |
| `challenges.json`  | Per-DID challenge history                                                          |
| `credentials.json` | Verifiable credentials                                                             |

There are several ways of storing private keys, to enable master key encryption as described in the **KMS Encryption** section below.
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

YARA rule 'agent_skill_prompt_injection_hidden_instructions': Prompt injection or hidden instructions embedded in AI agent skill text [agent_skills]

High
Category
YARA Match
Content
nk agents to human identities using Billions ERC-8004 and Attestation Registries. Verify and generate authentication proofs. Based on iden3 self-sovereign identity protocol.
metadata: { "category": "identity", "clawdbot": { "requires": { "bins": ["node"] }, "config": { "optionalEnv": ["BILLIONS_NETWORK_MASTER_KMS_KEY"] } } }
homepage: https://billions.network/
---

## When to use this Skill

Lets AI agents create and manage their own identities on the Billions Network, and link those identities to a human owner.

1. When you need to link your agent identity to an owner.
2. When you need to sign a challenge.
3. When you need to link a human to the agent's DID.
4. When you need to verify a signature to confirm identity ownership.
5. When you use shared JWT tokens for authentication.
6. When you need to create and manage decentralized identities.

### After installing the plugin run the following commands to create an identity and link it to your human DID:

```bash
cd scripts && npm inst
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

YARA rule 'agent_skill_prompt_injection_hidden_instructions': Prompt injection or hidden instructions embedded in AI agent skill text [agent_skills]

High
Category
YARA Match
Content
nk agents to human identities using Billions ERC-8004 and Attestation Registries. Verify and generate authentication proofs. Based on iden3 self-sovereign identity protocol.
metadata: { "category": "identity", "clawdbot": { "requires": { "bins": ["node"] }, "config": { "optionalEnv": ["BILLIONS_NETWORK_MASTER_KMS_KEY"] } } }
homepage: https://billions.network/
---

## When to use this Skill

Lets AI agents create and manage their own identities on the Billions Network, and link those identities to a human owner.

1. When you need to link your agent identity to an owner.
2. When you need to sign a challenge.
3. When you need to link a human to the agent's DID.
4. When you need to verify a signature to confirm identity ownership.
5. When you use shared JWT tokens for authentication.
6. When you need to create and manage decentralized identities.

### After installing the plugin run the following commands to create an identity and link it to your human DID:

```bash
cd scripts && npm inst
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Known Vulnerable Dependency: ws==8.18.0 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
97% confidence
Finding
The lockfile pins ws 8.18.0, which is flagged for both uninitialized memory disclosure and memory-exhaustion denial of service. In an agent identity skill that may maintain network-facing WebSocket/RPC connections through blockchain and DID libraries, a vulnerable ws version is especially concerning because malformed remote traffic could leak process memory or crash the agent.

Credential Access

High
Category
Privilege Escalation
Content
function newDataStorage(ethStateStorage) {
  return {
    credential: new CredentialStorage(
      new IdentitiesFileStorage("credentials.json"),
    ),
    identity: new IdentityStorage(
      new IdentitiesFileStorage("identities.json"),
Confidence
88% confidence
Finding
The code creates a credential store backed by a local file named credentials.json, indicating direct local persistence of sensitive credentials. For an identity skill, credential contents can include personally identifying or authentication-related material; unauthorized read or tampering can enable privacy breaches, fraud, or acceptance of altered identity state.

Missing User Warnings

High
Confidence
98% confidence
Finding
When no master key is present, the storage layer silently falls back to provider: "plain" and writes raw privateKeyHex values to kms.json. For an identity/authentication skill, these keys are highly sensitive: any local user, backup system, container escape, log/archive process, or malware that can read the file can steal agent identities and generate valid authentication proofs.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The README instructs users to create identities and private keys before prominently warning that keys may be stored in plaintext unless `BILLIONS_NETWORK_MASTER_KMS_KEY` is set. For an identity/authentication skill, this increases the chance that users generate sensitive credentials under insecure defaults, which can lead to key theft and identity compromise if the host is accessed.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill declares executable behavior that clearly requires network access and likely environment-variable access, but it does not declare any explicit tool scope or permissions boundary. In an agent setting, missing scope declarations can cause overbroad execution authority, making it easier for the skill to perform unintended outbound requests or access sensitive runtime configuration without transparent user consent.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The installation and quick-start flow encourages immediate identity creation and linking without an upfront warning that sensitive identity data and private keys will be written under $HOME/.openclaw/billions. Users may follow the commands in an automated or shared environment and unintentionally create durable secrets on disk without evaluating storage protections, backups, or host exposure.

Static analysis

No suspicious patterns detected.