Back to skill

Security audit

EasyClaw Brain Migration

Security checks for vulnerabilities and agentic risk

Overview

The skill has a legitimate migration purpose, but its import script can overwrite active OpenClaw memory and copy untrusted legacy files into agent-accessible storage.

Install only if you intend to migrate EasyClaw agent memory into OpenClaw. Prefer running the report and staging steps first, manually review staged memory and prompt files, and avoid --import-memory unless you accept that it may replace active MEMORY.md and make old instructions persistent in future sessions.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • 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 (2)

T02 · Agent Memory Poisoning

Error
Location
scripts/stage_easyclaw_brain.py:71
Finding

Unvalidated Legacy Content Can Poison Persistent Agent Memory and Overwrite Active Memory

Content
View full analysis

Vulnerability Details

File Location: scripts/stage_easyclaw_brain.py:71-98
Vulnerability Type: Unvalidated persistent memory import
Risk Level: High

Vulnerable Code

python
src_memory = SRC / 'MEMORY.md'
dst_memory = DST / 'MEMORY.md'
if src_memory.exists() and dst_memory.exists():
    b = backup(dst_memory)
    print(f'Backed up existing MEMORY.md -> {b}')
if src_memory.exists():
    copy_if_exists(src_memory, dst_memory)
    print(f'Imported MEMORY.md -> {dst_memory}')

src_mem_dir = SRC / 'memory'
dst_mem_dir = DST / 'memory'
if src_mem_dir.exists():
    dst_mem_dir.mkdir(parents=True, exist_ok=True)
    imported = 0
    skipped = 0
    for src_file in src_mem_dir.rglob('*'):
        if src_file.is_dir():
            continue
        rel = src_file.relative_to(src_mem_dir)
        dst_file = dst_mem_dir / rel
        if dst_file.exists():
            skipped += 1
            continue
        ensure_parent(dst_file)
        shutil.copy2(src_file, dst_file)
        imported += 1
    print(f'Imported memory files: {imported}, skipped existing: {skipped}')

Technical Analysis

When --import-memory is supplied, the script copies legacy MEMORY.md and files beneath the legacy memory/ directory directly into the active OpenClaw workspace. It does not inspect the content for prompt injection, behavioral directives, fabricated facts, secret-exfiltration instructions, or other attacker-controlled rules.

If an active MEMORY.md already exists, the script creates a backup and then replaces the active file. This behavior also conflicts with the statement in SKILL.md:75-78 that the operation “copies MEMORY.md if missing,” potentially causing users to underestimate the destructive and security-sensitive nature of the operation.

Because these files are intended to provide long-term Agent state, malicious instructions placed in them may continue to influence fu ...[truncated 1431 chars]

Remediation
View remediation

Remediation Suggestions

  1. Never replace active MEMORY.md automatically. Import it only when the destination is absent, or require an explicit overwrite option and confirmation.
  2. Stage all legacy memory before activation and show a content diff to the user.
  3. Require explicit approval for each file selected for active import.
  4. Treat legacy memory as untrusted input and scan for instruction-like content, embedded tool directives, secret-exfiltration requests, and suspicious links.
  5. Separate informational memories from executable behavioral instructions. Do not automatically activate content classified as instructions.
  6. Consider importing legacy data into a non-authoritative archive that is not loaded automatically by the Agent.
  7. Update SKILL.md to state accurately that the current implementation replaces an existing MEMORY.md, or change the implementation to match the documented “if missing” policy.
  8. Preserve backups using collision-resistant names and report a clear restoration procedure before any approved replacement.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/stage_easyclaw_brain.py:35
Finding

Unchecked Symlinks Can Copy Sensitive Files from Outside the Legacy Workspace

Content
View full analysis

Vulnerability Details

File Location: scripts/stage_easyclaw_brain.py:35-40, 58-64
Vulnerability Type: Unrestricted symlink traversal during file and directory copying
Risk Level: Medium

Vulnerable Code

python
def copy_if_exists(src: Path, dst: Path) -> bool:
    if not src.exists():
        return False
    ensure_parent(dst)
    shutil.copy2(src, dst)
    return True
python
# Stage directories for reference
for rel in ['memory', '.memos']:
    src_dir = SRC / rel
    if src_dir.exists():
        dst_dir = STAGE / rel
        if dst_dir.exists():
            shutil.rmtree(dst_dir)
        shutil.copytree(src_dir, dst_dir)
        print(f'- {rel}/ -> {dst_dir}')

Technical Analysis

