Back to skill

Security audit

Market Watch

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent market-alert purpose, but it runs background monitors that can proactively message an agent and it handles news content and transcript context with weak boundaries.

Install only if you are comfortable with background market monitoring, proactive agent messages, local storage of alert context and transcript references, and outbound calls to the listed market/news APIs. Prefer explicit session and transcript parameters, avoid the launchd watchdog unless you need restart persistence, use a virtual environment with pinned dependencies, and stop or cancel monitors when alerts are no longer needed.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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/news-monitor.py:375
Finding

Untrusted News Content Is Injected into Agent Instructions

Content
View full analysis
None: ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S") source_name = SOURCE_DISPLAY.get(item["source"], item["source"]) content = item.get("content", "").strip() link = item.get("link", "") msg = ( f"[NEWS_ALERT triggered - judgment required]\n\n" f"Matched keywords: {', '.join(matched_keywords)}\n" f"Source: {source_name}\n" f"Title: {item['title'][:200]}\n" f"{'Link: ' + link if link else ''}\n" f"Trigger time: {ts}\n\n" f"{'Article content:' + chr(10) + content[:2000] + chr(10) if content else ''}\n" f"Alert context:\n{alert.get('context_summary', '(not recorded)')}\n\n" f"Your task:\n" f"1. Read the complete news content and determine whether it is relevant to the alert context.\n" f"2. Keyword matching is only preliminary filtering; ignore irrelevant or unimportant content.\n" f"3. If it is relevant and important, contact the user with analysis and recommendations.\n" f"4. If uncertain, do not send it to avoid noise." ) log.info(f"NEWS_ALERT: [{item['source']}] {item['title'][:60]} | kw={matched_keywords}") deliver_message(alert, msg) ``` The displayed wording above is an English rendering of the original message strings; the executable data flow and formatting operations are unchanged. ### Technical Analysis The monitor retrieves article titles and bodies from external RSS feeds and unofficial news APIs. The remote `title` and `content` fields are inserted directly into the same natural-language message that contains instructions for the receiving OpenClaw agent. Removing HTML does not provide prompt-injection protection. Plain tex ...[truncated 2011 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/register-price-alert.py:33
Finding

Latest-Session Selection Can Associate Alerts with the Wrong Transcript

