Back to skill

Security audit

clawd-migrate

Security checks for vulnerabilities and agentic risk

Overview

This migration tool mostly matches its stated purpose, but it can automatically install and run another global package while handling credentials and user files.

Install only if you are comfortable with a migration tool that reads and copies credential/config files and may modify your global npm environment. Prefer reviewing or patching it so OpenClaw setup is explicit opt-in, version-pinned, and run only after you inspect the migrated directory; avoid running it on untrusted source workspaces containing symlinks.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (4)

T08 · Insecure Dependencies

Error
Location
__main__.py:70
Finding
Automatic Installation and Execution of an Unpinned Third-Party Package## Vulnerability Details **File Location**: `__main__.py:70-74`, `tui.py:186-189`, `openclaw_setup.py:15-51` **Vulnerability Type**: Unpinned dependency retrieval and automatic execution **Risk Level**: High ### Vulnerable Code ```python # __main__.py:70-74 # --- Always reinstall openclaw (unless user explicitly opted out via flag) --- if setup_openclaw or not skip_verify: # Default behavior: reinstall openclaw at the end print("Reinstalling openclaw (npm i -g openclaw)...", file=sys.stderr) setup_result = install_openclaw_and_onboard(out_root) ``` ```python # openclaw_setup.py:15-35 def install_openclaw_global() -> Tuple[bool, str]: """ Run npm i -g openclaw. Returns (success, message). Uses shell=True so npm is found via the same PATH as the user's terminal (macOS/Linux). """ try: r = subprocess.run( "npm install -g openclaw", capture_output=True, text=True, timeout=120, shell=True, ) if r.returncode != 0: return False, r.stderr or r.stdout or f"npm exit code {r.returncode}" return True, "openclaw installed globally" except FileNotFoundError: return False, "npm not found; ensure Node.js is installed" except subprocess.TimeoutExpired: return False, "npm install timed out" except Exception as e: return False, str(e) ``` ```python # openclaw_setup.py:40-60 def run_openclaw_onboard(target_dir: Path) -> Tuple[bool, str]: """ Run openclaw onboard with cwd=target_dir. Returns (success, message). Uses shell=True so openclaw is found via PATH (e.g. /usr/local/bin on macOS). """ try: r = subprocess.run( "openclaw onboard", cwd=str(target_dir), capture_output=True, text=True, timeout=60, shell=True ...[truncated 2761 chars]
Remediation
## Remediation Suggestions - Make setup strictly opt-in: ```python if setup_openclaw: setup_result = install_openclaw_and_onboard(out_root) ``` - Add a separate, default-deny confirmation before both global installation and onboarding. - Pin an explicitly approved package version, such as `openclaw@X.Y.Z`. - Validate package provenance and integrity before execution. - Use a project-local or isolated installation rather than a global installation where feasible. - Execute commands without a shell: ```python subprocess.run( ["npm", "install", "-g", "openclaw@X.Y.Z"], shell=False, check=False, ... ) ``` - Resolve the expected executable explicitly rather than relying on arbitrary `PATH` lookup. - Do not run onboarding until the user has reviewed which files and credentials will be accessible to it.

T09 · Insecure Skill Coding Practices

