Back to skill

Security audit

WeatherPanel Note AI PC

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly performs a weather dashboard workflow, but its security claims do not match the shipped code and it can run configurable local executables.

Review this skill before installing. Its weather and dashboard functions are understandable, but do not run it in a sensitive environment unless SUMMARIZE_BIN, OBSIDIAN_BIN, CANVAS_ROOT, and Obsidian paths are locked down and the shell=True summarize call is fixed. Be aware that the dashboard loads third-party web resources and that configured coordinates may be sent to Open-Meteo.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/summarize_weather.py:101
Finding

Shell Command Injection Through Configurable Summary Executable

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/flush_to_obsidian.py:57
Finding

Configurable Arbitrary Executable and Unvalidated Obsidian Destination

Content
View full analysis
\n" ) cmd = [OBSIDIAN_BIN, "create", NOTE_PATH] if OBSIDIAN_VAULT: cmd.extend(["--vault", OBSIDIAN_VAULT]) cmd.extend(["--content", formatted, "--append"]) try: result = subprocess.run( cmd, capture_output=True, text=True, timeout=30, encoding="utf-8", ) ``` ### Technical Analysis The use of an argument list prevents direct shell-metacharacter injection, but the first element of that list remains attacker-configurable. Consequently, a party that can influence `OBSIDIAN_BIN` can select an arbitrary local executable to run. The remaining values will be passed as arguments, but a malicious executable does not need those arguments to be meaningful. The vault and note paths are also accepted without canonicalization or containment checks. The implementation does not verify that the destination: - is inside a configu ...[truncated 1707 chars]
Remediation
View remediation

T03 · Remote Payload Retrieval and Execution

Warning
Location
scripts/dashboard.html:6
Finding

Dashboard Executes Remotely Hosted JavaScript Without Integrity Protection

Content
View full analysis
WeatherPanel Note AI PC @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;600;700&family=DM+Sans:wght@400;500;600;700&display=swap'); ``` ### Technical Analysis Although the dashboard is presented as a local Canvas asset, it downloads and executes Chart.js from a third-party CDN whenever it is opened. No Subresource Integrity hash is supplied, and no restrictive Content Security Policy limits what that script can do. The effective JavaScript payload can therefore change after the Skill package has been reviewed. If the CDN, DNS resolution, transport trust chain, or referenced resource is compromised, attacker-controlled JavaScript would execute in the dashboard's origin. The dashboard reads same-origin files including `timeseries.json`, `summaries.json`, and `token_cost.json`. A hostile CDN script running in that context could access those files and send their content to a remote endpoint. The Google Fonts import also generates an external request and discloses standard connection metadata such as the user's IP address, request headers, and access timing. ### Attack Path 1. An attacker compromises the CDN resource, its delivery path, or another trusted component involved in retrieving it. 2. A user opens the local WeatherPanel dashboard. 3. The browser downloads the altered Chart.js response because the dependency is loaded at runtime. 4. The response executes as JavaScript in the local Canvas dashboard origin. 5. The malicious script reads same-origin weather summaries, historical data, or token-accounting information. 6. The script can transmit the collected information t ...[truncated 600 chars]
Remediation
View remediation

other

Note
Location
scripts/fetch_weather.py:43
Finding

Configurable Location Coordinates Are Transmitted Contrary to the Declared Fixed Scope

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (20)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill description claims constrained behavior, but the analysis indicates the implementation omits the advertised summarization and Obsidian write steps while also storing additional local state and raw weather data. This mismatch undermines operator trust and can hide unexpected persistence or data handling that users did not consent to.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
99% confidence
Finding

This is tool parameter abuse because the subprocess tool is invoked with a shell command string instead of a literal argument vector. In the context of a local AI/automation skill, this is more dangerous because environment or configuration tampering can turn a weather summarization step into arbitrary local code execution on the host.

Content

Scanner excerpt · scripts/summarize_weather.py (reported line 102)May include surrounding context.

