Back to skill

Security audit

ClawHub Skill Installer

Security checks for vulnerabilities and agentic risk

Overview

This skill is a skill installer, but it has serious unsafe installation paths that can overwrite skills, run unpinned external commands, and install unverified GitHub search results.

Review this carefully before installing. It can permanently change your active OpenClaw skills directory, overwrite existing skills, and install code from remote archives that are not cryptographically verified. Avoid using it with untrusted skill names or elevated privileges, and prefer an installer that pins sources, verifies package identity and hashes, validates paths, and asks before overwriting anything.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
install.cjs:110
Finding
Command Injection Through CLI Fallback Commands<![CDATA[ ## Vulnerability Details **File Location**: `install.cjs`, lines 110-113 and 233-238 **Vulnerability Type**: OS command injection through unsanitized shell interpolation **Risk Level**: High ### Vulnerable Code ```javascript const output = execSync(`npx clawhub search ${query} 2>&1`, { encoding: 'utf-8', cwd: SKILLS_DIR }); ``` ```javascript execSync(`npx clawhub install ${skillName} --force`, { encoding: 'utf-8', cwd: SKILLS_DIR, stdio: 'inherit' }); ``` ### Technical Analysis The `query` and `skillName` values originate from command-line arguments and are inserted directly into command strings passed to `execSync`. Because `execSync` executes the string through a shell, shell metacharacters contained in either value are interpreted as command syntax rather than literal argument data. The vulnerable commands are reached when the corresponding direct API operation fails. No allowlist validation or shell escaping is applied before execution. ### Attack Path 1. An attacker persuades a user or automation process to invoke `search`, `install`, or `install-batch` with a crafted query or skill name containing shell metacharacters. 2. The attacker causes or waits for the ClawHub API request or installation process to fail. 3. The error handler invokes the ClawHub CLI fallback. 4. The crafted value is interpolated into the shell command. 5. The shell interprets the injected syntax and executes attacker-selected local commands under the installer's user account. ### Impact Assessment Successful exploitation provides arbitrary command execution with all privileges held by the user running the installer. This can allow reading or modifying user-accessible files, stealing credentials, altering OpenClaw configuration, installing malicious skills, or fully compromising the account. If the installer is run with elevated privileges, the impact extends to system-level compromise. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Replace shell-based `execSync` calls with `execFileSync` or `spawnSync`, passing each argument separately: ```javascript execFileSync('npx', ['clawhub', 'search', query], { encoding: 'utf8', cwd: SKILLS_DIR }); execFileSync('npx', ['clawhub', 'install', skillName, '--force'], { cwd: SKILLS_DIR, stdio: 'inherit' }); ``` - Validate skill identifiers against a strict allowlist, such as `^[A-Za-z0-9._-]+$`. - Reject control characters, shell metacharacters, path separators, and traversal sequences. - Avoid invoking `npx` dynamically where possible. Resolve and execute a trusted, preinstalled ClawHub binary. - Treat API failure as an explicit error unless fallback behavior is necessary and securely implemented. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
install.cjs:165
Finding
Path Traversal Enables Filesystem Escape and Recursive Deletion<![CDATA[ ## Vulnerability Details **File Location**: `install.cjs`, lines 165-177 and 192-201 **Vulnerability Type**: Path traversal and unsafe recursive filesystem operations **Risk Level**: High ### Vulnerable Code ```javascript const zipPath = path.join(TEMP_DIR, `${skillName}.zip`); console.log(`📥 下载: ${downloadUrl}`); await downloadFile(downloadUrl, zipPath); console.log('📂 解压中...'); const extractDir = path.join(TEMP_DIR, skillName); if (fs.existsSync(extractDir)) { fs.rmSync(extractDir, { recursive: true }); } fs.mkdirSync(extractDir, { recursive: true }); ``` ```javascript const sourceDir = path.join(extractDir, skillDir); const targetDir = path.join(SKILLS_DIR, skillName); // 去掉版本号(如果存在) const versionMatch = skillDir.match(/^(.+)-v?\d+\.\d+\.\d+$/); const finalName = versionMatch ? versionMatch[1] : skillName; const finalTargetDir = path.join(SKILLS_DIR, finalName); // 如果目标目录已存在,先删除 if (fs.existsSync(finalTargetDir)) { console.log(`🗑️ 删除旧版本: ${finalTargetDir}`); fs.rmSync(finalTargetDir, { recursive: true }); } ``` ### Technical Analysis The user-controlled `skillName` is used to construct ZIP, extraction, and installation paths without validation. `path.join` normalizes traversal components but does not guarantee that the resulting path remains under `TEMP_DIR` or `SKILLS_DIR`. The installer recursively removes existing extraction and destination paths. Consequently, a crafted identifier containing traversal components can cause operations outside the intended directories. The derived `finalName` may also originate from an archive-controlled directory name and is not subjected to a containment check. The unused `targetDir` variable does not mitigate the issue; installation and deletion use `finalTargetDir`. ### Attack Path 1. An attacker supplies a crafted skill name containing path traversal components through `install` or `install-batch`. 2. `path.join` resolves the resulting ZIP, extraction, or target path outside the expected ...[truncated 740 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Validate skill names before any network or filesystem operation: ```javascript if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(skillName)) { throw new Error('Invalid skill name'); } ``` - Resolve paths and enforce separator-aware containment: ```javascript function resolveWithin(root, child) { const resolvedRoot = path.resolve(root); const resolved = path.resolve(resolvedRoot, child); if ( resolved !== resolvedRoot && !resolved.startsWith(resolvedRoot + path.sep) ) { throw new Error('Path escapes permitted root'); } return resolved; } ``` - Reject `.` and `..`, absolute paths, path separators, null bytes, and control characters. - Apply containment checks independently to temporary, extraction, source, and destination paths. - Use `lstatSync` to reject symbolic links before destructive operations. - Do not recursively remove a destination solely because a user-derived name resolves to it. Require explicit confirmation or an independently verified package identity. - Remove the unused `targetDir` variable to reduce ambiguity in destination validation. ]]>

T08 · Insecure Dependencies

Error
Location
install.cjs:127
Finding
Unverified GitHub Search Result Is Installed as a Trusted Skill<![CDATA[ ## Vulnerability Details **File Location**: `install.cjs`, lines 127-144 **Vulnerability Type**: Unsafe third-party supply-chain fallback **Risk Level**: High ### Vulnerable Code ```javascript // 尝试从 GitHub API 获取(如果技能在 GitHub 上) try { const githubUrl = `https://api.github.com/search/repositories?q=${encodeURIComponent(skillName)}+clawhub+skill`; const data = await httpGet(githubUrl, { 'User-Agent': 'OpenClaw' }); const result = JSON.parse(data); if (result.items && result.items.length > 0) { const repo = result.items[0]; return { name: skillName, github_url: repo.html_url, download_url: `${repo.html_url}/archive/refs/heads/main.zip` }; } } catch (githubError) { console.error('❌ GitHub 搜索失败:', githubError.message); } ``` ### Technical Analysis When ClawHub metadata retrieval fails, the installer searches GitHub and unconditionally selects the first repository returned. It does not verify the repository owner, repository identity, relationship to the requested ClawHub skill, package manifest, publisher signature, commit hash, or archive digest. The download targets the mutable `main` branch. Its contents can therefore change after review and without a version update. Search-result ranking is not an authenticity mechanism and may be influenced by repository naming, metadata, popularity, or other search factors. The selected archive is subsequently downloaded, extracted, and moved into the active OpenClaw skills directory. ### Attack Path 1. An attacker creates a GitHub repository whose name and metadata match a targeted skill search. 2. The attacker attempts to make that repository rank first for the generated GitHub query. 3. The ClawHub metadata request fails because of service disruption, network conditions, an invalid identifier, or another API error. 4. The installer selects the first GitHub result and downloads its mutable `main` branch. 5. The attacker-controlled archive is installed into the Op ...[truncated 552 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove repository search as an automatic installation fallback. - Obtain download locations only from authenticated ClawHub metadata. - If GitHub fallback is required, use a registry-provided exact owner, repository, version, and immutable commit hash. - Verify a trusted SHA-256 or stronger digest before extraction. - Prefer publisher signatures or registry-signed package manifests with a documented trust root. - Require explicit user confirmation when changing package sources. - Never install directly from a mutable branch such as `main`. - Validate the downloaded package's manifest, expected skill identifier, publisher, and version before moving it into the active skills directory. ]]>

T08 · Insecure Dependencies

Warning
Location
install.cjs:56
Finding
Unrestricted Redirects and Missing Download Integrity Validation<![CDATA[ ## Vulnerability Details **File Location**: `install.cjs`, lines 56-77 and 164-167 **Vulnerability Type**: Untrusted download destination and unverified package content **Risk Level**: Medium ### Vulnerable Code ```javascript function downloadFile(url, destPath) { return new Promise((resolve, reject) => { const file = fs.createWriteStream(destPath); const request = https.get(url, (response) => { if (response.statusCode === 302 || response.statusCode === 301) { // 跟随重定向 downloadFile(response.headers.location, destPath).then(resolve).catch(reject); return; } response.pipe(file); file.on('finish', () => { file.close(); resolve(destPath); }); }); request.on('error', (err) => { fs.unlink(destPath, () => {}); reject(err); }); }); } ``` ```javascript const downloadUrl = details.download_url || `${CLAWHUB_API}/api/skills/${skillName}/download`; const zipPath = path.join(TEMP_DIR, `${skillName}.zip`); console.log(`📥 下载: ${downloadUrl}`); await downloadFile(downloadUrl, zipPath); ``` ### Technical Analysis The downloader follows redirects without validating the destination hostname or limiting the redirect count. A trusted initial endpoint can therefore redirect installation to content hosted on an unrelated HTTPS origin. The function also does not require a successful final status, validate the response content type, enforce a maximum archive size, or verify a cryptographic digest or signature. As a result, an error page, oversized response, substituted package, or other untrusted content can be saved and processed as an installation archive. When a redirect occurs, the initially opened output stream is not closed before the recursive download starts, which also creates unsafe file-handling behavior. ### Attack Path 1. ClawHub metadata supplies a malicious or compromised download URL, or a trusted endpoint responds with a redirect. 2 ...[truncated 811 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Allowlist acceptable download hosts and revalidate the hostname and protocol after every redirect. - Set a small, explicit maximum redirect count. - Resolve relative `Location` headers safely against the current URL. - Require a final 2xx status before writing or accepting the file. - Enforce an expected archive content type while recognizing that content type alone is not an integrity control. - Stream through a byte counter and abort when a configured maximum package size is exceeded. - Download to a unique temporary file and atomically rename it only after successful validation. - Verify a registry-provided cryptographic hash or publisher signature before extraction. - Close and remove any existing output stream before following a redirect. - Add network timeouts and robust cleanup for partial downloads. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
install.cjs:14
Finding
Predictable Shared Temporary Paths Permit Symlink and Race Attacks<![CDATA[ ## Vulnerability Details **File Location**: `install.cjs`, lines 14-19 and 56-58 **Vulnerability Type**: Insecure temporary-file handling **Risk Level**: Medium ### Vulnerable Code ```javascript const SKILLS_DIR = '/Users/xufan65/.openclaw/workspace/skills'; const TEMP_DIR = '/tmp/clawhub-downloads'; // 确保目录存在 if (!fs.existsSync(TEMP_DIR)) { fs.mkdirSync(TEMP_DIR, { recursive: true }); } ``` ```javascript function downloadFile(url, destPath) { return new Promise((resolve, reject) => { const file = fs.createWriteStream(destPath); ``` The predictable path is later combined with the requested skill name: ```javascript const zipPath = path.join(TEMP_DIR, `${skillName}.zip`); const extractDir = path.join(TEMP_DIR, skillName); ``` ### Technical Analysis The installer uses a fixed directory under the globally shared `/tmp` namespace and predictable filenames derived from skill names. It does not create a private per-process directory, request exclusive file creation, verify ownership, or reject symbolic links. A local attacker may pre-create the directory, archive path, or extraction path. Because `fs.createWriteStream` uses non-exclusive creation by default, a pre-existing symbolic link may redirect writes to another user-accessible file. The separate existence checks and subsequent recursive operations also create time-of-check/time-of-use race windows. ### Attack Path 1. A local attacker predicts the skill name that a victim will install. 2. Before installation, the attacker creates the shared temporary directory or a predictable archive/extraction entry, potentially as a symbolic link. 3. The victim starts the installer. 4. The installer opens, writes, removes, or recreates the attacker-prepared path without exclusive creation or ownership validation. 5. Files accessible to the victim may be overwritten, or installation data may be modified or deleted during the race. ### Impact Assessment Exploitation requires local access to the sam ...[truncated 309 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create a private temporary directory for every run: ```javascript const os = require('os'); const TEMP_DIR = fs.mkdtempSync( path.join(os.tmpdir(), 'clawhub-downloads-') ); fs.chmodSync(TEMP_DIR, 0o700); ``` - Create files with exclusive mode, such as `flag: 'wx'`, so existing entries are rejected. - Use `lstat` to reject symbolic links before reading, writing, moving, or deleting paths. - Keep all temporary artifacts under the uniquely created directory and verify path containment. - Remove only the private directory created by the current process. - Use `try`/`finally` to guarantee cleanup after successful and failed operations. - Where supported, use file descriptors and atomic operations to reduce time-of-check/time-of-use races. ]]>
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
  • Rogue AgentSelf-Modification, Session Persistence
Findings (22)

Ae1

High
Category
analysis-evasion
Content
You can modify these variables at the top of `install.cjs`:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Self-Modification

High
Category
Rogue Agent
Content
### Force Reinstall

Overwrite existing skill:

```bash
node install.cjs install todoist --force
Confidence
92% confidence
Finding
Documenting a `--force` mode that overwrites an existing skill indicates self-modifying behavior that can replace local files with newly downloaded content. In an installer context, this is dangerous because it can destroy prior state or silently replace trusted code with remote content, especially when combined with batch install and rate-limit bypass messaging.

Ssd 2

Medium
Confidence
95% confidence
Finding
The README markets the tool as a way to bypass API rate limits by directly downloading zip packages and falling back to alternative installation paths. Framing the tool around circumventing platform controls is a red flag because it encourages behavior outside intended safeguards and may facilitate abuse or installation of unreviewed content.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
**Linux (Ubuntu/Debian)**:
```bash
sudo apt-get install curl unzip jq
```

---
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The README explicitly describes downloading archives, unzipping them, stripping version names, and moving the contents into the user's skills directory, but provides no warning about installing untrusted code or modifying the filesystem. In the context of a skill installer, this omission is meaningful because users are encouraged to ingest remote packages directly into an execution path.

File System Enumeration

Medium
Category
Data Exfiltration
Content
### 查看已安装的技能

```bash
ls -la /Users/xufan65/.openclaw/workspace/skills/
```

---
Confidence
60% confidence
Finding
Code scans file system directories looking for sensitive files. This could be reconnaissance for credential theft.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
- **Bypassing API rate limits** - Intelligently handles ClawHub API restrictions
- **Batch installation** - Install multiple skills in one command
- **Automatic retries** - Smart retry logic when encountering rate limits
- **Version management** - Automatically removes version numbers from skill folder names
- **Comprehensive search** - Find skills directly from ClawHub

## ✨ Features
Confidence
80% 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.

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.

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.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The documentation advertises force reinstall and overwrite behavior without a clear warning that installation may modify or replace files in the user's skills directory. This increases the risk of accidental destructive changes, especially because the tool installs downloaded content and supports overwrite semantics.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This code file contains natural-language strings that force a specific language/locale for usage and operational output. Because there is no opt-in, fallback, or indication that the tool is intentionally region-specific, this is a language-policy concern under the natural-language policy rule.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding
The script invokes `npx clawhub search` without pinning an exact package version, so execution may fetch and run whatever version is current in the registry at install time. In a security-sensitive installer, this creates a supply-chain execution path outside the audited code and allows compromise if the package is malicious, hijacked, or unexpectedly changed.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The manifest describes a skill installer focused on searching, single/batch installation, retry logic, and version management, but does not indicate subprocess execution as part of its scope. This code invokes external CLI tooling and later also shells out to `unzip`, introducing command execution capability beyond the clearly stated installer behavior.

External Transmission

Medium
Category
Data Exfiltration
Content
// 尝试从 GitHub API 获取(如果技能在 GitHub 上)
    try {
      const githubUrl = `https://api.github.com/search/repositories?q=${encodeURIComponent(skillName)}+clawhub+skill`;
      const data = await httpGet(githubUrl, { 'User-Agent': 'OpenClaw' });
      const result = JSON.parse(data);
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The fallback install path executes `npx clawhub install ... --force` without version pinning, which can download and run arbitrary updated code from the package registry. Because this path performs installation with force semantics, it amplifies the trust placed in unreviewed remote code and increases supply-chain risk.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The package description is written only in Chinese ('直接从 ClawHub 下载 zip 包安装技能,绕过 API 速率限制') with no indication that users can choose another language or locale. This creates a natural-language locale constraint in user-facing metadata without documented opt-in or justification.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The file consistently presents usage instructions, examples, and operational details only in Chinese. Under the policy, forcing a specific language without user opt-in or a documented regional justification is a natural-language policy issue.

Natural-Language Policy Violations

Low
Confidence
85% confidence
Finding
The examples show user-facing command output entirely in Chinese, and similar Chinese output appears in the install examples, but the document does not state that the tool is Chinese-only or give users a language option. This can violate language or locale policy when a skill implicitly forces a specific language without opt-in.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
The single-install example presents the tool's interactive output in Chinese, reinforcing that the skill may default to a specific language. Without documentation of this constraint or a user-selectable locale, the skill's natural-language behavior may not align with language-choice expectations.

Context-Inappropriate Capability

Low
Confidence
84% confidence
Finding
The installer silently falls back to GitHub repository discovery and constructs a ZIP download URL from search results, expanding trust from ClawHub to arbitrary GitHub repositories. This broadens the attack surface and can install unverified code from an unintended repository if search results are poisoned, ambiguous, or manipulated.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
install.cjs:114