Content
View full analysis
tuple[str, str]: """Attempt to obtain the current session transcript and latest message ID.""" import subprocess try: result = subprocess.run( ["openclaw", "sessions", "--agent", agent_id, "--json"], capture_output=True, text=True, timeout=10, ) data = json.loads(result.stdout) sessions = data.get("sessions", []) if sessions: latest = sorted( sessions, key=lambda s: s.get("updatedAt", 0), reverse=True, )[0] session_id = latest.get("sessionId", "") sessions_dir = Path.home() / f".openclaw/agents/{agent_id}/sessions" transcript_file = str(sessions_dir / f"{session_id}.jsonl") return transcript_file, session_id except Exception: pass return "", "" ``` The comment and docstring above are translated into English; the executable statements reproduce the audited logic. Equivalent logic is present in `scripts/register-news-alert.py:47-62`. ### Technical Analysis When the caller does not explicitly provide `--transcript-file`, the registration scripts enumerate all sessions belonging to the selected agent and choose the session with the newest `updatedAt` value. They do not verify that this session corresponds to the supplied `--session-key`, notification recipient, or conversation that requested the alert. The returned transcript path is stored in `market-alerts.json`. When a price alert triggers, the generated message tells the receiving agent to read that transcript if additional context is needed. In a multi-session or multi-user environment, the most recently updated session may belong to another user. ...[truncated 1557 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
README.md:61
Finding

Installation Uses Unpinned Third-Party Python Dependencies

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • 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 (69)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The README documents that registration scripts 'automatically start the daemon' and that the daemon later 'proactively contacts the user', but it does not present this behavior as a clear safety/privacy warning. In an agent context, background processes that continue monitoring and send unsolicited outbound messages can surprise operators, create unintended persistence, and cause actions to occur after the original interaction has ended.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The README describes the alert JSON schema, including session identifiers and transcript file references under the agent private directory, but does not clearly warn that user context and metadata are written to disk. This can expose sensitive conversation context, targets, and routing identifiers to other local users, backups, or logs if filesystem permissions are weak or operators are unaware of the persistence.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% confidence
Finding

The launchd watchdog plist introduces persistence by automatically restarting monitoring processes, and the runtime file layout also uses predictable /tmp PID and log paths. In a monitoring skill that can proactively message users, undocumented persistence increases the chance of long-lived background activity, unexpected continued surveillance, and local tampering or information exposure through shared temporary paths.

Content

Scanner excerpt · README.md (reported line 301)May include surrounding context.

md
| News PID | `/tmp/market-watch-{agent}-news.pid` | news-monitor process ID |
| Price log | `/tmp/market-watch-{agent}.log` | Rotating log (max 512KB × 3) |
| News log | `/tmp/market-watch-{agent}-news.log` | Rotating log (max 512KB × 4) |
| Watchdog plist | `~/Library/LaunchAgents/com.openclaw.market-watch.{agent}.plist` | macOS launchd config |

---

Ssd 3

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The design explicitly states that alerts carry a transcript file path and message ID so the agent can reconstruct prior user intent and then proactively contact the user. This creates a natural-language data exposure path: when an alert fires, the agent may retrieve and surface more prior conversation content than is necessary, including sensitive details unrelated to the alert itself.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

README 的“什么时候激活”示例把“帮我盯 BTC”“帮我盯 ETF 相关新闻”等自然日常表达列为触发条件,但没有给出边界、限定上下文或反例。这类短语与普通聊天高度重叠,容易让代理在用户只是随口讨论市场时也尝试调用该技能。

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

md
| 新闻进程 PID | `/tmp/market-watch-{agent}-news.pid` | news-monitor PID |
| 价格日志 | `/tmp/market-watch-{agent}.log` | 轮转日志(512KB × 3) |
| 新闻日志 | `/tmp/market-watch-{agent}-news.log` | 轮转日志(512KB × 4) |
| 看门狗配置 | `~/Library/LaunchAgents/com.openclaw.market-watch.{agent}.plist` | macOS launchd 配置 |

---

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill advertises shell, file, environment, and network-capable workflows but does not declare any explicit tool scope or permission boundaries. That makes it easier for an agent runtime to over-grant capabilities and for downstream reviewers to miss that the skill can read/write local files, start daemons, and contact arbitrary external services.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The description says to use the skill when the user asks to 'watch a price,' 'monitor market conditions,' 'get notified,' or 'keep an eye on breaking news.' Several of these phrases are broad, common requests that could match ordinary conversation beyond the intended market-alert workflow, and the file does not provide exclusion conditions or tighter trigger boundaries.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill instructions and operational guidance are written in Chinese throughout, with no indication that the user can choose another language or that the locale restriction is intentional and justified. This creates a language/locale policy issue because the skill effectively assumes a fixed language without user opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The reference is written primarily in Chinese, including headings, notes, and usage guidance, while also containing some English labels. Under the policy, forcing a specific language without user opt-in can be a natural-language policy violation unless the locale constraint is explicitly documented and justified, which is not present here.

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

python
def get_session_uuid(session_key: str, agent_id: str) -> Optional[str]:
    """通过 session_key 查询对应的 sessionId(用于 --session-id 参数)"""
    try:
        result = subprocess.run(
            ["openclaw", "sessions", "--agent", agent_id, "--json"],
            capture_output=True, text=True, timeout=10,
        )

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/register-news-alert.py (reported line 50)May include surrounding context.

python
def get_session_uuid(session_key: str, agent_id: str) -> Optional[str]:
    """通过 session_key 查询对应的 sessionId(用于 --session-id 参数)"""
    try:
        result = subprocess.run(
            ["openclaw", "sessions", "--agent", agent_id, "--json"],
            capture_output=True, text=True, timeout=10,
        )

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/register-price-alert.py (reported line 37)May include surrounding context.

python
def get_session_uuid(session_key: str, agent_id: str) -> Optional[str]:
    """通过 session_key 查询对应的 sessionId(用于 --session-id 参数)"""
    try:
        result = subprocess.run(
            ["openclaw", "sessions", "--agent", agent_id, "--json"],
            capture_output=True, text=True, timeout=10,
        )

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

python
cmd += ["--agent", agent_id]
    cmd += ["--message", msg]

    subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)


def atomic_write_json(path: Path, data: object) -> None:

Session Persistence

Medium
Category
Rogue Agent
Confidence
65% 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 · scripts/daemon.sh (reported line 116)May include surrounding context.

sh
fi
    mkdir -p "$(dirname "$ALERTS_FILE")"
    # stdout/stderr 丢弃到 /dev/null;日志由 Python RotatingFileHandler 管理
    nohup python3 "$PRICE_MONITOR_PY" \
        --agent "$AGENT" \
        --alerts-file "$ALERTS_FILE" \
        > /dev/null 2>&1 &

Session Persistence

Medium
Category
Rogue Agent
Confidence
65% 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 · scripts/daemon.sh (reported line 139)May include surrounding context.

sh
fi
    mkdir -p "$(dirname "$ALERTS_FILE")"
    # stdout/stderr 丢弃到 /dev/null;日志由 Python RotatingFileHandler 管理
    nohup python3 "$PRICE_MONITOR_PY" \
        --agent "$AGENT" \
        --alerts-file "$ALERTS_FILE" \
        > /dev/null 2>&1 &

Session Persistence

Medium
Category
Rogue Agent
Confidence
65% 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 · scripts/price-monitor.py (reported line 561)May include surrounding context.

python
fi
    mkdir -p "$(dirname "$ALERTS_FILE")"
    # stdout/stderr 丢弃到 /dev/null;日志由 Python RotatingFileHandler 管理
    nohup python3 "$PRICE_MONITOR_PY" \
        --agent "$AGENT" \
        --alerts-file "$ALERTS_FILE" \
        > /dev/null 2>&1 &

Session Persistence

Medium
Category
Rogue Agent
Confidence
97% confidence
Finding

The referenced plist path under ~/Library/LaunchAgents indicates the script installs a launchd agent for recurring execution in the user session. In a security review, this is a real persistence mechanism, not merely a string match, because it causes the watchdog to survive restarts and continue invoking the daemon automatically.

Content

Scanner excerpt · scripts/install-watchdog.sh (reported line 30)May include surrounding context.

sh
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
DAEMON_SH="$SKILL_DIR/scripts/daemon.sh"
LABEL="com.openclaw.market-watch.${AGENT}"
PLIST="$HOME/Library/LaunchAgents/${LABEL}.plist"

case "$ACTION" in
    install)

Session Persistence

Medium
Category
Rogue Agent
Confidence
97% confidence
Finding

The referenced plist path under ~/Library/LaunchAgents indicates the script installs a launchd agent for recurring execution in the user session. In a security review, this is a real persistence mechanism, not merely a string match, because it causes the watchdog to survive restarts and continue invoking the daemon automatically.

Content

Scanner excerpt · scripts/install-watchdog.sh (reported line 30)May include surrounding context.

sh
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
DAEMON_SH="$SKILL_DIR/scripts/daemon.sh"
LABEL="com.openclaw.market-watch.${AGENT}"
PLIST="$HOME/Library/LaunchAgents/${LABEL}.plist"

case "$ACTION" in
    install)

Session Persistence

Medium
Category
Rogue Agent
Confidence
98% confidence
Finding

Writing a plist file into the user's LaunchAgents directory is the concrete step that establishes launchd-based persistence. This is dangerous from a security perspective because any later compromise of the daemon script or its directory will be re-executed automatically by the OS on schedule.

Content

Scanner excerpt · scripts/install-watchdog.sh (reported line 35)May include surrounding context.

sh
case "$ACTION" in
    install)
        mkdir -p "$HOME/Library/LaunchAgents"
        cat > "$PLIST" << PLIST_EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">

Session Persistence

Medium
Category
Rogue Agent
Confidence
96% confidence
Finding

This is another match on the same persistence definition and represents the same real behavior: creation of a launchd agent. In the context of an agent skill, persistent background execution increases attack surface because compromised monitoring logic can continue running and respawning without direct user action.

Content

Scanner excerpt · scripts/install-watchdog.sh (reported line 37)May include surrounding context.

sh
mkdir -p "$HOME/Library/LaunchAgents"
        cat > "$PLIST" << PLIST_EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>

Session Persistence

Medium
Category
Rogue Agent
Confidence
96% confidence
Finding

This is another match on the same persistence definition and represents the same real behavior: creation of a launchd agent. In the context of an agent skill, persistent background execution increases attack surface because compromised monitoring logic can continue running and respawning without direct user action.

Content

Scanner excerpt · scripts/install-watchdog.sh (reported line 37)May include surrounding context.

sh
mkdir -p "$HOME/Library/LaunchAgents"
        cat > "$PLIST" << PLIST_EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>

Session Persistence

Medium
Category
Rogue Agent
Confidence
95% confidence
Finding

The plist body contains the launchd configuration that binds the persistence label to the watchdog process. This enables durable scheduled execution, which is a genuine persistence mechanism and therefore a valid security concern even if intended for service reliability.

Content

Scanner excerpt · scripts/install-watchdog.sh (reported line 38)May include surrounding context.

sh
cat > "$PLIST" << PLIST_EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>${LABEL}</string>

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

This line occurs immediately after the plist definition and is part of the same persistence workflow. The security concern remains that launchd-managed background execution will continue across logins and can repeatedly invoke changed or compromised code from the skill directory.

Content

Scanner excerpt · scripts/install-watchdog.sh (reported line 59)May include surrounding context.

sh
<key>StandardErrorPath</key>
    <string>/tmp/market-watch-${AGENT}-watchdog.log</string>
</dict>
</plist>
PLIST_EOF
        launchctl unload "$PLIST" 2>/dev/null || true
        launchctl load "$PLIST"

Session Persistence

Medium
Category
Rogue Agent
Confidence
98% confidence
Finding

Loading the plist with launchctl activates the LaunchAgent immediately, turning the persistence definition into an active scheduled task. This is dangerous because it starts unattended execution now and on future sessions, increasing the impact of any bug or later tampering in daemon.sh.

Content

Scanner excerpt · scripts/install-watchdog.sh (reported line 60)May include surrounding context.

sh
<string>/tmp/market-watch-${AGENT}-watchdog.log</string>
</dict>
</plist>
PLIST_EOF
        launchctl unload "$PLIST" 2>/dev/null || true
        launchctl load "$PLIST"
        echo "[market-watch] 守卫已安装: $LABEL"

Static analysis

No suspicious patterns detected.