Back to skill

Security audit

Mobayilo Voice (Beta)

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its Mobayilo calling purpose, but its installation and entrypoint design create review-worthy local code execution and account-data handling risks.

Install only if you trust the Mobayilo CLI distribution path and are comfortable with a skill that can place real phone calls when --execute is used. Before production use, replace curl|sh and self-update instructions with pinned verified releases, use a structured non-shell entrypoint for parameters, and tighten logs/temp files so account email, exact balances, and unrelated environment secrets are not unnecessarily exposed.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (4)

T03 · Remote Payload Retrieval and Execution

Error
Location
docs/runbook.md:8
Finding
Unverified Remote Installer Is Downloaded and Executed Directly## Vulnerability Details **File Location**: `docs/runbook.md:8-11` **Vulnerability Type**: Remote payload retrieval and immediate shell execution **Risk Level**: Critical ### Vulnerable Code ```bash 2. Install/upgrade Mobayilo CLI: ```bash curl -fsSL https://mobycli.mobayilo.com/install.sh | sh moby self-update ``` ``` ### Technical Analysis The runbook instructs operators or agents to retrieve mutable content from an external URL and pipe it directly into `sh`. The downloaded script is not pinned to a version, inspected, validated against a cryptographic checksum, or authenticated using a trusted signing key. TLS protects the connection in transit but does not protect against compromise of the distribution server, domain, DNS configuration, hosting account, or future malicious modification of the installer. The payload that executes can therefore differ from the content originally reviewed during this audit. Although the remote installer is not automatically invoked by the Python implementation, installation instructions are part of the operational behavior of the Skill and may be followed by an operator or an automated agent. ### Attack Path 1. An attacker compromises or obtains control over `mobycli.mobayilo.com`, its hosting account, DNS, or the installer deployment pipeline. 2. The attacker replaces `install.sh` with a malicious payload. 3. An operator or agent follows the documented installation command. 4. `curl` downloads the current attacker-controlled response. 5. The response is passed immediately to `sh` without verification or review. 6. The payload executes with all privileges available to the user running the installation command. ### Impact Assessment Successful exploitation permits arbitrary command execution as the installing user. The payload could read user-accessible files and credentials, alter the installed `moby` executable, access environment variables, install persist ...[truncated 358 chars]
Remediation
## Remediation Suggestions 1. Remove the `curl | sh` installation pattern. 2. Publish immutable, versioned release artifacts. 3. Download the artifact separately and verify a pinned SHA-256 or stronger checksum before execution. 4. Prefer cryptographic signatures verified against a documented, independently distributed public key. 5. Display or inspect installation scripts before running them. 6. Pin installation and update instructions to an explicit version instead of retrieving mutable latest content. 7. Ensure `moby self-update` also authenticates release metadata and artifacts using signatures or pinned hashes. 8. Run installation with the least-privileged account and document exactly which files the installer modifies. A safer pattern would be: ```bash curl -fL -o moby-v0.2.0.tar.gz https://example.invalid/releases/moby-v0.2.0.tar.gz echo "PINNED_SHA256 moby-v0.2.0.tar.gz" | sha256sum --check - tar -xzf moby-v0.2.0.tar.gz install -m 0755 moby ~/.local/bin/moby ``` The placeholder URL and checksum must be replaced with an authenticated release location and the publisher's actual pinned digest.

T09 · Insecure Skill Coding Practices

