Back to skill

Security audit

Openclaw Skill Ansible

Security checks for vulnerabilities and agentic risk

Overview

This skill is a real mesh administration tool, but it gives task-controlled inputs too much power to install or execute code on an OpenClaw gateway.

Review before installing. This should only run in an administrator-controlled OpenClaw environment where task creation is authenticated and package sources are trusted. Do not enable deployment or plugin setup for untrusted callers; require signed or allowlisted artifacts, pin plugin identities and versions, stage extraction safely, and avoid automatic execution of package-provided smoke tests.

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

T03 · Remote Payload Retrieval and Execution

Error
Location
actions/deploy-skill.sh:19
Finding

Attacker-Supplied Remote Archive Is Downloaded and Automatically Executed

Content
View full analysis
&2 exit 2 fi if [ -z "$SHA_EXPECT" ]; then echo "sha is required and must be sha256" | tee -a "$LOG" >&2 exit 2 fi if [[ ! "$SHA_EXPECT" =~ ^[a-fA-F0-9]{64}$ ]]; then echo "sha must be a 64-char hex digest" | tee -a "$LOG" >&2 exit 2 fi if [ "${OPENCLAW_ALLOW_DEPLOY_SKILL:-0}" != "1" ]; then echo "deploy-skill disabled: set OPENCLAW_ALLOW_DEPLOY_SKILL=1" | tee -a "$LOG" >&2 exit 3 fi TMP_DIR="/tmp/${TASK_ID}" mkdir -p "$TMP_DIR" ARCHIVE="$TMP_DIR/artifact.tar.gz" echo "Downloading $URL" | tee -a "$LOG" curl -fsSL "$URL" -o "$ARCHIVE" if command -v sha256sum >/dev/null 2>&1; then SHA_ACT=$(sha256sum "$ARCHIVE" | awk '{print $1}') else SHA_ACT=$(shasum -a 256 "$ARCHIVE" | awk '{print $1}') fi if [ "$SHA_EXPECT" != "$SHA_ACT" ]; then echo "SHA mismatch expected=$SHA_EXPECT actual=$SHA_ACT" | tee -a "$LOG" >&2 exit 4 fi DEST="/opt/openclaw/skills/$NAME" mkdir -p "$DEST" echo "Extracting into $DEST" | tee -a "$LOG" tar -xzf "$ARCHIVE" -C "$DEST" if [ -x "$DEST/test_smoke.sh" ]; then echo "Running smoke test" | tee -a "$LOG" (cd "$DEST" && ./test_smoke.sh) || { echo "Smoke failed" | tee -a "$LOG" >&2; exit 5; } fi ``` ### Technical Analysis The deployment task controls both `params.artifact_url` and `params.sha`. The script only requires an HTTPS URL and a syntactically valid SHA-256 digest. Because the expected digest is supplied by the same untrusted task as the archive URL, it provides transfer-integrity checking but no publisher authentication. An attacker can therefore host an arbitrary archive, calculate its SHA-256 digest, and provide both values. After verification, the archive is extracted into the persistent Skill directory. If it c ...[truncated 1554 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Error
Location
actions/setup-ansible-plugin.sh:25
Finding

Arbitrary Plugin Sources Can Be Installed Without High-Risk Authorization

Content
View full analysis
&2 exit 2 fi INSTALL_TARGET="$PLUGIN_REF" ;; *) echo "unsupported source: $SOURCE" | tee -a "$LOG" >&2 exit 2 ;; esac run_cmd "plugin-install" openclaw plugins install "$INSTALL_TARGET" ``` The dispatcher only treats two other actions as high risk: ```python HIGH_RISK_ACTIONS = {"run-cmd", "deploy-skill"} ``` ```python if action in HIGH_RISK_ACTIONS: allow_high_risk = os.environ.get("OPENCLAW_ALLOW_HIGH_RISK", "0") == "1" if not allow_high_risk: return False, f"action '{action}' blocked (set OPENCLAW_ALLOW_HIGH_RISK=1 to enable)" ``` ### Technical Analysis `params.plugin_ref` can designate an arbitrary GitHub source, npm package, or local filesystem path. The selected target is passed directly to `openclaw plugins install`. There is no allowlist for package identities, no immutable commit or version requirement, no checksum or publisher-signature verification, and no dedicated approval artifact. This exposes the gateway to repository takeover, compromised package releases, dependency confusion, malicious local paths, and directly attacker-selected components. Although installing a plugin introduces executable code into the OpenClaw gateway, `setup-ansible-plugin` is absent from `HIGH_RISK_ACTIONS`. It therefore bypasses the global `OPENCLAW_ALLOW_HIGH_RISK` check applied to `run-cmd` and `deploy-skill`. The sam ...[truncated 1119 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/handler.py:29
Finding

