Back to skill

Security audit

ZenQuote

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its quote-delivery purpose, but its scheduled Telegram setup persists unvalidated chat/account inputs into an automated agent job, which needs review before installation.

Review this skill before installing if you plan to use daily delivery. Avoid running setup with untrusted chat_id or account_id values, inspect ~/.openclaw/cron after setup, and prefer a version that validates identifiers, escapes JSON safely, uses a private temporary image path, and documents how to remove scheduled jobs.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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
  • 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 (3)

T02 · Agent Memory Poisoning

Error
Location
scripts/zenquote.sh:111
Finding

Persistent Agent Prompt and JSON Injection Through Unvalidated Identifiers

Content
View full analysis
"$cron_config" << EOF { "name": "${job_name}", "enabled": true, "schedule": { "kind": "cron", "expr": "0 9 * * *", "tz": "Asia/Taipei" }, "payload": { "kind": "agentTurn", "message": "Send daily inspirational ${type} from ZenQuotes API to telegram:${chat_id} using the zenquote skill. Use ${message_cmd} command.", "model": "opencode-go/kimi-k2.5" }, "delivery": { "mode": "announce", "channel": "telegram", "to": "${chat_id}", "accountId": "${account_id}" } } EOF echo "{\"setup\": true, \"type\": \"${type}\", \"message\": \"Daily ${type} scheduled for 9:00 AM\", \"chat_id\": \"${chat_id}\"}" } ``` ### Technical Analysis The `chat_id` and `account_id` parameters are accepted without format validation or JSON escaping. They are interpolated directly into a persistent cron configuration using a shell heredoc. The `chat_id` is used in two security-sensitive contexts: 1. Inside the natural-language `agentTurn` message that will be interpreted during future scheduled executions. 2. Inside the JSON `delivery.to` property. The `account_id` is similarly inserted directly into `delivery.account ...[truncated 2177 chars]
Remediation
View remediation
"$cron_config" ``` 4. Do not place user-controlled routing identifiers inside a natural-language agent prompt. Keep routing data exclusively in structured, validated delivery fields. 5. Write the configuration to a temporary file first, validate it with `jq empty`, assign restrictive permissions, and then move it atomically into place. 6. Add tests containing quotes, backslashes, newlines, JSON fragments, and prompt-like text to confirm that input cannot alter the generated JSON or scheduled instructions. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/zenquote.sh:91
Finding

Untrusted Quote Count Evaluated as a Bash Arithmetic Expression

Content
View full analysis
Remediation
View remediation
&2 return 1 fi ``` 2. Apply a strict range limit consistent with the API response size: ```bash if (( 10#$count < 1 || 10#$count > 50 )); then printf '%s\n' 'Error: count must be between 1 and 50' >&2 return 1 fi ``` 3. Use `10#$count` after validation to force decimal interpretation and avoid accidental octal handling: ```bash local numeric_count=$((10#$count)) ``` 4. Avoid `seq` and command substitution; use a native arithmetic loop: ```bash for ((i = 0; i < numeric_count; i++)); do # Process quote. done ``` 5. Add negative tests for alphabetic input, arithmetic expressions, leading-zero values, negative numbers, very large integers, and shell expansion syntax. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/zenquote.sh:48
Finding

Predictable Shared Temporary Image Path Enables Symlink-Based File Overwrite