Error
Location
skill/skill.yaml:5
Finding
Unquoted Skill Parameters Permit Command Injection in the Entrypoint Template## Vulnerability Details **File Location**: `skill/skill.yaml:5-10` **Vulnerability Type**: Command injection through unsafe template interpolation **Risk Level**: High ### Vulnerable Code ```yaml entrypoints: check_status: run: python integrations/mobayilo_voice/actions/check_status.py --json output: json start_call: run: python integrations/mobayilo_voice/actions/start_call.py --destination "{{destination}}" --country "{{country}}" {{#if dry_run}}--dry-run{{/if}} {{#if approved}}--approved{{/if}} {{#if callback}}--callback{{/if}} {{#if fallback_callback}}--fallback-callback{{/if}} {{#if require_agent_ready}}--require-agent-ready{{/if}} {{#if execute}}--execute{{/if}} output: json ``` ### Technical Analysis The `destination` and `country` values are inserted directly into a shell-style command string. They are not quoted or escaped in this entrypoint definition. If the OpenClaw Skill executor passes the rendered `run` value to a shell, an attacker-controlled value can introduce command separators, substitutions, redirections, or other shell syntax. Validation in `actions/start_call.py` does not eliminate this issue because shell parsing occurs before Python starts and before `_validate_destination()` is called. While `lib/cli_runner.py` safely invokes `moby` through an argv list without `shell=True`, that protection applies only after the vulnerable Skill entrypoint has launched Python. Exploitability depends on whether the hosting framework executes the rendered `run` field through a shell. If it uses a direct, safely constructed argv array, shell metacharacters would not be interpreted. The current metadata does not enforce that safer execution model. ### Attack Path 1. An attacker influences a workflow's `destination` or `country` input. 2. The value contains shell syntax, for example a command separator followed by an attacker-selected command. 3. The Skill engine substitutes the value in ...[truncated 903 chars]
Remediation
## Remediation Suggestions 1. Replace the command string with a framework-supported argv array in which each input is a separate argument. 2. Disable shell evaluation explicitly when launching entrypoints. 3. Validate `destination` against a strict E.164 representation before command construction. 4. Restrict `country` to a documented allowlist, such as two-letter ISO country codes. 5. Do not rely solely on quoting as a substitute for structured process invocation. 6. Add tests using semicolons, command substitutions, quotes, newlines, redirections, and option-like values. 7. Reject embedded control characters and use `--` where supported to terminate option parsing. Conceptually, the entrypoint should use structured arguments: ```yaml command: - python - integrations/mobayilo_voice/actions/start_call.py - --destination - "{{destination}}" - --country - "{{country}}" shell: false ``` The exact syntax must follow the hosting framework's supported structured-command schema.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/verify.sh:39
Finding
Authentication and Balance Data Are Written to Predictable Shared Temporary Files## Vulnerability Details **File Location**: `scripts/verify.sh:39-56` **Vulnerability Type**: Unsafe temporary-file handling and local sensitive-data exposure **Risk Level**: Medium ### Vulnerable Code ```bash echo "[verify] Checking auth status" if ! "$cli_path" auth status --json >/tmp/moby_auth_status.json; then echo "[verify] ERROR: moby auth status failed" >&2 exit 1 fi cat /tmp/moby_auth_status.json echo "[verify] Checking balance" if ! "$cli_path" balance --json >/tmp/moby_balance.json; then echo "[verify] ERROR: moby balance failed" >&2 exit 1 fi cat /tmp/moby_balance.json echo "[verify] OK" ``` ### Technical Analysis The verification script writes authentication/account status and balance responses to fixed filenames in the shared `/tmp` directory. It does not use `mktemp`, set a restrictive `umask`, verify file ownership, prevent symbolic-link traversal, or remove the files after use. Shell redirection follows symbolic links. On systems where another local user can create these paths first, the script can therefore overwrite a file selected through a pre-created link, subject to the victim user's filesystem permissions. The resulting JSON also remains on disk after the script exits and may be readable according to the process umask and platform-specific `/tmp` behavior. ### Attack Path Disclosure path: 1. An operator executes `scripts/verify.sh`. 2. The CLI returns authentication/account or balance metadata. 3. The script stores the response at a predictable `/tmp` path. 4. The script does not remove the file. 5. Another local process or user reads the retained response if permissions allow. File-clobbering path: 1. A local attacker predicts `/tmp/moby_auth_status.json` or `/tmp/moby_balance.json`. 2. The attacker creates a symbolic link at that path to a file writable by the victim. 3. The victim runs the verification script. 4. Shell redirection follows the link ...[truncated 565 chars]
Remediation
## Remediation Suggestions 1. Avoid temporary files by piping CLI output directly to the required parser or display command. 2. If persistence is necessary, create a private temporary directory with `mktemp -d`. 3. Set `umask 077` before creating files containing account data. 4. Register an exit trap that removes temporary files on normal exit, errors, and signals. 5. Do not reuse predictable global filenames. 6. Validate that temporary files are regular files owned by the current user. 7. Avoid displaying the complete authentication response unless explicitly requested. Example hardening: ```bash umask 077 tmp_dir="$(mktemp -d)" trap 'rm -rf "$tmp_dir"' EXIT HUP INT TERM auth_file="$tmp_dir/auth-status.json" balance_file="$tmp_dir/balance.json" "$cli_path" auth status --json >"$auth_file" "$cli_path" balance --json >"$balance_file" ```

