Back to skill

Security audit

smart-keepalive

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its stated scheduled brief-sending purpose, but it needs review because it can run unattended message delivery while allowing broad URL overrides and custom shell commands.

Review this before installing if it will run unattended. Confirm the message channel and target, avoid custom shell commands unless they are trusted and audited, do not point RSS or weather URL overrides at internal or sensitive hosts, and consider disabling scheduled sending until you have reviewed logs and the generated messages.

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

Warning
Location
smart-keepalive.py:2188
Finding

Untrusted feed content is inserted directly into an Agent prompt

Content
View full analysis
tuple[str, str]: brief, theme_tag = build_keepalive_brief(with_links=True, skill_dir=skill_dir) weather_city = resolve_weather_city(skill_dir) if weather_append_enabled(): weather_line = fetch_nmc_weather_daily(weather_city, 1, with_links=True) if weather_line.strip(): brief = f"{brief}\n{weather_line}" now = datetime.now() style = os.getenv("KEEPALIVE_STYLE_GUIDE", "").strip() or "(无)" tpl = load_prompt_file(skill_dir, "rewrite-main.md") if not tpl.strip(): tpl = FALLBACK_REWRITE_PROMPT prompt = fill_prompt_template( tpl, { "LOCALE": locale, "BRIEF": brief, "HOUR": str(now.hour), "MINUTE": str(now.minute), "LOCAL_TIME": now.strftime("%Y-%m-%d %H:%M:%S"), "STYLE_GUIDE": style, "THEME_HINT": theme_section_label(locale, theme_tag), "WEATHER_CITY": weather_city, }, ) code, out = run_agent_command( openclaw_bin=openclaw_bin, agent_id=agent_id, prompt=prompt, timeout_sec=45, ) ``` The content placed in `brief` originates from external RSS and HTTP responses: ```python req = urllib.request.Request( safe_url, headers={ "User-Agent": ( "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " "AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15" ), }, ) with urllib.request.urlopen(req, timeout=12) as resp: data = resp.read() root = ET.fromstring(data) items = root.findall(".//item") if not i ...[truncated 2556 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
smart-keepalive.py:592
Finding

Configurable feed and weather URLs allow requests to arbitrary hosts

Content
View full analysis
str: b = (base or "").strip().rstrip("/") if not b: return "" if not (b.startswith("http://") or b.startswith("https://")): b = "https://" + b return b def rsshub_bases() -> list[str]: """RSSHub root address list; environment variable takes precedence.""" raw = os.getenv("KEEPALIVE_RSSHUB_BASES", "").strip() if raw: out = [_normalize_rsshub_base(x) for x in raw.split(",")] return [x for x in out if x] return [_normalize_rsshub_base(x) for x in DEFAULT_RSSHUB_BASES if x] def fetch_rsshub_route( route: str, count: int = 5, with_links: bool = False ) -> str: """Try multiple RSSHub instances in sequence.""" path = (route or "").strip().strip("/") if not path: return "" for base in rsshub_bases(): url = f"{base}/{path}" got = fetch_rss_titles(url, count, max_retries=2, with_links=with_links) if got.strip(): return got return "" ``` The weather override similarly controls both the page request and the origin used for REST requests: ```python def _nmc_base_url() -> str: """Derive the API base origin from the weather URL.""" import urllib.parse raw = os.getenv("KEEPALIVE_NMC_WEATHER_URL", "").strip() or DEFAULT_NMC_WEATHER_DAILY_URL try: u = urllib.parse.urlsplit(raw) if u.scheme and u.netloc: return f"{u.scheme}://{u.netloc}" except Exception: pass return "https://www.nmc.cn" ``` ```python def fetch_nmc_weather_daily(city: str, ...[truncated 3836 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (58)

Tainted flow: 'req' from os.getenv (line 549, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · smart-keepalive.py (reported line 558)May include surrounding context.

python
),
                },
            )
            with urllib.request.urlopen(req, timeout=12) as resp:
                data = resp.read()
            root = ET.fromstring(data)

Tainted flow: 'req' from os.getenv (line 549, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · smart-keepalive.py (reported line 644)May include surrounding context.

python
try:
        req = urllib.request.Request(url, headers=headers)
        with urllib.request.urlopen(req, timeout=10) as resp:
            data = json.loads(resp.read().decode('utf-8'))
        
        if data.get('code') != 0:

Tainted flow: 'req' from os.getenv (line 549, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
91% confidence
Finding

The code builds outbound HTTP requests from a base URL derived from the KEEPALIVE_NMC_WEATHER_URL environment variable, then fetches JSON from that host. Because the host is environment-controlled and only minimally constrained, a user or upstream scheduler can redirect requests to arbitrary internal or attacker-controlled endpoints, creating SSRF-style behavior and external data exfiltration risk.

Content

Scanner excerpt · smart-keepalive.py (reported line 767)May include surrounding context.

python
"Pragma": "no-cache",
        },
    )
    with urllib.request.urlopen(req, timeout=timeout) as resp:
        return json.loads(resp.read().decode("utf-8", errors="ignore"))

Tainted flow: 'req' from os.getenv (line 549, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
94% confidence
Finding

fetch_nmc_weather_daily reads KEEPALIVE_NMC_WEATHER_URL from the environment and performs a direct urlopen to that URL. In an agent/scheduled-skill context, environment-controlled network destinations are dangerous because they can be abused to contact arbitrary hosts, including internal services, and influence content inserted into outbound messages.

Content

Scanner excerpt · smart-keepalive.py (reported line 866)May include surrounding context.

python
"Pragma": "no-cache",
            },
        )
        with urllib.request.urlopen(req, timeout=12) as resp:
            html_text = resp.read().decode("utf-8", errors="ignore")
    except Exception:
        return ""

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
**何时读本文:** 用户要配置/调试 OpenClaw **定时 keepalive**、飞书或微信定时消息、`smart-keepalive.py` / `smart-keepalive.sh`、资讯简报、`prompts/`、`--doctor` / `--install-launchd` / `--instal

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
**何时读本文:** 用户要配置/调试 OpenClaw **定时 keepalive**、飞书或微信定时消息、`smart-keepalive.py` / `smart-keepalive.sh`、资讯简报、`prompts/`、`--doctor` / `--install-launchd` / `--instal

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
**何时读本文:** 用户要配置/调试 OpenClaw **定时 keepalive**、飞书或微信定时消息、`smart-keepalive.py` / `smart-keepalive.sh`、资讯简报、`prompts/`、`--doctor` / `--install-launchd` / `--instal

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
**何时读本文:** 用户要配置/调试 OpenClaw **定时 keepalive**、飞书或微信定时消息、`smart-keepalive.py` / `smart-keepalive.sh`、资讯简报、`prompts/`、`--doctor` / `--install-launchd` / `--instal

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
| **管线顺序、环境变量、`theme_tag` 含义、基准权重数字、状态 JSON 字段** | **`SKILL.md`(本文)** | 给人与 Agent 的**单一事实说明**;改脚本逻辑后**同步改本节**,避免脚本顶部长注释与文档漂移。 |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
| **管线顺序、环境变量、`theme_tag` 含义、基准权重数字、状态 JSON 字段** | **`SKILL.md`(本文)** | 给人与 Agent 的**单一事实说明**;改脚本逻辑后**同步改本节**,避免脚本顶部长注释与文档漂移。 |

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
97% confidence
Finding