python
print(f"[summarize] URL: {url}")

    try:
        result = subprocess.run(
            cmd_str,
            capture_output=True,
            text=True,

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill requests users to run Python scripts that fetch network data, invoke a local CLI, and write local files, but it does not declare any explicit tool scope or allowed-tools constraints. In an agent ecosystem, missing permission boundaries increases the chance of overbroad tool access and makes it harder to enforce least privilege if the skill is modified or abused.

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 · SKILL.md (reported line 28)May include surrounding context.

md
- Do **not** modify `HEARTBEAT.md`.
- Do **not** change global OpenClaw config.
- Do **not** create or run `.bat`, `.cmd`, or `.ps1` files.
- Do **not** use Windows Task Scheduler, startup folders, registry persistence, or shell profile persistence.
- Do **not** read generic secret-bearing files such as `env.bat`.
- Only run the Python scripts bundled with this skill.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The dashboard pulls executable JavaScript from cdnjs and fonts from Google at runtime, which introduces unnecessary external network dependencies into a skill described as local. If the CDN, network path, or hosting account is compromised, users could receive altered code or leak usage metadata despite expecting a local-only dashboard.

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/fetch_weather.py (reported line 38)May include surrounding context.

python
wind_unit = "mph" if UNITS == "imperial" else "kmh"
    precip_unit = "inch" if UNITS == "imperial" else "mm"

    base = "https://api.open-meteo.com/v1/forecast"
    params = (
        f"latitude={LAT}&longitude={LON}"
        f"&timezone={TZ}"

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/summarize_weather.py (reported line 79)May include surrounding context.

python
wind_unit = "mph" if UNITS == "imperial" else "kmh"
    precip_unit = "inch" if UNITS == "imperial" else "mm"

    base = "https://api.open-meteo.com/v1/forecast"
    params = (
        f"latitude={LAT}&longitude={LON}"
        f"&timezone={TZ}"

Tainted flow: 'TIMESERIES_FILE' from os.environ.get (line 22, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/fetch_weather.py (reported line 108)May include surrounding context.

python
with open(TIMESERIES_FILE, "r", encoding=enc) as f:
                    data = json.load(f)
                if enc != "utf-8":
                    with open(TIMESERIES_FILE, "w", encoding="utf-8") as f:
                        json.dump(data, f, indent=2, ensure_ascii=False)
                return data
            except (json.JSONDecodeError, UnicodeDecodeError, IOError):

Tainted flow: 'TIMESERIES_FILE' from os.environ.get (line 22, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/fetch_weather.py (reported line 120)May include surrounding context.

python
with open(TIMESERIES_FILE, "r", encoding=enc) as f:
                    data = json.load(f)
                if enc != "utf-8":
                    with open(TIMESERIES_FILE, "w", encoding="utf-8") as f:
                        json.dump(data, f, indent=2, ensure_ascii=False)
                return data
            except (json.JSONDecodeError, UnicodeDecodeError, IOError):

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/flush_to_obsidian.py (reported line 72)May include surrounding context.

python
cmd.extend(["--content", formatted, "--append"])

    try:
        result = subprocess.run(
            cmd, capture_output=True, text=True, timeout=30, encoding="utf-8",
        )
        if result.returncode != 0:

Tainted flow: 'cmd' from os.environ.get (line 66, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
87% confidence
Finding

OBSIDIAN_BIN is taken directly from the environment and executed as a program, so anyone who can influence the runtime environment can cause the script to launch an arbitrary executable instead of the intended obsidian-cli. In a local automation skill this is less severe than remote input-driven RCE, but it still creates a code-execution foothold in misconfigured or multi-user environments.

Content

Scanner excerpt · scripts/flush_to_obsidian.py (reported line 72)May include surrounding context.

python
cmd.extend(["--content", formatted, "--append"])

    try:
        result = subprocess.run(
            cmd, capture_output=True, text=True, timeout=30, encoding="utf-8",
        )
        if result.returncode != 0:

Tainted flow: 'DASHBOARD_DST' from os.environ.get (line 24, credential/environment) → shutil.copy2 (file write)

Medium
Category
Data Flow
Confidence
88% confidence
Finding

The destination path for shutil.copy2 is influenced by CANVAS_ROOT from the environment, and the code writes to that computed location without validating that it stays within an expected trusted directory. If an attacker can control the environment for this process, they can redirect the dashboard write to an unintended filesystem location, causing arbitrary file overwrite within the runner's privileges.

Content

Scanner excerpt · scripts/run_weatherpanel.py (reported line 30)May include surrounding context.

python
def prepare_dashboard() -> None:
    os.makedirs(CANVAS_DIR, exist_ok=True)
    shutil.copy2(DASHBOARD_SRC, DASHBOARD_DST)
    print(f"[runner] Dashboard prepared: {DASHBOARD_DST}")
    print(f"[runner] Suggested canvas URL: {BASE_URL}/__openclaw__/canvas/{SKILL_ID}/dashboard.html")

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/run_weatherpanel.py (reported line 39)May include surrounding context.

python
script_path = os.path.join(SCRIPT_DIR, script_name)
    cmd = [sys.executable, script_path]
    print(f"[runner] Running: {' '.join(cmd)}")
    result = subprocess.run(cmd, cwd=SCRIPT_DIR)
    print(f"[runner] Exit code for {script_name}: {result.returncode}")
    return result.returncode

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The docstring rationalizes shell=True as a safety measure for '&' in the URL, but this is backwards: invoking a shell is what causes '&' to become dangerous command syntax. This misleading comment increases the likelihood the insecure pattern will be retained or copied elsewhere, preserving an actual command-injection flaw.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
98% confidence
Finding

The code invokes subprocess.run with shell=True on a command string built from environment-controlled data (SUMMARIZE_BIN) and a URL string. This allows shell metacharacter interpretation and can lead to arbitrary command execution if an attacker can influence the environment or related configuration, which is especially risky for an automation skill expected to run unattended.

Content

Scanner excerpt · scripts/summarize_weather.py (reported line 102)May include surrounding context.

python
print(f"[summarize] URL: {url}")

    try:
        result = subprocess.run(
            cmd_str,
            capture_output=True,
            text=True,

Tainted flow: 'cmd_str' from os.environ.get (line 98, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
99% confidence
Finding

cmd_str is tainted by os.environ.get("SUMMARIZE_BIN", "summarize") and then executed through the shell, so a malicious environment value can inject additional shell commands. Because the skill also concatenates a URL into the same shell command, the attack surface includes both binary selection and shell parsing behavior.

Content

Scanner excerpt · scripts/summarize_weather.py (reported line 102)May include surrounding context.

python
print(f"[summarize] URL: {url}")

    try:
        result = subprocess.run(
            cmd_str,
            capture_output=True,
            text=True,

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The skill's purpose is weather retrieval, local summarization, dashboard updating, and note appending. Pulling UI libraries and fonts from unrelated third-party services adds network capability beyond the core weather workflow and is not explicitly declared in the manifest scope.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

The dashboard hard-codes toLocaleTimeString('en-US', ...), which forces U.S. English locale formatting regardless of the user's preferences. This is a natural-language/locale policy concern because the file does not offer a locale choice or document why the locale must be fixed.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This code reads a user-local config.json file and injects allowed values into process environment variables, but it does so without any logging, print statement, or other runtime disclosure. Although the module docstring documents the behavior, the actual safety-relevant operation of loading file-based configuration into environment variables is silent and could affect downstream behavior without the user noticing.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The code sets a hard-coded default locale-specific timezone of "Asia/Shanghai" in a natural-language configuration value. This can violate language/locale policy expectations because users are placed into a specific regional setting unless they explicitly override the environment variable.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.