T09 · Insecure Skill Coding Practices

Note
Location
actions/check_status.py:17
Finding
Status Logging Persists Account Email and Financial Metadata## Vulnerability Details **File Location**: `actions/check_status.py:17-50, 80-85`; logging sink at `lib/adapter.py:66-88` **Vulnerability Type**: Excessive plaintext logging of sensitive account metadata **Risk Level**: Low ### Vulnerable Code ```python def build_summary(status: Dict[str, Any]) -> Dict[str, str]: auth = status.get("auth", {}) or {} account = auth.get("account", {}) or {} actor = auth.get("actor", {}) or {} balance = status.get("balance", {}) or {} update = status.get("update", {}) or {} email = actor.get("email") or "unknown" authenticated = "Yes" if auth.get("authenticated") else "No" balance_cents = balance.get("available_balance_cents") if balance_cents is None: balance_cents = balance.get("balance_cents") if balance_cents is None: balance_cents = account.get("available_balance_cents") balance_human = cents_to_currency(balance_cents) caller_id = mask_phone(account.get("caller_id_e164")) or "unknown" caller_status = account.get("caller_id_status") or "unknown" caller_verified = "Yes" if caller_status == "verified" else "No" ready = "Yes" if status.get("ready") else "No" update_state = "Yes" if update.get("needs_update") else "No" warnings = "; ".join(status.get("warnings", [])) if status.get("warnings") else "None" return { "Authenticated": authenticated, "Email": email, "Balance": balance_human, "Caller ID": caller_id, "Verified": caller_verified, "CLI Update Available": update_state, "Warnings": warnings, "Ready to dial": ready, } ``` ```python adapter = MobayiloVoiceAdapter() status = adapter.get_status() summary = build_summary(status) adapter.log_event("check_status", {"summary": summary}) adapter.emit_metric("mobayilo.status.checked", 1, tags={"ready": status.get("ready")}) ``` The persi ...[truncated 1924 chars]
Remediation
## Remediation Suggestions 1. Do not include the actor email address in persistent status events. 2. Replace the exact balance with a non-sensitive category such as `sufficient`, `warning`, or `below_hard_floor`. 3. Separate operator-facing output from persistent telemetry. 4. Create sensitive log files with mode `0600` and verify existing file ownership and permissions. 5. Define retention and rotation limits. 6. Document every persisted field and its privacy purpose. 7. Ensure centralized logging and backup destinations apply equivalent access controls. 8. Add tests asserting that event and telemetry files never contain email addresses, full phone numbers, or exact balances.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Rogue AgentSelf-Modification, Session Persistence
Findings (29)

Self-Modification

High
Category
Rogue Agent
Content
- Strict direct-dial guard available via `--require-agent-ready` (fail fast if agent UI is not ready).
- Privacy-safe logging: phone numbers masked to last 4 digits in logs/telemetry.
- Telemetry JSONL emission (events + metrics).
- Warning-only CLI update guidance (`moby self-update --check`) for operator awareness.
- Pytest unit tests with mocked CLI runner.

## Layout
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- Strict direct-dial guard available via `--require-agent-ready` (fail fast if agent UI is not ready).
- Privacy-safe logging: phone numbers masked to last 4 digits in logs/telemetry.
- Telemetry JSONL emission (events + metrics).
- Warning-only CLI update guidance (`moby self-update --check`) for operator awareness.
- Pytest unit tests with mocked CLI runner.