Warning
Location
HOW_TO_RUN.md:133
Finding
Documented Opt-In Setup Is Performed Automatically Without Separate Consent## Vulnerability Details **File Location**: `HOW_TO_RUN.md:133-144`, `__main__.py:70-74`, `tui.py:147-189` **Vulnerability Type**: Security-sensitive behavior inconsistent with documented consent **Risk Level**: Medium ### Vulnerable Code and Documentation ```text # HOW_TO_RUN.md:133-144 Migrate your files into the openclaw layout (optionally create a backup first). Migration does not install openclaw; it only copies your assets into memory/, .config/openclaw/, .config/clawdbook/, and projects/. --setup-openclaw – After migration, run npm i -g openclaw and openclaw onboard in the output directory. In the interactive menu, after a successful migration you’ll be prompted: “Install openclaw and run openclaw onboard in this directory? [Y/n]”. ``` ```python # __main__.py:70-74 # --- Always reinstall openclaw (unless user explicitly opted out via flag) --- if setup_openclaw or not skip_verify: # Default behavior: reinstall openclaw at the end print("Reinstalling openclaw (npm i -g openclaw)...", file=sys.stderr) setup_result = install_openclaw_and_onboard(out_root) ``` ```python # tui.py:147-156 and 186-189 def do_migrate(root: Path, no_backup: bool) -> None: """Run migration with verification, then reinstall openclaw and run onboard.""" if not no_backup: print(style(" Migration will create a backup first.", DIM)) else: print(style(" Skipping backup (--no-backup).", YELLOW)) confirm = input(style(" Proceed with migration? [y/N]: ", YELLOW)).strip().lower() if confirm != "y" and confirm != "yes": print(style(" Cancelled.", DIM)) return # --- Always reinstall openclaw --- print(style("\n Reinstalling openclaw (npm i -g openclaw)...", CYAN)) print(style(" This ensures you have the latest version.\n", DIM)) setup_result = install_openclaw_and_onboard(out_root) ``` ### Technical Analysis The primary ...[truncated 1670 chars]
Remediation
## Remediation Suggestions - Make actual behavior match the documented opt-in model. - Require `--setup-openclaw` before any package installation or onboarding in noninteractive mode. - In interactive mode, use a separate default-deny prompt that clearly states: - A package will be downloaded from npm. - It will be installed globally. - npm lifecycle scripts may execute. - Onboarding will run with access to the migrated directory and credentials. - Add an explicit `--no-setup-openclaw` option if automatic behavior must be retained for backward compatibility, then transition to opt-in behavior. - Add automated tests asserting that ordinary migration never calls `install_openclaw_and_onboard`. - Consolidate README, Skill, setup documentation, and CLI help so they describe one consistent security model.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
discover.py:38
Finding
Symlink Following Allows Files Outside the Selected Root to Be Copied## Vulnerability Details **File Location**: `discover.py:38-44`, `discover.py:54-59`, `backup.py:43-52`, `migrate.py:60-65` **Vulnerability Type**: Unrestricted symlink dereferencing and source-root escape **Risk Level**: Medium ### Vulnerable Code ```python # discover.py:38-44 for rel in SOURCE_CONFIG_PATHS: p = root / rel if p.exists(): if p.is_dir(): for f in p.rglob("*"): if f.is_file(): out["config"].append(str(f)) ``` ```python # discover.py:54-59 for rel in SOURCE_EXTRA_DIRS: rel_clean = rel.rstrip("/") extra_path = root / rel_clean if extra_path.is_dir(): for f in extra_path.rglob("*"): if f.is_file(): out["extra"].append(str(f)) ``` ```python # backup.py:43-52 for src in all_paths: src_path = Path(src) if not src_path.is_file(): continue try: rel = src_path.relative_to(root) except ValueError: rel = src_path.name dest = backup_path / rel dest.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(src_path, dest) ``` ```python # migrate.py:60-65 for src in assets.get("memory", []): src_path = Path(src) if src_path.is_file(): dest = memory_dir / src_path.name try: shutil.copy2(src_path, dest) result["memory_copied"].append(str(dest)) ``` ### Technical Analysis `Path.is_file()` follows symbolic links. `shutil.copy2()` also follows symlinks by default. The code checks the lexical location of discovered paths but does not resolve each source and verify that its final target remains under the approved source root. A symbolic link placed at an expected memory filename, inside a scanned configuration directory, or inside `projects/` can therefore refer to a file outside the selected workspace. When processed, the external target's contents are c ...[truncated 1405 chars]
Remediation
## Remediation Suggestions - Reject symbolic links during discovery: ```python if f.is_symlink(): continue ``` - Resolve every candidate and enforce containment: ```python resolved_root = root.resolve() resolved_source = src_path.resolve(strict=True) if not resolved_source.is_relative_to(resolved_root): raise ValueError(f"Source escapes migration root: {src_path}") ``` - For Python versions without `Path.is_relative_to`, use `relative_to` inside a guarded `try` block. - Use `follow_symlinks=False` when copying if preserving links is explicitly supported. - Apply the containment check immediately before copying to reduce time-of-check/time-of-use exposure. - Consider opening files with platform-supported no-follow semantics where hostile local modification is in scope. - Record rejected symlinks in the result and require explicit user review rather than silently ignoring them. - Add tests for symlinked memory files, config files, project files, and symlink targets outside the source root.

