Back to skill

Security audit

Verified Agent Identity

Security checks for vulnerabilities and agentic risk

Overview

The skill does identity work it describes, but it stores durable identity private keys locally in plaintext by default and has authentication hardening gaps that require review before installation.

Install only if you are comfortable with this skill creating or importing agent identity keys and storing them under $HOME/.openclaw/billions. Configure BILLIONS_NETWORK_MASTER_KMS_KEY before creating identities, restrict local file access to that directory, avoid passing real private keys on command lines where shell history may capture them, and treat generated tokens and verification URLs as sensitive. Pin installer and dependency versions in controlled environments.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/shared/storage/keys.js:45
Finding
Private Keys Stored Unencrypted by Default Without Explicit Restrictive Permissions## Vulnerability Details **File Location**: `scripts/shared/storage/keys.js:45-57`; `scripts/shared/storage/base.js:9-12, 27-32` **Vulnerability Type**: Plaintext sensitive-data storage and insufficient filesystem permission enforcement **Risk Level**: High ### Vulnerable Code `scripts/shared/storage/keys.js:45-57` ```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 }, }; } ``` `scripts/shared/storage/base.js:9-12, 27-32` ```js async ensureDirectory() { const dir = path.dirname(this.filePath); await fs.mkdir(dir, { recursive: true }); } ``` ```js 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` intentionally stores the raw identity private key in `kms.json` using the `plain` provider. Encryption is therefore optional and disabled in the default configuration. The generic storage writer also creates directories and temporary files without explicit permission modes. Consequently, effective access depends on the process umask and any existing directory permissions. The temporary file receives the same sensitive content before being renamed and is not explicitly restricted to the owning user. This design creates a direct local key-disclosure risk. Although local storage is necessary for the declared identity functionality, plaintext storage and reliance on ambient umask settings ...[truncated 1327 chars]
Remediation
## Remediation Suggestions 1. Require encrypted private-key storage and fail closed when no valid master key or platform keystore is available. Do not silently fall back to plaintext. 2. Prefer an operating-system credential store, hardware-backed keystore, or dedicated secret-management service over a JSON file. 3. Create `$HOME/.openclaw/billions` with mode `0700`. 4. Create `kms.json` and its temporary file with mode `0600`, using exclusive creation where appropriate. 5. Before reading or writing, verify that the directory and file are owned by the expected user and are not symbolic links. 6. Use a securely generated temporary filename in the same protected directory and ensure cleanup after failed writes. 7. Detect existing plaintext entries and migrate them to encrypted storage after explicit user confirmation. 8. Reject weak master keys rather than treating them as if no key were configured. Use a password-based key derivation function such as scrypt or Argon2id when the master key is human-generated. 9. Document key rotation and recovery procedures for identities previously stored in plaintext.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/verifySignature.js:20
Finding
Authentication Challenges Can Be Replayed Indefinitely## Vulnerability Details **File Location**: `scripts/shared/storage/challenge.js:8-24, 30-33`; `scripts/verifySignature.js:20-23, 48-58` **Vulnerability Type**: Missing challenge expiration and single-use enforcement **Risk Level**: Medium ### Vulnerable Code `scripts/shared/storage/challenge.js:8-24` ```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); } ``` `scripts/shared/storage/challenge.js:30-33` ```js async getChallenge(did) { const entry = await this.find(did); return entry?.challenge; } ``` `scripts/verifySignature.js:20-23, 48-58` ```js const challenge = await challengeStorage.getChallenge(args.did); if (!challenge) { console.error(`Error: No challenge found for DID: ${args.did}`); console.error("Generate a challenge first with generateChallenge.js"); process.exit(1); } ``` ```js 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 Challenge records include a creation timestamp, but `getChallenge` returns only the challenge value and does not enforce an expiration time. After successful signature verification, `verifySignature.js` does not delete or invalidate the challenge. A signed token matching the stored DID and challenge therefore remains valid until another challenge is generated for that same DID. This violates the freshness and single-use properties expected from c ...[truncated 1467 chars]
Remediation
## Remediation Suggestions 1. Assign every challenge a short, explicit expiration time and reject expired records during verification. 2. Atomically consume the challenge after successful verification so it cannot be used again. 3. Use transactional storage, file locking, or an atomic compare-and-delete operation to prevent concurrent double verification. 4. Bind each challenge to the intended DID, verifier, operation, audience, and session identifier. 5. Generate challenges with sufficient cryptographic entropy and record a unique challenge identifier. 6. Avoid exposing signed tokens through process arguments or logs where feasible; accept sensitive tokens through protected standard input or another secure channel. 7. Add automated tests covering expiration, successful consumption, sequential replay, and concurrent replay.

T08 · Insecure Dependencies