Caller Authorization Can Be Spoofed Through Untrusted Task JSON

Content
View full analysis
tuple[bool, str]: action = str(task.get("action", "")).strip() caller = str(task.get("caller", "")).strip() if not caller: return False, "missing caller" allowed = _allowed_callers() if caller not in allowed: return False, f"caller '{caller}' not authorized" if action in HIGH_RISK_ACTIONS: allow_high_risk = os.environ.get("OPENCLAW_ALLOW_HIGH_RISK", "0") == "1" if not allow_high_risk: return False, f"action '{action}' blocked (set OPENCLAW_ALLOW_HIGH_RISK=1 to enable)" return True, "ok" ``` ### Technical Analysis The authorization decision trusts the `caller` string inside the same task JSON that contains the requested action and parameters. There is no evidence that this field is cryptographically signed, bound to an authenticated transport identity, issued by a trusted dispatcher, or verified using a gateway token. Consequently, possession of the ability to create or modify a task file is equivalent to possession of an allowlisted identity. An attacker can simply set `caller` to the default value `architect` or `chief-of-staff`. The environment-based action gates reduce exposure for two actions when disabled, but they do not authenticate the task origin. Other sensitive operations, particularly plugin installation, remain reachable after caller spoofing. ### Attack Path 1. The attacker gains any route for supplying or modifying the JSON file passed to `handler.py`. 2. The attacker sets `caller` to an entry from `OPENCLAW_ALLOWED_CALLERS`, such as `architect`. 3. The attacker selects an action and attacker-controlled parameters. 4. `authorize()` performs only a string-membership comparison. 5. The forged identity is accepted. 6. The dispatcher invokes the c ...[truncated 567 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
actions/deploy-skill.sh:35
Finding

Unvalidated Identifiers and Tar Entries Permit Filesystem Path Escape

Content
View full analysis
/dev/null 2>&1; then SHA_ACT=$(sha256sum "$ARCHIVE" | awk '{print $1}') else SHA_ACT=$(shasum -a 256 "$ARCHIVE" | awk '{print $1}') fi if [ "$SHA_EXPECT" != "$SHA_ACT" ]; then echo "SHA mismatch expected=$SHA_EXPECT actual=$SHA_ACT" | tee -a "$LOG" >&2 exit 4 fi DEST="/opt/openclaw/skills/$NAME" mkdir -p "$DEST" echo "Extracting into $DEST" | tee -a "$LOG" tar -xzf "$ARCHIVE" -C "$DEST" ``` The same unvalidated task identifier is used to construct output paths in other actions, for example: ```bash TASK_ID=$(jq -r ".task_id" "$TASK_FILE") OUT="$ARTROOT/${TASK_ID}-logs.txt" ``` ### Technical Analysis `TASK_ID` and `NAME` are inserted into filesystem paths without restricting them to safe basename characters or verifying that the resolved paths remain beneath their intended roots. Values containing `../`, absolute-path-like components, or crafted separators can therefore escape `/tmp`, the artifact directory, or `/opt/openclaw/skills`. The tar archive is extracted without inspecting member names. Malicious archives may contain traversal entries, absolute paths, symbolic links, hard links, or special files. Depending on the installed tar implementation and its protections, these entries may overwrite or create files outside the intended destination. The predictable `/tmp/${TASK_ID}` directory also creates symlink and pre-creation risks when local untrusted users can manipulate `/tmp`. ### Attack Path 1. The attacker submits a task with a malicious `task_id` or deployment `name`, such as a value containing `../`. 2. The script concate ...[truncated 1082 chars]
Remediation
View remediation
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (12)

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
94% confidence
Finding