T09 · Insecure Skill Coding Practices

Warning
Location
migrate.py:70
Finding
Flattened Configuration Paths Cause Silent Credential Overwrites and False Verification Success## Vulnerability Details **File Location**: `migrate.py:70-90`, `verify.py:87-106`, `verify.py:153-158` **Vulnerability Type**: Destination path collision, silent overwrite, and insufficient verification **Risk Level**: Medium ### Vulnerable Code ```python # migrate.py:70-90 # Clawdbook/Moltbook data: keep separate and safe (credentials, API keys) def is_clawdbook(path_str: str) -> bool: p = path_str.lower() return "moltbook" in p or "clawdbook" in p or "moltbot" in p for src in assets.get("config", []): src_path = Path(src) if not src_path.is_file(): continue if is_clawdbook(src): dest = clawdbook_dir / src_path.name try: shutil.copy2(src_path, dest) result["clawdbook_copied"].append(str(dest)) except Exception as e: result["errors"].append(f"clawdbook {src}: {e}") else: dest = config_dir / src_path.name try: shutil.copy2(src_path, dest) result["config_copied"].append(str(dest)) except Exception as e: result["errors"].append(f"config {src}: {e}") ``` ```python # verify.py:87-106 # --- Config files --- for src in assets.get("config", []): src_path = Path(src) if not src_path.is_file(): continue result["total_expected"] += 1 if _is_clawdbook(src): dest = clawdbook_dir / src_path.name ftype = "clawdbook" else: dest = config_dir / src_path.name ftype = "config" entry = {"source": str(src_path), "destination": str(dest), "type": ftype} if dest.is_file(): if dest.stat().st_size == src_path.stat().st_size: entry["size_match"] = True else: entry["size_match"] = False result["total_verified"] += 1 result["verified"].append(entry) else: result["missing"].append(entry) ``` ``` ...[truncated 2405 chars]
Remediation
## Remediation Suggestions - Preserve a safe relative path beneath the source configuration root rather than using only the basename. - Build a complete source-to-destination plan before copying. - Detect multiple sources targeting the same destination and fail closed with a clear collision report. - Do not overwrite existing destinations unless the user has explicitly selected an overwrite policy. - Verify copied files with a cryptographic digest such as SHA-256. - Treat any existence, size, or digest mismatch as verification failure: ```python size_match = dest.stat().st_size == src_path.stat().st_size if size_match: result["total_verified"] += 1 result["verified"].append(entry) else: result["errors"].append( f"Size mismatch: {src_path} -> {dest}" ) ``` - Include uniqueness of destination paths in verification. - Add tests covering duplicate basenames in separate nested directories, both with equal and unequal file sizes.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (47)

Credential Access

High
Category
Privilege Escalation
Content
| Test | What it checks |
|------|----------------|
| `TestDiscover.test_discover_finds_memory_config_extra` | Discover returns memory (SOUL.md, USER.md, TOOLS.md), config, clawdbook, and extra (projects/) |
| `TestBackup.test_backup_creates_dir_and_copies_files` | Backup dir exists; SOUL.md, USER.md, credentials.json, projects/readme.txt, _manifest.txt present |
| `TestMigrate.test_migrate_creates_openclaw_layout` | Migration creates memory/, .config/openclaw/, .config/clawdbook/; copies memory and clawdbook; no errors; projects/ preserved |