run_shell_command copies the full process environment and then invokes a shell command hook. In practice this can expose tokens, credentials, and internal configuration to any custom command, making the custom hook much more dangerous than a minimal, purpose-built subprocess call.

Content

Scanner excerpt · smart-keepalive.py (reported line 121)May include surrounding context.

python
cwd: Optional[Path] = None,
    extra_env: Optional[dict[str, str]] = None,
) -> tuple[int, str]:
    env = os.environ.copy()
    if extra_env:
        env.update(extra_env)
    return run_cmd(["/bin/sh", "-lc", command], timeout_sec=timeout_sec, cwd=cwd, env=env)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
70% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · smart-keepalive.py (reported line 160)May include surrounding context.

python
],
        timeout_sec=timeout_sec,
        cwd=None,
        env={k: v for k, v in os.environ.items() if k not in ["SKILL_DIR", "OPENCLAW_HOME", "OPENCLAW_CONFIG"]},
    )

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · smart-keepalive.py (reported line 223)May include surrounding context.

python
text=True,
            timeout=timeout_sec,
            cwd=str(cwd) if cwd else None,
            env=env if env is not None else os.environ.copy(),
        )
        return p.returncode, (p.stdout or "") + (p.stderr or "")
    except subprocess.TimeoutExpired as ex:

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · smart-keepalive.py (reported line 2254)May include surrounding context.