Content
View full analysis
/dev/null if [[ -f "$output_path" && -s "$output_path" ]]; then echo "$output_path" else echo "" fi } ``` ```bash image) local img_path=$(fetch_image "/tmp/zenquote_image.jpg") if [[ -n "$img_path" && -f "$img_path" ]]; then echo "{\"image_path\": \"${img_path}\", \"caption\": \"Provided by ZenQuotes API https://zenquotes.io/\"}" else echo "{\"error\": \"Failed to fetch image\"}" fi ;; ``` ### Technical Analysis The image command always writes to the fixed path `/tmp/zenquote_image.jpg`. Shared temporary directories are normally writable by other local users. Because the script does not create the file securely, check for symbolic links, or use an exclusive creation operation, a local attacker can prepare that path before the skill runs. `curl -o` follows the supplied path when opening the output file. If the predictable path is a symbolic link to another file writable by the victim account, the downloaded response can overwrite or corrupt the symlink target. The subsequent `-f` and `-s` checks occur only after the write and therefore do not prevent the overwrite. They can also follow a symbolic link. ### Attack Path 1. A local attacker determines that the victim uses the ZenQuote image command. 2. The attacker creates `/tmp/zenquote_image.jpg` as a symbolic link to a file that the victim account can write. 3. The victim or a scheduled agent invokes the `image` command. 4. `curl` opens the predictable path and follows the symbolic link. 5. The target file is overwritten with the d ...[truncated 740 chars]
Remediation
View remediation
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (11)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill is presented primarily as a quote-fetching utility, but its behavior includes persistent local changes via cron setup and targeting message delivery using chat/account identifiers. Persistent scheduled execution and local file writes are materially more sensitive than one-shot quote retrieval, and hiding or under-declaring them can lead to unauthorized persistence or unexpected outbound actions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The README documents features that modify the local system state by downloading files and installing persistent cron jobs, but it does not clearly warn users that these actions will write to disk and create scheduled tasks. In an agent/skill ecosystem, understated persistence and filesystem changes can mislead users into granting execution to a skill without understanding its ongoing behavior, increasing the risk of unwanted automation or abuse if the underlying scripts are changed or misused.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill documentation advertises and instructs use of shell and network capabilities but does not declare any explicit tool scope or permissions boundaries. This weakens reviewability and consent because an installer or orchestrator cannot clearly distinguish a simple quote-fetching skill from one that can execute commands and access external services.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The setup command is documented as configuration for daily delivery, but the nearby description does not clearly warn that it creates a persistent cron job. Lack of up-front disclosure about persistence can cause users or calling agents to authorize a one-time action while actually granting long-lived automated execution.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Autonomous daily sending is not inherently malicious in this context, but scheduled unattended actions do increase risk because they continue operating after the initial request and may send messages or fetch network content without fresh approval. In a messaging-integrated skill, this persistence makes mistakes or misuse more consequential than a one-time quote fetch.

Content

Scanner excerpt · SKILL.md (reported line 71)May include surrounding context.

Daily Cron Setup

To automatically send a quote every morning at 9:00 AM:

Daily Text Quote (default)

bash

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The natural-language triggers are broad enough that a generic user request like 'Setup daily quotes for me' could invoke persistence-creating behavior without clearly surfacing that a cron job will be installed. In an agent context, vague triggers increase the chance of overbroad execution and reduce meaningful user consent for actions that have ongoing effects.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill goes beyond passive quote retrieval and creates Telegram-targeted scheduled delivery jobs that can send messages automatically to a specified chat ID. This expands the trust boundary from local content fetching into persistent outbound messaging, creating risk of unsolicited messaging, misdelivery, and abuse if invoked with attacker-controlled parameters or by a user who does not understand the side effects.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The cron setup persists chat routing data, including destination chat ID and account ID, into a local scheduling configuration without any disclosure, consent prompt, or retention notice. This can expose sensitive messaging metadata to other local users/processes and may cause users to unknowingly create persistent outbound delivery rules tied to private chat destinations.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The cron configuration forces the timezone to Asia/Taipei in a natural-language-adjacent config value, which imposes a locale-specific behavior on all users. There is no opt-in, parameter, or documentation indicating that the timezone is region-specific or user-selectable.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

L140 states that the skill automatically includes attribution in all outputs. However, the documented image output at L62-L65 shows only Provided by ZenQuotes API https://zenquotes.io/, which does not match the required attribution text documented at L126-L138. This is a documentation-level contradiction about what the skill outputs.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

The help documentation claims image will "Get quote image URL", but the implementation calls curl -o to download the image into /tmp/zenquote_image.jpg and returns JSON containing image_path. This is an active contradiction between documentation and behavior, not just missing detail.

Content

No source excerpt is available for this finding.