Back to skill

Security audit

clawd-migrate

Security checks for vulnerabilities and agentic risk

Overview

This migration skill is mostly purpose-aligned, but it automatically performs high-impact global installs and can mishandle sensitive local files during migration.

Install only after reviewing the package and running it on a copy of your bot directory. Expect it to copy credentials/API keys and create backups; inspect for symlinks first, avoid shared output locations, and prefer pinned versions or manual OpenClaw installation/onboarding instead of the automatic global reinstall path.

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

T08 · Insecure Dependencies

Error
Location
__main__.py:70
Finding
Default Migration Executes an Unpinned Global Package Through Shell-Based PATH Resolution## Vulnerability Details **File Location**: `__main__.py:70-73`, `openclaw_setup.py:21-27`, `openclaw_setup.py:45-52`, `tui.py:186-190` **Vulnerability Type**: Unpinned dependency installation and unsafe executable resolution **Risk Level**: High ### Vulnerable Code ```python # __main__.py:70-73 # --- 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:21-27 r = subprocess.run( "npm install -g openclaw", capture_output=True, text=True, timeout=120, shell=True, ) ``` ```python # openclaw_setup.py:45-52 r = subprocess.run( "openclaw onboard", cwd=str(target_dir), capture_output=True, text=True, timeout=60, shell=True, ) ``` ```python # tui.py:186-190 # --- 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 noninteractive migration command installs OpenClaw when the following condition is true: ```python setup_openclaw or not skip_verify ``` Because verification is enabled by default, `skip_verify` is normally false and `not skip_verify` is true. Consequently, a standard migration invokes the installation even when the user did not provide the apparently opt-in `--setup-openclaw` option. The command installs the latest available version of `openclaw` globally without a pinned version or integrity constraint. It then invokes both `npm` and `openclaw` through `shell=True`, allowing the shell to resolve those executable names from the invoking proce ...[truncated 2173 chars]
Remediation
## Remediation Suggestions 1. Make OpenClaw setup strictly opt-in: ```python if setup_openclaw: setup_result = install_openclaw_and_onboard(out_root) ``` 2. Add a separate explicit `--no-setup-openclaw` option only if backward compatibility requires automatic behavior, and issue a clear confirmation prompt before any global installation. 3. Replace shell command strings with argument arrays and disable the shell: ```python subprocess.run( ["npm", "install", "--global", "openclaw@AUDITED_VERSION"], shell=False, check=False, capture_output=True, text=True, timeout=120, ) ``` 4. Resolve executables with `shutil.which`, reject suspicious or unexpected executable locations, and document the resolved path before execution. 5. Pin OpenClaw to a reviewed version. Where supported, verify package integrity or use a lockfile and trusted registry configuration. 6. Prefer a project-local or isolated installation over a global installation. 7. Separate migration from onboarding so copying files never implicitly executes third-party code. 8. Correct the documentation so installation behavior, network access, global modifications, and consent requirements are stated consistently.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
discover.py:40
Finding
Symlink Following Allows Files Outside the Selected Migration Root to Be Copied## Vulnerability Details **File Location**: `discover.py:40-42`, `discover.py:60-62`, `backup.py:43-52`, `migrate.py:60-65`, `migrate.py:74-82`, `migrate.py:94-107` **Vulnerability Type**: Missing symlink and source-root containment validation **Risk Level**: Medium ### Vulnerable Code ```python # discover.py:40-42 for f in p.rglob("*"): if f.is_file(): out["config"].append(str(f)) ``` ```python # discover.py:60-62 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:74-82 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)) ``` ```python # migrate.py:94-107 for src in assets.get("extra", []): src_path = Path(src) if src_path.is_file(): try: rel = src_path.relative_to(root) except ValueError: rel = Path("projects") / src_path.name dest = out_root / rel if src_path.resolve() == dest.resolve(): continue # in-place: same file, skip copy dest.parent.mkdir(parents=True, exist_ok=True) try: shutil.copy2(src_path, dest) result["config_copied"].append(str(dest)) ``` ### Technical Analysis Recursive discovery uses `Path.rglob` and accepts entries for which `Path.is_file()` returns true. ...[truncated 2357 chars]
Remediation
## Remediation Suggestions 1. Reject symbolic links during discovery: ```python if f.is_symlink(): continue if f.is_file(): ... ``` 2. Enforce resolved-path containment immediately before every copy: ```python root_resolved = root.resolve() source_resolved = src_path.resolve(strict=True) try: source_resolved.relative_to(root_resolved) except ValueError: raise ValueError(f"Source escapes migration root: {src_path}") ``` 3. Perform both the symlink check and containment check as close as possible to the copy operation to reduce time-of-check/time-of-use exposure. 4. Reject outside-root entries in caller-provided `asset_paths` instead of falling back to `src_path.name`. 5. Consider opening source files with platform-appropriate no-follow semantics where available if hostile concurrent modification is in scope. 6. Add tests covering symlinked files, symlinked directories, broken links, outside-root asset paths, and links changed between discovery and copying. 7. Report skipped unsafe paths clearly so users know the migration was intentionally incomplete.

