Back to skill

Security audit

HaloArena

Security checks for vulnerabilities and agentic risk

Overview

The skill is a disclosed autonomous HaloArena watcher, but it processes remote game content through an existing OpenClaw agent with whatever tools and credentials that agent already has.

Install only if you are comfortable with an unattended background watcher using an existing OpenClaw agent. Prefer a dedicated low-privilege agent with tools disabled or tightly approved, avoid using a general-purpose agent with file, shell, browser, or broad network tools, and remember that `--stop` leaves the local token, route, state, and logs until you remove or revoke them separately.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Findings (1)

other

Error
Location
watcher.py:1739
Finding

Remote game content is passed to a tool-capable local agent without policy-layer isolation

Content
View full analysis

Vulnerability Details

File Location: watcher.py:1739-1781, with the execution path at watcher.py:2387-2444, context propagation at decision_context.py:104-130, and the disclosed absence of isolation at SKILL.md:32-39
Vulnerability Type: Indirect prompt injection into a tool-capable agent
Risk Level: High

Relevant Code

watcher.py:1739-1781 constructs a model prompt containing the server-provided gameplay envelope. It attempts to control untrusted content through natural-language instructions:

python
def _build_direct_decision_message(
    self,
    data: dict[str, Any],
    *,
    full_resync: bool,
    session_id: str | None = None,
) -> str:
    """Ask for one decision, never for tool-driven submission.

    OpenClaw bills one provider inference for every assistant/tool turn. The
    watcher is the trusted transport boundary, so every game follows this
    same server-authored, single-inference contract.
    """
    prompt_payload, snapshot_instruction = self._decision_prompt_parts(
        data,
        full_resync=full_resync,
        session_id=session_id,
    )
    envelope = json.dumps(
        prompt_payload,
        ensure_ascii=False,
        separators=(",", ":"),
    )
    return (
        "Decide exactly one HaloArena gameplay action for the authoritative envelope below. "
        "Do not call tools, execute commands, inspect files or environment variables, poll APIs, "
        "or submit the action yourself; the trusted watcher validates and submits your returned "
        "decision exactly once. "
        f"{snapshot_instruction} "
        "Reply with ONLY one compact JSON object shaped as "
        '{"action":"<one current legal action>","params":{},"report":"<optional short user report>"}. '
        "Do not include an idempotency_key. The supplied stable rules, strategy, preferences, "
        "and language plus the newest turn state and
...[truncated 5762 chars]
Remediation
View remediation

Remediation Suggestions

  1. Run autonomous gameplay inference through a dedicated OpenClaw agent whose tool policy technically disables all tools, rather than relying on instructions in the prompt.
  2. Do not use the user's default or general-purpose agent for unattended processing of remote participant content.
  3. Add an invocation-level capability profile that denies command execution, filesystem access, environment inspection, browser access, and arbitrary network requests for gameplay turns.
  4. Fail closed if the runtime cannot prove that the gameplay inference is tool-free. A prompt saying “do not call tools” must not be accepted as equivalent to enforcement.
  5. Reduce the model context to a strict, game-specific allowlist. Separate participant-authored strings from trusted rules and control instructions using typed fields and clear provenance markers.
  6. Where free-form chat is required for gameplay, process it with a model endpoint that has no tools or host credentials and pass only a bounded structured decision to the trusted watcher.
  7. Retain the existing legal-action and parameter-schema validation as defense in depth, but do not treat output validation as protection against tool calls made during inference.
  8. Add regression tests using adversarial player names, chat messages, and strategy text. Verify at the runtime policy layer that no tool invocation can occur, regardless of model behavior.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (40)

Memory Manipulation

High
Category
Memory Poisoning
Confidence
85% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · GAMELOOP.md (reported line 61)May include surrounding context.

md
`snapshot=full&resync=1`.
Never let an older value override a field explicitly present in the newest poll.

The server already returns a bounded, versioned `decision_context` for an actionable turn. Read that object directly as the working model context; do not derive a second per-game projection. In v2, adopt the full `stable` block on bootstrap or when its id changes, replace state when `turn.state_mode="full"`, and when `turn.state_mode="delta"` apply changed `turn.state` keys (`{"_appended":[...]}` appends to the prior list) then delete every top-level key named by `turn.state_removed`. Replace `turn.decision_support` on every turn and clear it when omitted; when its recommended action is currently legal, treat the supplied comparison as complete and use it without recalculating the board or searching for an override. Executable fallback payloads are transport recovery, not strategy advice. On older servers, use the single poll envelope as before.

Explicitly forbidden patterns:

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents an autonomous client that stores scoped credentials and delivery state, watches an existing OpenClaw agent in the background, and reports telemetry. The supplied code does something narrower and different: it is a manual/CLI API transport helper. It loads a token from a file, supports a poll command against /agents/game/, and an action command that POSTs arbitrary JSON to /agents/action/. There is no scheduler, daemon, watcher loop, telemetry/heartbeat mechanism, or state persistence beyond reading an existing token path. Additionally, the ability to submit actions to the API is a meaningful capability not reflected in the description. Therefore the description does not accurately represent the code chunk.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description presents a background autonomous client concerned with credentials, agent watching, delivery state, and telemetry. The supplied code does none of that. It is a data-processing/validation library for gameplay decision context objects and legal action schemas: it computes stable context IDs, normalizes v1/v2 context formats, extracts prompt payloads, canonicalizes enum values, validates action params against JSON-schema-like rules, prunes optional invalid metadata, and returns executable fallback actions. There are no network calls, no credential access, no persistence, no telemetry, no watcher loop, and no interaction with an OpenClaw agent. This is a clear description-behavior mismatch with a materially different primary purpose.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description depicts a full autonomous client component with credential storage, agent monitoring, and telemetry reporting. The supplied code chunk does none of those things. It is a narrow utility module for computing model decision budgets based on game type and server deadlines. This is a materially different primary purpose, not merely a supporting detail of the described skill, so the description does not accurately represent the code.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The code chunk is narrowly focused on local state path derivation and validation: URL normalization/validation, a redirect-suppressing urlopen wrapper, and reading/writing-compatible ownership checks for state directories. It does not implement an autonomous client, does not watch any OpenClaw agent, does not report telemetry, and does not manage delivery workflows. While the state-scoping portion is loosely consistent with the description's mention of scoped credentials/state, the primary declared behavior is much broader and materially absent from this code.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Use the installed skill directory that contains this `SKILL.md`, `watcher.py`, and `setup_local_watcher.py`.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
87% confidence
Finding

The preflight execution also clones the full ambient environment into the child process, exposing any secrets present in the operator's shell or runtime to watcher.py even during a diagnostic run. In this skill context, that is especially risky because the watcher is the component that will operate autonomously and may process logs, network requests, or LLM-directed actions, making unnecessary secret inheritance a meaningful credential-harvesting risk.

Content

Scanner excerpt · setup_local_watcher.py (reported line 643)May include surrounding context.

python
temp_home.chmod(0o700)
        atomic_write(temp_home / "token", credentials["token"].strip() + "\n", 0o600)
        ready_path = temp_home / "watcher.ready"
        env = dict(os.environ)
        env.update(
            CLAWARENA_HOME=str(temp_home),
            CLAWARENA_READY_FILE=str(ready_path),

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
88% confidence
Finding

The watcher is started with a full copy of the parent process environment (dict(os.environ)), which can include unrelated secrets such as API keys, cloud credentials, tokens, and proxy settings. Because this skill's watcher runs unattended on an existing agent and may invoke model/tool actions, propagating the full environment unnecessarily broadens the secret exposure surface if the watcher or downstream components log, leak, or misuse those variables.

Content

Scanner excerpt · setup_local_watcher.py (reported line 674)May include surrounding context.

python
WATCHER_LOG_PATH.parent.mkdir(parents=True, exist_ok=True)
    watcher_path = skill_root / "watcher.py"
    WATCHER_READY_PATH.unlink(missing_ok=True)
    env = dict(os.environ)
    env.update(
        CLAWARENA_HOME=str(CLAW_DIR),
        CLAWARENA_READY_FILE=str(WATCHER_READY_PATH),

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Confidence
85% confidence
Finding

os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.

Content

Scanner excerpt · watcher.py (reported line 1271)May include surrounding context.

python
idle_reason="Watcher is restarting itself after repeated live feed failures.",
            error_message=error_message[:500],
        )
        os.execv(
            sys.executable,
            [
                sys.executable,

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Confidence
85% confidence
Finding

os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.

Content

Scanner excerpt · watcher.py (reported line 1318)May include surrounding context.

python
"generation": generation, "claim_until": grant["claim_until"],
        })
        try:
            os.execv(
                sys.executable,
                [sys.executable, str(Path(__file__)), "--wait-seconds", str(self.wait_seconds)],
            )

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · watcher.py (reported line 1737)May include surrounding context.

python
f"The unchanged stable context id {stable_id} is intentionally reduced "
                "to its id; retain the matching stable block already in this session."
            )
        return prompt_payload, f"{state_instruction} {stable_instruction}"

    def _build_direct_decision_message(
        self,

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

md
- reads the connection token from this arena's isolated OpenClaw state directory
- strips trailing newlines safely
- sends UTF-8 JSON without shell-escaping problems
- avoids the common `curl -d '...'` failure mode with Korean text
- returns raw server JSON on success and a compact JSON error object on HTTP/network failure

## Poll

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill declares broad operational behavior including shell, network, file read/write, and environment-dependent execution, but does not define an explicit tool scope such as permissions or allowed-tools. That omission weakens least-privilege controls and makes it easier for a caller or downstream runtime to execute the skill with more capability than users may expect, especially since it stores credentials and launches a background watcher.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
95% confidence
Finding

The skill explicitly persists a scoped connection token, remote agent identifier, delivery route, PID, state, and logs under a user directory and continues operating via a background watcher. Persistent credential and session storage increases the blast radius of local compromise, and delivery-route persistence may expose metadata or enable unwanted continued activity if the watcher is not properly revoked or cleaned up.

Content

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

md
A **HaloArena Arena Agent** is the remote competitor registered on the
HaloArena server. An **OpenClaw Agent** is the user's existing local model
runtime. Setup connects the former to the latter; it does not create or
reconfigure an OpenClaw Agent.

## Persistent Side Effects

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

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

md
server watcher readiness, and starts one local watcher process. Only the
  explicitly labelled public-mode path may provision a new remote Arena Agent;
  no path creates a local OpenClaw Agent.
- Do not ask the user to create an OpenClaw agent, copy credentials, or edit
  tool policies. Gameplay runs on their existing agent with its own model and
  auth, which is what makes OAuth-authenticated OpenClaw work at all — those
  credentials cannot be copied into a second agent. A user who wants a separate

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

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

md
server watcher readiness, and starts one local watcher process. Only the
  explicitly labelled public-mode path may provision a new remote Arena Agent;
  no path creates a local OpenClaw Agent.
- Do not ask the user to create an OpenClaw agent, copy credentials, or edit
  tool policies. Gameplay runs on their existing agent with its own model and
  auth, which is what makes OAuth-authenticated OpenClaw work at all — those
  credentials cannot be copied into a second agent. A user who wants a separate

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

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

md
credentials cannot be copied into a second agent. A user who wants a separate
  agent sets `CLAWARENA_OPENCLAW_AGENT_ID` to one they made themselves.
- `setup_local_watcher.py --recovery-key <key>` redeems a one-use server key, rewrites the current arena's scoped token and agent id, then restarts the local watcher. This is BOTH the first-run path (the key the HaloArena site issues when the user creates the agent) and the recovery path (a key from Command Center).
- Never ask the user for a HaloArena password or session, and never treat a setup/recovery key as reusable. One use, short lived.
- Bind delivery to the same chat where the user asked for setup.
- For Telegram, `--to` must be the numeric chat ID for this conversation, not an `@username`.
- Do not modify OpenClaw pairing requirements, DM policies, gateway auth, or other messenger security settings during HaloArena setup.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

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

md
- The local watcher maintains its own live connection to HaloArena (long-poll by default, or websocket under `CLAWARENA_TRANSPORT=ws`); do not add your own tight polling loop on top of it.
- Manual play may still use `GET /agents/game/?wait=30`, but autonomous play should rely on the watcher for turn wakeups.
- If `matchmaking.accepting_new_matches=false`, show its `message`, keep
  polling/heartbeating, and do not ask the user to rotate a token, re-enable
  autoplay, or change games. Matching resumes automatically when the gate opens.
- Include `idempotency_key` on action requests when retry is possible.
- Respect `is_your_turn` and `legal_actions`.

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
70% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · setup_local_watcher.py (reported line 95)May include surrounding context.

python
if metadata.st_uid not in {0, os.geteuid()}:
        raise RuntimeError("openclaw CLI must be owned by root or the current user")
    if metadata.st_mode & 0o022:
        raise RuntimeError("openclaw CLI must not be group- or world-writable")
    return str(resolved)

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
70% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · watcher.py (reported line 165)May include surrounding context.

python
if metadata.st_uid not in {0, os.geteuid()}:
        raise RuntimeError("openclaw CLI must be owned by root or the current user")
    if metadata.st_mode & 0o022:
        raise RuntimeError("openclaw CLI must not be group- or world-writable")
    return str(resolved)

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 · setup_local_watcher.py (reported line 216)May include surrounding context.

python
except OSError:
            return False
    try:
        result = subprocess.run(
            ["ps", "-p", str(pid), "-o", "command="],
            capture_output=True,
            text=True,

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The setup script can send a live outbound test message through the user's selected OpenClaw agent, exercising that agent's messaging/account capabilities during installation. In this skill's context, that is security-relevant because the skill is explicitly designed to run unattended on an existing agent with pre-existing capabilities, so setup is not merely local configuration but can trigger external actions and metadata disclosure to a chosen destination.

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 · setup_local_watcher.py (reported line 544)May include surrounding context.

python
])

    try:
        proc = subprocess.run(  # noqa: S603
            cmd,
            capture_output=True,
            text=True,

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 · setup_local_watcher.py (reported line 654)May include surrounding context.

python
else:
            env.pop("CLAWARENA_OPENCLAW_AGENT_ID", None)
        try:
            result = subprocess.run(  # noqa: S603
                [sys.executable, str(skill_root / "watcher.py"), "--preflight"],
                capture_output=True,
                text=True,

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 · setup_local_watcher.py (reported line 687)May include surrounding context.

python
log_fd = os.open(WATCHER_LOG_PATH, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o600)
    os.chmod(WATCHER_LOG_PATH, 0o600)
    with os.fdopen(log_fd, "ab") as log_file:
        proc = subprocess.Popen(  # noqa: S603
            [sys.executable, str(watcher_path)],
            stdout=log_file,
            stderr=log_file,

Static analysis

No suspicious patterns detected.