Warning
Location
.github/workflows/main.yml:23
Finding
CI Workflow Executes an Unpinned Latest Package## Vulnerability Details **File Location**: `.github/workflows/main.yml:23-25` **Vulnerability Type**: Mutable and unreviewed CI supply-chain dependency **Risk Level**: Medium ### Vulnerable Code ```yaml - name: Step 1 - Install ClawHub skill run: npx clawhub@latest install verified-agent-identity ``` ### Technical Analysis The workflow uses `npx` to download and execute the package version currently identified by the mutable `latest` tag. The effective code executed by the workflow can therefore change after this repository has been audited, without a corresponding repository change or review. This creates a remote supply-chain execution channel. If the upstream npm account, package, release process, or registry metadata is compromised, attacker-controlled package code may execute on the GitHub Actions runner. The workflow also uses mutable major-version action references such as `actions/checkout@v3` and `actions/setup-node@v3`. These are less mutable than `@latest` in ordinary operation but still do not provide the immutability of full commit-SHA pinning. ### Attack Path 1. An attacker compromises the `clawhub` package publishing account, its release pipeline, or another upstream distribution component. 2. The attacker publishes a malicious version and assigns it the `latest` tag. 3. A maintainer or authorized user dispatches the workflow. 4. `npx clawhub@latest` downloads the malicious version at runtime. 5. Package initialization or command execution runs attacker-controlled code on the CI runner. 6. The attacker can access the workflow workspace, modify generated artifacts, make network requests, and attempt to read any credentials or tokens exposed to that job. ### Impact Assessment Exploitation provides arbitrary code execution with the permissions of the GitHub Actions job. The attacker can read and alter checked-out repository content and workflow-generated files, tamper with identity-generation out ...[truncated 327 chars]
Remediation
## Remediation Suggestions 1. Replace `clawhub@latest` with an exact, reviewed package version. 2. Install dependencies from a committed lockfile using `npm ci` and verify package integrity. 3. Pin GitHub Actions to full immutable commit SHAs rather than mutable version tags. 4. Configure explicit minimal workflow permissions, such as read-only repository contents unless write access is required. 5. Avoid exposing repository or environment secrets to jobs that install or execute unnecessary third-party code. 6. Use dependency review, provenance verification, signed releases, and automated update tooling that opens reviewed pull requests. 7. Consider isolating identity-generation operations from CI or running them in a restricted environment without durable credentials.
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
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (36)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The skill is presented primarily as identity linking and verification, but the documentation reveals local private-key generation, import, storage, and credential persistence. That mismatch is dangerous because users may authorize a verification skill without realizing it also manages long-lived secrets on disk, including plaintext keys when no master key is configured.

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
entity
description: Billions decentralized identity for agents. Link 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"] } } }
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).

Credential Access

