Back to skill

Security audit

ZeeLin X Hourly Growth

Security checks for vulnerabilities and agentic risk

Overview

This skill is not clearly malicious, but it can run unattended and post public X replies through a logged-in browser using broad Chrome debugging access.

Review this carefully before installing. Use dry-run first, avoid enabling the LaunchAgent unless you intentionally want unattended hourly public replies, and keep the OpenClaw Chrome profile limited to X because the skill opens CDP access to that profile. Prefer pinned dependencies and remove the scheduler with the uninstall script when 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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run_hourly_growth.py:141
Finding

Authenticated Chrome Profile Exposed Through Overly Permissive CDP Origins

Content
View full analysis

Vulnerability Details

File Location: scripts/run_hourly_growth.py, lines 141-161
Vulnerability Type: Overly permissive browser remote-debugging configuration
Risk Level: High

python
# Restart only the OpenClaw profile Chrome so normal personal Chrome windows are left alone.
subprocess.run(["pkill", "-f", str(PROFILE_DIR)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
time.sleep(2)
for name in ("SingletonLock", "SingletonSocket", "SingletonCookie"):
    try:
        (PROFILE_DIR / name).unlink()
    except FileNotFoundError:
        pass

args = [
    "open", "-na", "Google Chrome", "--args",
    f"--remote-debugging-port={port}",
    f"--user-data-dir={PROFILE_DIR}",
    "--remote-allow-origins=*",
    "--no-first-run",
    "--no-default-browser-check",
    "https://x.com/home",
]
subprocess.Popen(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

Technical Analysis

The script starts Chrome with remote debugging enabled against a persistent OpenClaw browser profile that may contain authenticated sessions. The --remote-allow-origins=* option disables Chrome DevTools Protocol WebSocket origin restrictions for every origin.

This is broader than required for the declared functionality. The CDP client already attempts explicit localhost origins, so globally permitting all origins unnecessarily weakens a security boundary. Any malicious local process—and, depending on browser and network conditions, potentially attacker-controlled web content capable of reaching the debugging endpoint—could attempt to establish a DevTools connection.

A successful CDP connection provides powerful browser-control capabilities, including inspecting page content, evaluating JavaScript in authenticated tabs, navigating pages, and performing actions with the user's active browser sessions.

Attack Path

  1. The user invokes the Skill and the existing CDP connection attempt fails ...[truncated 1316 chars]
Remediation
View remediation

Remediation Suggestions

  • Remove --remote-allow-origins=*.
  • If an origin override is genuinely required, permit only the exact trusted loopback origin used by the CDP client.
  • Explicitly bind the remote-debugging service to the loopback interface and verify that it is not accessible from external interfaces.
  • Use a dedicated browser profile containing only the minimum authentication state needed for X.
  • Do not keep unrelated authenticated services open in the automation profile.
  • Fail safely if a secure CDP connection cannot be established instead of automatically weakening WebSocket origin protections.
  • Consider using a temporary automation profile and importing only narrowly scoped session state where operationally feasible.
  • Validate that the requested debugging port is local, available, and within an expected range before launching Chrome.

T08 · Insecure Dependencies

Warning
Location
scripts/run_hourly_growth.py:14
Finding

Unpinned Third-Party WebSocket Dependency Installation

Content
View full analysis

Vulnerability Details

File Location: scripts/run_hourly_growth.py, lines 14-17
Vulnerability Type: Unpinned third-party dependency and mutable supply-chain source
Risk Level: Medium

python
try:
    import websocket
except ImportError as exc:
    raise SystemExit("Missing websocket-client. Install with: python3 -m pip install websocket-client") from exc

Technical Analysis

When the required module is absent, the script instructs the user to install websocket-client without specifying a reviewed version, integrity hash, lockfile, isolated environment, or trusted package index. The command therefore resolves whatever package release and transitive dependencies are available from the configured Python package source at installation time.

This creates mutable supply-chain behavior: the code executed by the Skill can change after the Skill itself has been audited. The package name shown is consistent with the intended module and no dependency-confusion behavior is directly present in the repository, but the lack of reproducible dependency controls exposes users to a compromised upstream release, compromised package index, malicious index configuration, or an incompatible future version.

Attack Path

  1. The user runs the Skill on a system where websocket-client is not installed.
  2. The script displays the unrestricted installation command.
  3. The user executes python3 -m pip install websocket-client.
  4. Pip resolves the package from the user's configured index without a repository-provided version or hash constraint.
  5. If that package source or selected release is compromised, malicious package code is installed.
  6. The code executes during installation, import, or subsequent WebSocket operations with the user's privileges.

Impact Assessment

A malicious dependency would execute with the privileges of the user running the Skill. Depending on the payload, this could permit:

  • Re ...[truncated 544 chars]
Remediation
View remediation

Remediation Suggestions

  • Pin websocket-client to a reviewed, compatible version.
  • Provide a dependency lockfile with cryptographic hashes.
  • Install dependencies in a dedicated virtual environment rather than the user's global Python environment.
  • Use hash verification, such as pip's --require-hashes mode.
  • Document the trusted package index and avoid implicitly relying on arbitrary user-configured indexes.
  • Review and update pinned dependencies through a controlled process.
  • Declare the dependency in the Skill metadata or installation documentation so it can be provisioned reproducibly before execution.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
Findings (32)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description is about active Twitter/X growth operations: finding posts, replying through Chrome, and running on an hourly schedule. The supplied code does none of that. It is purely a local macOS maintenance/uninstall script that disables and removes a LaunchAgent associated with the named skill. While this may be related operationally to the skill's installation lifecycle, the code chunk's primary behavior is system uninstallation, which is not represented in the declared purpose. Therefore this is a material description-behavior mismatch.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill advertises and documents capabilities that include shell execution, file reads/writes, network access, and use of local browser state, but it does not declare any explicit tool scope or permission boundaries. In a skill that can post on a logged-in social account and install scheduled jobs, the lack of declared scope increases the risk of unintended invocation, overbroad execution, and misuse of sensitive local resources.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The trigger description is broad enough that the skill could activate for common requests about follower growth or engagement, causing posting automation to run when the user did not specifically intend scheduled social actions. Because this skill can act through a logged-in account and persist via a scheduler, accidental invocation has meaningful account and reputational risk.

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 specifies 'leaving high-quality English replies,' which imposes a language choice without indicating user preference, opt-in, or a justified locale restriction. This is a natural-language policy issue because it forces a specific language by default.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill description and run instructions do not prominently warn that it will publish replies from the user's logged-in X account on a recurring schedule. Hidden or understated posting behavior is dangerous because users may invoke it without understanding that it creates public account activity and ongoing automated actions.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The operational step says to 'Leave short, natural English replies,' which again hard-codes a language requirement. The file does not state that English is optional or that users can select another language, so it violates the language/locale policy criterion.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Restarting Chrome with --remote-allow-origins=* broadly weakens origin protections around the remote debugging interface beyond what is needed for this task. In combination with CDP on localhost and a logged-in browser profile, this can expose powerful browser control to other local processes or malicious web content able to reach the debugging endpoint, risking account takeover or data theft.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
88% confidence
Finding

This line begins writing a LaunchAgent plist into the user's LaunchAgents directory, which is a recognized persistence mechanism on macOS. In context, the skill is designed to automate hourly engagement activity in a logged-in browser, so installing a recurring background job materially increases the chance of stealthy or unintended repeated actions.

Content

Scanner excerpt · scripts/install_launchd.sh (reported line 13)May include surrounding context.

sh
mkdir -p "$STATE_DIR" "$(dirname "$PLIST")"

cat > "$PLIST" <<PLIST
<?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">

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/install_launchd.sh (reported line 48)May include surrounding context.

sh
</plist>
PLIST

chmod 644 "$PLIST"
launchctl bootout "gui/${UID_VALUE}" "$PLIST" >/dev/null 2>&1 || true
launchctl bootstrap "gui/${UID_VALUE}" "$PLIST"
launchctl enable "gui/${UID_VALUE}/${LABEL}"

Session Persistence

Medium
Category
Rogue Agent
Confidence
95% confidence
Finding

Calling 'launchctl bootout' against the plist is part of managing a persistent LaunchAgent installation, replacing any prior job definition. While not malicious on its own, it manipulates per-user session services and supports durable unattended execution of the automation skill.

Content

Scanner excerpt · scripts/install_launchd.sh (reported line 49)May include surrounding context.

sh
PLIST

chmod 644 "$PLIST"
launchctl bootout "gui/${UID_VALUE}" "$PLIST" >/dev/null 2>&1 || true
launchctl bootstrap "gui/${UID_VALUE}" "$PLIST"
launchctl enable "gui/${UID_VALUE}/${LABEL}"

Session Persistence

Medium
Category
Rogue Agent
Confidence
97% confidence
Finding

The 'launchctl bootstrap' command loads the LaunchAgent into the user's GUI session, activating the persisted scheduled task. In this skill's context—automated hourly posting/reply behavior through a logged-in browser—that persistence increases operational and abuse risk because it can continue generating actions even after the user forgets it is installed.

Content

Scanner excerpt · scripts/install_launchd.sh (reported line 50)May include surrounding context.

sh
chmod 644 "$PLIST"
launchctl bootout "gui/${UID_VALUE}" "$PLIST" >/dev/null 2>&1 || true
launchctl bootstrap "gui/${UID_VALUE}" "$PLIST"
launchctl enable "gui/${UID_VALUE}/${LABEL}"

echo "Installed ${LABEL}"

Session Persistence

Medium
Category
Rogue Agent
Confidence
95% confidence
Finding

The script explicitly enables a LaunchAgent that will persist across user sessions and run every hour. Persistence is security-relevant because it causes repeated automated browser-linked activity without further user approval, and if the referenced Python script or skill directory is later modified, the persisted job will continue executing the changed code.

Content

Scanner excerpt · scripts/install_launchd.sh (reported line 51)May include surrounding context.

sh
chmod 644 "$PLIST"
launchctl bootout "gui/${UID_VALUE}" "$PLIST" >/dev/null 2>&1 || true
launchctl bootstrap "gui/${UID_VALUE}" "$PLIST"
launchctl enable "gui/${UID_VALUE}/${LABEL}"

echo "Installed ${LABEL}"
echo "Schedule: every hour, active hours 09:00-15:59, max 8 replies/run, daily cap 56"

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill exceeds simple X engagement by forcibly terminating and relaunching a local Chrome instance, manipulating profile lock files, and attaching to the browser via remote debugging. In the context of a logged-in browser, this expands the blast radius from posting replies to broader control over the browser session and user environment.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The script kills Chrome processes and deletes profile lock files without a clear safety prompt or confirmation. This can lead to browser state corruption, loss of user work in the managed profile, and unexpected interruption of local applications, making the behavior risky even if intended for operational convenience.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
86% confidence
Finding

The script forcibly kills Chrome processes matching a profile path using OS process control. Even though shell injection is avoided by passing an argument list, this can still terminate unintended processes if the profile path is misconfigured or attacker-controlled, causing local denial of service and user disruption.

Content

Scanner excerpt · scripts/run_hourly_growth.py (reported line 155)May include surrounding context.

python
pass

    # Restart only the OpenClaw profile Chrome so normal personal Chrome windows are left alone.
    subprocess.run(["pkill", "-f", str(PROFILE_DIR)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    time.sleep(2)
    for name in ("SingletonLock", "SingletonSocket", "SingletonCookie"):
        try:

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

Medium
Category
Data Flow
Confidence
89% confidence
Finding

PROFILE_DIR is derived from an environment variable and then used as a pkill -f match string. If an attacker can influence the environment, they may broaden the match and cause arbitrary local processes to be killed, resulting in denial of service or disruption beyond the intended Chrome profile.

Content

Scanner excerpt · scripts/run_hourly_growth.py (reported line 155)May include surrounding context.

python
pass

    # Restart only the OpenClaw profile Chrome so normal personal Chrome windows are left alone.
    subprocess.run(["pkill", "-f", str(PROFILE_DIR)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    time.sleep(2)
    for name in ("SingletonLock", "SingletonSocket", "SingletonCookie"):
        try:

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_hourly_growth.py (reported line 172)May include surrounding context.

python
"--no-default-browser-check",
        "https://x.com/home",
    ]
    subprocess.Popen(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    deadline = time.time() + 30
    last = None
    while time.time() < deadline:

Tainted flow: 'args' from os.environ.get (line 163, credential/environment) → subprocess.Popen (code execution)

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

python
"--no-default-browser-check",
        "https://x.com/home",
    ]
    subprocess.Popen(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    deadline = time.time() + 30
    last = None
    while time.time() < deadline:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script automatically sends replies from the user's logged-in X account without an explicit confirmation step per run or per message. In this skill context, that is dangerous because it can perform unwanted public actions, create reputational harm, and amplify abuse if the search/filter logic or templates behave unexpectedly.

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

sh
set -euo pipefail

HOME_DIR="${HOME:-$(printf '%s' ~)}"
PLIST="${HOME_DIR}/Library/LaunchAgents/ai.zeelin.x-hourly-growth.plist"
LABEL="ai.zeelin.x-hourly-growth"
UID_VALUE="$(id -u)"

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 · scripts/install_launchd.sh (reported line 11)May include surrounding context.

sh
set -euo pipefail

HOME_DIR="${HOME:-$(printf '%s' ~)}"
PLIST="${HOME_DIR}/Library/LaunchAgents/ai.zeelin.x-hourly-growth.plist"
LABEL="ai.zeelin.x-hourly-growth"
UID_VALUE="$(id -u)"

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

sh
set -euo pipefail

HOME_DIR="${HOME:-$(printf '%s' ~)}"
PLIST="${HOME_DIR}/Library/LaunchAgents/ai.zeelin.x-hourly-growth.plist"
LABEL="ai.zeelin.x-hourly-growth"
UID_VALUE="$(id -u)"

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 · scripts/install_launchd.sh (reported line 46)May include surrounding context.

sh
set -euo pipefail

HOME_DIR="${HOME:-$(printf '%s' ~)}"
PLIST="${HOME_DIR}/Library/LaunchAgents/ai.zeelin.x-hourly-growth.plist"
LABEL="ai.zeelin.x-hourly-growth"
UID_VALUE="$(id -u)"

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 · scripts/install_launchd.sh (reported line 48)May include surrounding context.

sh
set -euo pipefail

HOME_DIR="${HOME:-$(printf '%s' ~)}"
PLIST="${HOME_DIR}/Library/LaunchAgents/ai.zeelin.x-hourly-growth.plist"
LABEL="ai.zeelin.x-hourly-growth"
UID_VALUE="$(id -u)"

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 · scripts/uninstall_launchd.sh (reported line 5)May include surrounding context.

sh
set -euo pipefail

HOME_DIR="${HOME:-$(printf '%s' ~)}"
PLIST="${HOME_DIR}/Library/LaunchAgents/ai.zeelin.x-hourly-growth.plist"
LABEL="ai.zeelin.x-hourly-growth"
UID_VALUE="$(id -u)"

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/run_hourly_growth.py:102