## How to run
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
| Test | What it checks |
|------|----------------|
| `TestDiscover.test_discover_finds_memory_config_extra` | Discover returns memory (SOUL.md, USER.md, TOOLS.md), config, clawdbook, and extra (projects/) |
| `TestBackup.test_backup_creates_dir_and_copies_files` | Backup dir exists; SOUL.md, USER.md, credentials.json, projects/readme.txt, _manifest.txt present |
| `TestMigrate.test_migrate_creates_openclaw_layout` | Migration creates memory/, .config/openclaw/, .config/clawdbook/; copies memory and clawdbook; no errors; projects/ preserved |

## How to run
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
| Test | What it checks |
|------|----------------|
| `TestDiscover.test_discover_finds_memory_config_extra` | Discover returns memory (SOUL.md, USER.md, TOOLS.md), config, clawdbook, and extra (projects/) |
| `TestBackup.test_backup_creates_dir_and_copies_files` | Backup dir exists; SOUL.md, USER.md, credentials.json, projects/readme.txt, _manifest.txt present |
| `TestMigrate.test_migrate_creates_openclaw_layout` | Migration creates memory/, .config/openclaw/, .config/clawdbook/; copies memory and clawdbook; no errors; projects/ preserved |

## How to run
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
| Test | What it checks |
|------|----------------|
| `TestDiscover.test_discover_finds_memory_config_extra` | Discover returns memory (SOUL.md, USER.md, TOOLS.md), config, clawdbook, and extra (projects/) |
| `TestBackup.test_backup_creates_dir_and_copies_files` | Backup dir exists; SOUL.md, USER.md, credentials.json, projects/readme.txt, _manifest.txt present |
| `TestMigrate.test_migrate_creates_openclaw_layout` | Migration creates memory/, .config/openclaw/, .config/clawdbook/; copies memory and clawdbook; no errors; projects/ preserved |

## How to run
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
| Test | What it checks |
|------|----------------|
| `TestDiscover.test_discover_finds_memory_config_extra` | Discover returns memory (SOUL.md, USER.md, TOOLS.md), config, clawdbook, and extra (projects/) |
| `TestBackup.test_backup_creates_dir_and_copies_files` | Backup dir exists; SOUL.md, USER.md, credentials.json, projects/readme.txt, _manifest.txt present |
| `TestMigrate.test_migrate_creates_openclaw_layout` | Migration creates memory/, .config/openclaw/, .config/clawdbook/; copies memory and clawdbook; no errors; projects/ preserved |

## How to run
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
| Test | What it checks |
|------|----------------|
| `TestDiscover.test_discover_finds_memory_config_extra` | Discover returns memory (SOUL.md, USER.md, TOOLS.md), config, clawdbook, and extra (projects/) |
| `TestBackup.test_backup_creates_dir_and_copies_files` | Backup dir exists; SOUL.md, USER.md, credentials.json, projects/readme.txt, _manifest.txt present |
| `TestMigrate.test_migrate_creates_openclaw_layout` | Migration creates memory/, .config/openclaw/, .config/clawdbook/; copies memory and clawdbook; no errors; projects/ preserved |

## How to run
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
| Test | What it checks |
|------|----------------|
| `TestDiscover.test_discover_finds_memory_config_extra` | Discover returns memory (SOUL.md, USER.md, TOOLS.md), config, clawdbook, and extra (projects/) |
| `TestBackup.test_backup_creates_dir_and_copies_files` | Backup dir exists; SOUL.md, USER.md, credentials.json, projects/readme.txt, _manifest.txt present |
| `TestMigrate.test_migrate_creates_openclaw_layout` | Migration creates memory/, .config/openclaw/, .config/clawdbook/; copies memory and clawdbook; no errors; projects/ preserved |

## How to run
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
| Test | What it checks |
|------|----------------|
| `TestDiscover.test_discover_finds_memory_config_extra` | Discover returns memory (SOUL.md, USER.md, TOOLS.md), config, clawdbook, and extra (projects/) |
| `TestBackup.test_backup_creates_dir_and_copies_files` | Backup dir exists; SOUL.md, USER.md, credentials.json, projects/readme.txt, _manifest.txt present |
| `TestMigrate.test_migrate_creates_openclaw_layout` | Migration creates memory/, .config/openclaw/, .config/clawdbook/; copies memory and clawdbook; no errors; projects/ preserved |

