Back to skill

Security audit

Clack

Security checks for vulnerabilities and agentic risk

Overview

Clack’s voice relay purpose is clear, but its installer and service use broad root-level persistence and under-scoped network/authentication controls that need review before installation.

Install only after reviewing the scripts and preferably on a dedicated host. Avoid multi-user machines, restrict firewall and Tailscale ACLs tightly, protect and rotate relay/gateway tokens, and be cautious with clack update until the service runs under an unprivileged account with pinned dependencies and implemented rate limits.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/setup.sh:347
Finding
Root Command Execution Through Unsafe API-Key Interpolation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh:347-357` **Vulnerability Type**: Python source injection from attacker-controlled configuration **Risk Level**: Critical ### Vulnerable Code ```bash CONFIG_FILE="$SKILL_DIR/config.json" python3 -c " import json c = {} el = '${ELEVENLABS_API_KEY:-}' oa = '${OPENAI_API_KEY:-}' dg = '${DEEPGRAM_API_KEY:-}' if el: c['ELEVENLABS_API_KEY'] = el if oa: c['OPENAI_API_KEY'] = oa if dg: c['DEEPGRAM_API_KEY'] = dg json.dump(c, open('$CONFIG_FILE', 'w'), indent=2) " ``` ### Technical Analysis The setup script runs as root and directly interpolates provider API-key values into Python source passed to `python3 -c`. These values may originate from interactive prompts, inherited environment variables, an existing configuration file, or a legacy service file. Shell variables are not safely encoded as Python string literals. A value containing a quote, statement separator, and Python expression can terminate the intended string and inject arbitrary Python statements. For example, a value shaped like: ```text '; __import__("os").system("ATTACKER_COMMAND"); # ``` would cause attacker-controlled Python to execute while setup is running with root privileges. ### Attack Path 1. An attacker controls or influences a provider API-key value supplied to the setup script. 2. The administrator runs `sudo bash scripts/setup.sh`. 3. The crafted value is inserted into the `python3 -c` source without escaping. 4. The value terminates the intended Python string and introduces an additional statement. 5. Python executes the injected statement as root. 6. The attacker can modify system files, create privileged accounts, install persistence, or extract credentials. ### Impact Assessment Successful exploitation provides arbitrary command execution as root. The resulting compromise is system-wide and can affect all users, system services, OpenClaw credentials, provider API keys, and stored conversation data. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Never generate Python source by interpolating untrusted values. - Pass values through environment variables and read them with `os.environ`. - Alternatively, pass values as positional arguments and consume them through `sys.argv`. - Serialize the complete configuration using a fixed Python program rather than `python3 -c`. - Validate expected API-key formats, while treating validation only as defense in depth. - Add regression tests using quotes, newlines, semicolons, and Python syntax in every configurable value. A safer pattern is: ```bash export ELEVENLABS_API_KEY OPENAI_API_KEY DEEPGRAM_API_KEY CONFIG_FILE python3 <<'PY' import json import os config = {} for key in ("ELEVENLABS_API_KEY", "OPENAI_API_KEY", "DEEPGRAM_API_KEY"): value = os.environ.get(key, "") if value: config[key] = value with open(os.environ["CONFIG_FILE"], "w", encoding="utf-8") as handle: json.dump(config, handle, indent=2) PY ``` ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/setup.sh:367
Finding
User-Modifiable Skill Code Is Persistently Executed as Root<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh:367-386` **Vulnerability Type**: Local privilege escalation through a root service executing user-writable code **Risk Level**: Critical ### Vulnerable Code ```bash cat > "$SERVICE_FILE" <<EOF [Unit] Description=Clack Voice Relay (OpenClaw) After=network.target [Service] Type=simple WorkingDirectory=$SKILL_DIR $ENV_LINES ExecStart=$SKILL_DIR/venv/bin/python server.py Restart=always RestartSec=3 [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable clack systemctl restart clack ``` ### Technical Analysis The generated systemd service does not specify a `User=` or `Group=` directive, so systemd starts it as root. The executable Python file and virtual environment are loaded directly from `SKILL_DIR`, which the documented installation places under the invoking user's home directory: ```text ~/.openclaw/skills/clack ``` Consequently, a non-root owner of that checkout can normally modify `server.py`, replace virtual-environment modules, or alter other imported Python files. The next service restart or reboot executes those modifications as root. The `Restart=always` and `systemctl enable clack` settings make this privilege boundary violation persistent across crashes and reboots. ### Attack Path 1. A local user with write access to the Skill checkout modifies `server.py` or an imported module in the virtual environment. 2. The service is restarted through administration, an update, a crash, or a system reboot. 3. systemd launches the modified program as root. 4. The injected code performs arbitrary privileged operations. 5. The attacker obtains persistent root-level control. ### Impact Assessment A user who can modify the Skill directory can escalate to root. Root access permits complete host compromise, including reading OpenClaw and relay tokens, provider credentials, stored conversation history, and other users' files. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Create a dedicated system account such as `clack` with no interactive login. - Add explicit `User=clack` and `Group=clack` directives. - Install application code into a root-owned, non-user-writable directory such as `/opt/clack`. - Keep writable history and configuration in dedicated paths owned by the service account. - Make the source tree, virtual environment, and dependency files immutable to the runtime account. - Add systemd hardening controls, including: ```ini User=clack Group=clack NoNewPrivileges=true ProtectSystem=strict ProtectHome=true PrivateTmp=true PrivateDevices=true ProtectKernelTunables=true ProtectKernelModules=true ProtectControlGroups=true ReadWritePaths=/var/lib/clack ``` - Validate ownership and permissions before starting the service, and abort setup if code is writable by untrusted users. ]]>

T08 · Insecure Dependencies

Error
Location
scripts/clack.sh:27
Finding
Unverified Mutable Updates Are Immediately Activated by a Privileged Service<![CDATA[ ## Vulnerability Details **File Location**: `scripts/clack.sh:27-31` **Vulnerability Type**: Unsafe software update and supply-chain execution path **Risk Level**: High ### Vulnerable Code ```bash case "${1:-}" in update) echo "Updating Clack..." git -C "$SKILL_DIR" pull --ff-only && systemctl restart clack && echo "✓ Updated and restarted" ;; ``` ### Technical Analysis The update command pulls the latest content from the currently configured Git branch and immediately restarts the service. It does not pin a release, verify a commit signature, validate a checksum, or present changes for approval. Because the installed service runs as root, compromise of the upstream repository, maintainer account, Git remote configuration, or delivery channel can become privileged code execution when an administrator invokes `clack update`. `--ff-only` prevents a local merge commit but does not establish the authenticity or safety of the fetched commit. ### Attack Path 1. An attacker compromises the configured Git repository, maintainer credentials, or local remote configuration. 2. The attacker publishes a malicious modification to `server.py` or an imported dependency. 3. An administrator runs `clack update`. 4. Git accepts the malicious fast-forward update. 5. The command immediately restarts the service. 6. The malicious code executes with the service's privileges, currently root. ### Impact Assessment This can result in remote supply-chain compromise of the host. Under the current service configuration, the effective impact is root code execution, credential theft, persistent backdoor installation, and access to voice and conversation data. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Update only to pinned, versioned releases. - Require cryptographically signed tags or commits and verify them before installation. - Publish and validate release checksums. - Download updates into a staging directory and review or verify them before replacing active code. - Do not automatically restart until verification succeeds. - Run the service under a dedicated unprivileged account so update compromise cannot directly yield root access. - Protect Git remote configuration from modification by untrusted users. - Consider packaging releases as immutable system packages with a controlled repository and rollback support. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
server.py:977
Finding
Public Pairing Endpoint Lacks the Documented Brute-Force Protections<![CDATA[ ## Vulnerability Details **File Location**: `server.py:977-991` **Vulnerability Type**: Missing authentication rate limiting **Risk Level**: Medium ### Vulnerable Code ```python @app.post("/pair") async def redeem_pairing(code: str = Query(default="")): """Redeem a pairing code to get the auth token. No auth required.""" if not code: return JSONResponse({"error": "code required"}, status_code=400) # Guest token acts as a permanent pairing code if CLACK_GUEST_TOKEN and hmac.compare_digest(code.upper().strip(), CLACK_GUEST_TOKEN.upper().strip()): print(f"[Pair] Guest token accepted") return {"token": RELAY_AUTH_TOKEN} if _redeem_pairing_code(code): print(f"[Pair] Code redeemed successfully") return {"token": RELAY_AUTH_TOKEN} else: print(f"[Pair] Invalid/expired code: {code}") return JSONResponse({"error": "invalid or expired code"}, status_code=401) ``` ### Technical Analysis The unauthenticated pairing endpoint accepts unlimited attempts. The implementation contains no per-IP attempt counter, time window, failure delay, lockout, or HTTP 429 response. This contradicts the security claims in `README.md` and `SKILL.md`, which state that pairing is limited to five attempts per IP every five minutes with a two-second delay after failures. Pairing codes are six alphanumeric characters and expire after five minutes. Although the search space is substantial, the lack of throttling permits distributed and high-rate guessing and removes an explicitly claimed security control. Successful redemption returns the long-lived `RELAY_AUTH_TOKEN`. ### Attack Path 1. The attacker reaches the publicly exposed `POST /pair` endpoint. 2. The attacker repeatedly submits candidate six-character codes. 3. The server processes every request immediately without throttling. 4. If a currently active code is guessed, the endpoint returns the relay authentication token. 5. The attacker uses that t ...[truncated 315 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Implement server-side rate limiting keyed by source IP and, where available, authenticated identity. - Enforce the documented limit of five failures per five-minute window. - Add a failure delay and return HTTP 429 with a suitable `Retry-After` header. - Limit concurrent pairing requests and add a global rate ceiling to reduce distributed attacks. - Avoid logging submitted invalid codes. - Use longer pairing secrets or bind each code to a specific initiating session. - Add automated tests proving that repeated failures trigger throttling. - Correct the documentation immediately if the control cannot be implemented. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
server.py:593
Finding
All Clients in the Tailscale Address Range Bypass Application Authentication<![CDATA[ ## Vulnerability Details **File Location**: `server.py:593-614` **Vulnerability Type**: Network-location-based authentication bypass **Risk Level**: High ### Vulnerable Code ```python TAILSCALE_NETWORK = ipaddress.ip_network("100.64.0.0/10") def is_tailscale_ip(ip: str) -> bool: """Check if an IP is in the Tailscale CGNAT range (100.64.0.0/10).""" try: return ipaddress.ip_address(ip) in TAILSCALE_NETWORK except ValueError: return False CLACK_GUEST_TOKEN = os.getenv("CLACK_GUEST_TOKEN", "") def verify_token(token: str, client_ip: str = "") -> bool: """Verify auth token. Tailscale IPs bypass pairing. Non-Tailscale requires valid token.""" if is_tailscale_ip(client_ip): return True if not RELAY_AUTH_TOKEN: return False # No token configured = Tailscale-only mode if CLACK_GUEST_TOKEN and hmac.compare_digest(token, CLACK_GUEST_TOKEN): return True return hmac.compare_digest(token, RELAY_AUTH_TOKEN) ``` ### Technical Analysis The application treats every client whose apparent source address belongs to `100.64.0.0/10` as authenticated. An IP address in the carrier-grade NAT range is not a cryptographic identity and does not prove that the client is the intended phone or administrator. Any reachable peer on a shared or compromised tailnet receives access equivalent to possession of the relay token. The application does not verify Tailscale user identity, node identity, tags, or grants. ### Attack Path 1. An attacker controls a device that can reach the server over the relevant tailnet or otherwise reaches it with an accepted source address. 2. The attacker calls a protected endpoint without supplying a token. 3. `verify_token()` sees an address in `100.64.0.0/10` and returns `True`. 4. The attacker reads history or context, generates pairing codes, lists sessions, or opens a voice session. 5. The attacker can interact with the OpenClaw agent and consume configured provider servic ...[truncated 339 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Require `RELAY_AUTH_TOKEN` for all clients, including Tailscale clients. - Treat Tailscale as transport encryption and network segmentation, not as a replacement for application authentication. - If identity-based Tailscale authorization is desired, verify authenticated Tailscale identity through a supported mechanism and enforce explicit user, node, or tag allowlists. - Configure restrictive Tailscale ACLs or grants so only intended devices can reach the service. - Use separate tokens with limited scopes rather than one administrator-equivalent token. - Log authorization decisions without logging secrets. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
server.py:1241
Finding
Unbounded WebSocket Audio Buffer Permits Memory Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `server.py:1241-1246` **Vulnerability Type**: Denial of service through unbounded memory allocation **Risk Level**: Medium ### Vulnerable Code ```python elif "bytes" in message and session: if not authenticated: continue if not session.local_stt: session.audio_buffer.extend(message["bytes"]) ``` ### Technical Analysis Binary WebSocket frames are appended to `session.audio_buffer` without a maximum recording size, per-frame limit, session quota, or recording timeout. The 960,000-byte chunking logic in `transcribe_audio()` only runs after the client sends `end_speech`; it does not constrain the amount accumulated beforehand. An authenticated client, or any Tailscale client accepted by the authentication bypass, can continuously stream binary frames and force the Python process to allocate memory until it is killed or the host becomes unstable. ### Attack Path 1. The attacker connects to `/voice` and starts a session. 2. The attacker authenticates or connects from a trusted Tailscale address. 3. The attacker continuously sends binary audio frames. 4. The attacker never sends `end_speech`. 5. `audio_buffer` grows without limit. 6. The process or host exhausts available memory, disrupting the relay and potentially other services. ### Impact Assessment Exploitation can terminate the Clack service, trigger repeated systemd restarts, degrade the host, and consume resources needed by OpenClaw or unrelated services. Because the service currently runs as root, ordinary per-user resource controls may not protect the host. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Define a strict maximum recording size based on supported audio duration. - Reject and close connections that exceed the limit. - Enforce maximum WebSocket frame and message sizes at the ASGI server layer. - Add recording inactivity and total-duration timeouts. - Process audio incrementally or write it to a bounded temporary stream rather than retaining it entirely in memory. - Limit concurrent sessions per token and source identity. - Add systemd resource controls such as `MemoryMax=`, `TasksMax=`, and appropriate CPU limits. - Test oversized and never-ending streams. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/setup.sh:361
Finding
Long-Lived Authentication Secrets Are Embedded in Service Files and URL Query Strings<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh:361-364` **Vulnerability Type**: Insecure credential storage and transmission **Risk Level**: High ### Vulnerable Code ```bash ENV_LINES="Environment=OPENCLAW_GATEWAY_URL=$OPENCLAW_GATEWAY_URL Environment=OPENCLAW_GATEWAY_TOKEN=$OPENCLAW_GATEWAY_TOKEN Environment=RELAY_AUTH_TOKEN=$RELAY_AUTH_TOKEN Environment=VOICE_RELAY_PORT=$PORT Environment=PYTHONUNBUFFERED=1" ``` The same relay token is also placed in a URL by `scripts/pair.sh:29`: ```bash RESPONSE=$(curl -s "http://localhost:${PORT}/pair?token=${RELAY_AUTH_TOKEN}") ``` ### Technical Analysis The OpenClaw gateway token and relay token are written directly into `/etc/systemd/system/clack.service`. No explicit restrictive permission is applied to that unit. System service definitions are commonly readable by local users, making them inappropriate for long-lived secrets. The relay token is additionally passed in a query string. Query-string credentials can appear in process argument listings, reverse-proxy access logs, request logs, browser history, monitoring systems, or diagnostic output. The documentation also promotes query-parameter authentication for HTTP and WebSocket requests. ### Attack Path 1. A local user, monitoring process, proxy, or logging component obtains the service unit or a request URL. 2. The attacker extracts `RELAY_AUTH_TOKEN` or `OPENCLAW_GATEWAY_TOKEN`. 3. The relay token is reused against protected Clack endpoints. 4. If the gateway token is exposed and the gateway is reachable, it is reused directly against OpenClaw. 5. The attacker gains access to conversation data or agent capabilities. ### Impact Assessment Disclosure of the relay token permits broad access to protected relay functionality. Disclosure of the gateway token may provide direct access to the OpenClaw gateway and its enabled endpoints. Scope depends on gateway reachability and the permissions associated with the token. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Store secrets through systemd credentials, a dedicated root-owned environment file, or a secrets manager. - Set strict ownership and mode, such as root/service-account ownership with mode `0600`. - Do not place bearer tokens in URLs. - Accept authentication through an `Authorization: Bearer` header. - For WebSockets, use a protected initial authentication message or a short-lived, single-purpose connection credential. - Use separate tokens for administration, normal clients, pairing, and gateway access. - Rotate existing tokens after migrating storage. - Ensure logs redact authorization headers and credentials. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/setup.sh:189
Finding
Privileged Setup Installs Unpinned Python Dependencies<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup.sh:189-194` **Vulnerability Type**: Unpinned and non-reproducible dependency installation **Risk Level**: Medium ### Vulnerable Code ```bash echo "" echo "Setting up Python environment..." python3 -m venv "$SKILL_DIR/venv" "$SKILL_DIR/venv/bin/pip" install -q fastapi uvicorn aiohttp websockets echo " ✓ Python dependencies installed" ``` ### Technical Analysis The setup process installs the latest available releases of four packages and their transitive dependencies without fixed versions or hashes. Installation behavior can therefore change after the Skill has been audited. A compromised package release, dependency account, package index, or transitive dependency can introduce malicious code. The installed modules are subsequently imported by a persistent root service, amplifying the impact of supply-chain compromise. ### Attack Path 1. A required package or transitive dependency is compromised upstream. 2. An administrator runs or re-runs `scripts/setup.sh`. 3. pip resolves and installs the compromised release because no version or hash is pinned. 4. The Clack service starts and imports the malicious package. 5. Malicious dependency code executes with the service's privileges, currently root. ### Impact Assessment A dependency compromise can provide persistent arbitrary code execution and access to provider API keys, relay credentials, OpenClaw credentials, audio content, and stored conversation history. Under the current systemd configuration, execution occurs as root. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Maintain a lock file with exact direct and transitive dependency versions. - Require hashes for every artifact, for example with pip's `--require-hashes`. - Build and test dependencies in CI before publishing a release. - Use a controlled package index or verified wheel repository where practical. - Scan locked dependencies for known vulnerabilities. - Perform installation under an unprivileged account. - Upgrade dependencies through reviewed releases rather than resolving arbitrary latest versions during setup. ]]>
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
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (72)

External Script Fetching

High
Category
Supply Chain
Content
## 1.5.3 (2026-02-26)

### Security
- **Tailscale APT install**: Replaced `curl | sh` with official APT repository for Tailscale installation
- **Context sanitization**: User context is stripped to natural-language characters only (letters, numbers, common punctuation). IP addresses and domains are removed. Capped at 1000 characters.
- **Provider keys in config.json**: API keys stored in `config.json` (chmod 600) instead of systemd environment variables
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding
The skill markets itself primarily as deployment/management for a relay server, but it also includes uninstall behavior and privileged modification/removal of system files and services. That gap is risky because destructive system changes may be triggered under a benign-sounding skill description, especially in an agent context where users may not expect service removal or config deletion.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill markets itself primarily as deployment/management for a relay server, but it also includes uninstall behavior and privileged modification/removal of system files and services. That gap is risky because destructive system changes may be triggered under a benign-sounding skill description, especially in an agent context where users may not expect service removal or config deletion.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
Context is sanitized before saving — only natural-language characters are kept (letters, numbers, common punctuation). IP addresses and domains are stripped. The server returns the sanitized text in the response so the app can show the user exactly what will be sent as context.

Context persists across calls and server restarts. Clear it via `DELETE /context` or by sending an empty `set_context` message.

## Conversation History
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
Context is sanitized before saving — only natural-language characters are kept (letters, numbers, common punctuation). IP addresses and domains are stripped. The server returns the sanitized text in the response so the app can show the user exactly what will be sent as context.

Context persists across calls and server restarts. Clear it via `DELETE /context` or by sending an empty `set_context` message.

## Conversation History
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
- **Max messages:** 50 (configurable via `CLACK_MAX_HISTORY`)
- History persists across calls and server restarts
- Viewable via `GET /history`, clearable via `DELETE /history`

## Echo Test Mode
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
- **Max messages:** 50 (configurable via `CLACK_MAX_HISTORY`)
- History persists across calls and server restarts
- Viewable via `GET /history`, clearable via `DELETE /history`

## Echo Test Mode
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Memory Manipulation

High
Category
Memory Poisoning
Content
| `GET /voices` | GET | Yes | List available TTS voices |
| `GET /sessions` | GET | Yes | List active sessions |
| `GET /history` | GET | Yes | Get conversation history |
| `DELETE /history` | DELETE | Yes | Clear conversation history |
| `GET /context` | GET | Yes | Get current user context |
| `PUT /context` | PUT | Yes | Set user context (query param `text`) |
| `POST /context` | POST | Yes | Set user context (JSON body `{"text": "..."}`) |
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

External Script Fetching

High
Category
Supply Chain
Content
fi

# Generate pairing code
RESPONSE=$(curl -s "http://localhost:${PORT}/pair?token=${RELAY_AUTH_TOKEN}")
CODE=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['code'])" 2>/dev/null)

if [[ -z "$CODE" ]]; then
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Ssd 1

High
Confidence
99% confidence
Finding
The service accepts a client-supplied systemPrompt and uses it as the session’s top-level instruction, allowing any client to redefine safeguards and behavior. This defeats the server’s own protective prompt and makes jailbreaks, unsafe actions, and policy bypass straightforward.

Ssd 1

High
Confidence
98% confidence
Finding
Untrusted context text is concatenated directly into the system prompt, giving user-supplied content system-level influence over downstream model behavior. In a voice-agent bridge, this can subvert safety rules, bias decisions, exfiltrate retained information, or manipulate future sessions because the context is also persisted.

Memory Manipulation

High
Category
Memory Poisoning
Content
session.update_context(ctx_text)
                        await session.send_json({"type": "context_updated", "text": ctx_text})
                    else:
                        # Clear context
                        session.user_context = {}
                        if CONTEXT_FILE.exists():
                            CONTEXT_FILE.unlink()
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Memory Manipulation

High
Category
Memory Poisoning
Content
session.update_context(ctx_text)
                        await session.send_json({"type": "context_updated", "text": ctx_text})
                    else:
                        # Clear context
                        session.user_context = {}
                        if CONTEXT_FILE.exists():
                            CONTEXT_FILE.unlink()
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Credential Access

High
Category
Privilege Escalation
Content
if ! command -v tailscale &>/dev/null; then
      echo ""
      echo "Installing Tailscale via APT..."
      curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.noarmor.gpg | tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
      curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.tailscale-keyring.list | tee /etc/apt/sources.list.d/tailscale.list >/dev/null
      apt-get update -qq
      apt-get install -y -qq tailscale > /dev/null 2>&1
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
if ! command -v tailscale &>/dev/null; then
      echo ""
      echo "Installing Tailscale via APT..."
      curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.noarmor.gpg | tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
      curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.tailscale-keyring.list | tee /etc/apt/sources.list.d/tailscale.list >/dev/null
      apt-get update -qq
      apt-get install -y -qq tailscale > /dev/null 2>&1
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
if ! command -v tailscale &>/dev/null; then
      echo ""
      echo "Installing Tailscale via APT..."
      curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.noarmor.gpg | tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
      curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.tailscale-keyring.list | tee /etc/apt/sources.list.d/tailscale.list >/dev/null
      apt-get update -qq
      apt-get install -y -qq tailscale > /dev/null 2>&1
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
if ! command -v tailscale &>/dev/null; then
      echo ""
      echo "Installing Tailscale via APT..."
      curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.noarmor.gpg | tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
      curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.tailscale-keyring.list | tee /etc/apt/sources.list.d/tailscale.list >/dev/null
      apt-get update -qq
      apt-get install -y -qq tailscale > /dev/null 2>&1
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
echo "Stopping clack service..."
  systemctl stop clack 2>/dev/null || true
  systemctl disable clack 2>/dev/null || true
  rm -f /etc/systemd/system/clack.service
  systemctl daemon-reload
  echo "  ✓ Service removed"
else
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# Remove CLI symlink
if [[ -L /usr/local/bin/clack ]]; then
  rm -f /usr/local/bin/clack
  echo "  ✓ 'clack' command removed"
fi
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# Remove venv
if [[ -d "$SKILL_DIR/venv" ]]; then
  rm -rf "$SKILL_DIR/venv"
  echo "  ✓ Python venv removed"
fi
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Intent-Code Divergence

High
Confidence
98% confidence
Finding
The authentication logic allows unauthenticated WebSocket access when no relay token is configured by treating 'no token' as implicitly allowed, even though the startup log suggests otherwise. This can expose the voice relay, STT/TTS usage, history, and downstream OpenClaw interaction to any reachable client if the service is internet-accessible or mis-networked.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Security
- **Tailscale APT install**: Replaced `curl | sh` with official APT repository for Tailscale installation
- **Context sanitization**: User context is stripped to natural-language characters only (letters, numbers, common punctuation). IP addresses and domains are removed. Capped at 1000 characters.
- **Provider keys in config.json**: API keys stored in `config.json` (chmod 600) instead of systemd environment variables

### Features
- **Sanitized context returned to client**: All context endpoints (PUT, POST, WebSocket) return the sanitized text so the app can show users exactly what is stored
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Security
- **Tailscale APT install**: Replaced `curl | sh` with official APT repository for Tailscale installation
- **Context sanitization**: User context is stripped to natural-language characters only (letters, numbers, common punctuation). IP addresses and domains are removed. Capped at 1000 characters.
- **Provider keys in config.json**: API keys stored in `config.json` (chmod 600) instead of systemd environment variables

### Features
- **Sanitized context returned to client**: All context endpoints (PUT, POST, WebSocket) return the sanitized text so the app can show users exactly what is stored
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
```bash
git clone https://github.com/fbn3799/clack-skill.git ~/.openclaw/skills/clack
sudo bash ~/.openclaw/skills/clack/scripts/setup.sh
```

This clones the repo and runs the interactive setup.
Confidence
88% confidence
Finding
The README instructs users to run a repository-controlled setup script with `sudo bash`, which grants full root privileges to whatever code is in the cloned repo. Even if the project is legitimate, this creates a real supply-chain and privilege-escalation risk because a compromised repo, malicious commit, or unsafe setup logic would execute as root on the host.

Session Persistence

Medium
Category
Rogue Agent
Content
## Configuration

Service configuration is via environment variables (set in the systemd service file). Provider API keys are stored separately in `config.json` (created by the setup script).

| Variable | Default | Description |
|----------|---------|-------------|
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.

Static analysis

No suspicious patterns detected.