High
Category
Privilege Escalation
Content
- `kms.json` - **CRITICAL**: Contains private keys (encrypted if BILLIONS_NETWORK_MASTER_KMS_KEY is set, otherwise in plaintext)
- `defaultDid.json` - DID identifiers and public keys
- `challenges.json` - Authentication challenges history
- `credentials.json` - Verifiable credentials
- `identities.json` - Identity metadata
- `profiles.json` - Profile data
Confidence
95% confidence
Finding
The skill explicitly documents storage of private keys, credentials, challenge history, and profile data under a predictable path in the user's home directory. In an agent environment, any overbroad file access, compromise of the host, or misuse by another skill could expose these materials, enabling impersonation, credential theft, or privacy violations.

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
98% confidence
Finding
ws 8.18.0 is present and the cited advisories describe memory disclosure and memory exhaustion denial of service in WebSocket handling. In an identity/authentication skill that may process network traffic or proofs over WebSocket-capable stacks, a vulnerable ws version can expose process memory or allow remote resource exhaustion.

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
90% confidence
Finding
brace-expansion 2.0.2 has multiple denial-of-service issues involving pathological expansion behavior that can hang or exhaust memory. Even though it is commonly used in tooling, inclusion in the runtime dependency graph means attacker-controlled patterns could trigger excessive CPU or memory use if exposed through glob-like processing.

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
96% confidence
Finding
fast-uri 3.1.0 is associated with host-confusion and SSRF-class parsing flaws. This is especially relevant for decentralized identity software, which often resolves DIDs, fetches remote JSON-LD contexts, or contacts registries; malformed URLs could bypass allowlists or redirect requests to unintended internal targets.

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
97% confidence
Finding
ws 7.5.10 is affected by a memory exhaustion denial-of-service issue from tiny fragments and data chunks. If any component using this older ws version accepts untrusted WebSocket peers, an attacker may be able to consume excessive memory and crash or stall the service.

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
91% confidence
Finding
underscore 1.13.6 is reported to allow unbounded recursion in functions like _.flatten and _.isEqual, enabling denial of service with crafted nested inputs. For an agent skill that may compare or normalize nested identity/proof structures from external sources, this can be a reachable resource-exhaustion issue.

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
98% confidence
Finding
undici 5.29.0 is affected by multiple HTTP parsing and request-smuggling/poisoning issues. This is particularly concerning in a DID/attestation ecosystem where the skill may fetch remote documents, talk to registries, or relay HTTP requests, since parser confusion can lead to SSRF, cache poisoning, or cross-request contamination.

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
98% confidence
Finding
ws 8.17.1 is affected by memory disclosure and memory exhaustion issues, both serious for network-facing software. In a security-sensitive identity skill, leaking process memory or allowing trivial remote DoS materially increases operational risk, especially if proofs or credentials are handled in the same process.

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
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
function newDataStorage(ethStateStorage) {
  return {
    credential: new CredentialStorage(
      new IdentitiesFileStorage("credentials.json"),
    ),
    identity: new IdentityStorage(
      new IdentitiesFileStorage("identities.json"),
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Missing User Warnings

High
Confidence
97% confidence
Finding
The code not only permits plaintext storage but does so without any warning, prompt, or explicit opt-in, making insecure deployment likely. Because users may reasonably assume a key-management store protects cryptographic secrets, this silent fallback increases the chance of unnoticed exposure of identity keys used for agent authentication and attestations.

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
95% confidence
Finding
The README instructs users to run `npx clawhub@latest install verified-agent-identity`, which fetches and executes the latest version of a remote package at install time. Using `@latest` makes the executed code mutable over time and exposes users to supply-chain compromise or malicious updates if the package or publishing account is hijacked.

Vague Triggers

Medium
Confidence
88% confidence
Finding
The README suggests starting the process with a generic natural-language message, "Please link your agent identity to me," without defining exact trigger phrases, scope constraints, or negative examples. In a markdown skill description, this can create uncertainty about when the skill should activate and increases the risk of unintended invocation from similar everyday phrasing.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
This is the same supply-chain risk repeated in the human installation path: `npx clawhub@latest install verified-agent-identity` executes unpinned remote code. In a security-sensitive identity skill, mutable installer references are especially risky because compromise could lead to wallet, key, or credential theft during setup.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill declares Node execution and describes networked identity operations, but it does not define any explicit tool scope or allowed-tools boundary. In an agent setting, missing tool restrictions can let the skill invoke environment or network capabilities more broadly than users expect, increasing the chance of unintended data exposure or unauthorized outbound requests.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The README instructs users to create an identity and perform linking immediately, but it does not present a clear warning that this generates and stores sensitive private key material locally. Because the same document later states keys may be stored unencrypted, users could unknowingly create durable credentials with insufficient protection.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The invocation example uses broad natural-language phrasing like 'Link your agent identity to me,' which can overlap with ordinary conversation and cause accidental skill triggering. In an autonomous agent context, ambiguous activation can lead to unplanned signing or identity-linking flows involving sensitive identity material.

Missing User Warnings

Medium
Confidence
76% confidence
Finding
The script constructs a `JsonRpcProvider` from `billionsMainnetConfig.url` and passes an Ethereum signer into `createEthereumBasedIdentity`, which implies network communication with an external RPC endpoint. This file does not include a user-facing warning, confirmation, or disclosure that identity-related data may be transmitted over the network.

Missing User Warnings

Medium
Confidence
85% confidence
Finding
The script persists a newly created DID, public key, and default-status flag via `didsStorage.save(...)`. While there are code comments, they are not user-facing disclosures, and this file does not provide a confirmation prompt or visible warning that identity metadata will be stored.

Context-Inappropriate Capability

Medium
Confidence
98% confidence
Finding
When no master key is configured, _encodeEntry() silently stores private keys with provider: "plain", resulting in unencrypted key material on disk. For a decentralized identity agent handling authentication proofs, plaintext persistence of signing keys creates a serious compromise risk from local file disclosure, backups, logs, container escapes, or shared-host access.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The list() method returns every stored alias together with the raw privateKeyHex, which unnecessarily broadens exposure of highly sensitive material. In an identity/attestation skill, enumeration of key metadata may be legitimate, but bulk export of all private keys makes accidental disclosure, logging, misuse by callers, or downstream compromise much more likely.

Known Vulnerable Dependency: uuid==13.0.0 — 1 advisory(ies): CVE-2026-41907 (uuid: Missing buffer bounds check in v3/v5/v6 when buf is provided)

Low
Category
Supply Chain
Confidence
95% confidence
Finding
The lockfile includes uuid 13.0.0, and the cited issue affects v3/v5/v6 when a caller supplies a destination buffer without proper bounds. This is a real supply-chain risk, though in a lockfile alone we cannot confirm reachable exploit paths; impact is limited unless the skill or a dependency uses the affected API pattern on attacker-controlled input.

Static analysis

No suspicious patterns detected.