Back to skill

Security audit

OpenClaw Key Management

Security checks for vulnerabilities and agentic risk

Overview

The skill is meant to protect credentials, but its code can expose or lose those credentials and several advertised protections are not actually implemented.

Do not use this skill for real credentials without fixes. Test only with synthetic secrets in an isolated workspace, and require fixes for the temporary-script execution pattern, passphrase mode, hard-coded paths, MEMORY.md migration confirmation, vault permission hardening, and fail-closed error handling before installing in a real OpenClaw environment.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/key_manager.sh:29
Finding

Arbitrary JavaScript Execution Through Unescaped Secret Input

Content
View full analysis
"$TEMP_SCRIPT" << EOF const SecureKeyVault = require('$WORKSPACE_DIR/scripts/key_vault_simple.js'); const vault = new SecureKeyVault('$WORKSPACE_DIR'); async function addSecret() { try { await vault.initialize(); await vault.setSecret('$SECRET_NAME', '$SECRET_VALUE', {type: 'manual'}); console.log('Secret added successfully'); } catch (err) { console.error('Error:', err.message); process.exit(1); } } addSecret(); EOF node "$TEMP_SCRIPT" rm "$TEMP_SCRIPT" ``` The same unsafe source-code generation pattern is used for the secret name in the `get` operation at `scripts/key_manager.sh:61-86` and for migrated credentials at `scripts/key_manager.sh:139-167`. ### Technical Analysis `SECRET_NAME` and `SECRET_VALUE` are inserted directly into JavaScript single-quoted string literals without escaping. The generated file is then passed to `node`, meaning these values are treated as JavaScript source code rather than data. Shell quoting around the here-document does not make the resulting JavaScript safe. A value containing a quote, closing parenthesis, and additional statements can terminate the intended string literal and inject arbitrary JavaScript. For example, a malicious secret value shaped like the following can break out of the `setSecret` call: ```text '); require('child_process').execSync('id'); // ``` The resulting temporary JavaScript contains attacker-controlled executable statements. ### Attack Path 1. An attacker invokes or causes a user or aut ...[truncated 1281 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/key_vault.js:17
Finding

Configured Passphrase Mode Is Ignored

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/key_manager.sh:37
Finding

Plaintext Credentials Written to Temporary Executable Files

