Back to skill

Security audit

Global Weather Service

Security checks for vulnerabilities and agentic risk

Overview

This weather skill mostly does what it says, but its scheduled push feature stores delivery data and can modify persistent cron subscriptions without enough ownership and input-safety controls.

Review before installing in any shared or multi-user OpenClaw environment. Only use it where subscription commands are restricted to trusted callers, and consider clearing packaged data files before deployment. The publisher should add owner checks, sanitize city names before writing cron prompts, document stored data and deletion behavior, and scope permissions to Open-Meteo, the two data files, and OpenClaw cron commands.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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)

T01 · Skill Instruction Hijacking

Error
Location
scripts/manage_weather_subscription.py:287
Finding

Persistent Agent Prompt Injection Through Unsanitized City Input

Content
View full analysis
str: day_phrase = DAY_MODE_LABEL.get(mode, "天气") if mode == "tomorrow": fetch_hint = "查询明天的天气" report_days = 1 elif mode == "7day": fetch_hint = f"输出未来{days}天的天气" report_days = days else: fetch_hint = "查询当天天气" report_days = 1 normalized_to = normalize_target(to) return ( f"请运行 global-weather-service 技能中的 weather_report.py,为用户 {normalized_to} 查询 {city} 的{day_phrase},并直接投递最终天气内容。" "数据源必须使用 Open-Meteo。" f"请在触发时重新查询最新数据,{fetch_hint}。" f"请使用命令 `python scripts\\weather_report.py \"{city}\" --days {report_days}` 生成天气简报。" "如果 mode 是 tomorrow,请在输出文案中明确这是明日天气,但仍只输出一份单日简报。" "输出格式必须为中文天气简报,包含标题、日期、天气、温度、生活指数、穿衣建议、紫外线和其他建议。" "不要解释过程,不要输出系统说明。" ) ``` The resulting message is persisted as an OpenClaw cron instruction: ```python message = build_agent_message(to, city, mode, days) cmd = [ openclaw_cmd, "cron", "add", "--name", name, "--session", "isolated", "--message", message, "--channel", "qqbot", "--to", normalize_target(to), "--announce", "--json", ] ``` ### Technical Analysis The user-controlled `city` value is interpolated verbatim into natural-language Agent instructions and into a quoted command displayed inside those instructions. The application does not constrain its length or character set and does not reject newlines, backticks, quotation marks, or instruction-like content. Although `subprocess.run()` receives a list of arguments and therefore does not directly introduce shell command injection at cron creation time, this does not protect the downstream Agent ...[truncated 1701 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/manage_weather_subscription.py:389
Finding

Missing Ownership Checks for Subscription Updates and Deletions

Content
View full analysis
dict: items = load_state() if job_id: found = next((x for x in items if x.get("id") == job_id), None) if not found: raise SystemExit("subscription not found") return found if name: matched = [x for x in items if x.get("name") == name or x.get("city") == name] if not matched: raise SystemExit("subscription not found") if len(matched) > 1: raise SystemExit("matched multiple subscriptions, please remove by --id") return matched[0] raise SystemExit("need --id or --name") def remove_subscription(job_id: str | None = None, name: str | None = None) -> dict: existing = find_subscription(job_id=job_id, name=name) openclaw_cmd = find_openclaw_cmd() result = run_cmd([openclaw_cmd, "cron", "rm", existing["id"], "--json"]) if result.returncode != 0: raise SystemExit(result.stderr or result.stdout) items = [x for x in load_state() if x.get("id") != existing["id"]] save_state(items) wait_for_state(lambda xs: all(x.get("id") != existing["id"] for x in xs)) try: payload = json.loads(result.stdout or "{}") except Exception: payload = {"ok": True, "id": existing["id"]} payload["removedSubscription"] = existing return payload def update_subscription(job_id: str, *, city: str | None, time_text: str | None, days: int | None, mode: str | None) -> dict: existing = find_subscription(job_id=job_id) to = existing.get("to") if not to: raise SystemExit("subscription missing target") next_city = city or existing.get("city") next_time = time_text or existing.get("time") ...[truncated 3715 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (27)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The code clearly implements weather lookup and formatted Chinese forecast reporting using Open-Meteo, which matches part of the description. However, the description prominently claims a broader 'complete weather system' with scheduled weather push/subscription management capabilities (create fixed-time pushes, list subscriptions, update subscriptions, delete subscriptions). None of those capabilities appear in the supplied code. The script is limited to one-off forecast retrieval and report generation from the command line. There are no persistence, scheduler, database, subscription APIs, or management commands. Therefore the declared description materially overstates the implemented functionality.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/manage_weather_subscription.py set-timezone --to "<target>" --timezone "Asia/Shanghai"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/manage_weather_subscription.py set-timezone --to "<target>" --timezone "Asia/Shanghai"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/manage_weather_subscription.py set-timezone --to "<target>" --timezone "Asia/Shanghai"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/manage_weather_subscription.py set-timezone --to "<target>" --timezone "Asia/Shanghai"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/manage_weather_subscription.py set-timezone --to "<target>" --timezone "Asia/Shanghai"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/manage_weather_subscription.py set-timezone --to "<target>" --timezone "Asia/Shanghai"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/manage_weather_subscription.py set-timezone --to "<target>" --timezone "Asia/Shanghai"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/manage_weather_subscription.py set-timezone --to "<target>" --timezone "Asia/Shanghai"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/manage_weather_subscription.py set-timezone --to "<target>" --timezone "Asia/Shanghai"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/manage_weather_subscription.py set-timezone --to "<target>" --timezone "Asia/Shanghai"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/manage_weather_subscription.py set-timezone --to "<target>" --timezone "Asia/Shanghai"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python scripts/manage_weather_subscription.py set-timezone --to "<target>" --timezone "Asia/Shanghai"

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README explicitly says the skill produces Chinese formatted weather bulletins, and later repeats that queries are formatted into a Chinese bulletin. This imposes a language/locale behavior without indicating user opt-in, alternative language support, or a documented region-specific justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This line describes a fixed Chinese-language output format for all global city queries. Under the policy, forcing a specific language without user choice or documented justification is a natural-language policy concern.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The README documents persistent weather subscriptions, stored user timezones, and push delivery targets such as qqbot:c2c:<id> without clearly warning users that identifiers and scheduling preferences are retained and that automated messages will continue until removed. This creates a real privacy and consent risk: users may not understand the persistence of stored personal routing data or the ongoing nature of scheduled delivery, increasing the chance of unintended data retention or unsolicited messaging.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill invokes shell commands, reads and writes local data files, and performs network-backed weather operations, but it does not declare any explicit tool scope or permission boundaries. That makes the skill harder to sandbox and audit, and can enable unintended execution or data modification if the agent framework grants broad default capabilities.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The invocation guidance is broad enough to trigger the skill for many generic weather-related requests without clear boundaries, increasing the chance of over-invocation. In an agent environment, that can lead to unnecessary shell execution, network requests, and stateful subscription actions when a simpler response or user confirmation would have been safer.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The description explicitly says the skill should be used when the user wants a formatted Chinese weather bulletin, and later the document says to always use a fixed structure unless the user asks otherwise. This imposes a language/locale-specific output pattern without stating a user choice or default-language policy justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The document instructs the agent to always use a specific Chinese-language weather bulletin structure unless the user explicitly asks for another format. This is a natural-language locale policy issue because it defaults to a specific language and presentation without opt-in or a documented regional constraint.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The file hard-codes specific users or identifiers to the Asia/Shanghai timezone without any visible per-user consent, discovery flow, or documented opt-in. In a weather subscription skill, timezone directly affects scheduling and message timing, so incorrect or silently assigned values can cause privacy concerns about inferred location/locale and operational errors such as weather pushes at unintended times.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/manage_weather_subscription.py (reported line 64)May include surrounding context.

python
def run_cmd(cmd: list[str]) -> subprocess.CompletedProcess[str]:
    return subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", errors="replace")


def find_openclaw_cmd() -> str:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
72% confidence
Finding

The code searches for openclaw.cmd by first trusting whatever executable named openclaw.cmd appears in PATH, then treats any invocation producing output as acceptable even if the return code is nonzero. In environments where PATH can be influenced by a local attacker or untrusted installation, this can cause execution of a trojaned binary and subsequent command execution in the context of this skill.

Content

Scanner excerpt · scripts/manage_weather_subscription.py (reported line 74)May include surrounding context.

python
]
    for candidate in candidates:
        try:
            result = subprocess.run([candidate, "--version"], capture_output=True, text=True, encoding="utf-8", errors="replace")
            if result.returncode == 0 or result.stdout or result.stderr:
                return candidate
        except FileNotFoundError:

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The instruction string mandates that output '必须为中文天气简报', which is a natural-language locale restriction. The file does not offer any user opt-in or configurable language choice, and there is no clear justification that this skill is region- or locale-specific only.

Content

No source excerpt is available for this finding.

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 · scripts/weather_report.py (reported line 15)May include surrounding context.

python
sys.stdout.reconfigure(encoding="utf-8")

GEOCODE_URL = "https://geocoding-api.open-meteo.com/v1/search"
FORECAST_URL = "https://api.open-meteo.com/v1/forecast"
FETCH_RETRIES = 3
FETCH_RETRY_DELAY_SECONDS = 1.2