Back to skill

Security audit

OpenClaw Migrator

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real OpenClaw migration tool, but it can overwrite persistent agent state and its archive/password handling is not safely scoped enough for automatic approval.

Review before installing. Only use this with archives you created and trust, avoid command-line passwords, restore into a clean or temporary directory first, and keep backups of existing OpenClaw state. The tool is not evidence of malware, but its restore behavior and secret-handling choices are risky for sensitive agent migrations.

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

T09 · Insecure Skill Coding Practices

Warning
Location
src/restore.js:81
Finding
Archive Contents Are Extracted Before AES-GCM Authentication Completes<![CDATA[ ## Vulnerability Details **File Location**: `src/restore.js:81-103` **Vulnerability Type**: Authenticated decryption performed after filesystem modification **Risk Level**: Medium ### Vulnerable Code ```js splitter.on('tag', (tag) => { try { decipher.setAuthTag(tag); } catch (e) { reject(new Error('Invalid auth tag')); } }); const extractor = tar.x({ cwd: targetDir, onentry: (entry) => { console.log(`Extracting: ${entry.path}`); } }); decipher.on('error', () => reject(new Error('Decryption failed (Wrong password or corrupted archive)'))); extractor.on('error', reject); extractor.on('end', () => { console.log('🔓 Decryption & Extraction complete.'); resolve(); }); input.pipe(splitter).pipe(decipher).pipe(extractor); ``` ### Technical Analysis AES-GCM provides authenticity only after the complete ciphertext has been processed and the authentication tag has been verified. However, the implementation streams plaintext emitted by `decipher` directly into `tar.x()`. The TAR extractor can therefore create or overwrite files in `targetDir` before AES-GCM authentication finishes. If the supplied archive is corrupted, truncated, encrypted under a different password, or deliberately modified, the decipher may emit unauthenticated plaintext before eventually reporting an authentication failure. Rejecting the Promise does not roll back filesystem changes already made by the extractor. Consequently, an unsuccessful import can still leave the destination in a partially modified state. ### Attack Path 1. An attacker obtains or produces a modified `.oca` archive and supplies it to the victim as a migration backup. 2. The victim invokes `migrator import` with the archive. 3. The program begins decrypting the ciphertext and immediately streams produced plaintext to the TAR extractor. 4. The extractor creates or overwrites files under the selected destination before the final GCM tag is validated. 5. Final authentication fails and ...[truncated 972 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not stream unauthenticated plaintext directly into the final destination. 2. Decrypt the archive into a securely created temporary file first. 3. Wait for the decipher stream to complete successfully, including final GCM authentication, before passing the resulting TAR archive to `tar.x()`. 4. Extract the authenticated archive into a separate temporary directory rather than directly into the destination. 5. Validate extracted entries and expected archive structure before installation. 6. Move validated files into the final destination only after both authentication and extraction succeed. Use atomic rename operations where possible. 7. Remove all temporary files and directories on authentication, extraction, or installation failure. 8. Consider creating a backup of files that will be replaced so a failed installation phase can be rolled back. 9. Bind the unencrypted archive header to the ciphertext using AES-GCM additional authenticated data, or include equivalent validated metadata inside the encrypted content. A safer sequence is: ```text Encrypted archive -> secure temporary encrypted/decrypted storage -> complete AES-GCM tag verification -> temporary extraction directory -> archive structure validation -> atomic installation into destination ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/index.js:10
Finding
Encryption Passwords Are Accepted and Documented as Command-Line Arguments<![CDATA[ ## Vulnerability Details **File Location**: `src/index.js:10-30` **Additional Locations**: `src/index.js:46-50`, `README.md:25-34`, `README.md:56-64`, `SKILL.md:18-27` **Vulnerability Type**: Sensitive information exposure through process arguments and shell history **Risk Level**: Medium ### Vulnerable Code ```js // Helper to get password function getPassword(options) { if (options.password) return options.password; if (process.env.MIGRATOR_PASSWORD) return process.env.MIGRATOR_PASSWORD; console.error("❌ Error: Password required. Use --password or set MIGRATOR_PASSWORD env var."); process.exit(1); } program .name('migrator') .description('Securely migrate OpenClaw agents.') .version('0.1.0'); program.command('export') .description('Export agent state to an encrypted archive') .option('-o, --output <path>', 'Output archive path', 'agent-backup.oca') .option('-p, --password <string>', 'Encryption password') .option('--source <paths...>', 'Source directories', [ path.join(process.env.HOME, '.openclaw'), path.join(process.env.HOME, 'clawd') ]) ``` The import command exposes the same option: ```js program.command('import') .description('Restore agent state from an archive') .requiredOption('-i, --input <path>', 'Input archive path') .option('-p, --password <string>', 'Decryption password') .option('-d, --dest <path>', 'Destination directory (defaults to HOME)', process.env.HOME) ``` The documented usage explicitly encourages placing the secret on the command line: ```bash migrator export --out my-agent.oca --password "secret" migrator import --in my-agent.oca --password "secret" ``` ### Technical Analysis Command-line arguments are not an appropriate secret-input mechanism. Depending on the operating system and surrounding tooling, process arguments can be exposed through process inspection interfaces, monitoring agents, audit logs, terminal recording, diagnostic reports, or command telemetr ...[truncated 1871 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make a hidden interactive prompt the default password-input mechanism when a TTY is available. 2. Disable terminal echo while reading the password and clear the in-memory input buffer when practical. 3. Remove password-bearing commands from `README.md` and `SKILL.md`. 4. Deprecate or remove `--password`. If retained for compatibility, display a clear warning that command-line secrets may be recorded or inspected. 5. Support protected non-interactive input through a file descriptor, standard input with an explicit option, or integration with an operating-system secret manager. 6. If password-file support is added, verify that the file is a regular file and reject overly permissive file modes where supported. 7. Document the exposure risks of `MIGRATOR_PASSWORD`, particularly in CI systems, process environments, crash reports, and debug logs. 8. Ensure errors and diagnostic output never reproduce the supplied password. 9. Recommend a strong, unique archive password because the encrypted archive can be copied and subjected to offline password guessing. A safer interactive interface would resemble: ```bash migrator export --out my-agent.oca Archive password: [hidden input] Confirm password: [hidden input] ``` ]]>
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (8)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared purpose is secure migration of real agent state, but the detected behavior reportedly does not perform actual migration and instead writes synthetic fixtures with hardcoded mock credentials and paths. In a security-sensitive migration context, this mismatch is dangerous because operators may trust the skill to safely handle real secrets and state while it behaves unexpectedly, potentially overwriting data, leaking assumptions into production workflows, or masking incomplete or deceptive functionality.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The README instructs users to export and import agent configuration, memory, and skills, which are likely to contain sensitive data such as secrets, personal information, API keys, or untrusted code, but it does not warn about those risks. It also omits cautions about restoring over an existing environment, which could overwrite trusted state or import malicious skills from a transferred archive, making unsafe use more likely.

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill declares no explicit tool scope or permissions while static analysis detected environment access capability. For a migration skill that handles sensitive files like configuration and auth tokens, undeclared env access expands the trust boundary and can expose secrets or enable behavior not visible from the manifest, making review and least-privilege enforcement difficult.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"author": "Lucas <lucas.xuan>",
  "license": "MIT",
  "dependencies": {
    "archiver": "^7.0.0",
    "tar": "^7.4.3",
    "fs-extra": "^11.2.0",
    "commander": "^12.0.0"
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"license": "MIT",
  "dependencies": {
    "archiver": "^7.0.0",
    "tar": "^7.4.3",
    "fs-extra": "^11.2.0",
    "commander": "^12.0.0"
  }
Confidence
84% confidence
Finding
The tar dependency is specified with a caret range, and the finding notes many known advisories affecting tar across versions. In a migration/backup skill, archive extraction and creation are core functions, so leaving the installed tar version floating makes it plausible that vulnerable releases could be installed, enabling archive-based attacks such as path traversal, overwrite, or denial of service.

Unverifiable Dependency: tar has 16 known advisory(ies) (CVE-2026-59873 (node-tar: Decompression/parse DoS via unlimited input); CVE-2025-64118 (node-tar has a race condition leading to uninitialized memory exposure); CVE-2026-24842 (node-tar Vulnerable to Arbitrary File Creation/Overwrite via Hardlink Path Trave) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
92% confidence
Finding
This manifest includes tar without pinning to a verifiably safe version, while the analyzer reports numerous advisories against that package. Because this skill's stated purpose is migration of config, memory, and skills between machines, archive handling is likely security-sensitive, so an affected tar release could expose users to malicious archives that cause file overwrite, traversal, memory exposure, or denial of service.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"dependencies": {
    "archiver": "^7.0.0",
    "tar": "^7.4.3",
    "fs-extra": "^11.2.0",
    "commander": "^12.0.0"
  }
}
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"archiver": "^7.0.0",
    "tar": "^7.4.3",
    "fs-extra": "^11.2.0",
    "commander": "^12.0.0"
  }
}
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Static analysis

No suspicious patterns detected.