Back to skill

Security audit

Credential Vault

Security checks for vulnerabilities and agentic risk

Overview

This is a local credential vault with no phone-home behavior, but its security design and documented workflows can expose stored secrets or run unintended shell commands.

Install only after reviewing the tradeoffs. Use it as a local development convenience, not for production or shared machines. Do not use the documented eval workflows with untrusted or imported secret values, avoid printing secrets to terminals, lock the vault before logout or reboot, and treat the session file as equivalent to all stored credentials while unlocked.

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

T09 · Insecure Skill Coding Practices

Error
Location
lib/store.py:90
Finding

Plaintext Persistent Session Key Lacks Restrictive File Permissions

Content
View full analysis
None: """Lock the vault and clear session key.""" self._session_key = None if self.session_file.exists(): self.session_file.unlink() print("🔒 Vault locked") def is_unlocked(self) -> bool: """Check if vault is currently unlocked.""" if self._session_key: return True if self.session_file.exists(): self._session_key = self.session_file.read_bytes() return True return False ``` ### Technical Analysis The derived AES-256 vault key is written directly to `~/.openclaw/vault/session` as plaintext. Unlike the encrypted vault file, the session file is not explicitly assigned mode `0600`. Its effective permissions therefore depend on the process umask. The key remains on disk after the unlocking process terminates and can persist across reboots until `vault lock` is explicitly invoked. Any process or local account able to read this file can decrypt every credential without knowing or brute-forcing the master password. Persisting a raw encryption key is necessary only for the current multi-command session design, not for the core encrypted-storage functionality. The implementation consequently exceeds the minimum exposure required for a memory-only vault session. ### Attack Path 1. The victim executes `vault unlock`. 2. The application derives the AES key and writes it to `~/.openclaw/vault/session`. 3. The victim leaves the vault unlocked or reboots without invoking `vault lock`. 4. A local process or account with permission to read the session file copies its 32-byte contents. 5. The attacker reads `~/.openclaw/vault/vault.enc.json`. ...[truncated 637 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/vault.py:149
Finding

Unquoted Credential Export Enables Shell Command Injection Through Documented eval Workflow

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
lib/crypto.py:85
Finding

Unsalted Fast Password Verifier Undermines PBKDF2 Brute-Force Resistance

Content
View full analysis
str: """Create a verification hash of the master password. This is stored to verify password on unlock, without storing the password itself. Uses SHA-256 (not for key derivation, just verification). Returns: Base64-encoded hash """ return base64.b64encode( hashlib.sha256(password.encode('utf-8')).digest() ).decode('ascii') ``` The stored verifier is checked before PBKDF2 key derivation: ```python # Verify password password_hash = crypto.hash_password(master_password) if password_hash != vault_data["password_hash"]: raise VaultError("❌ Incorrect master password") # Derive and store session key salt = crypto.decode_b64(vault_data["salt"]) self._session_key = crypto.derive_key(master_password, salt) ``` ### Technical Analysis Encryption keys are correctly derived using PBKDF2-SHA256 with 600,000 iterations. However, the vault also stores an unsalted, single-round SHA-256 digest of the same master password. An attacker who obtains the vault file can test password guesses against this fast SHA-256 verifier. The attacker only needs to perform the expensive PBKDF2 operation after discovering a matching password. This bypasses the intended computational cost of PBKDF2 for nearly all guesses and allows identical passwords to have identical verification hashes across vaults. The verifier therefore materially reduces the security provided by the key-derivation function. ### Attack Path 1. The attacker obtains a copy of `vault.enc.json`, such as through a backup leak or local file disclosure. 2. The attacker extracts the Base64-encoded `password_hash`. 3. The attacker performs a dictionary or brute-force attack by calculating one SH ...[truncated 645 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
lib/audit.py:12
Finding

Audit Log Exposes Credential Metadata Without Enforced Owner-Only Permissions

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (31)

Credential Access

High
Category
Privilege Escalation
Confidence
92% confidence
Finding