## Layout
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- Strict direct-dial guard available via `--require-agent-ready` (fail fast if agent UI is not ready).
- Privacy-safe logging: phone numbers masked to last 4 digits in logs/telemetry.
- Telemetry JSONL emission (events + metrics).
- Warning-only CLI update guidance (`moby self-update --check`) for operator awareness.
- Pytest unit tests with mocked CLI runner.

## Layout
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- Strict direct-dial guard available via `--require-agent-ready` (fail fast if agent UI is not ready).
- Privacy-safe logging: phone numbers masked to last 4 digits in logs/telemetry.
- Telemetry JSONL emission (events + metrics).
- Warning-only CLI update guidance (`moby self-update --check`) for operator awareness.
- Pytest unit tests with mocked CLI runner.

## Layout
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- Strict direct-dial guard available via `--require-agent-ready` (fail fast if agent UI is not ready).
- Privacy-safe logging: phone numbers masked to last 4 digits in logs/telemetry.
- Telemetry JSONL emission (events + metrics).
- Warning-only CLI update guidance (`moby self-update --check`) for operator awareness.
- Pytest unit tests with mocked CLI runner.

## Layout
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- Strict direct-dial guard available via `--require-agent-ready` (fail fast if agent UI is not ready).
- Privacy-safe logging: phone numbers masked to last 4 digits in logs/telemetry.
- Telemetry JSONL emission (events + metrics).
- Warning-only CLI update guidance (`moby self-update --check`) for operator awareness.
- Pytest unit tests with mocked CLI runner.

## Layout
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Self-Modification

High
Category
Rogue Agent
Content
- Strict direct-dial guard available via `--require-agent-ready` (fail fast if agent UI is not ready).
- Privacy-safe logging: phone numbers masked to last 4 digits in logs/telemetry.
- Telemetry JSONL emission (events + metrics).
- Warning-only CLI update guidance (`moby self-update --check`) for operator awareness.
- Pytest unit tests with mocked CLI runner.

## Layout
Confidence
90% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The implementation reportedly reads local configuration, checks auth/account state, and retrieves account balance while not actually implementing the advertised call flow. Undisclosed access to local config and account data can expose sensitive information, and the mismatch undermines informed consent and accurate privilege review for a skill expected to only manage outbound calls.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The implementation reportedly reads local configuration, checks auth/account state, and retrieves account balance while not actually implementing the advertised call flow. Undisclosed access to local config and account data can expose sensitive information, and the mismatch undermines informed consent and accurate privilege review for a skill expected to only manage outbound calls.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The implementation reportedly reads local configuration, checks auth/account state, and retrieves account balance while not actually implementing the advertised call flow. Undisclosed access to local config and account data can expose sensitive information, and the mismatch undermines informed consent and accurate privilege review for a skill expected to only manage outbound calls.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The implementation reportedly reads local configuration, checks auth/account state, and retrieves account balance while not actually implementing the advertised call flow. Undisclosed access to local config and account data can expose sensitive information, and the mismatch undermines informed consent and accurate privilege review for a skill expected to only manage outbound calls.

External Script Fetching

High
Category
Supply Chain
Content
1. Confirm host hardware (Mac mini) is online and Mobayilo desktop audio agent is running.
2. Install/upgrade Mobayilo CLI:
   ```bash
   curl -fsSL https://mobycli.mobayilo.com/install.sh | sh
   moby self-update
   ```
   Fallback when release endpoint is unavailable:
Confidence
98% confidence
Finding
`curl ... | sh` fetches a remote script and immediately executes it without inspection, signature verification, or checksum validation. In an agent-operated voice integration, compromise of the remote host, DNS, TLS trust chain, or distribution pipeline could result in arbitrary code execution on the host that places calls and handles authentication state.

Chaining Abuse

High
Category
Tool Misuse
Content
1. Confirm host hardware (Mac mini) is online and Mobayilo desktop audio agent is running.
2. Install/upgrade Mobayilo CLI:
   ```bash
   curl -fsSL https://mobycli.mobayilo.com/install.sh | sh
   moby self-update
   ```
   Fallback when release endpoint is unavailable:
Confidence
97% confidence
Finding
The shell pipe into `sh` is dangerous because it chains network retrieval directly into execution, eliminating opportunities for validation and making exploitation trivial if the fetched content is malicious. Given this skill operates on a host with telephony tooling and credentials, successful abuse could grant immediate arbitrary command execution and downstream access to calling infrastructure.

Self-Modification

High
Category
Rogue Agent
Content
2. Install/upgrade Mobayilo CLI:
   ```bash
   curl -fsSL https://mobycli.mobayilo.com/install.sh | sh
   moby self-update
   ```
   Fallback when release endpoint is unavailable:
   ```bash
Confidence
94% confidence
Finding
The runbook instructs operators to run `moby self-update`, which modifies the installed executable by fetching and applying remote code outside the repository's normal review path. In an agent skill context, this increases supply-chain risk because a compromised update channel or tampered release could change future behavior of the calling tool without local code review.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
try:
            subprocess.Popen(
                [self.config.cli_path, "agent", "run"],
                env={**os.environ, **self._base_env()},
                stdout=out,
                stderr=subprocess.STDOUT,
                start_new_session=True,
Confidence
96% confidence
Finding
Passing `{**os.environ, **self._base_env()}` to a spawned subprocess forwards the entire parent environment, which often contains secrets such as API keys, tokens, CI credentials, or proxy settings. If the spawned CLI or any plugin/library it loads is compromised or replaced, those secrets become immediately available, making this a real secret-exposure risk.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
def run(self, args: List[str], parse_json: bool = False, env: Optional[Dict[str, str]] = None) -> CliResult:
        command = self._resolve_command(args)
        merged_env = os.environ.copy()
        if env:
            merged_env.update(env)
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill advertises executable shell commands and implicitly requires broad capabilities (environment access, filesystem, network, shell) but does not declare any explicit tool scope or permissions boundary. That makes it harder for a host system or reviewer to constrain execution, increasing the chance of over-privileged operation or abuse if the referenced actions perform more than the documentation claims.

Session Persistence

Medium
Category
Rogue Agent
Content
```bash
   brew install go
   cd ~/Documents/Code/p/mobayilo/cli
   mkdir -p bin
   go build -o bin/moby ./cmd/moby
   install -m 755 bin/moby ~/.local/bin/moby
   ```
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.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def _open_agent_tab(self, agent_url: str = "http://127.0.0.1:7788/") -> None:
        try:
            if sys.platform == "darwin":
                subprocess.Popen(["open", agent_url], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        except Exception:
            # best-effort only
            pass
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
This section adds local process enumeration, singleton enforcement, process termination, and background agent startup capabilities that go beyond the declared purpose of placing outbound calls. Those behaviors increase the blast radius of the skill: a telephony adapter should not silently manage unrelated local processes unless clearly scoped and consented, because it can disrupt local services or be repurposed as a host-control primitive.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def _agent_run_pids(self) -> list[int]:
        try:
            proc = subprocess.run(
                ["ps", "-ax", "-o", "pid=,command="],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The adapter enumerates all processes and terminates those whose command line contains `moby agent run`, without user confirmation and without verifying parent-child ownership. This can kill unrelated legitimate processes matching that string and gives the skill host-management behavior beyond its stated role, creating denial-of-service and abuse potential on the local machine.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The adapter auto-starts a background agent subprocess with no user confirmation. In the context of a skill whose purpose is outbound calling, silently launching a resident local process is a significant expansion of authority and can surprise users, consume resources, and combine with configurable executable paths to increase abuse potential.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
log_file.parent.mkdir(parents=True, exist_ok=True)
        out = log_file.open("a", encoding="utf-8")
        try:
            subprocess.Popen(
                [self.config.cli_path, "agent", "run"],
                env={**os.environ, **self._base_env()},
                stdout=out,
Confidence
94% confidence
Finding
The adapter auto-starts an executable from configurable `cli_path` and passes through the full parent environment to it. If configuration or runtime environment is influenced by an attacker, this can execute an unintended binary and expose sensitive environment variables to that subprocess, turning a call-placement skill into a general local code-execution and secret-propagation surface.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
if env:
            merged_env.update(env)

        proc = subprocess.run(
            command,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Static analysis

No suspicious patterns detected.