T09 · Insecure Skill Coding Practices

Warning
Location
migrate.py:74
Finding
Flattened Configuration Destinations Cause Silent Overwrites and False Verification Success## Vulnerability Details **File Location**: `migrate.py:74-89`, `verify.py:89-112`, `verify.py:155-159` **Vulnerability Type**: Destination filename collision and incomplete integrity verification **Risk Level**: Medium ### Vulnerable Code ```python # migrate.py:74-89 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:89-112 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) ``` ```python # verify.py:155-159 result["passed"] = ( result["total_verified"] == result["total_expected"] and len(result["missing"]) == 0 and len(result["errors"]) == 0 ) ``` ### Technical Analysis Config files are recu ...[truncated 2427 chars]
Remediation
## Remediation Suggestions 1. Preserve each source file's relative path beneath its declared configuration root rather than flattening all files to their basenames. 2. Build the complete source-to-destination mapping before copying and reject any duplicate destination: ```python if dest in planned_destinations: raise ValueError( f"Destination collision: {src_path} and " f"{planned_destinations[dest]} both map to {dest}" ) planned_destinations[dest] = src_path ``` 3. Do not overwrite an existing destination unless the user explicitly authorizes a documented conflict-resolution policy. 4. Treat size mismatches as verification failures: ```python if dest.is_file() and dest.stat().st_size == src_path.stat().st_size: result["total_verified"] += 1 result["verified"].append(entry) else: result["missing"].append(entry) ``` 5. Compare a cryptographic digest, such as SHA-256, for every source and destination instead of relying only on file size. 6. Require a one-to-one relationship between source files and destination files in the final verification result. 7. Add tests for duplicate basenames in separate directories, equal-sized files with different contents, different-sized collisions, pre-existing destination files, and verification of cryptographic mismatches.
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 (52)

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
94% confidence
Finding
This is a tool-parameter abuse issue because the skill directly executes a high-impact system command that installs and runs external software, with shell=True and no integrity validation or consent mechanism. In the context of an agent skill, this effectively grants the skill the ability to alter the system state and execute untrusted package lifecycle scripts, making compromise through supply chain or PATH manipulation significantly more dangerous.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The documentation explicitly states that credentials and API keys are migrated and stored under a new configuration path, but it gives no warning about handling secrets, backup exposure, file permissions, or the privacy risks of copying sensitive material between systems. In a migration tool context, this omission can lead users or downstream implementers to expose secrets unintentionally through backups, logs, shared directories, or weak destination permissions.

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
93% confidence
Finding
The documentation for a verification feature also discloses that the tool automatically performs unrelated system-modifying actions: a global npm reinstall and an onboarding step. Bundling verification with package installation changes user expectations and can lead to unexpected code execution or environment modification, especially because global installs may run lifecycle scripts and alter system state.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation states that a global reinstall and onboarding run automatically, but does not present a warning commensurate with the risk of modifying the host environment. Automatically invoking package management and setup commands can execute third-party code, change PATH-visible binaries, and affect user or CI systems without informed consent.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
The documentation instructs users to execute `npx clawd-migrate` without pinning a specific package version. This causes code to be fetched and executed from the npm registry at runtime, so a compromised latest release, typosquat, or supply-chain takeover could result in arbitrary code execution on the user's machine.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
This command again directs users to run an unpinned npm package via `npx`, which executes whatever version is current in the registry. If the package or dependency chain is compromised, the user may unknowingly run attacker-controlled code.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
Using `npx clawd-migrate backup` without a version pin creates a supply-chain risk because `npx` resolves and executes the latest available package. That is especially sensitive here because the tool processes local files and backups, so arbitrary package code would have access to potentially sensitive data.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
This example promotes running the migrate command from an unpinned package version, exposing users to arbitrary code execution through a registry compromise or malicious update. Because migration touches project content and configuration, the blast radius includes theft or modification of local data.

Rp1

Medium
Category
MCP Rug Pull
Confidence
86% confidence
Finding
The install command `npm install clawd-migrate` is also unpinned and may pull an unexpected or compromised latest version. While install-time risk is common in package ecosystems, documenting an unversioned install still weakens reproducibility and exposes users to supply-chain attacks.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
This example again has users execute an unpinned package directly from npm. Any compromise of the package namespace, release process, or dependency chain could lead to immediate code execution on the user's system.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
This backup example uses an unpinned `npx` invocation, which is risky because the package is downloaded and executed at the time the command is run. Since the operation may access and copy sensitive user files, a malicious package could exfiltrate them.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
The migration example invokes the package without a version pin, creating a straightforward supply-chain execution risk. In this context the danger is elevated because migration tooling typically reads, writes, and reorganizes many local files, increasing the potential for tampering or data theft.

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