T09 · Insecure Skill Coding Practices
- Location
scripts/shared/storage/keys.js:47- Finding
Private keys are stored in plaintext by default without enforced filesystem permissions
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill is mostly coherent identity tooling, but it persists private keys insecurely by default and includes payment-signing behavior with under-scoped consent controls.
Review before installing. Use only a dedicated no-funds identity key, set BILLIONS_NETWORK_MASTER_KMS_KEY before creating or importing any identity, and avoid the x402 payment flow unless you can independently verify the destination, recipient, asset contract, network, and amount.
scripts/shared/storage/keys.js:47Private keys are stored in plaintext by default without enforced filesystem permissions
scripts/buildX402Payment.js:49x402 payment consent is not securely bound to the final resource destination and complete transaction details
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
| `identities.json` | Identity metadata |
| `defaultDid.json` | Active DID and associated public key |
| `challenges.json` | Per-DID challenge history |
| `credentials.json` | Verifiable credentials |
After the first run, restrict access to this directory: `chmod 700 ~/.openclaw/billions`
The skill claims identity verification functions, but it also manages and stores private keys in local files, including possible plaintext storage when the master key is unset. Hidden key-management behavior materially increases risk because a user expecting attestation/proof tooling may unknowingly permit creation, import, retrieval, and persistence of sensitive cryptographic material.
The skill claims identity verification functions, but it also manages and stores private keys in local files, including possible plaintext storage when the master key is unset. Hidden key-management behavior materially increases risk because a user expecting attestation/proof tooling may unknowingly permit creation, import, retrieval, and persistence of sensitive cryptographic material.
The skill explicitly stores highly sensitive material locally, including private keys and verifiable credentials, and notes that keys may be plaintext if the master key is not set. In an agent setting this materially raises credential theft risk, especially when combined with signing features and network-driven workflows that could be abused to exfiltrate or misuse those secrets.
- `kms.json` — **CRITICAL**: Contains private keys (encrypted if `BILLIONS_NETWORK_MASTER_KMS_KEY` is set, otherwise 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
This file documents and operationalizes an x402 payment workflow inside a skill whose declared purpose is agent identity verification. That capability mismatch is dangerous because it can cause an agent or operator to invoke network/payment behavior they would not reasonably expect from an identity skill, increasing the chance of unauthorized fund movement or privacy-impacting outbound requests under false pretenses.
The documented capability explicitly instructs handling 402 responses by executing payments and fetching protected resources, which is unjustified for an identity-verification skill. In context, this broadens the skill from verification into value transfer and external access, creating a confused-deputy risk where users grant identity-related trust but the skill can drive payment actions.
ws 8.18.0 is present and is flagged for memory disclosure and memory-exhaustion denial of service issues. In an agent identity skill that may maintain websocket or RPC connections to blockchain infrastructure, a vulnerable websocket stack is particularly relevant because remote peers could trigger crashes, resource exhaustion, or unintended data exposure.
brace-expansion 2.1.0 has multiple denial-of-service advisories related to pathological expansion behavior. Even though it is often used in tooling, a vulnerable parser in a long-lived agent environment can still be abused if untrusted patterns reach it indirectly through CLI, build, or dependency-driven processing.
fast-uri 3.1.2 is flagged for several host confusion and SSRF-related parsing issues. This is especially relevant in decentralized identity software because DIDs, schemas, attestations, and remote metadata often reference URLs; a parser confusion bug can let attacker-controlled identifiers bypass host allowlists or redirect backend fetches.
ws 7.5.10 is present in the jayson websocket/RPC path and is affected by memory exhaustion DoS. Because blockchain and identity integrations frequently expose or consume JSON-RPC over websockets, this vulnerable version can be reachable from remote peers and destabilize the agent process.
underscore 1.13.6 is flagged for unlimited recursion in flatten and isEqual, enabling denial of service with crafted input. If used in RPC or JSON-processing paths, attacker-controlled nested data could consume stack or CPU and disrupt identity verification services.
undici 5.29.0 carries numerous HTTP smuggling, queue poisoning, and injection-related advisories. This is particularly dangerous for a verification skill that may fetch remote DID documents, schemas, attestations, or API resources, because malformed upstream/downstream HTTP handling can enable SSRF, response confusion, cache poisoning, or credential leakage.
ws 8.18.3 is flagged for both uninitialized memory disclosure and memory-exhaustion DoS. Given the skill's blockchain/RPC and identity-verification context, websocket libraries are not incidental; compromise of availability or exposure of process memory can directly affect authentication and proof workflows.
ws 8.17.1 is also present and affected by the same high-severity websocket issues. Multiple vulnerable copies increase the chance that at least one reachable network path remains exploitable even after partial upgrades.
Credential data is persisted to credentials.json, which likely contains sensitive verifiable credentials or related identity artifacts. In the context of a KYA/identity skill, credential storage is especially sensitive because local disclosure could expose personal identity data, attestations, or metadata useful for impersonation, surveillance, or correlation attacks.
function newDataStorage(ethStateStorage) {
return {
credential: new CredentialStorage(
new IdentitiesFileStorage("credentials.json"),
),
identity: new IdentityStorage(
new IdentitiesFileStorage("identities.json"),
When no master key is available, _encodeEntry() silently stores private keys on disk in plaintext under provider: "plain". For a KYA/identity skill handling authentication keys, plaintext at-rest storage creates a high risk of agent key theft, identity takeover, and unauthorized proof generation if the filesystem, backups, or logs are accessed.
The README instructs users to run npx clawhub@latest install ..., which executes whatever version is current at install time rather than a fixed, reviewed release. In a security-sensitive identity skill, this creates a supply-chain execution path where a compromised or malicious upstream package update could run arbitrary code during installation and potentially access local secrets or key material.
The README recommends npx skills add ... without pinning a specific package version, so the command may fetch and execute an unreviewed upstream release at runtime. Because this skill manages decentralized identity and private keys, an installer compromise could directly lead to credential theft or malicious modification of the environment.
This second npx clawhub@latest example repeats the same unpinned remote-code-execution risk in the human installation flow. The danger is elevated by context because the same workflow later creates and stores identity keys, so a compromised installer could intercept those secrets from the start.
The unpinned npx skills add ... command appears again in the human setup path, creating another supply-chain attack opportunity. Since this skill handles authentication proofs and local key storage, arbitrary code execution during install would have especially sensitive consequences.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
| `BILLIONS_NETWORK_MASTER_KMS_KEY` | `provider` on disk | `key` value on disk | Posture |
| --------------------------------- | ------------------ | ----------------------- | ---------------------------- |
| Not set | `"plain"` | Raw hex string | Acceptable on a single-user host with `chmod 700 ~/.openclaw/billions`. |
| Set | `"encrypted"` | `iv:authTag:ciphertext` | **Recommended for all deployments.** AES-256-GCM at rest. |
Mode is selected per-write, so an operator can switch from `plain` to `encrypted` at any time by exporting the variable before the next key creation or import — no migration step is required.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
| `BILLIONS_NETWORK_MASTER_KMS_KEY` | `provider` on disk | `key` value on disk | Posture |
| --------------------------------- | ------------------ | ----------------------- | ---------------------------- |
| Not set | `"plain"` | Raw hex string | Acceptable on a single-user host with `chmod 700 ~/.openclaw/billions`. |
| Set | `"encrypted"` | `iv:authTag:ciphertext` | **Recommended for all deployments.** AES-256-GCM at rest. |
Mode is selected per-write, so an operator can switch from `plain` to `encrypted` at any time by exporting the variable before the next key creation or import — no migration step is required.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
| `BILLIONS_NETWORK_MASTER_KMS_KEY` | `provider` on disk | `key` value on disk | Posture |
| --------------------------------- | ------------------ | ----------------------- | ---------------------------- |
| Not set | `"plain"` | Raw hex string | Acceptable on a single-user host with `chmod 700 ~/.openclaw/billions`. |
| Set | `"encrypted"` | `iv:authTag:ciphertext` | **Recommended for all deployments.** AES-256-GCM at rest. |
Mode is selected per-write, so an operator can switch from `plain` to `encrypted` at any time by exporting the variable before the next key creation or import — no migration step is required.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
| `BILLIONS_NETWORK_MASTER_KMS_KEY` | `provider` on disk | `key` value on disk | Posture |
| --------------------------------- | ------------------ | ----------------------- | ---------------------------- |
| Not set | `"plain"` | Raw hex string | Acceptable on a single-user host with `chmod 700 ~/.openclaw/billions`. |
| Set | `"encrypted"` | `iv:authTag:ciphertext` | **Recommended for all deployments.** AES-256-GCM at rest. |
Mode is selected per-write, so an operator can switch from `plain` to `encrypted` at any time by exporting the variable before the next key creation or import — no migration step is required.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
The following mitigations are present in the codebase and the documented installation flow:
- **Out-of-workspace storage.** Keys live under `~/.openclaw/billions/`, never inside the project directory. Tools (and the agent itself) that operate inside the workspace cannot read or exfiltrate them.
- **Filesystem hardening.** The README instructs the operator to run `chmod 700 ~/.openclaw/billions` after the first run (`README.md` → "Key Storage and Isolation").
- **Dedicated-key warning.** The README warns the operator never to import an Ethereum wallet key that holds assets, only a dedicated identity key (`README.md` step 2 warning under the Human CTA).
- **At-rest encryption available behind one env var.** AES-256-GCM is provided via `BILLIONS_NETWORK_MASTER_KMS_KEY`. No code change, no migration, no extra dependency.
- **Versioned on-disk format.** Each `kms.json` entry carries a `version` and `provider` field, so future format upgrades (e.g. an OS-keystore provider) can ship without breaking existing installs. Legacy entries auto-migrate on next write (see `scripts/shared/storage/keys.js`, `_decodeEntry` legacy branch).
Detected: suspicious.secret_argv_exposure