The guidance says users can delete .env 'but keep a backup,' which encourages retention of a plaintext credential backup after migration. Keeping backup copies of .env undermines the purpose of moving secrets into encrypted storage and increases the chance of later disclosure via cloud sync, filesystem compromise, or accidental sharing.

Content

Scanner excerpt · EXAMPLE.md (reported line 226)May include surrounding context.

Lock vault

uv run vault lock

echo "✅ Migration complete. You can now delete .env (but keep a backup!)"

text

**Run migration:**

Credential Access

High
Category
Privilege Escalation
Confidence
97% confidence
Finding

Exporting all production secrets to /tmp/prod.env creates a plaintext file in a commonly accessible temporary location, which materially increases exposure risk. Even with later cleanup, secrets may be read by other processes/users, captured by backups, left behind on failure, or persisted in shell workflows and CI artifacts.

Content

Scanner excerpt · EXAMPLE.md (reported line 268)May include surrounding context.

Scenario 9: Bulk Export for CI/CD

bash
# Export all production keys as .env format
$ uv run vault unlock
$ uv run vault env --tag production > /tmp/prod.env

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The debugging workflow instructs users to run vault get OPENAI_API_KEY and echo $OPENAI_API_KEY, which reveal the full secret in plaintext on screen. This can expose credentials through terminal history tools, logging wrappers, screen sharing, shell session capture, CI logs, or nearby observers, making credential compromise much more likely.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

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

Content

Scanner excerpt · EXAMPLE.md (reported line 153)May include surrounding context.

Before:

bash
# .env (plaintext, easily leaked)
OPENAI_API_KEY=sk-proj-abc123...
TAVILY_API_KEY=tvly-xyz789...
GITHUB_TOKEN=ghp_def456...

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

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

Content

Scanner excerpt · EXAMPLE.md (reported line 157)May include surrounding context.

Before:

bash
# .env (plaintext, easily leaked)
OPENAI_API_KEY=sk-proj-abc123...
TAVILY_API_KEY=tvly-xyz789...
GITHUB_TOKEN=ghp_def456...

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

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

Content

Scanner excerpt · EXAMPLE.md (reported line 209)May include surrounding context.

Before:

bash
# .env (plaintext, easily leaked)
OPENAI_API_KEY=sk-proj-abc123...
TAVILY_API_KEY=tvly-xyz789...
GITHUB_TOKEN=ghp_def456...

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

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

Content

Scanner excerpt · EXAMPLE.md (reported line 218)May include surrounding context.

Before:

bash
# .env (plaintext, easily leaked)
OPENAI_API_KEY=sk-proj-abc123...
TAVILY_API_KEY=tvly-xyz789...
GITHUB_TOKEN=ghp_def456...

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

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

Content

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

Before:

bash
# .env (plaintext, easily leaked)
OPENAI_API_KEY=sk-proj-abc123...
TAVILY_API_KEY=tvly-xyz789...
GITHUB_TOKEN=ghp_def456...

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill is presented as secure encrypted credential storage, but it also records access events in a plaintext audit log. Even if secret values are omitted, plaintext audit data can reveal credential names, usage patterns, projects, and timing information that may aid attackers in targeting high-value secrets or understanding operator behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill is presented as secure encrypted credential storage, but it also records access events in a plaintext audit log. Even if secret values are omitted, plaintext audit data can reveal credential names, usage patterns, projects, and timing information that may aid attackers in targeting high-value secrets or understanding operator behavior.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documentation claims encrypted credential storage, but the implementation stores the active session key unencrypted on disk. This is security-misleading behavior: users may trust the tool to protect secrets more strongly than it actually does, causing unsafe deployment and broader compromise if the session file is accessed.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The vault writes the derived session encryption key directly to a predictable file on disk, which defeats the security boundary of an encrypted credential store. Any local process, malware, backup system, or other user with access to that file can recover the in-memory unlock key and decrypt stored secrets without the master password.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The example explicitly prints part of an API key to terminal output, normalizing the practice of displaying secrets during routine use. Even partial disclosure can leak sensitive prefixes into terminals, shell scrollback, logs, recordings, screenshots, or shared debugging sessions, and the example does not warn users about that risk.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · EXAMPLE.md (reported line 354)May include surrounding context.

