Back to skill

Security audit

Dota2 Coach Publish

Security checks for vulnerabilities and agentic risk

Overview

This is a Dota 2 coaching skill that mostly reads bundled game data, with optional manual update scripts that fetch public game data and rewrite local databases.

Install only if you want a Chinese-language Dota 2 coaching skill. Normal use should read bundled data locally. Run the update scripts only when you intentionally want to refresh the game databases, preferably from the skill directory and not as root; avoid the merge scripts unless you trust the /tmp dotabase JSON files they consume.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/merge_abilities.py:10
Finding
Predictable Shared Temporary File Allows Ability Database Poisoning<![CDATA[ ## Vulnerability Details **File Location**: `scripts/merge_abilities.py`, lines 10-12, 25-26, and 73-74 **Vulnerability Type**: Unsafe predictable temporary file and unvalidated data ingestion **Risk Level**: Medium ### Vulnerable Code ```python DOTABASE_FILE = '/tmp/dotabase_abilities.json' OUTPUT_FILE = '/root/.openclaw/workspace/magi/skills/dota2-coach/scripts/abilities_db.json' HEROES_FILE = '/root/.openclaw/workspace/magi/skills/dota2-coach/scripts/heroes_db.json' ``` ```python # Load dotabase abilities with open(DOTABASE_FILE) as f: dotabase = json.load(f) ``` ```python with open(OUTPUT_FILE, 'w') as f: json.dump(result, f, ensure_ascii=False, indent=2) ``` ### Technical Analysis The script consumes `/tmp/dotabase_abilities.json`, a fixed and predictable path in a globally shared temporary directory. It does not verify the source file's ownership, permissions, type, provenance, or symbolic-link status. It also performs no schema or integrity validation before using the records to construct the persistent ability database. A local user who can create or replace this path may supply attacker-controlled JSON before a more privileged user runs the script. The script then incorporates that content into `abilities_db.json`. The fixed output path under `/root/.openclaw` makes the issue more consequential when the script is run with elevated privileges. The direct write to the destination is also non-atomic. Interruption during serialization can leave the database truncated or partially written. ### Attack Path 1. A local attacker creates or replaces `/tmp/dotabase_abilities.json` with syntactically valid but malicious or misleading ability records. 2. The attacker waits for an operator or automated process to run `scripts/merge_abilities.py`. 3. The script opens the predictable temporary file without checking its owner, permissions, symlink status, or integrity. 4. The attacker-controlled records are grouped and copied into the generat ...[truncated 1017 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Eliminate the fixed shared temporary filename. Use `tempfile.NamedTemporaryFile` or `tempfile.TemporaryDirectory` with permissions restricted to the current user. 2. Resolve database destinations relative to the script rather than using a hard-coded root workspace: ```python SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) OUTPUT_FILE = os.path.join(SCRIPT_DIR, "abilities_db.json") HEROES_FILE = os.path.join(SCRIPT_DIR, "heroes_db.json") ``` 3. If an externally supplied source path is necessary, require it as an explicit command-line argument and reject symbolic links or non-regular files. 4. Verify that the source file is owned by the expected user and is not writable by unrelated users. 5. Validate the JSON against a strict schema, including allowed field types, required identifiers, maximum string lengths, and expected hero IDs. 6. Verify downloaded data provenance with a pinned digest or authenticated source before merging it. 7. Write the generated database to a same-directory temporary file, flush and `fsync` it, and replace the destination atomically with `os.replace`. 8. Run the update process with the least-privileged account that can write only to the Skill's data directory. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/merge_items.py:11
Finding
Predictable Shared Temporary File Allows Item Database Poisoning<![CDATA[ ## Vulnerability Details **File Location**: `scripts/merge_items.py`, lines 11-13, 23-24, and 60-61 **Vulnerability Type**: Unsafe predictable temporary file and unvalidated data ingestion **Risk Level**: Medium ### Vulnerable Code ```python DOTABASE_FILE = '/tmp/dotabase_items.json' LOCAL_FILE = '/root/.openclaw/workspace/magi/skills/dota2-coach/scripts/items_db.json' OUTPUT_FILE = '/root/.openclaw/workspace/magi/skills/dota2-coach/scripts/items_db.json' ``` ```python # Load dotabase items with open(DOTABASE_FILE) as f: dotabase_items = json.load(f) ``` ```python with open(OUTPUT_FILE, 'w') as f: json.dump(merged, f, ensure_ascii=False, indent=2) ``` ### Technical Analysis The script trusts `/tmp/dotabase_items.json`, a predictable path in a shared temporary directory. It does not establish that the file was created by the expected updater, belongs to the current user, is a regular file, or has safe permissions. Symbolic links and attacker-created files are not rejected. The parsed records are merged directly into the persistent item database without schema validation or integrity verification. Consequently, a local attacker may pre-position valid JSON containing falsified item names, descriptions, costs, or other fields. When an operator runs the script, this content is written to the hard-coded database path under `/root/.openclaw`. The destination is opened directly in write mode, so a crash, storage failure, or interruption can truncate the existing database. ### Attack Path 1. A local attacker creates or replaces `/tmp/dotabase_items.json` with attacker-controlled, syntactically valid JSON. 2. A victim with access to the hard-coded workspace runs `scripts/merge_items.py`, potentially with elevated privileges. 3. The script reads the shared temporary file without ownership, permission, symlink, provenance, or schema checks. 4. Attacker-controlled item records are merged with the existing local records. 5. The script overwrites `/ ...[truncated 965 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace `/tmp/dotabase_items.json` with a securely created per-run temporary file using Python's `tempfile` module. 2. Derive local paths from `__file__` instead of embedding `/root/.openclaw`: ```python SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) LOCAL_FILE = os.path.join(SCRIPT_DIR, "items_db.json") OUTPUT_FILE = LOCAL_FILE ``` 3. Reject symbolic links and verify that the source is a regular file owned by the expected account with restrictive permissions. 4. Validate all imported records against a strict schema. Enforce expected types, required IDs and names, reasonable numeric ranges, and bounded text lengths. 5. Authenticate the source data using a pinned checksum, signed release, or another integrity mechanism. 6. Perform updates atomically by writing to a temporary file in the destination directory, flushing it, and replacing the original with `os.replace`. 7. Preserve a verified backup and restore it automatically if validation or serialization fails. 8. Avoid running the merge process as root; grant a dedicated updater account write access only to the required database files. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (50)

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger list includes broad generic phrases such as "dota", "dota2", and "dota攻略", which can easily appear in normal conversation and cause unintended activation. While this skill is game-related and not handling sensitive operations, ambiguous activation can still produce unwanted responses, context hijacking, or accidental invocation during unrelated chats about the game.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger list contains very broad terms such as "dota" and "dota2", which can cause the skill to activate in many unrelated conversations that merely mention the game. Over-triggering can lead to unintended invocation, response hijacking, or interference with other skills, especially in multi-skill environments where routing is based on keyword matching.

YARA rule 'network_reconnaissance': Network reconnaissance and scanning patterns [hacktools]

Medium
Category
YARA Match
Content
on the level of Exort.",
        "impact_damage": "55/75/95/115/135/155/175/195/215"
      },
      {
        "key": "invoker_sun_strike",
        "name": "阳炎冲击",
        "mc": "175",
        "cd": "23",
        "behavior": "",
        "scepter_grants": false,
        "shard_grants": false,
        "cast_range": "0",
        "bkbpierce": "Yes",
        "desc": "Sends a catastrophic ray of fierce energy from the sun at any targeted location, incinerating all enemies standing beneath it once it reaches the earth. Deals damage based on the level of Exort, however this damage is spread evenly over all enemies hit."
      },
      {
        "key": "invoker_forge_spirit",
        "name": "熔炉精灵",
        "mc": "75",
        "cd": "27",
        "behavior": "",
        "scepter_grants": false,
        "shard_grants": false,
        "cast_range": null,
        "bkbpierce": "No",
        "desc": "Invoker forges a spirit embodying the strength of fire and fortitude of ice. Damage a
Confidence
65% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

YARA rule 'network_reconnaissance': Network reconnaissance and scanning patterns [hacktools]

Medium
Category
YARA Match
Content
on the level of Exort.",
        "impact_damage": "55/75/95/115/135/155/175/195/215"
      },
      {
        "key": "invoker_sun_strike",
        "name": "阳炎冲击",
        "mc": "175",
        "cd": "23",
        "behavior": "",
        "scepter_grants": false,
        "shard_grants": false,
        "cast_range": "0",
        "bkbpierce": "Yes",
        "desc": "Sends a catastrophic ray of fierce energy from the sun at any targeted location, incinerating all enemies standing beneath it once it reaches the earth. Deals damage based on the level of Exort, however this damage is spread evenly over all enemies hit."
      },
      {
        "key": "invoker_forge_spirit",
        "name": "熔炉精灵",
        "mc": "75",
        "cd": "27",
        "behavior": "",
        "scepter_grants": false,
        "shard_grants": false,
        "cast_range": null,
        "bkbpierce": "No",
        "desc": "Invoker forges a spirit embodying the strength of fire and fortitude of ice. Damage a
Confidence
65% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
This code's natural-language strings are entirely in Chinese, including the module description and runtime status messages, with no indication that the skill is intentionally region-specific or that users can select another language. That creates a locale-policy concern because the skill implicitly forces one language without opt-in.

External Transmission

Medium
Category
Data Exfiltration
Content
import subprocess

def fetch_ability_details():
    url = "https://api.opendota.com/api/constants/abilities"
    result = subprocess.run(['curl', '-s', url], capture_output=True, text=True)
    return json.loads(result.stdout)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import subprocess

def fetch_ability_details():
    url = "https://api.opendota.com/api/constants/abilities"
    result = subprocess.run(['curl', '-s', url], capture_output=True, text=True)
    return json.loads(result.stdout)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import subprocess

def fetch_ability_details():
    url = "https://api.opendota.com/api/constants/abilities"
    result = subprocess.run(['curl', '-s', url], capture_output=True, text=True)
    return json.loads(result.stdout)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import subprocess

def fetch_ability_details():
    url = "https://api.opendota.com/api/constants/abilities"
    result = subprocess.run(['curl', '-s', url], capture_output=True, text=True)
    return json.loads(result.stdout)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import subprocess

def fetch_ability_details():
    url = "https://api.opendota.com/api/constants/abilities"
    result = subprocess.run(['curl', '-s', url], capture_output=True, text=True)
    return json.loads(result.stdout)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import subprocess

def fetch_ability_details():
    url = "https://api.opendota.com/api/constants/abilities"
    result = subprocess.run(['curl', '-s', url], capture_output=True, text=True)
    return json.loads(result.stdout)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import subprocess

def fetch_ability_details():
    url = "https://api.opendota.com/api/constants/abilities"
    result = subprocess.run(['curl', '-s', url], capture_output=True, text=True)
    return json.loads(result.stdout)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import subprocess

def fetch_ability_details():
    url = "https://api.opendota.com/api/constants/abilities"
    result = subprocess.run(['curl', '-s', url], capture_output=True, text=True)
    return json.loads(result.stdout)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import subprocess

def fetch_ability_details():
    url = "https://api.opendota.com/api/constants/abilities"
    result = subprocess.run(['curl', '-s', url], capture_output=True, text=True)
    return json.loads(result.stdout)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import subprocess

def fetch_ability_details():
    url = "https://api.opendota.com/api/constants/abilities"
    result = subprocess.run(['curl', '-s', url], capture_output=True, text=True)
    return json.loads(result.stdout)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def fetch_ability_details():
    url = "https://api.opendota.com/api/constants/abilities"
    result = subprocess.run(['curl', '-s', url], capture_output=True, text=True)
    return json.loads(result.stdout)

def get_ability_field(ability_data, key, attrib_header_keyword=None):
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This code performs a file write that modifies the local heroes_db.json database, but there is no confirmation prompt and no explicit user-facing warning near the write operation. Although the top docstring says the script merges data into the file, the actual overwrite behavior is safety-relevant and should be more clearly disclosed at execution time or through stronger inline warning language.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The module docstring states the script is for Dota 2 hero/item queries in Chinese, and the script's usage and output strings are consistently hard-coded in Chinese. This creates a natural-language locale policy issue because the skill forces a specific language without any visible opt-in, alternative locale, or documented region-specific justification.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def fetch_item_popularity(hero_id):
    url = f'https://api.opendota.com/api/heroes/{hero_id}/itemPopularity'
    try:
        result = subprocess.run(
            ['curl', '-sL', url, '-H', 'User-Agent: Mozilla/5.0', '--max-time', '15'],
            capture_output=True, text=True, timeout=20
        )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
This JSON dataset contains user-facing `localized_name` values in Chinese, beginning at L0006 and continuing throughout the file, with no indication that language selection is optional or contextually limited. Under the stated policy, forcing a specific locale in natural-language content without opt-in is a policy violation.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The script performs a file write to a fixed path, replacing the existing abilities database in place. While the module docstring describes usage, there is no explicit warning, confirmation prompt, or inline notice at the write site that existing data will be overwritten.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This Python file contains natural-language content entirely in Chinese, including the module description and runtime messages, but it does not indicate that the skill is region-specific or provide any user opt-in for language/locale. Under the policy for natural-language violations, forcing a specific language without choice is reportable across all file types.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def curl_json(url, timeout=30):
    """用 curl 拉取 JSON"""
    result = subprocess.run(
        ['curl', '-sL', url, '-H', 'User-Agent: Mozilla/5.0', '--max-time', str(timeout)],
        capture_output=True, text=True, timeout=timeout + 5
    )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
This JSON file consists entirely of Chinese-language strings for hero talent text, indicating a hard-coded locale choice. Because no accompanying in-file note documents that this is a region-specific localization dataset or offers any language selection, it constitutes a natural-language locale policy concern under the stated rule.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
This Python file contains natural-language content entirely in Chinese in the module docstring and later prints Chinese-only status messages, which imposes a specific language on users/operators. Under the policy, forced language or locale without user opt-in or a documented justification is a natural-language policy violation.

Static analysis

No suspicious patterns detected.