Back to skill

Security audit

Daily Hotspot Push

Security checks for vulnerabilities and agentic risk

Overview

The skill is purpose-aligned as a scheduled news pusher, but it needs Review because it creates persistent OpenClaw cron jobs and has weak controls around job ownership and prompt content.

Review before installing, especially in shared or multi-user environments. Only use it with trusted callers, because it can create, update, and delete real OpenClaw cron jobs, store QQ target timezone/subscription data, and send future agent prompts based on user-supplied topics. The owner checks, topic validation, and update rollback behavior should be tightened before broad deployment.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/manage_daily_news.py:144
Finding

Persistent Agent Prompt Injection Through Unvalidated News Topics

Content
View full analysis
str: topic_text = "、".join(topics) prefix = "[TEST] " if test_mode else "" return ( f"{prefix}请为 QQ 用户 {to} 生成并投递一份今日新闻简报,主题:{topic_text}。" "先抓取当天相关新闻,再筛选、去重、整理为简洁中文简报。" "输出时直接给用户最终内容,不要解释过程,不要输出系统说明。" "每个主题 2 到 4 条,每条包含标题、来源、要点。" "如果是测试,只需要立即发出一份简洁可读的真实新闻简报即可。" ) ``` The resulting message is passed directly to the scheduled agent: ```python def create_cron_job(openclaw_cmd: str, *, to: str, topics: list[str], time_text: str, test_mode: bool = False) -> dict: timezone = ensure_timezone(to) at_value, cron_expr = resolve_schedule(time_text, timezone) name = build_content(topics) message = build_agent_message(to, topics, test_mode=test_mode) cmd = [ openclaw_cmd, "cron", "add", "--name", name, "--session", "isolated", "--message", message, "--channel", "qqbot", "--to", to, "--announce", "--json", ] ``` ### Technical Analysis The `--topics` argument accepts arbitrary strings. `normalize_topics()` only splits the value on commas and removes surrounding whitespace; it does not enforce an allowlist, maximum length, character restrictions, or a data-only format. `build_agent_message()` concatenates these topic values into the natural-language instruction sent to a future OpenClaw agent. Consequently, a topic can contain newlines or imperative text that the scheduled agent may interpret as additional instructions rather than as a news category. The use of a subprocess argument list prevents operating- ...[truncated 1609 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/manage_daily_news.py:382
Finding

Subscription Update and Removal Operations Do Not Enforce Ownership

Content
View full analysis
list[dict]: items = load_state() return [x for x in items if x.get("to") == to] def remove_subscription(job_id: str) -> dict: openclaw_cmd = find_openclaw_cmd() result = run_cmd([openclaw_cmd, "cron", "rm", job_id, "--json"]) if result.returncode != 0: raise SystemExit(result.stderr or result.stdout) items = [x for x in load_state() if x.get("id") != job_id] save_state(items) try: return json.loads(result.stdout or "{}") except Exception: return {"ok": True, "id": job_id} ``` The update operation also locates subscriptions solely by job ID: ```python def update_subscription(job_id: str, *, time_text: str | None, topics_raw: str | None) -> dict: items = load_state() existing = next((x for x in items if x.get("id") == job_id), None) if not existing: raise SystemExit("subscription not found") to = existing.get("to") if not to: raise SystemExit("subscription missing target") time_value = time_text or existing.get("time") topics = normalize_topics(topics_raw) if topics_raw is not None else existing.get("topics") or DEFAULT_TOPICS.copy() remove_subscription(job_id) ``` The command-line dispatch does not require a target or authenticated owner for either operation: ```python if args.action == "remove": if not args.id: raise SystemExit("remove needs --id") print_json(remove_subscription(args.id)) return 0 if args.action == "update": if not args.id: raise SystemExit("update needs --id") ...[truncated 2447 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/manage_daily_news.py:395
Finding

Non-Atomic Subscription Update Can Permanently Delete a Valid Job

Content
View full analysis
dict: items = load_state() existing = next((x for x in items if x.get("id") == job_id), None) if not existing: raise SystemExit("subscription not found") to = existing.get("to") if not to: raise SystemExit("subscription missing target") time_value = time_text or existing.get("time") topics = normalize_topics(topics_raw) if topics_raw is not None else existing.get("topics") or DEFAULT_TOPICS.copy() remove_subscription(job_id) try: return create_subscription(to, time_value, topics) except Exception: recreated = create_subscription(to, existing.get("time"), existing.get("topics") or DEFAULT_TOPICS.copy()) raise SystemExit(f"update failed and original subscription was restored: {recreated.get('id')}") ``` ### Technical Analysis The function deletes the existing OpenClaw cron job and removes its local state record before validating and creating the replacement. This produces an avoidable interval in which no valid subscription exists. The rollback logic is also incomplete. Several relevant failure paths raise `SystemExit`, including invalid time parsing, missing timezone state, OpenClaw command failures, and explicit validation failures. In Python, `SystemExit` inherits from `BaseException`, not `Exception`; therefore, `except Exception` does not catch it. Even when an ordinary `Exception` is caught, the attempted restoration can itself fail. That secondary failure is not handled, and there is no transactional state or durable recovery marker. ### Attack Path 1. A valid subscription exists and its job ID is supplied to the update operation. 2. The attacker or user provi ...[truncated 950 chars]
Remediation
View remediation
Vulnerability Patterns
  • 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
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (26)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `python scripts/manage_daily_news.py get-timezone --to "<qq-target>"`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `python scripts/manage_daily_news.py get-timezone --to "<qq-target>"`

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_daily_news.py get-timezone --to "<qq-target>"`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `python scripts/manage_daily_news.py get-timezone --to "<qq-target>"`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `python scripts/manage_daily_news.py get-timezone --to "<qq-target>"`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `python scripts/manage_daily_news.py get-timezone --to "<qq-target>"`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `python scripts/manage_daily_news.py get-timezone --to "<qq-target>"`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `python scripts/manage_daily_news.py get-timezone --to "<qq-target>"`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `python scripts/manage_daily_news.py get-timezone --to "<qq-target>"`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `python scripts/manage_daily_news.py get-timezone --to "<qq-target>"`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `python scripts/manage_daily_news.py get-timezone --to "<qq-target>"`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `python scripts/manage_daily_news.py get-timezone --to "<qq-target>"`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `python scripts/manage_daily_news.py get-timezone --to "<qq-target>"`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `python scripts/manage_daily_news.py get-timezone --to "<qq-target>"`

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The manifest explicitly limits the skill to fixed-time recurring subscriptions and says it is not for one-off temporary news retrieval. However, the README advertises 'schedule a one-off news push in 5 minutes for testing,' which expands the documented behavior beyond the declared manifest scope.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The manifest says the skill is intended for creating and managing recurring scheduled subscriptions and specifically says it is not for one-time temporary news checks. The documented commands add --time "5分钟后" and test present the skill as supporting immediate or one-off delivery, which materially broadens the described purpose.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest says the skill is not intended for one-off temporary news usage. Yet the README lists '先测试一下,五分钟后给我推送一条今日新闻' as a supported request, indicating a semantic mismatch between claimed scope and documented intended behavior.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill instructs the agent to use shell commands and persist data, but it declares no explicit tool scope or permission boundaries. That creates a real capability mismatch: an orchestrator may expose broad shell/file access without the user or platform having a clear, reviewable declaration of what the skill is allowed to do.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

L003 明确写明内置“中文时间解析”,且全文示例与输出规则均默认中文,没有说明可根据用户语言偏好切换。对于通用技能,这种默认语言约束可能构成语言/locale 政策问题,因为未提供用户选择或明确的地域限定理由。

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The trigger phrases include broad everyday requests like '订阅国际科技新闻' or '帮我做新闻订阅', which can cause the skill to activate when the user did not clearly intend to create a persistent scheduled task. In this skill's context, mis-triggering is more dangerous because activation can lead to stored personal preferences, timezone handling, and creation of real cron jobs.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill stores subscription metadata and timezone data and creates real scheduled pushes, but the user-facing description does not prominently disclose these persistent side effects before setup. This is dangerous because users may unknowingly consent to ongoing monitoring-like behavior, background execution, and retention of identifiable routing data.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The file hard-codes Chinese defaults for news topics, and later prompt/message strings instruct the agent to produce a Chinese news brief. This imposes a specific language choice as part of the skill behavior without offering the user any language or locale selection, which matches the language/locale policy violation criteria.

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_daily_news.py (reported line 68)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
84% confidence
Finding

The code resolves openclaw.cmd by probing PATH and accepts the first candidate that produces any output, even if the return code is nonzero. In a compromised environment, an attacker could place a malicious executable earlier in PATH and have this script invoke it, leading to arbitrary code execution under the skill's privileges.

Content

Scanner excerpt · scripts/manage_daily_news.py (reported line 78)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
98% confidence
Finding

The instruction string says the fetched news should be organized into a concise Chinese brief, making Chinese output mandatory. Because the file provides no opt-in or alternative locale handling, this is a natural-language locale policy issue rather than a functional bug.

Content

No source excerpt is available for this finding.