Back to skill

Security audit

Arknights Operator Gacha

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches its stated goal, but it automatically creates persistent OpenClaw agents from live wiki content without a review or cleanup step.

Install only if you are comfortable with a gacha request creating a durable OpenClaw agent and workspace, downloading wiki assets, committing generated files, and spawning the new agent. Review the generated SOUL.md and IDENTITY.md before using the agent, and restrict the generated agent's tools because its persona is derived from externally editable wiki pages.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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
  • 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)

T02 · Agent Memory Poisoning

Warning
Location
SKILL.md:90
Finding
Untrusted Web Content Can Poison a Persistent Agent Persona<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 90-116 **Vulnerability Type**: Persistent memory poisoning through untrusted external content **Risk Level**: Medium ### Vulnerable Code ```python # Fetch English lore from Fandom (multiple subpages) en_file = web_fetch(f"{result['operator']['en_detail_url']}/File") # Basic profile, stats, files en_story = web_fetch(f"{result['operator']['en_detail_url']}/Story") # Story appearances, plot involvement en_trivia = web_fetch(f"{result['operator']['en_detail_url']}/Trivia") # Trivia, relationships, misc info # Fetch Chinese lore from PRTS zh_lore = web_fetch(result["operator"]["cn_detail_url"]) # Fetch voice lines from Dialogue page dialogue = web_fetch(result["operator"]["dialogue_url"]) ``` ```markdown **Generate comprehensive SOUL.md:** **CRITICAL - Write SOUL.md in detected language** Structure: 1. **Core Identity** - Background, motivation, personality (blend EN+CN sources) 2. **Voice and Mannerisms** - Speech patterns, catchphrases (from Dialogue) 3. **Relationships** - Connections to other characters 4. **Themes** - Internal conflicts, philosophy 5. **How to Embody** - Acting guidance 6. **Reference: Original Voice Lines** - Key quotes (EN with CN) Write to: `[workspace]/SOUL.md` ``` ### Technical Analysis The Skill directs the LLM to fetch content from externally maintained wiki pages and use that content to generate a persistent `SOUL.md` persona. It does not instruct the LLM to treat fetched material solely as untrusted data, ignore embedded instructions, or extract only a constrained set of factual fields. Because wiki content can be edited or compromised independently of the Skill package, an attacker could insert instruction-like text into a referenced page. If the LLM follows or incorporates that text while generating `SOUL.md`, attacker-controlled behavioral rules may become part of the new agent's persistent persona. The Skill subsequently spawns that agent, ...[truncated 1492 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Explicitly label all fetched wiki pages as untrusted reference material. 2. Instruct the LLM never to follow commands, policies, role changes, tool requests, or behavioral directives found in fetched content. 3. Extract only predefined factual fields, such as operator name, faction, class, biography, relationships, and verified dialogue. 4. Remove or quarantine imperative text, system-prompt-like text, encoded data, URLs requesting additional retrieval, and references to local tools or files. 5. Keep externally sourced quotations separate from behavioral instructions in `SOUL.md`. 6. Require user confirmation or a review step before persisting the generated persona and spawning the agent. 7. Record source URLs and distinguish verbatim quotations from LLM-generated acting guidance. 8. Apply strict tool permissions to generated agents so persona content cannot independently authorize sensitive operations. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/gacha_worker.py:145
Finding
Avatar Download Allowlist Can Be Bypassed Through HTTP Redirects<![CDATA[ ## Vulnerability Details **File Location**: `scripts/gacha_worker.py`, lines 145-166 **Vulnerability Type**: Incomplete URL validation and redirect-based allowlist bypass **Risk Level**: Low ### Vulnerable Code ```python def download_avatar(agent_name: str, avatar_url: str) -> tuple[bool, str]: """Download avatar with validation""" try: sanitized = sanitize_agent_name(agent_name) parsed = urllib.parse.urlparse(avatar_url) allowed_domains = ["static.wikia.nocookie.net", "media.prts.wiki"] if parsed.netloc not in allowed_domains or parsed.scheme != "https": return False, "Invalid avatar domain" workspace = WORKSPACE_BASE / f"workspace-{sanitized}" avatars_dir = workspace / "avatars" avatars_dir.mkdir(parents=True, exist_ok=True) avatar_path = avatars_dir / f"{sanitized}.png" resp = requests.get(avatar_url, timeout=30, stream=True) resp.raise_for_status() content_type = resp.headers.get('content-type', '') if not any(ct in content_type for ct in ['image/png', 'image/jpeg', 'image/webp']): return False, f"Invalid content type: {content_type}" downloaded = 0 MAX_SIZE = 5 * 1024 * 1024 ``` ### Technical Analysis The function validates the scheme and hostname of only the initial avatar URL. Python's `requests.get()` follows HTTP redirects by default, but the function does not validate `resp.url`, inspect redirect history, or require every redirect target to remain on an approved HTTPS host. An approved host can therefore redirect the request to a destination outside the documented allowlist. The response's declared content type and total downloaded size are checked, which limits some file-based risks, but those controls do not enforce the network destination policy. The function also trusts the server-supplied `Content-Type` without validating the downloaded file's actual signature. ### Attack Path 1. An avata ...[truncated 1296 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic redirects with `allow_redirects=False` and reject redirects unless explicitly required. 2. If redirects are required, process them manually with a small maximum redirect count. 3. Validate the scheme and exact normalized hostname of every redirect target. 4. Validate `resp.url` again before reading or writing any response body. 5. Reject redirects to non-HTTPS destinations, unapproved ports, URLs containing credentials, and hosts outside the exact allowlist. 6. Resolve and evaluate destination addresses where appropriate to prevent access to loopback, link-local, private, or otherwise prohibited networks. 7. Verify image magic bytes with a trusted image parser instead of relying solely on the server-provided `Content-Type`. 8. Download into a temporary file and move it into the workspace only after all URL, size, and image validations succeed. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared purpose frames the skill as a content-generation utility, but the workflow also creates agents, writes persistent files, downloads remote content, checks local state, and commits to git. This mismatch can mislead operators into approving or invoking the skill without realizing it performs state-changing and externally connected actions, which is a meaningful security and trust risk.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The docstring says the worker outputs JSON with operator info and URLs, but the code also performs agent creation, file writes, avatar downloads, and git commits. This deceptive or misleading behavior undermines reviewability and trust, making it easier for side-effecting actions to escape scrutiny in an automation pipeline.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill claims to generate a random Arknights operator, but the implementation also creates local agents, writes files, downloads content, and modifies git state. That mismatch materially expands the skill's authority and creates unintended local side effects, which is dangerous in an agent environment because a seemingly harmless request can alter persistent system state.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill invokes shell execution, network fetches, file writes, and git operations but does not declare any explicit tool scope or permission boundaries. That makes the skill's effective capabilities opaque to reviewers and users, increasing the chance of unintended or over-privileged execution in environments that rely on manifest-level constraints.

Session Persistence

Medium
Category
Rogue Agent
Content
---
name: arknights-operator-gacha
description: Generate an Arknights operator agent based on gacha probabilities. Use when user wants to create a random Arknights character agent with authentic lore and personality.
---

# Arknights Operator Gacha
Confidence
86% confidence
Finding
The skill is explicitly designed to create persistent agents and workspaces, which means execution leaves durable state beyond the current interaction. Persistence is not inherently malicious, but without strong disclosure, naming controls, cleanup guidance, and consent, it can clutter environments, retain unwanted artifacts, and create long-lived entities that may later be invoked unexpectedly.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The skill description does not clearly warn that execution will create or modify local workspaces, download remote assets, and commit changes. Hidden side effects reduce informed consent and can lead to accidental persistence of unreviewed content or repository changes in sensitive environments.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The instructions require automatic language detection from the user's command and then mandate using that language for all subsequent steps. This imposes a language/locale choice without explicitly asking the user whether they want Chinese or English output.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The file explicitly marks it as critical to write SOUL.md in the detected language, reinforcing a forced locale policy rather than a user preference. This is a natural-language policy issue because the user is not offered a language choice or override.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
Calling the external openclaw CLI to create agents gives the skill system-modifying capabilities beyond what its description suggests. In a multi-skill or semi-trusted environment, hidden agent creation increases the blast radius of simple user requests and can lead to unwanted persistence or resource sprawl.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
try:
        sanitized = sanitize_agent_name(agent_name)
        workspace = WORKSPACE_BASE / f"workspace-{sanitized}"
        result = subprocess.run(
            ["openclaw", "agents", "add", sanitized,
             "--workspace", str(workspace), "--non-interactive"],
            capture_output=True, text=True, timeout=30, shell=False
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
Automatically staging and committing workspace changes is not necessary to fulfill the stated gacha/operator-generation purpose and creates persistent, hard-to-notice side effects. In an agent context, this can be abused to alter repositories, pollute history, or make unreviewed changes appear legitimate.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
try:
        sanitized = sanitize_agent_name(agent_name)
        workspace = WORKSPACE_BASE / f"workspace-{sanitized}"
        subprocess.run(["git", "add", "-A"], cwd=workspace, check=True, timeout=10)
        result = subprocess.run(
            ["git", "commit", "-m", message],
            cwd=workspace, capture_output=True, timeout=10
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
sanitized = sanitize_agent_name(agent_name)
        workspace = WORKSPACE_BASE / f"workspace-{sanitized}"
        subprocess.run(["git", "add", "-A"], cwd=workspace, check=True, timeout=10)
        result = subprocess.run(
            ["git", "commit", "-m", message],
            cwd=workspace, capture_output=True, timeout=10
        )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The script unconditionally resolves and emits a Chinese name and Chinese wiki URL for every selected operator. This is a natural-language locale choice embedded in the skill behavior, and the file does not offer the user any language preference or opt-in before doing so.

Static analysis

No suspicious patterns detected.