bash
# Option 1: Store master password in a separate file (less secure)
echo "my-master-password" > ~/.vault_password
chmod 600 ~/.vault_password

# Cron job
0 9 * * * cat ~/.vault_password | /path/to/vault unlock && eval $(/path/to/vault env --tag daily) && /path/to/daily_task.sh

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

Adding alias venv='eval $(uv run vault env)' to persistent shell startup files causes broad, repeated secret-export behavior to become a convenience command available in every shell session. This encourages long-lived exposure of credentials in user environments and increases the chance of accidental export into unrelated commands, subshells, debug sessions, or recorded terminals.

Content

Scanner excerpt · EXAMPLE.md (reported line 372)May include surrounding context.

Alias for convenience

bash
# Add to ~/.zshrc or ~/.bashrc
alias v='uv run vault'
alias venv='eval $(uv run vault env)'

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The README explicitly recommends eval $(uv run vault env --tag ...), which injects decrypted secrets into the current shell environment. Environment variables are commonly exposed to child processes, shell history patterns, crash reports, debugging tools, and other local inspection mechanisms, so presenting this workflow without a clear warning encourages users to weaken the protection the vault is meant to provide.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
88% confidence
Finding

The troubleshooting note confirms that an unlocked session may persist after reboot, which implies decrypted access material or unlock state can survive longer than users expect. That weakens the vault's local security model because a reboot is often assumed to clear sensitive runtime state, and an attacker with later access to the machine could retrieve secrets without re-entering the master password.

Content

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

md
**Solution:** Double-check your password. If forgotten, you'll need to reinitialize (⚠️ loses all credentials).

### "Vault not initialized"
**Solution:** Run `vault init` to create a new vault.

### Session persists after reboot
**Solution:** Run `vault lock` before shutdown, or add to logout script.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill documents shell execution, file reads, and file writes but does not declare any explicit tool scope or permissions boundary. For a credential-management skill, this increases risk because the agent may be granted broader-than-necessary capabilities around sensitive material without clear least-privilege constraints.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The documented use of eval $(uv run vault env --tag tavily) injects decrypted secrets into the current shell environment without warning. Environment variables are commonly exposed through shell history, child processes, debug output, crash reports, or process inspection, so this can undermine the confidentiality guarantees of the vault.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
95% confidence
Finding

The skill explicitly stores a session key on disk until vault lock, and notes it is not automatically cleared on reboot. A decryption key persisted on disk weakens the vault's security model because any local compromise, stolen account access, backup capture, or opportunistic file read while the session remains unlocked can bypass the master password entirely.

Content

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

md
- Session key: `~/.openclaw/vault/session` (temporary, cleared on lock)

### Permissions
- Vault file: `0600` (owner read/write only)
- Session key: deleted on `vault lock`

### Threat Model

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The documentation encourages direct secret retrieval via vault get KEY_NAME and library calls like store.get(...) without warning that plaintext secrets may be printed, logged, copied into terminal scrollback, or mishandled by downstream code. For a credential vault, normalizing plaintext retrieval without guardrails materially increases accidental exposure risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

Writing the session key to disk without a clear warning or consent increases the likelihood that operators will unknowingly expose decryptable credentials on shared or monitored systems. The absence of a user-facing warning compounds the core design flaw by preventing informed risk decisions.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The list operation reads and returns credential names, tags, and timestamps without requiring the vault to be unlocked. Even if secret values remain encrypted, metadata such as service names, environments, or token labels can reveal sensitive operational details and aid targeted attacks.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The get command prints the retrieved secret value directly to stdout, which can leak credentials into logs, transcripts, shell integrations, or calling processes. Because this skill is specifically for secret storage, exposing secrets through the least-controlled output channel is a meaningful security weakness.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The env command enumerates stored secrets and prints them in plaintext to stdout as KEY=VALUE. In practice, stdout is often captured by terminal history tools, CI logs, shell tracing, wrappers, or parent processes, so this defeats much of the protection expected from an encrypted vault once unlocked.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.