Back to skill

Security audit

Persona Consent Telegram (Hub)

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly does what it claims, but it handles persona data and secrets through a detached background shell client with weak endpoint and environment scoping.

Install only if you trust the persona-service endpoint and understand that approved persona content, request metadata, and optional shared secrets leave the machine. Use HTTPS-only trusted endpoints, a dedicated Telegram bot token, restricted permissions on OpenClaw config files, and be prepared to stop the detached persona_client.sh process manually. Consider waiting for a version that validates endpoints, allowlists child-process environment variables, validates numeric backoff settings, and ties the polling client lifecycle to the gateway.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/request_persona.sh:225
Finding
Unrestricted service endpoints permit plaintext transmission of credentials and persona data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/request_persona.sh:225, 258-259`; `scripts/persona_client.sh:35-54` **Vulnerability Type**: Unvalidated endpoint configuration and insecure transmission of sensitive data **Risk Level**: High ### Vulnerable Code ```bash # scripts/request_persona.sh local telegram_api_base="${TELEGRAM_API_BASE:-https://api.telegram.org}" ``` ```bash # scripts/request_persona.sh if ! send_owner_prompt "$telegram_api_base" "$telegram_bot_token" "$telegram_owner_chat_id" "$request_id" "$requester_id" "$reason"; then print_refusal return 0 fi ``` The called function constructs a URL containing the Telegram bot token: ```bash curl -sS --fail \ --request POST \ --data-urlencode "chat_id=$owner_chat_id" \ --data-urlencode "text=$text" \ --data-urlencode "reply_markup=$reply_markup" \ "$api_base/bot$bot_token/sendMessage" >/dev/null ``` The persona-service client similarly accepts an unrestricted base URL and sends credentials and approved persona data to it: ```bash http_get_next() { local url url="${PERSONA_SERVICE_URL%/}/persona/client/next?client_id=${PERSONA_CLIENT_ID}" local args=() if [[ -n "$PERSONA_CLIENT_SHARED_SECRET" ]]; then args+=(-H "X-Client-Secret: ${PERSONA_CLIENT_SHARED_SECRET}") fi curl -sS --fail "${args[@]}" "$url" } http_post_response() { local body="$1" local url url="${PERSONA_SERVICE_URL%/}/persona/client/responses" local args=(-H "Content-Type: application/json") if [[ -n "$PERSONA_CLIENT_SHARED_SECRET" ]]; then args+=(-H "X-Client-Secret: ${PERSONA_CLIENT_SHARED_SECRET}") fi curl -sS --fail "${args[@]}" -d "$body" "$url" } ``` ### Technical Analysis `TELEGRAM_API_BASE` and `PERSONA_SERVICE_URL` are used without validating their URL scheme, hostname, port, embedded credentials, or destination. Consequently, the scripts allow plaintext HTTP endpoints or attacker-controlled HTTPS endpoints. The Telegram bot token is included directly in the re ...[truncated 2044 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse both endpoint settings with a dedicated URL parser before invoking `curl`. 2. Require the `https:` scheme for all non-development deployments. 3. Reject URLs containing embedded usernames or passwords, fragments, control characters, or unsupported ports. 4. Restrict `TELEGRAM_API_BASE` to `https://api.telegram.org` by default. If custom endpoints are required for testing, gate them behind an explicit development-only option. 5. Add a configurable hostname allowlist for `PERSONA_SERVICE_URL`. 6. Use `curl` options such as `--proto '=https'`, `--proto-redir '=https'`, and an appropriate `--max-redirs` value. 7. Consider certificate or public-key pinning for controlled persona-service deployments. 8. Avoid placing the Telegram token in logs or errors, and document immediate token and shared-secret rotation after suspected exposure. 9. Add automated tests confirming that `http://`, malformed URLs, embedded credentials, and unapproved hosts are rejected before any network request occurs. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/persona_client.sh:13
Finding
Unvalidated backoff configuration reaches Bash arithmetic evaluation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/persona_client.sh:13-14, 96-103, 113-120` **Vulnerability Type**: Shell arithmetic-expression injection through environment configuration **Risk Level**: Medium ### Vulnerable Code ```bash POLL_INTERVAL_SECONDS="${PERSONA_CLIENT_POLL_INTERVAL_SECONDS:-10}" MAX_BACKOFF_SECONDS="${PERSONA_CLIENT_MAX_BACKOFF_SECONDS:-60}" ``` The values are subsequently used in retry logic without numeric validation: ```bash local backoff="$POLL_INTERVAL_SECONDS" while true; do if http_post_response "$body"; then log "submitted result for request_id=${request_id}" break fi log "failed to submit result for request_id=${request_id}, retrying in ${backoff}s" sleep "$backoff" backoff=$((backoff * 2)) if (( backoff > MAX_BACKOFF_SECONDS )); then backoff="$MAX_BACKOFF_SECONDS" fi done ``` The same pattern occurs in the polling loop: ```bash if ! response="$(http_get_next)"; then log "error contacting persona-service, sleeping ${backoff}s" sleep "$backoff" backoff=$((backoff * 2)) if (( backoff > MAX_BACKOFF_SECONDS )); then backoff="$MAX_BACKOFF_SECONDS" fi continue fi ``` ### Technical Analysis Bash arithmetic contexts do not treat variable contents as inert strings. Values referenced inside `$((...))` and `((...))` are interpreted as arithmetic expressions, and malicious expressions can trigger additional shell expansions in vulnerable arithmetic contexts. The script validates neither `PERSONA_CLIENT_POLL_INTERVAL_SECONDS` nor `PERSONA_CLIENT_MAX_BACKOFF_SECONDS`. In particular, `MAX_BACKOFF_SECONDS` is evaluated directly by: ```bash (( backoff > MAX_BACKOFF_SECONDS )) ``` A crafted value can therefore be interpreted as an arithmetic expression rather than a decimal limit. The `POLL_INTERVAL_SECONDS` value is also unsafe input to both `sleep` and arithmetic operations; malformed values can at minimum terminate the detached client because `set -e` is enabled. The configur ...[truncated 1691 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Validate and normalize both values before they are used by `sleep` or any arithmetic context: ```bash POLL_INTERVAL_SECONDS="${PERSONA_CLIENT_POLL_INTERVAL_SECONDS:-10}" MAX_BACKOFF_SECONDS="${PERSONA_CLIENT_MAX_BACKOFF_SECONDS:-60}" if ! [[ "$POLL_INTERVAL_SECONDS" =~ ^[1-9][0-9]*$ ]]; then log "PERSONA_CLIENT_POLL_INTERVAL_SECONDS must be a positive integer" exit 1 fi if ! [[ "$MAX_BACKOFF_SECONDS" =~ ^[1-9][0-9]*$ ]]; then log "PERSONA_CLIENT_MAX_BACKOFF_SECONDS must be a positive integer" exit 1 fi if (( POLL_INTERVAL_SECONDS > 3600 || MAX_BACKOFF_SECONDS > 86400 )); then log "Polling configuration exceeds allowed bounds" exit 1 fi if (( MAX_BACKOFF_SECONDS < POLL_INTERVAL_SECONDS )); then log "Maximum backoff must not be lower than the poll interval" exit 1 fi ``` Additional hardening should include: 1. Convert validated values to canonical decimal integers before reuse. 2. Apply explicit lower and upper bounds to prevent excessively frequent polling or extremely long sleeps. 3. Fail securely before spawning the detached process if validation fails. 4. Add tests using nonnumeric strings, negative numbers, zero, whitespace, arithmetic operators, array syntax, and shell-expansion metacharacters. 5. Restrict modification of `openclaw.json` and related environment files to the gateway owner, using least-privilege filesystem permissions. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
Findings (23)

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
The declared purpose centers on consent mediation via Telegram and responding to specific persona-related requests or pending persona-service requests. The actual code chunk exposes only an `onGatewayStart` hook that launches `scripts/persona_client.sh` if persona-service environment variables are configured. That indicates integration/bootstrap behavior for a persona client, but there is no visible logic for approval workflows, Telegram interactions, or request-triggered persona sharing. The startup hook is a materially different observable behavior from the declared trigger model, so this is a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description emphasizes consent gating for persona sharing via Telegram owner approval plus persona-service integration for chatbot requests. The supplied code chunk, however, only starts a background client script when the gateway starts and required environment variables are present. That startup behavior may support persona-service connectivity, but the core declared functionality—approval workflow, Telegram interaction, and handling of persona-related requests—is absent from this code. Because the actual trigger and implemented behavior are materially narrower and different from the declared purpose, this is a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
82% confidence
Finding
The core declared behavior of gating persona sharing behind explicit Telegram owner approval is accurately reflected by the code. However, the description also claims it 'connects to persona-service for external chatbots' and should be used when 'persona-service sends a pending request.' This code does not interact with any persona-service, external chatbot API, or pending-request mechanism; it only takes requester_id/reason arguments, talks to the Telegram Bot API, and reads a local persona file after approval. That missing integration is a material description/behavior mismatch, even though the approval gate itself matches.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
This code chunk’s primary behavior is process orchestration: load config, optionally spawn a background persona client, and run the gateway. The declared description emphasizes a consent/approval mechanism for sharing persona information via Telegram and use in response to persona-related user requests or pending persona-service requests. Those behaviors are not present here. While starting a persona client could support the broader declared system, this chunk itself materially differs from the declared purpose because it lacks the approval gating and trigger-handling logic and instead adds undeclared lifecycle/launcher behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description emphasizes consent gating for persona sharing via explicit Telegram owner approval and integration with persona-service for chatbot requests. The provided code only starts a detached shell script at gateway startup if PERSONA_SERVICE_URL and PERSONA_CLIENT_ID are configured. That startup behavior is a materially different trigger from the declared usage conditions, and the key claimed behavior—approval gating through Telegram—is absent from this code chunk. While launching a persona client could support persona-service integration, the supplied code does not substantiate the stated primary purpose, so this is a mismatch.

Credential Access

High
Category
Privilege Escalation
Content
return; // skill installed but persona-service not configured
    }
    const scriptPath = node_path_1.default.join(skillDir, "scripts", "persona_client.sh");
    const childEnv = { ...process.env, ...env };
    const child = (0, node_child_process_1.spawn)("bash", [scriptPath], {
        cwd: skillDir,
        env: childEnv,
Confidence
89% confidence
Finding
The child process receives a merged environment of all parent process variables plus skill-specific env, which can expose unrelated secrets and tokens to the spawned shell script and anything it launches. Because the process is detached and shell-based, this increases the risk of accidental secret leakage, misuse, or exfiltration if the script is compromised or overly broad in its behavior.

Credential Access

High
Category
Privilege Escalation
Content
* Usage:
 *   node scripts/run-gateway-with-persona-client.js [--] [openclaw gateway args...]
 *
 * Reads ~/.openclaw/openclaw.json and uses skills.entries["persona-consent-telegram-hub"].env
 * for the persona-client. If PERSONA_SERVICE_URL and PERSONA_CLIENT_ID are not set,
 * only openclaw gateway is run.
 */
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
const scriptPath = path.join(skillDir, "scripts", "persona_client.sh");

  const childEnv = { ...process.env, ...env };

  const child = spawn("bash", [scriptPath], {
    cwd: skillDir,
Confidence
88% confidence
Finding
The code merges the full parent process environment with skill-provided variables and passes all of it to the spawned script, potentially exposing unrelated secrets such as tokens, credentials, or internal service configuration. Since the child is a shell-launched detached process, any bug, compromise, or unexpected behavior in the script can access and exfiltrate a much broader credential set than necessary.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill declares executable capabilities via metadata requirements and explicitly instructs running a shell script, but it does not declare any tool scope such as permissions or allowed-tools. That creates an authorization gap where the skill may gain shell, environment, and file access without transparent least-privilege constraints, which is risky for a skill that handles sensitive persona data.

Vague Triggers

Medium
Confidence
84% confidence
Finding
The invocation criteria are broad enough to trigger on ordinary conversation about identity, profile, or writing style, which can cause unnecessary execution of a sensitive persona-sharing workflow. In context, this is more dangerous because the skill is designed to access and potentially disclose persona content; broad matching increases the chance of accidental invocation, consent fatigue, or unintended data exposure pathways.

Context-Inappropriate Capability

Medium
Confidence
84% confidence
Finding
The manifest describes a skill that gates persona sharing behind Telegram owner approval and connects to a persona service. While contacting a persona service is in-scope, launching an arbitrary bash script as a detached child process is a more powerful execution capability than that purpose implies and is not justified by the stated role alone.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The code spawns `bash` to run `scripts/persona_client.sh` as a detached background process and suppresses all stdio with `ignore`. Although comments describe the behavior for developers, there is no confirmation prompt, logging, or other user-visible disclosure that a background shell process is being launched.

Session Persistence

Medium
Category
Rogue Agent
Content
Use your own persona-service base URL and `client_id` (from `skills.entries["persona-consent-telegram-hub"].env`) in the commands below.

## 1. Create a request (use `client_id` for your Clawbot)

From any machine (e.g. your Mac):
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.

External Transmission

Medium
Category
Data Exfiltration
Content
From any machine (e.g. your Mac):

```bash
curl -s $PERSONA_SERVICE_URL/persona/requests \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The documentation explicitly instructs operators to read and copy a shared secret from one host into another system configuration, and later to export Telegram bot credentials and persona paths in a shell session. While this is test documentation rather than executable code, it normalizes manual handling of sensitive secrets without guidance on secure storage, redaction, shell history, or least-privilege practices, increasing the chance of credential leakage.