The script assumes that paths beneath ~/.easyclaw/workspace refer only to files physically contained in that workspace. It does not reject symbolic links or verify that each resolved source path remains below the canonical SRC directory.

shutil.copy2() follows a source-file symbolic link by default. In addition, shutil.copytree() uses symlinks=False by default, meaning that the contents referenced by symbolic links are copied rather than preserving the links. Consequently, a malicious legacy workspace can make a nominal migration file or a file inside memory/ or .memos/ point to unrelated data elsewhere on the filesystem.

The operation runs with the invoking user's permissions, so it can read any linked file available to that user and place a copy in the OpenClaw workspace.

Attack Path

  1. An attacker gains the ability to modify the legacy EasyClaw workspace.
  2. The attacker replaces an expected migration file, such as AGENTS.md, with a symbolic link to a sensitive file readable by the user.
  3. Alternatively, the attacker places symbolic links inside memory/ or .memos/ that point to sensitive files or directories outside the legacy workspace.
  4. The user ...[truncated 994 chars]
Remediation
View remediation

Remediation Suggestions

  1. Reject symbolic links at every level of the source tree by checking Path.is_symlink() or using lstat() before copying.
  2. Resolve each source path and verify that it remains under the canonical legacy workspace root:
    python
    resolved_src = src.resolve(strict=True)
    resolved_root = SRC.resolve(strict=True)
    if not resolved_src.is_relative_to(resolved_root):
        raise ValueError(f'Source escapes legacy workspace: {src}')
    
  3. Apply containment checks to every recursively discovered file, not only the top-level source directory.
  4. Copy only regular files and explicitly reject devices, sockets, FIFOs, and other special filesystem objects.
  5. Replace the default copytree() behavior with a guarded traversal. If links must be retained for archival purposes, use symlinks=True and ensure they cannot later be followed from the staging area.
  6. Produce a pre-copy manifest containing resolved paths, file types, and sizes, and require review when a path is unusual.
  7. Apply file-count and total-size limits to prevent unexpectedly large linked directory trees from being copied.
  8. Add automated tests covering top-level symlinks, nested symlinks, links escaping SRC, broken links, and special files.
Vulnerability Patterns
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (6)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill instructs the agent to run migration scripts and copy files into the current workspace, which implies file-write capability, but it declares no explicit tool scope or permissions. That mismatch is dangerous because an agent or reviewer cannot tell from the manifest what filesystem access the skill expects, increasing the chance of overbroad execution and unintended modification of sensitive workspace files.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

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

md
## Rebuild manually in OpenClaw

These represent automation wiring rather than plain memory:
- `~/Library/LaunchAgents/com.easyclaw.*.plist`
- `~/.easyclaw/workspace/scripts/*.sh` and automation runners
- heartbeat-driven queues like `.evolution-tasks/`, `.killerapp-tasks/`, `.influencer-tasks/`

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

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

md
## Rebuild manually in OpenClaw

These represent automation wiring rather than plain memory:
- `~/Library/LaunchAgents/com.easyclaw.*.plist`
- `~/.easyclaw/workspace/scripts/*.sh` and automation runners
- heartbeat-driven queues like `.evolution-tasks/`, `.killerapp-tasks/`, `.influencer-tasks/`

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · references/mapping.md (reported line 24)May include surrounding context.

md
## Rebuild manually in OpenClaw

These represent automation wiring rather than plain memory:
- `~/Library/LaunchAgents/com.easyclaw.*.plist`
- `~/.easyclaw/workspace/scripts/*.sh` and automation runners
- heartbeat-driven queues like `.evolution-tasks/`, `.killerapp-tasks/`, `.influencer-tasks/`

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/report_easyclaw_brain.py (reported line 51)May include surrounding context.

python
## Rebuild manually in OpenClaw

These represent automation wiring rather than plain memory:
- `~/Library/LaunchAgents/com.easyclaw.*.plist`
- `~/.easyclaw/workspace/scripts/*.sh` and automation runners
- heartbeat-driven queues like `.evolution-tasks/`, `.killerapp-tasks/`, `.influencer-tasks/`

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/report_easyclaw_brain.py (reported line 53)May include surrounding context.

python
## Rebuild manually in OpenClaw

These represent automation wiring rather than plain memory:
- `~/Library/LaunchAgents/com.easyclaw.*.plist`
- `~/.easyclaw/workspace/scripts/*.sh` and automation runners
- heartbeat-driven queues like `.evolution-tasks/`, `.killerapp-tasks/`, `.influencer-tasks/`