python
def merge_plist_env_into_environ(plist_path: Path) -> dict[str, str]:
    """将 launchd plist 中的 EnvironmentVariables 合并进当前环境,用于模拟定时任务环境。"""
    merged: dict[str, str] = dict(os.environ)
    if not plist_path.is_file():
        return merged
    try:

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · smart-keepalive.py (reported line 2288)May include surrounding context.

python
def merge_plist_env_into_environ(plist_path: Path) -> dict[str, str]:
    """将 launchd plist 中的 EnvironmentVariables 合并进当前环境,用于模拟定时任务环境。"""
    merged: dict[str, str] = dict(os.environ)
    if not plist_path.is_file():
        return merged
    try:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill explicitly supports periodic unattended fetching and automatic message sending, including via custom commands, but the documentation does not prominently warn users that content will be transmitted over the network on a schedule. In a scheduled automation context, users may unknowingly exfiltrate message content, prompts, targets, or fetched material to external services without realizing the privacy and operational implications.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The Hermes/custom-command integration instructs users to set shell command strings that consume sensitive environment variables such as KEEPALIVE_PROMPT, KEEPALIVE_MESSAGE, KEEPALIVE_CHANNEL, and KEEPALIVE_TARGET, but it does not clearly warn that these commands execute arbitrary user-configured shell/CLI actions and can forward data to external tools or endpoints. In practice, this expands the trust boundary significantly and can lead to unreviewed exfiltration of message contents or prompt data, especially in unattended scheduled runs.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The prompt mandates that when locale={{LOCALE}}, zh must use the Chinese-format second paragraph and en must produce the body in English. This enforces a language choice from configuration rather than offering the user a language/locale option or documenting explicit opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The prompt explicitly maps every locale except en to Chinese, which overrides the user's actual language preference for all other supported locales. This can cause incorrect or misleading output in multilingual environments and may break downstream workflows or user expectations that depend on locale-appropriate messaging.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This instruction hard-codes a language policy: English is allowed only when {{LOCALE}} equals en, and all other cases must be Chinese. That creates a locale restriction without offering the user a language choice or documenting a justified region-specific constraint.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% 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 · smart-keepalive.py (reported line 4)May include surrounding context.

python
#!/usr/bin/env python3
import json
import os
import plistlib
import re
import shutil
import subprocess

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% 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 · smart-keepalive.py (reported line 2249)May include surrounding context.

python
#!/usr/bin/env python3
import json
import os
import plistlib
import re
import shutil
import subprocess

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% 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 · smart-keepalive.py (reported line 2252)May include surrounding context.

python
#!/usr/bin/env python3
import json
import os
import plistlib
import re
import shutil
import subprocess

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% 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 · smart-keepalive.py (reported line 2253)May include surrounding context.

python
#!/usr/bin/env python3
import json
import os
import plistlib
import re
import shutil
import subprocess

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% 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 · smart-keepalive.py (reported line 2255)May include surrounding context.

python
#!/usr/bin/env python3
import json
import os
import plistlib
import re
import shutil
import subprocess