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

Content
View full analysis
/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
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/persona_client.sh:13
Finding

Unvalidated backoff configuration reaches Bash arithmetic evaluation

Content
View full analysis
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
View remediation
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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
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.

Content

Scanner excerpt · dist/index.js (reported line 34)May include surrounding context.

js
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,

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/run-gateway-with-persona-client.js (reported line 10)May include surrounding context.

js
* 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.
 */

Credential Access

High
Category
Privilege Escalation
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.

Content

Scanner excerpt · src/index.ts (reported line 34)May include surrounding context.

ts
const scriptPath = path.join(skillDir, "scripts", "persona_client.sh");

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

  const child = spawn("bash", [scriptPath], {
    cwd: skillDir,

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

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/E2E-TEST.md (reported line 5)May include surrounding context.

md
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):

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · docs/E2E-TEST.md (reported line 10)May include surrounding context.

From any machine (e.g. your Mac):

bash
curl -s $PERSONA_SERVICE_URL/persona/requests \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

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/E2E-TEST.md (reported line 133)May include surrounding context.

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"

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/persona_client.sh (reported line 40)May include surrounding context.

sh
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() {

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 18)May include surrounding context.

json
"node": ">=18"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.0.0"
  }
}

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 19)May include surrounding context.

json
},
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.0.0"
  }
}

Missing User Warnings

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

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