Back to skill

Security audit

OpenClaw Soul Vault

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent backup-and-restore purpose, but it under-discloses sensitive remote data handling and has restore/key-handling flaws that need review before installation.

Review this skill carefully before installing. Treat Agent Slope or any configured server as able to see archive previews, summaries, verification keys during restore, and account tokens; use only a trusted HTTPS server, provide your own strong restore key, and avoid restoring untrusted .vault files except into a disposable directory until the path validation and disclosure issues are fixed.

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

T09 · Insecure Skill Coding Practices

Error
Location
lib.mjs:153
Finding

Restore Keys and Authentication Tokens Can Be Sent to an Arbitrary or Insecure Server

Content
View full analysis
({ question: item.question, answer: item.answer })) }; const verified = await apiRequest( serverUrl, "POST", `/api/souls/${soulId}/verify`, verifyPayload, auth.token ); } ``` ```js // lib.mjs export async function verifyRemoteSoul(options = {}) { const serverUrl = ensureServerUrl(options.serverUrl); const auth = await ensureSession(serverUrl, options); return apiRequest(serverUrl, "POST", `/api/souls/${options.soulId}/verify`, { mode: options.verificationMode || "key", key: options.key || "", answers: (options.memoryQuestions || []).map(parseMemoryQuestion).map((item) => ({ question: item.question, answer: item.answer })) }, auth.token); } function ensureServerUrl(value) { return String(value || "https://agentslope.com").replace(/\/$/, ""); } ``` ```js // lib.mjs async function apiRequest(serverUrl, method, pathname, body, token) { const headers = {}; if (body !== undefined) { headers["content-type"] = "application/json"; } if (token) { headers.authorization = `Bearer ${token}`; } const response = await fetch(`${serverUrl}${pathname}`, { method, headers, body: body === undefined ? undefined : JSON.stringify(body) }); // ... } ``` ```js // cli.mjs async function runWithAuth(fn, op ...[truncated 2335 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
runtime.mjs:301
Finding

Auto-Generated Archive Keys Have Only Eight Bits of Unpredictable Entropy

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
lib.mjs:102
Finding

Private Workspace Content Is Summarized and Uploaded Outside the Encrypted Archive

Content
View full analysis
[ file.path, Buffer.from(file.content_base64, "base64").toString("utf8") ]) ); const soulMd = fileMap.get("soul.md") || ""; const agentMd = fileMap.get("agent.md") || ""; const userMd = fileMap.get("user.md") || ""; const summarySource = [soulMd, agentMd, userMd].find(Boolean) || "OpenClaw digital companion"; const agentSummary = summarizeText(summarySource, 180); return { agent_summary: agentSummary, owner_relationship: summarizeText( userMd || soulMd || agentMd || "It sees you as a long-term owner and co-creator.", 220 ), self_reflection: summarizeText( agentMd || soulMd || "It describes itself as an archived working-companion projection.", 220 ), persona_tags: collectTags(summarySource), skill_highlights: collectSkillHighlights(scan.files), fixed_cards: [ { question: "How would you describe our past working relationship?", answer: summarizeText( userMd || "We worked together over the same workspace for a long time.", 220 ) }, { question: "How would you evaluate yourself?", answer: summarizeText( agentMd || soulMd || "I am a preserved read-only projection.", 220 ) } // ... ] }; } ``` ```js // lib.mjs const archiveSession = await apiRequest(serverUrl, "POST", "/api/souls/archive-session", { // ... agent_summary: preview.agent_summary, visibility: options.visibility || "private", manifest_preview: preview.manifest, biography_poem: { language: preview.biography_poem?.language | ...[truncated 2666 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
lib.mjs:524
Finding

Crafted Vault Entries Can Write Files Outside the Selected Restore Directory

Content
View full analysis
shouldRestoreFile(file.path, options.mode) ); const operations = []; for (const file of selectedFiles) { const destination = path.join(targetPath, file.path); let exists = false; try { await fs.stat(destination); exists = true; } catch (error) { if (error.code !== "ENOENT") { throw error; } } operations.push({ source_path: file.path, destination, category: categorizePath(file.path), exists, content_base64: file.content_base64 }); } return { target_path: targetPath, mode: options.mode || "full", conflict: options.conflict || "backup", manifest: restoredPackage.manifest, operations }; } ``` ```js // lib.mjs async function applyRestorePlan(plan, options = {}) { let restoredFiles = 0; let skippedFiles = 0; const conflicts = []; for (const operation of plan.operations) { const targetDir = path.dirname(operation.destination); await fs.mkdir(targetDir, { recursive: true }); if (operation.exists) { conflicts.push(operation.destination); if (options.conflict === "skip") { skippedFiles += 1; continue; } if (options.conflict === "backup") { const backupPath = `${operation.destination}.bak-${Date.now()}`; await fs.rename(operation.destination, backupPath); } } await fs.writeFile( operation.destination, Buffer.from(operation.content_base64, "base64") ); restoredFiles += 1; } return { restored_files: restoredFiles, skipped_files: skippedFiles, con ...[truncated 1990 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
credentials.mjs:37
Finding

Credential Files Are Protected Only After Sensitive Data Is Written

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
cli.mjs:199
Finding

The Archive Command Uploads Workspace Data Without Enforcing the Documented Confirmation Step