Session Persistence

Medium
Category
Rogue Agent
Content
### 3. After creating a request, does the next poll return it?

From your Mac (or wherever you run curl): create a request and note the `request_id`. Then on the Clawbot server, within a few seconds run:

```bash
curl -s "$PERSONA_SERVICE_URL/persona/client/next?client_id=$PERSONA_CLIENT_ID"
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.

External Transmission

Medium
Category
Data Exfiltration
Content
if [[ -n "$PERSONA_CLIENT_SHARED_SECRET" ]]; then
    args+=(-H "X-Client-Secret: ${PERSONA_CLIENT_SHARED_SECRET}")
  fi
  curl -sS --fail "${args[@]}" "$url"
}

http_post_response() {
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The script reads the persona file and emits its full contents in JSON when approval is granted, but there is no user-facing log, prompt, comment, or docstring warning that local file contents will be disclosed. For a code file, this is a data-access and data-return operation that lacks any visible disclosure in the file itself.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script sends `requester_id` and `reason` to the Telegram API via `curl`, which is a network transmission of user-supplied or system-related data. There is no confirmation prompt, user-facing notice, or explanatory comment indicating that this information will be sent to an external service.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The startup hook launches a detached shell process that continues running independently of the parent gateway, which expands the skill's execution capability beyond simple consent gating. Because it invokes a shell script with inherited environment variables and no supervision, compromise of the script or its dependencies could enable persistent background execution and make security monitoring and lifecycle control harder.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"node": ">=18"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.0.0"
  }
}
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.0.0"
  }
}
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Missing User Warnings

Low
Confidence
80% confidence
Finding
The script performs repeated network calls to Telegram's `getUpdates` endpoint using the bot token, but the file contains no user-facing notice or explanatory comment about this external communication. This is a network operation in a code file that lacks visible disclosure.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/run-gateway-with-persona-client.js:55

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
src/index.ts:36