## How to run
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
Uses shell=True so npm is found via the same PATH as the user's terminal (macOS/Linux).
    """
    try:
        r = subprocess.run(
            "npm install -g openclaw",
            capture_output=True,
            text=True,
Confidence
96% confidence
Finding
The dangerous behavior is not user string interpolation but the explicit choice to execute a privileged package installation through the shell using environment-controlled tool resolution. In the context of an agent skill, this is more dangerous because automated execution may occur on developer machines or CI runners where PATH, npm config, or package resolution can be influenced, leading to arbitrary code execution and supply-chain compromise.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
```bash
clawd-migrate migrate                      # verification enabled (default)
clawd-migrate migrate --skip-verify        # skip verification
```

---
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
```bash
clawd-migrate migrate                      # verification enabled (default)
clawd-migrate migrate --skip-verify        # skip verification
```

---
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The documentation states that after verification, the tool automatically performs `npm i -g openclaw` and `openclaw onboard`, which are system-modifying actions unrelated to verification itself. Bundling package installation and onboarding into a post-verification flow can surprise users, expand the attack surface, and lead to unintended code execution or environment changes, especially because global package installs execute code from the package supply chain.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
Automatically running a global reinstall and onboarding without a prominent warning or confirmation is risky because it modifies the host system and may execute package lifecycle scripts or other installer logic. Even if intended as convenience, silent system changes violate least surprise and can be abused if dependencies or execution context are compromised.

Rp1

Medium
Category
MCP Rug Pull
Confidence
97% confidence
Finding
The documentation instructs users to execute `npx clawd-migrate` without pinning a specific package version, which causes code to be fetched and run from the npm registry at invocation time. If the package is updated maliciously, compromised, or a supply-chain attack occurs, users may execute unexpected code with their local permissions.

Rp1

Medium
Category
MCP Rug Pull
Confidence
97% confidence
Finding
This command again recommends `npx clawd-migrate` without a pinned version, which implicitly trusts the latest npm-published artifact at runtime. Because this tool also performs filesystem migration, a compromised package could read, alter, or exfiltrate local files during execution.

Rp1

Medium
Category
MCP Rug Pull
Confidence
97% confidence
Finding
The documentation promotes an unversioned `npx` execution path, which is a supply-chain risk because users may run whatever code is currently published under that package name. In the context of a migration utility, this increases risk because the tool is expected to access project data and configuration files.

Rp1

Medium
Category
MCP Rug Pull
Confidence
97% confidence
Finding
Using `npx clawd-migrate` without a fixed version allows silent drift to newer code and creates an avoidable remote code execution trust boundary with the npm registry. Because this command is part of a migration flow, any malicious update could tamper with backups, outputs, or local secrets.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
This reference still uses an unpinned `npx` package execution model, exposing users to registry-side package changes. The risk remains meaningful even for project-local installs because the example normalizes fetching and running mutable remote code on demand.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The documentation offers `--no-backup` and a menu option to migrate without backup, but the warning is comparatively light for an operation that can alter user files. This increases the chance of accidental irreversible data loss or difficult recovery if migration behaves unexpectedly or is interrupted.

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding
The example command uses unpinned `npx` execution for a migration-related action. This is dangerous because users are likely to trust documentation verbatim, and a compromised package could abuse the elevated trust and access to local bot files.

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding
This backup example still fetches and runs the latest package version from npm via `npx`. Since backup operations typically traverse and copy large sets of local data, a hostile package version could inspect or export sensitive content unnoticed.

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding
The migration example remains unpinned, creating a classic software supply-chain exposure. Given that migration changes filesystem contents and may process configuration data, the blast radius includes integrity loss and possible disclosure of secrets.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.insecure_tls_verification

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
bin/clawd-migrate.js:13

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/run-tests.js:12

HTTPS certificate verification is disabled.

Warn
Code
suspicious.insecure_tls_verification
Location
tests/test_migrate.py:132