This script downloads a remote archive, extracts it into the live skills directory, and then executes a shipped script from that artifact. Even though HTTPS, a SHA-256 check, and an environment gate are present, the design still intentionally enables deployment and execution of externally supplied code, which is dangerous if the task input, artifact source, or expected hash can be influenced by an attacker.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% 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 114)May include surrounding context.

md
| Tool | Purpose |
|------|---------|
| `ansible_delegate_task` | Create task for another node/agent set |
| `ansible_claim_task` | Claim pending task |
| `ansible_update_task` | Update task status/progress |
| `ansible_complete_task` | Complete task and notify requester |

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
80% confidence
Finding

This code creates a destination under /opt/openclaw/skills and extracts downloaded content into it, which is a file-writing operation that changes the local system state. While the script logs that extraction is happening, there is no user-facing warning, confirmation, or inline documentation explaining that running the skill will install files onto the host.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script executes test_smoke.sh directly from the deployed artifact, meaning arbitrary code from the package runs on the host during deployment. The prior SHA check only proves the file matches the provided digest; it does not establish that the artifact is trustworthy, so a malicious or compromised artifact can execute commands with the deployer's privileges.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The script installs a plugin and may run setup and restart gateway operations, which can change system state and affect service availability. Although commands are logged, there is no comment, docstring, or explicit warning in the file disclosing these safety-impacting actions before execution.

Content

No source excerpt is available for this finding.

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
80% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

Scanner excerpt · docs/ansible-coding-skill-spec.md (reported line 251)May include surrounding context.

md
"action": "scaffold",
    "branch": "skills/feature/secrets-mgmt",
    "files_created": [
      "skills/openclaw-skill-ansible/subskills/secrets-mgmt/SKILL.md",
      "playbooks/secrets-mgmt.yml",
      "roles/vault/tasks/main.yml"
    ],

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% 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 · docs/ansible-coding-skill-spec.md (reported line 501)May include surrounding context.

"status": "failed", "issues": [ "Git branch creation failed: branch already exists", "Cannot write to repository: permission denied" ] }

text

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
80% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

Scanner excerpt · docs/ansible-coding-skill-spec.md (reported line 690)May include surrounding context.

md
### Skill Loading

OpenClaw loads `SKILL.md` files from:
- `~/.openclaw/workspace/skills/ansible/SKILL.md` — core coordination skill
- `~/.openclaw/workspace/skills/ansible/skills/*/SKILL.md` — skill catalog entries

Agents receive skill context when:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This shell script performs safety-relevant actions by moving the source skill into a destination directory and creating a git commit, which can alter user files and repository history. While there is a brief comment and a final success echo, there is no confirmation prompt or explicit user-facing warning before these irreversible changes occur.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
78% confidence
Finding

The docstring makes an affirmative security claim, but the implementation's main behavior is to invoke action-specific shell scripts and optionally allow explicitly marked high-risk actions when an environment variable is set. That is not merely incomplete documentation; it overstates the safety properties of the module relative to its actual behavior.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

No manifest is available, so there is no stated purpose that would justify a generic shell-script dispatcher. The code resolves an action name to an .sh file and executes it via /bin/bash, which is a materially powerful capability beyond what can be inferred from the file's minimal labeling alone.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · src/handler.py (reported line 60)May include surrounding context.

python
return 2

    # Run script directly with argv, never shell interpolation.
    result = subprocess.run(["/bin/bash", str(script), task_path], check=False)
    return int(result.returncode)