Content
View full analysis
"$TEMP_SCRIPT" << EOF const SecureKeyVault = require('$WORKSPACE_DIR/scripts/key_vault_simple.js'); const vault = new SecureKeyVault('$WORKSPACE_DIR'); async function addSecret() { try { await vault.initialize(); await vault.setSecret('$SECRET_NAME', '$SECRET_VALUE', {type: 'manual'}); console.log('Secret added successfully'); } catch (err) { console.error('Error:', err.message); process.exit(1); } } addSecret(); EOF node "$TEMP_SCRIPT" rm "$TEMP_SCRIPT" ``` Migration also embeds a recovered API key in a temporary script: ```bash TEMP_SCRIPT=$(mktemp) cat > "$TEMP_SCRIPT" << EOF const SecureKeyVault = require('$WORKSPACE_DIR/scripts/key_vault_simple.js'); const fs = require('fs'); const vault = new SecureKeyVault('$WORKSPACE_DIR'); async function migrateCredentials() { try { await vault.initialize(); await vault.setSecret('instreet_api_key', '$API_KEY', {type: 'api_key', service: 'instreet'}); ``` ### Technical Analysis The secret value is persisted in plaintext as part of a JavaScript source file. Although `mktemp` generally creates a uniquely named file, the script does not set a restrictive `umask`, verify resulting permissions, or register cleanup handlers. Cleanup happens only after the Node.js process returns to the next shell statement. The script does not use `set -e`, and it has no `EXIT`, `INT`, `TERM`, or `HUP` trap. Interruption, process termination, system crashes, or abnormal control flow can leave the temporary file behind. Writing secrets to disk also makes them potentially available to local forensic recovery, filesystem snapshots, backup software, security scanners, and processes ...[truncated 965 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/key_manager.sh:20
Finding

Vault Storage Is Created Without Explicit Access-Control Hardening

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/key_vault.js:75
Finding

Vault Authentication Failures Are Silently Treated as an Empty Vault

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/key_vault.js:109
Finding

Credential Cache Does Not Securely Erase Plaintext and Ignores Runtime Configuration

Content
View full analysis
{ if (this.runtimeCache.has(name)) { this.secureZero(this.runtimeCache.get(name).value); this.runtimeCache.delete(name); } }, 30000); return decrypted; } ``` The clearing function is: ```javascript secureZero(buffer) { if (typeof buffer === 'string') { // Convert string to buffer for zeroing const buf = Buffer.from(buffer, 'utf8'); buf.fill(0); } else if (Buffer.isBuffer(buffer)) { buffer.fill(0); } } ``` ### Technical Analysis JavaScript strings are immutable and managed by the runtime. When `secureZero()` receives a string, `Buffer.from(buffer, 'utf8')` creates a new copy. Clearing that copy does not overwrite the original string held in the cache, prior variables, return values, or JavaScript heap. The configured timeout is also ignored. Both cache checks and cleanup timers hard-code 30 seconds rather than using `config.runtime.credential_timeout_seconds`. The configuration additionally advertises memory locking and automatic cleanup on exit, but this implementation does not loc ...[truncated 1163 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/key_manager.sh:3
Finding

CLI Loads a Missing Module From a Hard-Coded Workspace Path

Content
View full analysis
"$TEMP_SCRIPT" << EOF const SecureKeyVault = require('$WORKSPACE_DIR/scripts/key_vault_simple.js'); const vault = new SecureKeyVault('$WORKSPACE_DIR'); ``` The same `require` target appears in the `add`, `get`, `list`, and `migrate` implementations. The audited package contains: ```text scripts/key_vault.js ``` It does not contain: ```text $WORKSPACE_DIR/scripts/key_vault_simple.js ``` ### Technical Analysis The CLI does not load the vault implementation shipped alongside itself. Instead, it resolves an absolute path under a hard-coded workspace and expects a differently named module that is absent from the package. This makes normal operation dependent on an undeclared external local file. If that path contains attacker-controlled JavaScript, invoking the legitimate key-manager command executes that code through Node.js `require`. The hard-coded workspace also conflicts with `install.sh`, which accepts a configurable workspace path. Installing into another workspace does not update `key_manager.sh`, so commands continue accessing `$HOME/.openclaw/zhaining`. The module-substitution scenario requires the attacker to be able to create or modify the referenced file. It is therefore primarily a local tool-hijacking opportunity and a trust-boundary defect rather than an independent privilege escalation. ### Attack Path 1. The package is installed and the user invokes `scripts/key_manager.sh`. 2. A local actor has previously created or modified: ```text $HOME/.openclaw/zhaining/scripts/key_ ...[truncated 958 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (19)

Self-Modification

High
Category
Rogue Agent
Confidence
85% confidence
Finding

Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Content

Scanner excerpt · README.md (reported line 24)May include surrounding context.

md
### Phase 2: Skill Creation (In Progress)
- [ ] Create skill directory structure
- [ ] Write SKILL.md documentation
- [ ] Package implementation files
- [ ] Add installation and setup scripts
- [ ] Integrate with OpenClaw skill system

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

The documented 'convenience mode' derives trust from host identity and claims protection from memory/file compromise, yet the same document admits decrypted credentials may persist in runtime memory for 30 seconds and that backups are created. This creates a misleading security posture: users may believe the system resists host or memory compromise when in reality host-bound key derivation and credential caching weaken that claim and broaden the attack surface.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

The documented 'convenience mode' derives trust from host identity and claims protection from memory/file compromise, yet the same document admits decrypted credentials may persist in runtime memory for 30 seconds and that backups are created. This creates a misleading security posture: users may believe the system resists host or memory compromise when in reality host-bound key derivation and credential caching weaken that claim and broaden the attack surface.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The documented 'convenience mode' derives trust from host identity and claims protection from memory/file compromise, yet the same document admits decrypted credentials may persist in runtime memory for 30 seconds and that backups are created. This creates a misleading security posture: users may believe the system resists host or memory compromise when in reality host-bound key derivation and credential caching weaken that claim and broaden the attack surface.

Content

No source excerpt is available for this finding.

Self-Modification

High
Category
Rogue Agent
Confidence
85% confidence
Finding

Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Content

Scanner excerpt · SKILL.md (reported line 66)May include surrounding context.

md
./skills/openclaw-key-management/scripts/key_manager.sh init

# 3. Configure security mode (optional)
# Edit skills/openclaw-key-management/config/key_management.json
# Set "master_key_mode" to "system_key" (default) or "passphrase"

# 4. Migrate existing credentials (if any)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 113)May include surrounding context.

md
│ ├── scripts/key_vault.js # Node.js encryption module

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · USAGE.md (reported line 95)May include surrounding context.

  • Tier 4 (Long-term): Secure references only in MEMORY.md

With Other Skills

Other skills can access credentials securely:

javascript
// In another skill
const { getSecret } = require('../openclaw-key-management/scripts/key_vault.js');

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The add flow embeds the secret value directly into a temporary JavaScript file on disk and then executes it with Node.js. This exposes sensitive data to local file disclosure, backups, crash artifacts, process monitoring, and race conditions before the temp file is removed, undermining the stated goal of secure credential storage.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The migration flow extracts the API key from MEMORY.md and writes that raw key into a temporary script file before execution. This creates another plaintext copy of the credential on disk during handling, increasing exposure to local attackers and forensic recovery while processing highly sensitive material.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The test plan instructs destructive actions such as deleting the current vault and corrupting vault files, but it does not explicitly require a non-production environment, backups before testing, or a warning about irreversible data loss. In a credential-management skill, these steps could cause accidental destruction of real secrets or service disruption if a tester runs them against live data.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The activation contexts are broad enough to match common discussions about API keys, credential security, or sensitive data, which can cause the skill to invoke outside narrowly intended scenarios. Because this skill also declares access to memory files, workspace files, and system commands, unintended activation increases the chance of exposing or manipulating sensitive credentials in contexts where the user did not explicitly request this skill.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The migrate command scans MEMORY.md for a credential pattern, extracts the value, and then rewrites the document in place. Even though migration is somewhat related to key management, automatically reading and modifying a general memory/document file exceeds the minimally necessary scope for a vault helper and can unexpectedly alter user data or harvest secrets from broader workspace content.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The migration path rewrites MEMORY.md immediately after finding a matching key, with no confirmation prompt, preview, or consent gate. This can silently change important workspace state and destroy or corrupt context if the regex matches unintended content or the write fails partway through.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The manifest describes a secure credential storage system that encrypts and protects secrets from file compromise. Reading the machine UUID and hostname to bind encryption to the host is a hardware-fingerprinting capability that goes beyond ordinary secret storage behavior and is not justified by the stated purpose alone.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

When /etc/machine-id is unavailable, the code inspects network interfaces and derives an identifier from a MAC address. Accessing network interface metadata is not an obvious requirement for a credential vault whose stated purpose is secure storage, so this is an unjustified capability relative to the manifest description.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The code creates a persistent backup of the secret vault by copying the encrypted vault file into a backup directory, but there is no confirmation prompt and no user-facing log, print, docstring, or comment disclosing that an extra copy of sensitive data will be created. Creating additional secret-bearing files affects user data handling and retention, so this operation should be explicitly surfaced.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

Several trigger phrases, such as 'key management' or 'credential storage,' are generic and may overlap with ordinary conversation, making accidental invocation more likely. In a credential-handling skill, even low-friction unintended activation is security-relevant because the skill advertises privileged integrations that could touch sensitive data or invoke commands.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

The code reads /etc/machine-id and, on fallback, inspects network interfaces to derive a key from MAC-address-related data. Although comments explain the implementation, there is no user-facing disclosure that host-identifying system data is being collected and used, which fits the missing-warning criterion for access to sensitive system identifiers.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

The vault-loading path reads the file using base64 decoding and then base64-decodes again before JSON parsing, while the save path writes plain JSON text. This format mismatch can prevent vault decryption, trigger error handling that silently resets the in-memory vault to an empty object, and lead to denial of service or accidental secret loss/overwrite when the application later saves the empty vault.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.