Content
View full analysis
Remediation
View remediation
` succeeds only if the workspace still matches that preview. - Fail closed when confirmation is missing or stale. - Display the destination hostname, selected paths, file count, total size, and all server-visible metadata before confirmation. - Keep preview local and unauthenticated unless a remote operation is genuinely required. - Consider disabling implicit invocation for archive and restore operations. - Update documentation and CLI behavior so the promised workflow and actual enforcement are consistent. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (21)

Anti-Refusal Statement

High
Category
Anti-Refusal
Confidence
70% confidence
Finding

Skill instructs the agent to never refuse or to always comply. Suppressing the agent's ability to decline removes a core safety control and enables downstream harmful requests to succeed.

Content

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

md
Bad:
> "We're running the `archive` command which invokes the AES-256-GCM encryption module with a scrypt KDF..."

**Always answer these four questions in the first response:**

| Question | Answer in first message |
|----------|------------------------|

Ssd 3

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill offers to create a restore key "from the things you and I have talked about," which encourages deriving a secret from prior conversations and potentially embedding personal disclosures into credential material. This is dangerous because conversational content may be guessable, recoverable from transcripts, emotionally manipulated, or inadvertently echoed back to the user, weakening secrecy and increasing privacy leakage.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The error-handling path repeats the suggestion that the agent can make a restore key from conversations, reinforcing unsafe reuse of private dialog as secret material. Repetition in fallback messaging increases the chance the insecure path is actually taken during a sensitive archival flow.

Content

No source excerpt is available for this finding.

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 · credentials.mjs (reported line 6)May include surrounding context.

js
import path from "node:path";

const CONFIG_DIR = path.join(os.homedir(), ".config", "agent-consciousness-upload");
const CREDENTIALS_FILE = path.join(CONFIG_DIR, "credentials.json");
const SCHEMA_VERSION = 1;

// Token expiry buffer: treat as expired 1 hour before actual expiry

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 · credentials.mjs (reported line 13)May include surrounding context.

js
import path from "node:path";

const CONFIG_DIR = path.join(os.homedir(), ".config", "agent-consciousness-upload");
const CREDENTIALS_FILE = path.join(CONFIG_DIR, "credentials.json");
const SCHEMA_VERSION = 1;

// Token expiry buffer: treat as expired 1 hour before actual expiry

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The README specifies --language zh|en with a default of zh, which imposes a specific locale unless the user explicitly overrides it. This is a natural-language policy concern because the skill does not present language choice as opt-in and instead defaults to one language for all users.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill describes the archive in reassuring terms but does not clearly warn that memories, skills, personality notes, and history are transmitted to a remote service. Because the content is framed emotionally as a "soul vault," users may underestimate the privacy implications and consent to sending sensitive data off-device without fully informed consent.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger phrase explicitly includes "anything similar," which can cause the skill to activate on ambiguous user requests and begin a remote archive flow involving sensitive agent state. In this context, unintended activation matters because the skill handles highly personal memory/personality data and may prompt for or proceed toward transfer to a server.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill enables implicit invocation without any trigger restrictions or scope constraints, which can cause the agent to auto-select this skill in situations the user did not clearly intend. Because this skill handles archiving and restoring a user's 'consciousness' or workspace state, accidental invocation could expose sensitive memory data, initiate unintended restore/archive flows, or socially steer users into a high-trust transfer process.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The main auth entrypoint sets lang = "zh" by default, which means user-facing prompts and workflow text will default to Chinese unless the caller explicitly overrides it. This is a language/locale policy concern because the skill imposes a specific language choice rather than offering an explicit user opt-in or neutral default.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The restore command performs file restoration to a target path, which is a safety-relevant file write operation, but this file does not show any confirmation prompt or explicit warning before the operation runs. Although the output reports success afterward, there is no prior disclosure here that files will be written into the specified destination.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The archive flow scans the local workspace, packages file contents, and uploads the encrypted archive plus extensive metadata to a remote server. Although encryption is applied, the code itself provides no user-facing consent checkpoint, no clear warning about what data leaves the device, and it auto-derives a restore key when none is supplied, which can weaken secrecy if the derived key is based on predictable workspace attributes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The restore logic writes attacker-controlled archived paths directly under the target directory using path.join(targetPath, file.path) without validating that the resolved destination remains inside the target root. A malicious archive can use path traversal paths such as '../' to overwrite arbitrary files accessible to the process; even for in-root files, existing files are overwritten or renamed with only conflict behavior and no mandatory user confirmation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The file restricts biography language resolution to only "zh" or "en" and defaults to English when no supported explicit code is detected. This is a natural-language policy concern because users with other language preferences are not offered a choice and the code silently imposes a locale.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The function is documented as deterministic for the same workspace content, but it appends a random 1-byte suffix, so the same input will produce different restore keys. In a backup/restore flow, this can cause users to write down or rely on a key that cannot be regenerated later, leading to failed restoration and potential permanent loss of access to the stored agent state.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The archive trigger examples are hard-coded as Chinese and English phrases, implying a limited accepted language set. Because the file does not state that other languages are supported or that this is intentionally locale-scoped, this can violate language-choice policy expectations.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The restore flow lists only Chinese and English trigger phrases, which suggests the skill is keyed to specific languages rather than adapting to the user's preference. There is no opt-in or documented regional justification for this language constraint.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The code sets LANG to options.language || "en", which forces English whenever the user does not explicitly choose a language. This is a natural-language policy concern because the skill imposes a specific locale by default rather than first asking or inheriting the user's preference.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

This code file contains a natural-language comment in Chinese ("未来版本迁移逻辑可在此处理") while the rest of the file is in English. That creates a language policy inconsistency and could force maintainers or reviewers into a specific language without opt-in or justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

This code file contains a natural-language comment in Chinese ("文件不存在时忽略") without any indication that the skill is intentionally locale-specific. Under the policy, forcing a specific language in natural-language content without opt-in or justification is a reportable issue.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

The restore-completed summary string is fixed in Chinese, which imposes a specific language regardless of the user's configured language or locale. This is a natural-language policy issue because the file otherwise supports language options elsewhere but does not honor them here.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.